feat: move Files to its own process; add window server messaging (multi-format clipboard, drag and drop), undo/redo, sidebar, breadcrumbs, thumbnails, file type icons, New menu and proper text fields

This commit is contained in:
daniel committed 2026-10-24 12:44:20 +02:00
1 parent 07191b1286
commit d3e49a925d
66 files changed
+6855 -905

No files matched your search

+156
View File
@@ -0,0 +1,156 @@
/*
* DataTransfer.cpp
* Multi-format payloads shared by the clipboard and drag and drop
* Copyright (c) 2026 Daniel Hammer
*/
#include "DataTransfer.hpp"
#include "UserMemory.hpp"
#include <Memory/Heap.hpp>
namespace DataTransfer {
using namespace montauk::abi;
static bool MimeEquals(const char* a, const char* b) {
int i = 0;
for (; a[i] && b[i]; i++) {
char ca = (a[i] >= 'A' && a[i] <= 'Z') ? a[i] + 32 : a[i];
char cb = (b[i] >= 'A' && b[i] <= 'Z') ? b[i] + 32 : b[i];
if (ca != cb) return false;
}
return a[i] == b[i];
}
// Length of the "type/subtype" part, before any ";parameters".
static int MimeBaseLen(const char* mime) {
int n = 0;
while (mime[n] && mime[n] != ';') n++;
while (n > 0 && mime[n - 1] == ' ') n--;
return n;
}
static bool MimeBaseEquals(const char* a, const char* b) {
int la = MimeBaseLen(a);
int lb = MimeBaseLen(b);
if (la != lb) return false;
for (int i = 0; i < la; i++) {
char ca = (a[i] >= 'A' && a[i] <= 'Z') ? a[i] + 32 : a[i];
char cb = (b[i] >= 'A' && b[i] <= 'Z') ? b[i] + 32 : b[i];
if (ca != cb) return false;
}
return true;
}
bool ValidMime(const char* mime) {
if (mime == nullptr) return false;
bool slash = false;
uint32_t i = 0;
for (; mime[i]; i++) {
if (i + 1 >= TRANSFER_MIME_MAX) return false;
char c = mime[i];
if (c < 0x20 || c > 0x7E) return false;
if (c == '/') slash = true;
}
return i > 0 && slash;
}
void Free(Transfer& transfer) {
for (uint32_t i = 0; i < transfer.count; i++) {
if (transfer.formats[i].data != nullptr) Memory::g_heap->Free(transfer.formats[i].data);
transfer.formats[i].data = nullptr;
transfer.formats[i].len = 0;
}
transfer.count = 0;
}
static int AddFormat(Transfer& transfer, const char* mime, const uint8_t* data, uint32_t len) {
if (transfer.count >= TRANSFER_MAX_FORMATS) return InvalidArgument;
Format& f = transfer.formats[transfer.count];
uint32_t m = 0;
for (; mime[m] && m + 1 < TRANSFER_MIME_MAX; m++) f.mime[m] = mime[m];
f.mime[m] = '\0';
// Never hand out a null pointer for an empty format; one byte keeps
// "present but empty" distinct from "absent".
f.data = (uint8_t*)Memory::g_heap->Request(len > 0 ? len : 1);
if (f.data == nullptr) return OutOfMemory;
for (uint32_t i = 0; i < len; i++) f.data[i] = data[i];
f.len = len;
transfer.count++;
return Success;
}
int BuildFromUser(const TransferItem* items, uint32_t count, Transfer& out) {
out.count = 0;
if (items == nullptr || count == 0 || count > TRANSFER_MAX_FORMATS) return InvalidArgument;
if (Memory::g_heap == nullptr) return OutOfMemory;
// Validate everything before allocating anything.
uint64_t total = 0;
for (uint32_t i = 0; i < count; i++) {
if (!UserMemory::String(items[i].mime, TRANSFER_MIME_MAX)) return BadPointer;
if (!ValidMime((const char*)items[i].mime)) return InvalidArgument;
if (items[i].len > 0 && !UserMemory::Range(items[i].data, items[i].len, false)) return BadPointer;
total += items[i].len;
for (uint32_t j = 0; j < i; j++) {
if (MimeEquals((const char*)items[i].mime, (const char*)items[j].mime))
return InvalidArgument;
}
}
if (total > TRANSFER_MAX_BYTES) return InvalidArgument;
for (uint32_t i = 0; i < count; i++) {
int rc = AddFormat(out, (const char*)items[i].mime,
(const uint8_t*)items[i].data, items[i].len);
if (rc != Success) {
Free(out);
return rc;
}
}
return Success;
}
int BuildText(const char* text, uint32_t len, Transfer& out) {
out.count = 0;
if (Memory::g_heap == nullptr) return OutOfMemory;
int rc = AddFormat(out, TRANSFER_MIME_TEXT, (const uint8_t*)text, len);
if (rc != Success) Free(out);
return rc;
}
const Format* Find(const Transfer& transfer, const char* mime) {
if (mime == nullptr) return nullptr;
for (uint32_t i = 0; i < transfer.count; i++) {
if (MimeEquals(transfer.formats[i].mime, mime)) return &transfer.formats[i];
}
bool has_params = false;
for (int i = 0; mime[i]; i++) {
if (mime[i] == ';') { has_params = true; break; }
}
if (has_params) return nullptr;
for (uint32_t i = 0; i < transfer.count; i++) {
if (MimeBaseEquals(transfer.formats[i].mime, mime)) return &transfer.formats[i];
}
return nullptr;
}
uint32_t ListFormats(const Transfer& transfer, TransferFormatInfo* out, uint32_t max) {
uint32_t n = 0;
for (; n < transfer.count && n < max; n++) {
for (uint32_t c = 0; c < TRANSFER_MIME_MAX; c++) out[n].mime[c] = transfer.formats[n].mime[c];
out[n].len = transfer.formats[n].len;
out[n]._reserved = 0;
}
return n;
}
int CopyOut(const Format& format, void* buffer, uint32_t bufferLen, uint32_t* outLen) {
if (outLen != nullptr) *outLen = format.len;
if (format.len == 0) return Success;
if (buffer == nullptr || bufferLen < format.len) return BufferTooSmall;
uint8_t* dst = (uint8_t*)buffer;
for (uint32_t i = 0; i < format.len; i++) dst[i] = format.data[i];
return Success;
}
}