// This file is part of the Essence operating system. // It is released under the terms of the MIT license -- see LICENSE.md. // Written by: nakst. // TODO Reject opening handles if the handle table has been destroyed!! #ifndef IMPLEMENTATION struct Handle { void *object; uint32_t flags; KernelObjectType type; }; struct ConstantBuffer { volatile size_t handles; size_t bytes; bool isPaged; // Data follows. }; struct Pipe { #define PIPE_READER (1) #define PIPE_WRITER (2) #define PIPE_BUFFER_SIZE (256) #define PIPE_CLOSED (0) volatile char buffer[PIPE_BUFFER_SIZE]; // TODO Make this 4KB? volatile size_t writers, readers; volatile uintptr_t writePosition, readPosition, unreadData; KEvent canWrite, canRead; KMutex mutex; size_t Access(void *buffer, size_t bytes, bool write, bool userBlockRequest); }; struct MessageQueue { bool SendMessage(void *target, EsMessage *message); // Returns false if the message queue is full. bool SendMessage(_EsMessageWithObject *message); // Returns false if the message queue is full. bool GetMessage(_EsMessageWithObject *message); #define MESSAGE_QUEUE_MAX_LENGTH (4096) Array<_EsMessageWithObject, K_FIXED> messages; uintptr_t mouseMovedMessage, windowResizedMessage, eyedropResultMessage, keyRepeatMessage; bool pinged; KMutex mutex; KEvent notEmpty; }; size_t totalHandleCount; struct HandleTableL2 { #define HANDLE_TABLE_L2_ENTRIES (256) Handle t[HANDLE_TABLE_L2_ENTRIES]; }; struct HandleTableL1 { #define HANDLE_TABLE_L1_ENTRIES (256) HandleTableL2 *t[HANDLE_TABLE_L1_ENTRIES]; uint16_t u[HANDLE_TABLE_L1_ENTRIES]; }; struct HandleTable { HandleTableL1 l1r; KMutex lock; struct Process *process; bool destroyed; uint32_t handleCount; // Be careful putting handles in the handle table! // The process will be able to immediately close it. // If this fails, the handle is closed and ES_INVALID_HANDLE is returned. EsHandle OpenHandle(void *_object, uint32_t _flags, KernelObjectType _type, EsHandle at = ES_INVALID_HANDLE); bool CloseHandle(EsHandle handle); void ModifyFlags(EsHandle handle, uint32_t newFlags); // Resolve the handle if it is valid. // The initial value of type is used as a mask of expected object types for the handle. #define RESOLVE_HANDLE_FAILED (0) #define RESOLVE_HANDLE_NO_CLOSE (1) #define RESOLVE_HANDLE_NORMAL (2) uint8_t ResolveHandle(Handle *outHandle, EsHandle inHandle, KernelObjectType typeMask); void Destroy(); }; void InitialiseObjectManager(); #endif #ifdef IMPLEMENTATION // A lock used to change the handle count on several objects. // TODO Make changing handle count lockless wherever possible? KMutex objectHandleCountChange; // TODO Use uint64_t for handle counts, or restrict OpenHandleToObject to some maximum (...but most callers don't check if OpenHandleToObject succeeds). bool OpenHandleToObject(void *object, KernelObjectType type, uint32_t flags) { bool hadNoHandles = false, failed = false; switch (type) { case KERNEL_OBJECT_EVENT: { KMutexAcquire(&objectHandleCountChange); KEvent *event = (KEvent *) object; if (!event->handles) hadNoHandles = true; else event->handles++; KMutexRelease(&objectHandleCountChange); } break; case KERNEL_OBJECT_PROCESS: { hadNoHandles = 0 == __sync_fetch_and_add(&((Process *) object)->handles, 1); } break; case KERNEL_OBJECT_THREAD: { hadNoHandles = 0 == __sync_fetch_and_add(&((Thread *) object)->handles, 1); } break; case KERNEL_OBJECT_SHMEM: { MMSharedRegion *region = (MMSharedRegion *) object; KMutexAcquire(®ion->mutex); if (!region->handles) hadNoHandles = true; else region->handles++; KMutexRelease(®ion->mutex); } break; case KERNEL_OBJECT_WINDOW: { // NOTE The handle count of Window object is modified elsewhere. Window *window = (Window *) object; hadNoHandles = 0 == __sync_fetch_and_add(&window->handles, 1); } break; case KERNEL_OBJECT_EMBEDDED_WINDOW: { EmbeddedWindow *window = (EmbeddedWindow *) object; hadNoHandles = 0 == __sync_fetch_and_add(&window->handles, 1); } break; case KERNEL_OBJECT_CONSTANT_BUFFER: { ConstantBuffer *buffer = (ConstantBuffer *) object; KMutexAcquire(&objectHandleCountChange); if (!buffer->handles) hadNoHandles = true; else buffer->handles++; KMutexRelease(&objectHandleCountChange); } break; #ifdef ENABLE_POSIX_SUBSYSTEM case KERNEL_OBJECT_POSIX_FD: { POSIXFile *file = (POSIXFile *) object; KMutexAcquire(&file->mutex); if (!file->handles) hadNoHandles = true; else file->handles++; KMutexRelease(&file->mutex); } break; #endif case KERNEL_OBJECT_NODE: { failed = ES_SUCCESS != FSNodeOpenHandle((KNode *) object, flags, FS_NODE_OPEN_HANDLE_STANDARD); } break; case KERNEL_OBJECT_PIPE: { Pipe *pipe = (Pipe *) object; KMutexAcquire(&pipe->mutex); if (((flags & PIPE_READER) && !pipe->readers) || ((flags & PIPE_WRITER) && !pipe->writers)) { hadNoHandles = true; } else { if (flags & PIPE_READER) { pipe->readers++; } if (flags & PIPE_WRITER) { pipe->writers++; } } KMutexRelease(&pipe->mutex); } break; case KERNEL_OBJECT_CONNECTION: { NetConnection *connection = (NetConnection *) object; hadNoHandles = 0 == __sync_fetch_and_add(&connection->handles, 1); } break; case KERNEL_OBJECT_DEVICE: { KDeviceOpenHandle((KDevice *) object, flags); } break; default: { KernelPanic("OpenHandleToObject - Cannot open object of type %x.\n", type); } break; } if (hadNoHandles) { KernelPanic("OpenHandleToObject - Object %x of type %x had no handles.\n", object, type); } return !failed; } void CloseHandleToObject(void *object, KernelObjectType type, uint32_t flags) { switch (type) { case KERNEL_OBJECT_PROCESS: { Process *process = (Process *) object; uintptr_t previous = __sync_fetch_and_sub(&process->handles, 1); KernelLog(LOG_VERBOSE, "Scheduler", "close process handle", "Closed handle to process %d; %d handles remain.\n", process->id, process->handles); if (previous == 0) { KernelPanic("CloseHandleToProcess - All handles to process %x have been closed.\n", process); } else if (previous == 1) { ProcessRemove(process); } } break; case KERNEL_OBJECT_THREAD: { Thread *thread = (Thread *) object; uintptr_t previous = __sync_fetch_and_sub(&thread->handles, 1); if (previous == 0) { KernelPanic("CloseHandleToObject - All handles to thread %x have been closed.\n", thread); } else if (previous == 1) { ThreadRemove(thread); } } break; case KERNEL_OBJECT_NODE: { FSNodeCloseHandle((KNode *) object, flags); } break; case KERNEL_OBJECT_EVENT: { KEvent *event = (KEvent *) object; KMutexAcquire(&objectHandleCountChange); bool destroy = event->handles == 1; event->handles--; KMutexRelease(&objectHandleCountChange); if (destroy) { EsHeapFree(event, sizeof(KEvent), K_FIXED); } } break; case KERNEL_OBJECT_CONSTANT_BUFFER: { ConstantBuffer *buffer = (ConstantBuffer *) object; KMutexAcquire(&objectHandleCountChange); bool destroy = buffer->handles == 1; buffer->handles--; KMutexRelease(&objectHandleCountChange); if (destroy) { EsHeapFree(object, sizeof(ConstantBuffer) + buffer->bytes, buffer->isPaged ? K_PAGED : K_FIXED); } } break; case KERNEL_OBJECT_SHMEM: { MMSharedRegion *region = (MMSharedRegion *) object; KMutexAcquire(®ion->mutex); bool destroy = region->handles == 1; region->handles--; KMutexRelease(®ion->mutex); if (destroy) { MMSharedDestroyRegion(region); } } break; case KERNEL_OBJECT_WINDOW: { Window *window = (Window *) object; unsigned previous = __sync_fetch_and_sub(&window->handles, 1); if (!previous) KernelPanic("CloseHandleToObject - Window %x has no handles.\n", window); if (previous == 2) { KEventSet(&windowManager.windowsToCloseEvent, true /* maybe already set */); } else if (previous == 1) { window->Destroy(); } } break; case KERNEL_OBJECT_EMBEDDED_WINDOW: { EmbeddedWindow *window = (EmbeddedWindow *) object; unsigned previous = __sync_fetch_and_sub(&window->handles, 1); if (!previous) KernelPanic("CloseHandleToObject - EmbeddedWindow %x has no handles.\n", window); if (previous == 2) { KEventSet(&windowManager.windowsToCloseEvent, true /* maybe already set */); } else if (previous == 1) { window->Destroy(); } } break; #ifdef ENABLE_POSIX_SUBSYSTEM case KERNEL_OBJECT_POSIX_FD: { POSIXFile *file = (POSIXFile *) object; KMutexAcquire(&file->mutex); file->handles--; bool destroy = !file->handles; KMutexRelease(&file->mutex); if (destroy) { if (file->type == POSIX_FILE_NORMAL || file->type == POSIX_FILE_DIRECTORY) CloseHandleToObject(file->node, KERNEL_OBJECT_NODE, file->openFlags); if (file->type == POSIX_FILE_PIPE) CloseHandleToObject(file->pipe, KERNEL_OBJECT_PIPE, file->openFlags); EsHeapFree(file->path, 0, K_FIXED); EsHeapFree(file->directoryBuffer, file->directoryBufferLength, K_PAGED); EsHeapFree(file, sizeof(POSIXFile), K_FIXED); } } break; #endif case KERNEL_OBJECT_PIPE: { Pipe *pipe = (Pipe *) object; KMutexAcquire(&pipe->mutex); if (flags & PIPE_READER) { pipe->readers--; if (!pipe->readers) { // If there are no more readers, wake up any blocking writers. KEventSet(&pipe->canWrite, true); } } if (flags & PIPE_WRITER) { pipe->writers--; if (!pipe->writers) { // If there are no more writers, wake up any blocking readers. KEventSet(&pipe->canRead, true); } } bool destroy = pipe->readers == 0 && pipe->writers == 0; KMutexRelease(&pipe->mutex); if (destroy) { EsHeapFree(pipe, sizeof(Pipe), K_PAGED); } } break; case KERNEL_OBJECT_CONNECTION: { NetConnection *connection = (NetConnection *) object; unsigned previous = __sync_fetch_and_sub(&connection->handles, 1); if (!previous) KernelPanic("CloseHandleToObject - NetConnection %x has no handles.\n", connection); if (previous == 1) NetConnectionClose(connection); } break; case KERNEL_OBJECT_DEVICE: { KDeviceCloseHandle((KDevice *) object, flags); } break; default: { KernelPanic("CloseHandleToObject - Cannot close object of type %x.\n", type); } break; } } uintptr_t HandleShare(Handle share, Process *process, uint32_t mode, EsHandle at = ES_INVALID_HANDLE) { #define HANDLE_SHARE_TYPE_MASK ((KernelObjectType) (KERNEL_OBJECT_SHMEM | KERNEL_OBJECT_CONSTANT_BUFFER | KERNEL_OBJECT_PROCESS \ | KERNEL_OBJECT_DEVICE | KERNEL_OBJECT_NODE | KERNEL_OBJECT_EVENT | KERNEL_OBJECT_PIPE)) if ((share.type & HANDLE_SHARE_TYPE_MASK) == 0) { KernelPanic("HandleShare - Invalid object type %x; allowed types are %x.\n", share.type, HANDLE_SHARE_TYPE_MASK); } uint32_t sharedFlags = share.flags; // TODO Sort out flag modes. if (share.type == KERNEL_OBJECT_SHMEM) { sharedFlags &= mode; } else if (share.type == KERNEL_OBJECT_NODE) { sharedFlags = (mode & 1) && (share.flags & (ES_FILE_WRITE_SHARED | ES_FILE_WRITE)) ? ES_FILE_READ_SHARED : share.flags; if (mode & 2) sharedFlags &= ~ES__NODE_DIRECTORY_WRITE; } else if (share.type == KERNEL_OBJECT_PIPE) { } if (!OpenHandleToObject(share.object, share.type, sharedFlags)) { return ES_ERROR_PERMISSION_NOT_GRANTED; } else { return process->handleTable.OpenHandle(share.object, sharedFlags, share.type, at); } } bool HandleTable::CloseHandle(EsHandle handle) { if (handle > HANDLE_TABLE_L1_ENTRIES * HANDLE_TABLE_L2_ENTRIES) { return false; } KMutexAcquire(&lock); HandleTableL1 *l1 = &l1r; HandleTableL2 *l2 = l1->t[handle / HANDLE_TABLE_L2_ENTRIES]; if (!l2) { KMutexRelease(&lock); return false; } Handle *_handle = l2->t + (handle % HANDLE_TABLE_L2_ENTRIES); KernelObjectType type = _handle->type; uint64_t flags = _handle->flags; void *object = _handle->object; if (!object) { KMutexRelease(&lock); return false; } EsMemoryZero(_handle, sizeof(Handle)); l1->u[handle / HANDLE_TABLE_L2_ENTRIES]--; handleCount--; KMutexRelease(&lock); __sync_fetch_and_sub(&totalHandleCount, 1); CloseHandleToObject(object, type, flags); return true; } void HandleTable::ModifyFlags(EsHandle handle, uint32_t newFlags) { KMutexAcquire(&lock); EsDefer(KMutexRelease(&lock)); if ((!handle) || handle >= HANDLE_TABLE_L1_ENTRIES * HANDLE_TABLE_L2_ENTRIES) return; HandleTableL2 *l2 = l1r.t[handle / HANDLE_TABLE_L2_ENTRIES]; if (!l2) return; Handle *_handle = l2->t + (handle % HANDLE_TABLE_L2_ENTRIES); if (!_handle->object) return; _handle->flags = newFlags; } uint8_t HandleTable::ResolveHandle(Handle *outHandle, EsHandle inHandle, KernelObjectType typeMask) { // Special handles. if (inHandle == ES_CURRENT_THREAD && (typeMask & KERNEL_OBJECT_THREAD)) { outHandle->type = KERNEL_OBJECT_THREAD; outHandle->object = GetCurrentThread(); outHandle->flags = 0; return RESOLVE_HANDLE_NO_CLOSE; } else if (inHandle == ES_CURRENT_PROCESS && (typeMask & KERNEL_OBJECT_PROCESS)) { outHandle->type = KERNEL_OBJECT_PROCESS; outHandle->object = GetCurrentThread()->process; outHandle->flags = 0; return RESOLVE_HANDLE_NO_CLOSE; } else if (inHandle == ES_INVALID_HANDLE && (typeMask & KERNEL_OBJECT_NONE)) { outHandle->type = KERNEL_OBJECT_NONE; outHandle->object = nullptr; outHandle->flags = 0; return RESOLVE_HANDLE_NO_CLOSE; } // Check that the handle is within the correct bounds. if ((!inHandle) || inHandle >= HANDLE_TABLE_L1_ENTRIES * HANDLE_TABLE_L2_ENTRIES) { return RESOLVE_HANDLE_FAILED; } KMutexAcquire(&lock); EsDefer(KMutexRelease(&lock)); HandleTableL2 *l2 = l1r.t[inHandle / HANDLE_TABLE_L2_ENTRIES]; if (!l2) return RESOLVE_HANDLE_FAILED; Handle *_handle = l2->t + (inHandle % HANDLE_TABLE_L2_ENTRIES); if ((_handle->type & typeMask) && (_handle->object)) { // Open a handle to the object so that it can't be destroyed while the system call is still using it. // The handle is closed in the KObject's destructor. if (OpenHandleToObject(_handle->object, _handle->type, _handle->flags)) { *outHandle = *_handle; return RESOLVE_HANDLE_NORMAL; } } return RESOLVE_HANDLE_FAILED; } // TODO Switch the order of flags and type, so that the default value of flags can be 0. EsHandle HandleTable::OpenHandle(void *object, uint32_t flags, KernelObjectType type, EsHandle at) { KMutexAcquire(&lock); EsDefer(KMutexRelease(&lock)); Handle handle = {}; handle.object = object; handle.flags = flags; handle.type = type; { if (destroyed) goto error; if (!handle.object) { KernelPanic("HandleTable::OpenHandle - Invalid object.\n"); } HandleTableL1 *l1 = &l1r; uintptr_t l1Index = HANDLE_TABLE_L1_ENTRIES; for (uintptr_t i = 1 /* The first set of handles are reserved. */; i < HANDLE_TABLE_L1_ENTRIES; i++) { if (at) i = at / HANDLE_TABLE_L2_ENTRIES; if (l1->u[i] != HANDLE_TABLE_L2_ENTRIES) { l1->u[i]++; handleCount++; l1Index = i; break; } if (at) goto error; } if (l1Index == HANDLE_TABLE_L1_ENTRIES) goto error; if (!l1->t[l1Index]) l1->t[l1Index] = (HandleTableL2 *) EsHeapAllocate(sizeof(HandleTableL2), true, K_FIXED); HandleTableL2 *l2 = l1->t[l1Index]; if (!l2) goto error; uintptr_t l2Index = HANDLE_TABLE_L2_ENTRIES; for (uintptr_t i = 0; i < HANDLE_TABLE_L2_ENTRIES; i++) { if (at) i = at % HANDLE_TABLE_L2_ENTRIES; if (!l2->t[i].object) { l2Index = i; break; } if (at) goto error; } if (l2Index == HANDLE_TABLE_L2_ENTRIES) KernelPanic("HandleTable::OpenHandle - Unexpected lack of free handles.\n"); Handle *_handle = l2->t + l2Index; *_handle = handle; __sync_fetch_and_add(&totalHandleCount, 1); EsHandle index = l2Index + (l1Index * HANDLE_TABLE_L2_ENTRIES); return index; } error:; CloseHandleToObject(object, type, flags); return ES_INVALID_HANDLE; } void HandleTable::Destroy() { KMutexAcquire(&lock); EsDefer(KMutexRelease(&lock)); if (destroyed) { return; } destroyed = true; HandleTableL1 *l1 = &l1r; for (uintptr_t i = 0; i < HANDLE_TABLE_L1_ENTRIES; i++) { if (!l1->u[i]) continue; for (uintptr_t k = 0; k < HANDLE_TABLE_L2_ENTRIES; k++) { Handle *handle = &l1->t[i]->t[k]; if (handle->object) CloseHandleToObject(handle->object, handle->type, handle->flags); } EsHeapFree(l1->t[i], 0, K_FIXED); } } ConstantBuffer *ConstantBufferCreate(K_USER_BUFFER const void *data, size_t bytes) { ConstantBuffer *buffer = (ConstantBuffer *) EsHeapAllocate(sizeof(ConstantBuffer) + bytes, false, K_FIXED); if (!buffer) return nullptr; EsMemoryZero(buffer, sizeof(ConstantBuffer)); buffer->handles = 1; buffer->bytes = bytes; EsMemoryCopy(buffer + 1, data, buffer->bytes); return buffer; } EsHandle ConstantBufferCreate(K_USER_BUFFER const void *data, size_t bytes, Process *process) { void *object = ConstantBufferCreate(data, bytes); return object ? process->handleTable.OpenHandle(object, 0, KERNEL_OBJECT_CONSTANT_BUFFER) : ES_INVALID_HANDLE; } size_t Pipe::Access(void *_buffer, size_t bytes, bool write, bool user) { size_t amount = 0; Thread *currentThread = GetCurrentThread(); // EsPrint("--> %z %d\n", write ? "Write" : "Read", bytes); while (bytes) { if (user) currentThread->terminatableState = THREAD_USER_BLOCK_REQUEST; if (write) { // Wait until we can write to the pipe. KEventWait(&canWrite, ES_WAIT_NO_TIMEOUT); } else { // Wait until we can read from the pipe. KEventWait(&canRead, ES_WAIT_NO_TIMEOUT); } if (user) { currentThread->terminatableState = THREAD_IN_SYSCALL; if (currentThread->terminating) goto done; } KMutexAcquire(&mutex); EsDefer(KMutexRelease(&mutex)); if (write) { // EsPrint("Write:\n"); size_t spaceAvailable = PIPE_BUFFER_SIZE - unreadData; size_t toWrite = bytes > spaceAvailable ? spaceAvailable : bytes; size_t spaceAvailableRight = PIPE_BUFFER_SIZE - writePosition; size_t toWriteRight = toWrite > spaceAvailableRight ? spaceAvailableRight : toWrite; size_t toWriteLeft = toWrite - toWriteRight; // EsPrint("\tunread: %d; wp: %d\n", unreadData, writePosition); // EsPrint("\t%d, %d, %d, %d, %d\n", spaceAvailable, spaceAvailableRight, toWrite, toWriteRight, toWriteLeft); if (_buffer) EsMemoryCopy((uint8_t *) buffer + writePosition, _buffer, toWriteRight); if (_buffer) EsMemoryCopy((uint8_t *) buffer, (uint8_t *) _buffer + toWriteRight, toWriteLeft); writePosition += toWrite; writePosition %= PIPE_BUFFER_SIZE; unreadData += toWrite; bytes -= toWrite; if (_buffer) _buffer = (uint8_t *) _buffer + toWrite; amount += toWrite; KEventSet(&canRead, true); if (!readers) { // EsPrint("\tPipe closed\n"); // Nobody is reading from the pipe, so there's no point writing to it. goto done; } else if (PIPE_BUFFER_SIZE == unreadData) { KEventReset(&canWrite); // EsPrint("\treset canWrite\n"); } } else { // EsPrint("Read:\n"); size_t dataAvailable = unreadData; size_t toRead = bytes > dataAvailable ? dataAvailable : bytes; size_t spaceAvailableRight = PIPE_BUFFER_SIZE - readPosition; size_t toReadRight = toRead > spaceAvailableRight ? spaceAvailableRight : toRead; size_t toReadLeft = toRead - toReadRight; // EsPrint("\tunread: %d; rp: %d\n", unreadData, readPosition); // EsPrint("\t%d, %d, %d, %d, %d\n", dataAvailable, spaceAvailableRight, toRead, toReadRight, toReadLeft); if (_buffer) EsMemoryCopy(_buffer, (uint8_t *) buffer + readPosition, toReadRight); if (_buffer) EsMemoryCopy((uint8_t *) _buffer + toReadRight, (uint8_t *) buffer, toReadLeft); readPosition += toRead; readPosition %= PIPE_BUFFER_SIZE; unreadData -= toRead; bytes -= toRead; if (_buffer) _buffer = (uint8_t *) _buffer + toRead; amount += toRead; KEventSet(&canWrite, true); if (!writers) { // Nobody is writing to the pipe, so there's no point reading from it. // EsPrint("\tPipe closed\n"); goto done; } else if (!unreadData) { KEventReset(&canRead); } // Don't block when reading from pipes after the first chunk of data. // TODO Change this behaviour? goto done; } } done:; // EsPrint("<-- %d (%d remaining, %z)\n", amount, bytes, write ? "Write" : "Read"); return amount; } bool MessageQueue::SendMessage(void *object, EsMessage *_message) { // TODO Remove unnecessary copy. _EsMessageWithObject message = { object, *_message }; return SendMessage(&message); } bool MessageQueue::SendMessage(_EsMessageWithObject *_message) { // TODO Don't send messages if the process has been terminated. KMutexAcquire(&mutex); EsDefer(KMutexRelease(&mutex)); if (messages.Length() == MESSAGE_QUEUE_MAX_LENGTH) { KernelLog(LOG_ERROR, "Messages", "message dropped", "Message of type %d and target %x has been dropped because queue %x was full.\n", _message->message.type, _message->object, this); return false; } #define MERGE_MESSAGES(variable, change) \ do { \ if (variable && messages[variable - 1].object == _message->object) { \ if (change) EsMemoryCopy(&messages[variable - 1], _message, sizeof(_EsMessageWithObject)); \ } else if (messages.AddPointer(_message)) { \ variable = messages.Length(); \ } else { \ return false; \ } \ } while (0) // NOTE Don't forget to update GetMessage with the merged messages! if (_message->message.type == ES_MSG_MOUSE_MOVED) { MERGE_MESSAGES(mouseMovedMessage, true); } else if (_message->message.type == ES_MSG_WINDOW_RESIZED) { MERGE_MESSAGES(windowResizedMessage, true); } else if (_message->message.type == ES_MSG_EYEDROP_REPORT) { MERGE_MESSAGES(eyedropResultMessage, true); } else if (_message->message.type == ES_MSG_KEY_DOWN && _message->message.keyboard.repeat) { MERGE_MESSAGES(keyRepeatMessage, false); } else { if (!messages.AddPointer(_message)) { return false; } if (_message->message.type == ES_MSG_PING) { pinged = true; } } KEventSet(¬Empty, true); return true; } bool MessageQueue::GetMessage(_EsMessageWithObject *_message) { KMutexAcquire(&mutex); EsDefer(KMutexRelease(&mutex)); if (!messages.Length()) { return false; } *_message = messages[0]; messages.Delete(0); if (mouseMovedMessage) mouseMovedMessage--; if (windowResizedMessage) windowResizedMessage--; if (eyedropResultMessage) eyedropResultMessage--; if (keyRepeatMessage) keyRepeatMessage--; pinged = false; if (!messages.Length()) { KEventReset(¬Empty); } return true; } #endif