/* * Clipboard.hpp * SYS_CLIPBOARD_* plain-text clipboard syscalls * Copyright (c) 2026 Daniel Hammer */ #pragma once #include #include #include #include "Syscall.hpp" namespace montauk::abi { static constexpr int ClipboardMaxSlots = 32; struct ClipboardSlot { bool inUse; char user[32]; char* text; uint32_t len; uint64_t serial; }; static ClipboardSlot g_clipboardSlots[ClipboardMaxSlots]; static kcp::Mutex g_clipboardLock; static int Sys_ClipboardClear(); static const char* ClipboardCurrentUser() { auto* proc = Sched::GetCurrentProcessPtr(); if (proc == nullptr) return nullptr; return proc->user[0] ? proc->user : "default"; } static void ClipboardCopyUser(char dst[32], const char* src) { if (src == nullptr || src[0] == '\0') src = "default"; int i = 0; for (; i < 31 && src[i]; i++) dst[i] = src[i]; dst[i] = '\0'; } static bool ClipboardUserEquals(const char* a, const char* b) { int i = 0; while (a[i] && b[i]) { if (a[i] != b[i]) return false; i++; } return a[i] == b[i]; } static uint64_t ClipboardNextSerial(uint64_t serial) { serial++; return serial == 0 ? 1 : serial; } static ClipboardSlot* ClipboardFindSlotLocked(const char* user) { for (int i = 0; i < ClipboardMaxSlots; i++) { if (g_clipboardSlots[i].inUse && ClipboardUserEquals(g_clipboardSlots[i].user, user)) return &g_clipboardSlots[i]; } return nullptr; } static ClipboardSlot* ClipboardEnsureSlotLocked(const char* user) { ClipboardSlot* freeSlot = nullptr; for (int i = 0; i < ClipboardMaxSlots; i++) { ClipboardSlot* slot = &g_clipboardSlots[i]; if (slot->inUse) { if (ClipboardUserEquals(slot->user, user)) return slot; } else if (freeSlot == nullptr) { freeSlot = slot; } } if (freeSlot == nullptr) return nullptr; freeSlot->inUse = true; ClipboardCopyUser(freeSlot->user, user); freeSlot->text = nullptr; freeSlot->len = 0; freeSlot->serial = 0; return freeSlot; } static int Sys_ClipboardSetText(const char* data, uint32_t len) { if (len == 0) return Sys_ClipboardClear(); if (data == nullptr || len > CLIPBOARD_MAX_TEXT_BYTES || Memory::g_heap == nullptr) return -1; for (uint32_t i = 0; i < len; i++) { if (data[i] == '\0') return -1; } const char* currentUser = ClipboardCurrentUser(); if (currentUser == nullptr) return -1; char user[32]; ClipboardCopyUser(user, currentUser); char* newText = (char*)Memory::g_heap->Request(len + 1); if (newText == nullptr) return -1; for (uint32_t i = 0; i < len; i++) newText[i] = data[i]; newText[len] = '\0'; char* oldText = nullptr; g_clipboardLock.Acquire(); ClipboardSlot* slot = ClipboardEnsureSlotLocked(user); if (slot == nullptr) { g_clipboardLock.Release(); Memory::g_heap->Free(newText); return -1; } oldText = slot->text; slot->text = newText; slot->len = len; slot->serial = ClipboardNextSerial(slot->serial); g_clipboardLock.Release(); if (oldText != nullptr) Memory::g_heap->Free(oldText); return 0; } static int Sys_ClipboardGetInfo(ClipboardInfo* out) { if (out == nullptr) return -1; out->textBytes = 0; out->_pad = 0; out->serial = 0; const char* currentUser = ClipboardCurrentUser(); if (currentUser == nullptr) return -1; char user[32]; ClipboardCopyUser(user, currentUser); g_clipboardLock.Acquire(); ClipboardSlot* slot = ClipboardFindSlotLocked(user); if (slot != nullptr) { out->textBytes = slot->len; out->serial = slot->serial; } g_clipboardLock.Release(); return 0; } static int Sys_ClipboardGetText(char* buffer, uint32_t bufferLen, uint32_t* outLen, uint64_t* outSerial) { if (outLen == nullptr) return -1; *outLen = 0; if (outSerial != nullptr) *outSerial = 0; const char* currentUser = ClipboardCurrentUser(); if (currentUser == nullptr) return -1; char user[32]; ClipboardCopyUser(user, currentUser); g_clipboardLock.Acquire(); ClipboardSlot* slot = ClipboardFindSlotLocked(user); if (slot == nullptr || slot->len == 0) { if (slot != nullptr && outSerial != nullptr) *outSerial = slot->serial; g_clipboardLock.Release(); return 0; } *outLen = slot->len; if (outSerial != nullptr) *outSerial = slot->serial; if (buffer == nullptr || bufferLen < slot->len) { g_clipboardLock.Release(); return -1; } for (uint32_t i = 0; i < slot->len; i++) buffer[i] = slot->text[i]; g_clipboardLock.Release(); return 0; } static int Sys_ClipboardClear() { const char* currentUser = ClipboardCurrentUser(); if (currentUser == nullptr) return -1; char user[32]; ClipboardCopyUser(user, currentUser); char* oldText = nullptr; g_clipboardLock.Acquire(); ClipboardSlot* slot = ClipboardEnsureSlotLocked(user); if (slot == nullptr) { g_clipboardLock.Release(); return -1; } oldText = slot->text; slot->text = nullptr; slot->len = 0; slot->serial = ClipboardNextSerial(slot->serial); g_clipboardLock.Release(); if (oldText != nullptr) Memory::g_heap->Free(oldText); return 0; } }