feat: implement new IPC layer
This commit is contained in:
+107
-29
@@ -1,37 +1,115 @@
|
||||
#pragma once
|
||||
|
||||
#include <Sched/Scheduler.hpp>
|
||||
#include <Ipc/Ipc.hpp>
|
||||
|
||||
namespace Montauk {
|
||||
// Find the process that owns the I/O ring buffers for a redirected process.
|
||||
// If proc owns buffers itself (spawned via spawn_redir), returns proc.
|
||||
// If proc inherited redirection (spawned via spawn from a redirected parent),
|
||||
// follows parentPid to find the buffer owner.
|
||||
|
||||
static Sched::Process* GetRedirTarget(Sched::Process* proc) {
|
||||
if (!proc || !proc->redirected) return nullptr;
|
||||
if (proc->outBuf) return proc; // owns buffers
|
||||
return Sched::GetProcessByPid(proc->parentPid);
|
||||
}
|
||||
|
||||
// SPSC ring buffer helpers. On x86 TSO, atomic-width aligned
|
||||
// loads/stores are naturally atomic. The compiler barrier ensures
|
||||
// the data write is visible before the head update.
|
||||
static void RingWrite(uint8_t* buf, volatile uint32_t& head, uint32_t /*tail*/, uint32_t size, uint8_t byte) {
|
||||
uint32_t h = head;
|
||||
buf[h] = byte;
|
||||
asm volatile("" ::: "memory"); // compiler barrier
|
||||
head = (h + 1) % size;
|
||||
}
|
||||
|
||||
static int RingRead(uint8_t* buf, volatile uint32_t& head, volatile uint32_t& tail, uint32_t size, uint8_t* out, int maxLen) {
|
||||
int count = 0;
|
||||
uint32_t t = tail;
|
||||
uint32_t h = head;
|
||||
while (t != h && count < maxLen) {
|
||||
out[count++] = buf[t];
|
||||
t = (t + 1) % size;
|
||||
if (proc->ioOutHandle >= 0 || proc->ioInHandle >= 0 || proc->ioKeyHandle >= 0) {
|
||||
return proc;
|
||||
}
|
||||
asm volatile("" ::: "memory"); // compiler barrier
|
||||
tail = t;
|
||||
return count;
|
||||
return (proc->parentPid >= 0) ? Sched::GetProcessByPid(proc->parentPid) : nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
static bool ResolveProcessHandle(Sched::Process* proc, int handle, Ipc::HandleType expectedType,
|
||||
Ipc::Object*& outObject, uint32_t* outRights = nullptr) {
|
||||
if (proc == nullptr || handle < 0) return false;
|
||||
|
||||
int slot = Ipc::SlotForPid(proc->pid);
|
||||
if (slot < 0) return false;
|
||||
|
||||
Ipc::HandleType type = Ipc::HandleType::None;
|
||||
Ipc::Object* object = nullptr;
|
||||
uint32_t rights = 0;
|
||||
if (!Ipc::SnapshotHandleForSlot(slot, handle, type, object, rights)) return false;
|
||||
if (type != expectedType || object == nullptr) return false;
|
||||
|
||||
outObject = object;
|
||||
if (outRights != nullptr) *outRights = rights;
|
||||
return true;
|
||||
}
|
||||
|
||||
static Ipc::Stream* GetRedirOutStream(Sched::Process* proc) {
|
||||
proc = GetRedirTarget(proc);
|
||||
if (proc == nullptr) return nullptr;
|
||||
|
||||
Ipc::Object* object = nullptr;
|
||||
if (!ResolveProcessHandle(proc, proc->ioOutHandle, Ipc::HandleType::Stream, object)) return nullptr;
|
||||
return (Ipc::Stream*)object;
|
||||
}
|
||||
|
||||
static Ipc::Stream* GetRedirInStream(Sched::Process* proc) {
|
||||
proc = GetRedirTarget(proc);
|
||||
if (proc == nullptr) return nullptr;
|
||||
|
||||
Ipc::Object* object = nullptr;
|
||||
if (!ResolveProcessHandle(proc, proc->ioInHandle, Ipc::HandleType::Stream, object)) return nullptr;
|
||||
return (Ipc::Stream*)object;
|
||||
}
|
||||
|
||||
static Ipc::Mailbox* GetRedirKeyMailbox(Sched::Process* proc) {
|
||||
proc = GetRedirTarget(proc);
|
||||
if (proc == nullptr) return nullptr;
|
||||
|
||||
Ipc::Object* object = nullptr;
|
||||
if (!ResolveProcessHandle(proc, proc->ioKeyHandle, Ipc::HandleType::Mailbox, object)) return nullptr;
|
||||
return (Ipc::Mailbox*)object;
|
||||
}
|
||||
|
||||
static int DuplicateHandleBetweenSlots(int srcSlot, int handle, int dstSlot) {
|
||||
if (srcSlot < 0 || dstSlot < 0 || handle < 0) return -1;
|
||||
|
||||
Ipc::HandleType type = Ipc::HandleType::None;
|
||||
Ipc::Object* object = nullptr;
|
||||
uint32_t rights = 0;
|
||||
if (!Ipc::SnapshotHandleForSlot(srcSlot, handle, type, object, rights)) return -1;
|
||||
if ((rights & Ipc::RightDup) == 0) return -1;
|
||||
return Ipc::InstallHandleForSlot(dstSlot, object, type, rights);
|
||||
}
|
||||
|
||||
static bool ConfigureRedirWaitsetForSlot(int slot, Sched::Process* proc) {
|
||||
if (slot < 0 || proc == nullptr) return false;
|
||||
|
||||
int waitset = Ipc::CreateWaitsetHandleForSlot(slot);
|
||||
if (waitset < 0) return false;
|
||||
|
||||
if (proc->ioInHandle >= 0) {
|
||||
if (Ipc::WaitsetAddHandleForSlot(slot, waitset, proc->ioInHandle,
|
||||
Ipc::SignalReadable | Ipc::SignalPeerClosed) < 0) {
|
||||
Ipc::CloseHandleForSlot(slot, waitset);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (proc->ioKeyHandle >= 0) {
|
||||
if (Ipc::WaitsetAddHandleForSlot(slot, waitset, proc->ioKeyHandle,
|
||||
Ipc::SignalReadable | Ipc::SignalPeerClosed) < 0) {
|
||||
Ipc::CloseHandleForSlot(slot, waitset);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
proc->ioWaitsetHandle = waitset;
|
||||
return true;
|
||||
}
|
||||
|
||||
static int WriteAllToStream(Ipc::Stream* stream, const uint8_t* data, int len) {
|
||||
if (stream == nullptr || data == nullptr || len <= 0) return -1;
|
||||
|
||||
int total = 0;
|
||||
while (total < len) {
|
||||
int written = Ipc::StreamWrite(stream, data + total, len - total, false);
|
||||
if (written < 0) {
|
||||
return (total > 0) ? total : -1;
|
||||
}
|
||||
if (written == 0) {
|
||||
Sched::BlockOnObject(stream, 0);
|
||||
continue;
|
||||
}
|
||||
total += written;
|
||||
}
|
||||
return total;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,25 +12,26 @@
|
||||
#include <Memory/HHDM.hpp>
|
||||
#include <Memory/Paging.hpp>
|
||||
#include <Libraries/Memory.hpp>
|
||||
#include <Ipc/Ipc.hpp>
|
||||
#include "Path.hpp"
|
||||
|
||||
namespace Montauk {
|
||||
static int Sys_Open(const char* path) {
|
||||
char resolved[256];
|
||||
if (!ResolveProcessPath(path, resolved, sizeof(resolved))) return -1;
|
||||
return Fs::Vfs::VfsOpen(resolved);
|
||||
return Ipc::OpenFileHandle(resolved);
|
||||
}
|
||||
|
||||
static int Sys_Read(int handle, uint8_t* buffer, uint64_t offset, uint64_t size) {
|
||||
return Fs::Vfs::VfsRead(handle, buffer, offset, size);
|
||||
return Ipc::FileReadHandle(handle, buffer, offset, size);
|
||||
}
|
||||
|
||||
static uint64_t Sys_GetSize(int handle) {
|
||||
return Fs::Vfs::VfsGetSize(handle);
|
||||
return Ipc::FileGetSizeHandle(handle);
|
||||
}
|
||||
|
||||
static void Sys_Close(int handle) {
|
||||
Fs::Vfs::VfsClose(handle);
|
||||
Ipc::CloseHandle(handle);
|
||||
}
|
||||
|
||||
static int Sys_ReadDir(const char* path, const char** outNames, int maxEntries) {
|
||||
@@ -89,13 +90,13 @@ namespace Montauk {
|
||||
}
|
||||
|
||||
static int Sys_FWrite(int handle, const uint8_t* data, uint64_t offset, uint64_t size) {
|
||||
return Fs::Vfs::VfsWrite(handle, data, offset, size);
|
||||
return Ipc::FileWriteHandle(handle, data, offset, size);
|
||||
}
|
||||
|
||||
static int Sys_FCreate(const char* path) {
|
||||
char resolved[256];
|
||||
if (!ResolveProcessPath(path, resolved, sizeof(resolved))) return -1;
|
||||
return Fs::Vfs::VfsCreate(resolved);
|
||||
return Ipc::CreateFileHandle(resolved);
|
||||
}
|
||||
|
||||
static int Sys_FDelete(const char* path) {
|
||||
|
||||
@@ -66,6 +66,9 @@ namespace Montauk {
|
||||
// If the process is redirected to a GUI terminal, return those dimensions
|
||||
auto* proc = Sched::GetCurrentProcessPtr();
|
||||
if (proc && proc->redirected) {
|
||||
if (proc->termCols > 0 && proc->termRows > 0) {
|
||||
return ((uint64_t)proc->termRows << 32) | ((uint64_t)proc->termCols & 0xFFFFFFFF);
|
||||
}
|
||||
auto* target = GetRedirTarget(proc);
|
||||
if (target && target->termCols > 0 && target->termRows > 0) {
|
||||
return ((uint64_t)target->termRows << 32) | ((uint64_t)target->termCols & 0xFFFFFFFF);
|
||||
|
||||
+63
-26
@@ -7,8 +7,6 @@
|
||||
|
||||
#pragma once
|
||||
#include <Sched/Scheduler.hpp>
|
||||
#include <Memory/PageFrameAllocator.hpp>
|
||||
|
||||
#include "Syscall.hpp"
|
||||
#include "Common.hpp"
|
||||
#include "Path.hpp"
|
||||
@@ -19,53 +17,92 @@ namespace Montauk {
|
||||
char resolved[256];
|
||||
if (!ResolveProcessPath(path, resolved, sizeof(resolved))) return -1;
|
||||
|
||||
int parentSlot = Ipc::CurrentSlot();
|
||||
int childPid = Sched::Spawn(resolved, args);
|
||||
if (childPid < 0) return -1;
|
||||
|
||||
auto* child = Sched::GetProcessByPid(childPid);
|
||||
if (child == nullptr) return -1;
|
||||
int childSlot = Ipc::SlotForPid(childPid);
|
||||
if (child == nullptr || childSlot < 0 || parentSlot < 0) return -1;
|
||||
|
||||
// Allocate ring buffers
|
||||
void* outPage = Memory::g_pfa->AllocateZeroed();
|
||||
void* inPage = Memory::g_pfa->AllocateZeroed();
|
||||
if (!outPage || !inPage) return -1;
|
||||
Ipc::Stream* outStream = Ipc::CreateStream();
|
||||
Ipc::Stream* inStream = Ipc::CreateStream();
|
||||
Ipc::Mailbox* keyMailbox = Ipc::CreateMailbox();
|
||||
if (outStream == nullptr || inStream == nullptr || keyMailbox == nullptr) {
|
||||
if (outStream != nullptr) Ipc::ReleaseStream(outStream, false, false);
|
||||
if (inStream != nullptr) Ipc::ReleaseStream(inStream, false, false);
|
||||
if (keyMailbox != nullptr) Ipc::ReleaseMailbox(keyMailbox, false, false);
|
||||
Sched::KillProcess(childPid);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int parentOutHandle = Ipc::InstallHandleForSlot(
|
||||
parentSlot, (Ipc::Object*)outStream, Ipc::HandleType::Stream, Ipc::RightRead);
|
||||
int parentInHandle = Ipc::InstallHandleForSlot(
|
||||
parentSlot, (Ipc::Object*)inStream, Ipc::HandleType::Stream, Ipc::RightWrite);
|
||||
int parentKeyHandle = Ipc::InstallHandleForSlot(
|
||||
parentSlot, (Ipc::Object*)keyMailbox, Ipc::HandleType::Mailbox, Ipc::RightSend);
|
||||
|
||||
child->ioOutHandle = Ipc::InstallHandleForSlot(
|
||||
childSlot, (Ipc::Object*)outStream, Ipc::HandleType::Stream,
|
||||
Ipc::RightWrite | Ipc::RightWait | Ipc::RightDup);
|
||||
child->ioInHandle = Ipc::InstallHandleForSlot(
|
||||
childSlot, (Ipc::Object*)inStream, Ipc::HandleType::Stream,
|
||||
Ipc::RightRead | Ipc::RightWait | Ipc::RightDup);
|
||||
child->ioKeyHandle = Ipc::InstallHandleForSlot(
|
||||
childSlot, (Ipc::Object*)keyMailbox, Ipc::HandleType::Mailbox,
|
||||
Ipc::RightRecv | Ipc::RightWait | Ipc::RightDup);
|
||||
|
||||
if (parentOutHandle < 0 || parentInHandle < 0 || parentKeyHandle < 0 ||
|
||||
child->ioOutHandle < 0 || child->ioInHandle < 0 || child->ioKeyHandle < 0 ||
|
||||
!ConfigureRedirWaitsetForSlot(childSlot, child)) {
|
||||
if (parentOutHandle >= 0) Ipc::CloseHandleForSlot(parentSlot, parentOutHandle);
|
||||
if (parentInHandle >= 0) Ipc::CloseHandleForSlot(parentSlot, parentInHandle);
|
||||
if (parentKeyHandle >= 0) Ipc::CloseHandleForSlot(parentSlot, parentKeyHandle);
|
||||
if (child->ioOutHandle < 0) Ipc::ReleaseStream(outStream, false, false);
|
||||
if (child->ioInHandle < 0) Ipc::ReleaseStream(inStream, false, false);
|
||||
if (child->ioKeyHandle < 0) Ipc::ReleaseMailbox(keyMailbox, false, false);
|
||||
Sched::KillProcess(childPid);
|
||||
return -1;
|
||||
}
|
||||
|
||||
child->outBuf = (uint8_t*)outPage;
|
||||
child->inBuf = (uint8_t*)inPage;
|
||||
child->outHead = 0;
|
||||
child->outTail = 0;
|
||||
child->inHead = 0;
|
||||
child->inTail = 0;
|
||||
child->keyHead = 0;
|
||||
child->keyTail = 0;
|
||||
child->redirected = true;
|
||||
child->parentPid = Sched::GetCurrentPid();
|
||||
child->termCols = 0;
|
||||
child->termRows = 0;
|
||||
|
||||
return childPid;
|
||||
}
|
||||
|
||||
static int Sys_ChildIoRead(int childPid, char* buf, int maxLen) {
|
||||
auto* child = Sched::GetProcessByPid(childPid);
|
||||
if (child == nullptr || !child->redirected || !child->outBuf) return -1;
|
||||
return RingRead(child->outBuf, child->outHead, child->outTail, Sched::Process::IoBufSize, (uint8_t*)buf, maxLen);
|
||||
Ipc::Stream* stream = GetRedirOutStream(child);
|
||||
if (child == nullptr || !child->redirected || stream == nullptr) return -1;
|
||||
return Ipc::StreamRead(stream, (uint8_t*)buf, maxLen, true);
|
||||
}
|
||||
|
||||
static int Sys_ChildIoWrite(int childPid, const char* data, int len) {
|
||||
auto* child = Sched::GetProcessByPid(childPid);
|
||||
if (child == nullptr || !child->redirected || !child->inBuf) return -1;
|
||||
for (int i = 0; i < len; i++) {
|
||||
RingWrite(child->inBuf, child->inHead, child->inTail, Sched::Process::IoBufSize, (uint8_t)data[i]);
|
||||
}
|
||||
return len;
|
||||
Ipc::Stream* stream = GetRedirInStream(child);
|
||||
if (child == nullptr || !child->redirected || stream == nullptr) return -1;
|
||||
return WriteAllToStream(stream, (const uint8_t*)data, len);
|
||||
}
|
||||
|
||||
static int Sys_ChildIoWriteKey(int childPid, const KeyEvent* key) {
|
||||
if (key == nullptr) return -1;
|
||||
auto* child = Sched::GetProcessByPid(childPid);
|
||||
if (child == nullptr || !child->redirected) return -1;
|
||||
child->keyBuf[child->keyHead] = *key;
|
||||
child->keyHead = (child->keyHead + 1) % 64;
|
||||
return 0;
|
||||
Ipc::Mailbox* mailbox = GetRedirKeyMailbox(child);
|
||||
if (child == nullptr || !child->redirected || mailbox == nullptr) return -1;
|
||||
|
||||
for (;;) {
|
||||
int rc = Ipc::MailboxSend(mailbox, 0, key, sizeof(KeyEvent));
|
||||
if (rc < 0) return -1;
|
||||
if (rc == 0) {
|
||||
Sched::BlockOnObject(mailbox, 0);
|
||||
continue;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int Sys_ChildIoSetTermsz(int childPid, int cols, int rows) {
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* IpcSyscall.hpp
|
||||
* Generic IPC handle, stream, mailbox, waitset, process, and surface syscalls
|
||||
* Copyright (c) 2026 Daniel Hammer
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <Ipc/Ipc.hpp>
|
||||
#include <Sched/Scheduler.hpp>
|
||||
#include "Syscall.hpp"
|
||||
|
||||
namespace Montauk {
|
||||
|
||||
static int Sys_DupHandle(int handle) {
|
||||
return Ipc::DupHandle(handle);
|
||||
}
|
||||
|
||||
static uint32_t Sys_WaitHandle(int handle, uint32_t wantedSignals, uint64_t timeoutMs) {
|
||||
return Ipc::WaitOnHandle(handle, wantedSignals, timeoutMs);
|
||||
}
|
||||
|
||||
static int Sys_StreamCreate(int* outReadHandle, int* outWriteHandle, uint32_t capacity) {
|
||||
if (outReadHandle == nullptr || outWriteHandle == nullptr) return -1;
|
||||
|
||||
int readHandle = -1;
|
||||
int writeHandle = -1;
|
||||
if (Ipc::CreateStreamHandlePair(capacity, readHandle, writeHandle) < 0) return -1;
|
||||
|
||||
*outReadHandle = readHandle;
|
||||
*outWriteHandle = writeHandle;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int Sys_StreamRead(int handle, uint8_t* buffer, int maxLen) {
|
||||
return Ipc::StreamReadHandle(handle, buffer, maxLen);
|
||||
}
|
||||
|
||||
static int Sys_StreamWrite(int handle, const uint8_t* data, int len) {
|
||||
return Ipc::StreamWriteHandle(handle, data, len);
|
||||
}
|
||||
|
||||
static int Sys_MailboxCreate(int* outSendHandle, int* outRecvHandle) {
|
||||
if (outSendHandle == nullptr || outRecvHandle == nullptr) return -1;
|
||||
|
||||
int sendHandle = -1;
|
||||
int recvHandle = -1;
|
||||
if (Ipc::CreateMailboxHandlePair(sendHandle, recvHandle) < 0) return -1;
|
||||
|
||||
*outSendHandle = sendHandle;
|
||||
*outRecvHandle = recvHandle;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int Sys_MailboxSend(int handle, uint32_t msgType, const void* data, uint16_t len, int attachHandle) {
|
||||
return Ipc::MailboxSendHandle(handle, msgType, data, len, attachHandle);
|
||||
}
|
||||
|
||||
static int Sys_MailboxRecv(int handle, uint32_t* outMsgType, void* data,
|
||||
uint16_t* inOutLen, int* outAttachHandle) {
|
||||
return Ipc::MailboxRecvHandle(handle, outMsgType, data, inOutLen, outAttachHandle);
|
||||
}
|
||||
|
||||
static int Sys_WaitsetCreate() {
|
||||
return Ipc::CreateWaitsetHandleForCurrent();
|
||||
}
|
||||
|
||||
static int Sys_WaitsetAdd(int waitsetHandle, int targetHandle, uint32_t signals) {
|
||||
return Ipc::WaitsetAddHandle(waitsetHandle, targetHandle, signals);
|
||||
}
|
||||
|
||||
static int Sys_WaitsetRemove(int waitsetHandle, int index) {
|
||||
return Ipc::WaitsetRemoveIndex(waitsetHandle, index);
|
||||
}
|
||||
|
||||
static int Sys_WaitsetWait(int waitsetHandle, IpcWaitResult* outReady, uint64_t timeoutMs) {
|
||||
Ipc::WaitsetReady ready = {-1, 0};
|
||||
int result = Ipc::WaitsetWaitHandle(waitsetHandle, outReady ? &ready : nullptr, timeoutMs);
|
||||
if (result > 0 && outReady != nullptr) {
|
||||
outReady->index = ready.index;
|
||||
outReady->signals = ready.signals;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static int Sys_ProcOpen(int pid) {
|
||||
return Ipc::OpenProcessHandle(pid);
|
||||
}
|
||||
|
||||
static int Sys_SurfaceCreate(uint64_t byteSize) {
|
||||
return Ipc::CreateSurfaceHandle(byteSize);
|
||||
}
|
||||
|
||||
static uint64_t Sys_SurfaceMap(int handle) {
|
||||
return Ipc::MapSurfaceHandle(handle);
|
||||
}
|
||||
|
||||
static int Sys_SurfaceResize(int handle, uint64_t newSize) {
|
||||
return Ipc::ResizeSurfaceHandle(handle, newSize);
|
||||
}
|
||||
|
||||
}
|
||||
+43
-23
@@ -7,6 +7,7 @@
|
||||
#pragma once
|
||||
#include <Sched/Scheduler.hpp>
|
||||
#include <Drivers/PS2/Keyboard.hpp>
|
||||
#include <Libraries/Memory.hpp>
|
||||
|
||||
#include "Common.hpp"
|
||||
|
||||
@@ -14,8 +15,8 @@ namespace Montauk {
|
||||
static bool Sys_IsKeyAvailable() {
|
||||
auto* proc = Sched::GetCurrentProcessPtr();
|
||||
if (proc && proc->redirected) {
|
||||
auto* target = GetRedirTarget(proc);
|
||||
if (target) return target->keyHead != target->keyTail;
|
||||
Ipc::Mailbox* mailbox = GetRedirKeyMailbox(proc);
|
||||
if (mailbox != nullptr) return Ipc::MailboxHasMessage(mailbox);
|
||||
}
|
||||
return Drivers::PS2::Keyboard::IsKeyAvailable();
|
||||
}
|
||||
@@ -24,15 +25,18 @@ namespace Montauk {
|
||||
if (outEvent == nullptr) return;
|
||||
auto* proc = Sched::GetCurrentProcessPtr();
|
||||
if (proc && proc->redirected) {
|
||||
auto* target = GetRedirTarget(proc);
|
||||
if (target) {
|
||||
// Wait for key in target's keyBuf ring
|
||||
while (target->keyHead == target->keyTail) {
|
||||
Sched::Schedule();
|
||||
Ipc::Mailbox* mailbox = GetRedirKeyMailbox(proc);
|
||||
if (mailbox != nullptr) {
|
||||
for (;;) {
|
||||
uint16_t len = sizeof(KeyEvent);
|
||||
int rc = Ipc::MailboxRecv(mailbox, nullptr, outEvent, &len, true);
|
||||
if (rc > 0) return;
|
||||
if (rc < 0) {
|
||||
memset(outEvent, 0, sizeof(KeyEvent));
|
||||
return;
|
||||
}
|
||||
Sched::BlockOnObject(mailbox, 0);
|
||||
}
|
||||
*outEvent = target->keyBuf[target->keyTail];
|
||||
target->keyTail = (target->keyTail + 1) % 64;
|
||||
return;
|
||||
}
|
||||
}
|
||||
auto k = Drivers::PS2::Keyboard::GetKey();
|
||||
@@ -47,24 +51,40 @@ namespace Montauk {
|
||||
static char Sys_GetChar() {
|
||||
auto* proc = Sched::GetCurrentProcessPtr();
|
||||
if (proc && proc->redirected) {
|
||||
auto* target = GetRedirTarget(proc);
|
||||
if (target) {
|
||||
while (true) {
|
||||
if (target->inBuf && target->inTail != target->inHead) {
|
||||
uint8_t c = target->inBuf[target->inTail];
|
||||
target->inTail = (target->inTail + 1) % Sched::Process::IoBufSize;
|
||||
return (char)c;
|
||||
Ipc::Stream* input = GetRedirInStream(proc);
|
||||
Ipc::Mailbox* mailbox = GetRedirKeyMailbox(proc);
|
||||
if (input != nullptr || mailbox != nullptr) {
|
||||
for (;;) {
|
||||
if (input != nullptr) {
|
||||
uint8_t c = 0;
|
||||
int rc = Ipc::StreamRead(input, &c, 1, true);
|
||||
if (rc > 0) return (char)c;
|
||||
if (rc < 0 && mailbox == nullptr) return 0;
|
||||
}
|
||||
|
||||
if (target->keyTail != target->keyHead) {
|
||||
auto ev = target->keyBuf[target->keyTail];
|
||||
target->keyTail = (target->keyTail + 1) % 64;
|
||||
if (ev.pressed && ev.ascii != 0) {
|
||||
return ev.ascii;
|
||||
if (mailbox != nullptr) {
|
||||
KeyEvent ev{};
|
||||
uint16_t len = sizeof(ev);
|
||||
int rc = Ipc::MailboxRecv(mailbox, nullptr, &ev, &len, true);
|
||||
if (rc > 0) {
|
||||
if (ev.pressed && ev.ascii != 0) return ev.ascii;
|
||||
continue;
|
||||
}
|
||||
if (rc < 0 && input == nullptr) return 0;
|
||||
}
|
||||
|
||||
Sched::Schedule();
|
||||
if (proc->ioWaitsetHandle >= 0) {
|
||||
Ipc::WaitsetReady ready{};
|
||||
if (Ipc::WaitsetWaitHandle(proc->ioWaitsetHandle, &ready, ~0ULL) < 0) {
|
||||
return 0;
|
||||
}
|
||||
} else if (input != nullptr) {
|
||||
Sched::BlockOnObject(input, 0);
|
||||
} else if (mailbox != nullptr) {
|
||||
Sched::BlockOnObject(mailbox, 0);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+24
-13
@@ -14,6 +14,7 @@
|
||||
#include <Fs/Vfs.hpp>
|
||||
|
||||
#include "Syscall.hpp"
|
||||
#include "Common.hpp"
|
||||
#include "WinServer.hpp"
|
||||
#include "Path.hpp"
|
||||
|
||||
@@ -44,22 +45,32 @@ namespace Montauk {
|
||||
if (!ResolveProcessPath(path, resolved, sizeof(resolved))) return -1;
|
||||
|
||||
auto* parent = Sched::GetCurrentProcessPtr();
|
||||
int parentSlot = Ipc::CurrentSlot();
|
||||
int childPid = Sched::Spawn(resolved, args);
|
||||
if (childPid < 0) return childPid;
|
||||
|
||||
// Inherit I/O redirection: if the parent is redirected, the child
|
||||
// is marked redirected too. It stores a parentPid pointing to the
|
||||
// process that owns the actual ring buffers (the one spawned via
|
||||
// spawn_redir). The child does NOT get its own buffers — Sys_Print
|
||||
// et al. look up the buffer owner at write time.
|
||||
if (parent && parent->redirected) {
|
||||
auto* child = Sched::GetProcessByPid(childPid);
|
||||
if (child) {
|
||||
child->redirected = true;
|
||||
// Point to the buffer owner: if parent owns buffers, target parent;
|
||||
// if parent itself inherited, follow the chain.
|
||||
child->parentPid = parent->outBuf ? parent->pid : parent->parentPid;
|
||||
int childSlot = Ipc::SlotForPid(childPid);
|
||||
if (child == nullptr || childSlot < 0 || parentSlot < 0) {
|
||||
Sched::KillProcess(childPid);
|
||||
return -1;
|
||||
}
|
||||
|
||||
child->ioOutHandle = DuplicateHandleBetweenSlots(parentSlot, parent->ioOutHandle, childSlot);
|
||||
child->ioInHandle = DuplicateHandleBetweenSlots(parentSlot, parent->ioInHandle, childSlot);
|
||||
child->ioKeyHandle = DuplicateHandleBetweenSlots(parentSlot, parent->ioKeyHandle, childSlot);
|
||||
|
||||
if (child->ioOutHandle < 0 || child->ioInHandle < 0 || child->ioKeyHandle < 0 ||
|
||||
!ConfigureRedirWaitsetForSlot(childSlot, child)) {
|
||||
Sched::KillProcess(childPid);
|
||||
return -1;
|
||||
}
|
||||
|
||||
child->redirected = true;
|
||||
child->parentPid = parent->pid;
|
||||
child->termCols = parent->termCols;
|
||||
child->termRows = parent->termRows;
|
||||
}
|
||||
|
||||
return childPid;
|
||||
@@ -157,9 +168,9 @@ namespace Montauk {
|
||||
const char* entries[1];
|
||||
if (Fs::Vfs::VfsReadDir(resolved, entries, 1) < 0) return -1;
|
||||
} else {
|
||||
int handle = Fs::Vfs::VfsOpen(resolved);
|
||||
if (handle < 0) return -1;
|
||||
Fs::Vfs::VfsClose(handle);
|
||||
Fs::Vfs::BackendFile file = {-1, -1};
|
||||
if (Fs::Vfs::OpenBackendFile(resolved, file) < 0) return -1;
|
||||
Fs::Vfs::CloseBackendFile(file);
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include "Window.hpp" // SYS_WINCREATE, SYS_WINDESTROY, SYS_WINPRESENT, SYS_WINPOLL, SYS_WINENUM, SYS_WINMAP, SYS_WINSENDEVENT, SYS_WINRESIZE, SYS_WINSETSCALE, SYS_WINGETSCALE
|
||||
#include "Audio.hpp" // SYS_AUDIOOPEN, SYS_AUDIOCLOSE, SYS_AUDIOWRITE, SYS_AUDIOCTL
|
||||
#include "BluetoothSyscall.hpp" // SYS_BTSCAN, SYS_BTCONNECT, SYS_BTDISCONNECT, SYS_BTLIST, SYS_BTINFO
|
||||
#include "IpcSyscall.hpp" // SYS_DUPHANDLE, SYS_WAIT_HANDLE, SYS_STREAM_CREATE, SYS_STREAM_READ, SYS_STREAM_WRITE, SYS_MAILBOX_CREATE, SYS_MAILBOX_SEND, SYS_MAILBOX_RECV, SYS_WAITSET_CREATE, SYS_WAITSET_ADD, SYS_WAITSET_REMOVE, SYS_WAITSET_WAIT, SYS_PROC_OPEN, SYS_SURFACE_CREATE, SYS_SURFACE_MAP, SYS_SURFACE_RESIZE
|
||||
|
||||
// Assembly entry point
|
||||
extern "C" void SyscallEntry();
|
||||
@@ -256,6 +257,8 @@ namespace Montauk {
|
||||
return (int64_t)Sys_WinEnum((WinInfo*)frame->arg1, (int)frame->arg2);
|
||||
case SYS_WINMAP:
|
||||
return (int64_t)Sys_WinMap((int)frame->arg1);
|
||||
case SYS_WINUNMAP:
|
||||
return (int64_t)Sys_WinUnmap((int)frame->arg1);
|
||||
case SYS_WINSENDEVENT:
|
||||
if (!ValidUserPtr(frame->arg2)) return -1;
|
||||
return (int64_t)Sys_WinSendEvent((int)frame->arg1, (const WinEvent*)frame->arg2);
|
||||
@@ -353,6 +356,56 @@ namespace Montauk {
|
||||
case SYS_CHDIR:
|
||||
if (!ValidUserPtr(frame->arg1)) return -1;
|
||||
return Sys_Chdir((const char*)frame->arg1);
|
||||
case SYS_DUPHANDLE:
|
||||
return Sys_DupHandle((int)frame->arg1);
|
||||
case SYS_WAIT_HANDLE:
|
||||
return (int64_t)Sys_WaitHandle((int)frame->arg1, (uint32_t)frame->arg2, frame->arg3);
|
||||
case SYS_STREAM_CREATE:
|
||||
if (!ValidUserPtr(frame->arg1) || !ValidUserPtr(frame->arg2)) return -1;
|
||||
return Sys_StreamCreate((int*)frame->arg1, (int*)frame->arg2, (uint32_t)frame->arg3);
|
||||
case SYS_STREAM_READ:
|
||||
if (!ValidUserPtr(frame->arg2)) return -1;
|
||||
return Sys_StreamRead((int)frame->arg1, (uint8_t*)frame->arg2, (int)frame->arg3);
|
||||
case SYS_STREAM_WRITE:
|
||||
if (!ValidUserPtr(frame->arg2)) return -1;
|
||||
return Sys_StreamWrite((int)frame->arg1, (const uint8_t*)frame->arg2, (int)frame->arg3);
|
||||
case SYS_MAILBOX_CREATE:
|
||||
if (!ValidUserPtr(frame->arg1) || !ValidUserPtr(frame->arg2)) return -1;
|
||||
return Sys_MailboxCreate((int*)frame->arg1, (int*)frame->arg2);
|
||||
case SYS_MAILBOX_SEND:
|
||||
if (frame->arg3 != 0 && !ValidUserPtr(frame->arg3)) return -1;
|
||||
return Sys_MailboxSend((int)frame->arg1, (uint32_t)frame->arg2,
|
||||
(const void*)frame->arg3, (uint16_t)frame->arg4,
|
||||
(int)frame->arg5);
|
||||
case SYS_MAILBOX_RECV:
|
||||
if (frame->arg2 != 0 && !IsUserPtr(frame->arg2)) return -1;
|
||||
if (frame->arg3 != 0 && !IsUserPtr(frame->arg3)) return -1;
|
||||
if (!ValidUserPtr(frame->arg4)) return -1;
|
||||
if (frame->arg5 != 0 && !IsUserPtr(frame->arg5)) return -1;
|
||||
return Sys_MailboxRecv((int)frame->arg1,
|
||||
IsUserPtr(frame->arg2) ? (uint32_t*)frame->arg2 : nullptr,
|
||||
IsUserPtr(frame->arg3) ? (void*)frame->arg3 : nullptr,
|
||||
(uint16_t*)frame->arg4,
|
||||
IsUserPtr(frame->arg5) ? (int*)frame->arg5 : nullptr);
|
||||
case SYS_WAITSET_CREATE:
|
||||
return Sys_WaitsetCreate();
|
||||
case SYS_WAITSET_ADD:
|
||||
return Sys_WaitsetAdd((int)frame->arg1, (int)frame->arg2, (uint32_t)frame->arg3);
|
||||
case SYS_WAITSET_REMOVE:
|
||||
return Sys_WaitsetRemove((int)frame->arg1, (int)frame->arg2);
|
||||
case SYS_WAITSET_WAIT:
|
||||
if (frame->arg2 != 0 && !ValidUserPtr(frame->arg2)) return -1;
|
||||
return Sys_WaitsetWait((int)frame->arg1,
|
||||
IsUserPtr(frame->arg2) ? (IpcWaitResult*)frame->arg2 : nullptr,
|
||||
frame->arg3);
|
||||
case SYS_PROC_OPEN:
|
||||
return Sys_ProcOpen((int)frame->arg1);
|
||||
case SYS_SURFACE_CREATE:
|
||||
return Sys_SurfaceCreate(frame->arg1);
|
||||
case SYS_SURFACE_MAP:
|
||||
return (int64_t)Sys_SurfaceMap((int)frame->arg1);
|
||||
case SYS_SURFACE_RESIZE:
|
||||
return Sys_SurfaceResize((int)frame->arg1, frame->arg2);
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -123,6 +123,7 @@ namespace Montauk {
|
||||
static constexpr uint64_t SYS_WINPOLL = 57;
|
||||
static constexpr uint64_t SYS_WINENUM = 58;
|
||||
static constexpr uint64_t SYS_WINMAP = 59;
|
||||
static constexpr uint64_t SYS_WINUNMAP = 97;
|
||||
static constexpr uint64_t SYS_WINSENDEVENT = 60;
|
||||
static constexpr uint64_t SYS_WINRESIZE = 64;
|
||||
static constexpr uint64_t SYS_WINSETSCALE = 65;
|
||||
@@ -187,6 +188,30 @@ namespace Montauk {
|
||||
static constexpr uint64_t SYS_GETCWD = 95;
|
||||
static constexpr uint64_t SYS_CHDIR = 96;
|
||||
|
||||
/* IpcSyscall.hpp */
|
||||
static constexpr uint64_t SYS_DUPHANDLE = 98;
|
||||
static constexpr uint64_t SYS_WAIT_HANDLE = 99;
|
||||
static constexpr uint64_t SYS_STREAM_CREATE = 100;
|
||||
static constexpr uint64_t SYS_STREAM_READ = 101;
|
||||
static constexpr uint64_t SYS_STREAM_WRITE = 102;
|
||||
static constexpr uint64_t SYS_MAILBOX_CREATE = 103;
|
||||
static constexpr uint64_t SYS_MAILBOX_SEND = 104;
|
||||
static constexpr uint64_t SYS_MAILBOX_RECV = 105;
|
||||
static constexpr uint64_t SYS_WAITSET_CREATE = 106;
|
||||
static constexpr uint64_t SYS_WAITSET_ADD = 107;
|
||||
static constexpr uint64_t SYS_WAITSET_REMOVE = 108;
|
||||
static constexpr uint64_t SYS_WAITSET_WAIT = 109;
|
||||
static constexpr uint64_t SYS_PROC_OPEN = 110;
|
||||
static constexpr uint64_t SYS_SURFACE_CREATE = 111;
|
||||
static constexpr uint64_t SYS_SURFACE_MAP = 112;
|
||||
static constexpr uint64_t SYS_SURFACE_RESIZE = 113;
|
||||
|
||||
static constexpr uint32_t IPC_SIGNAL_READABLE = 1u << 0;
|
||||
static constexpr uint32_t IPC_SIGNAL_WRITABLE = 1u << 1;
|
||||
static constexpr uint32_t IPC_SIGNAL_PEER_CLOSED = 1u << 2;
|
||||
static constexpr uint32_t IPC_SIGNAL_EXITED = 1u << 3;
|
||||
static constexpr uint32_t IPC_SIGNAL_READY = 1u << 4;
|
||||
|
||||
static constexpr int SOCK_TCP = 1;
|
||||
static constexpr int SOCK_UDP = 2;
|
||||
|
||||
@@ -239,6 +264,11 @@ namespace Montauk {
|
||||
uint8_t buttons;
|
||||
};
|
||||
|
||||
struct IpcWaitResult {
|
||||
int32_t index;
|
||||
uint32_t signals;
|
||||
};
|
||||
|
||||
// Window server shared types
|
||||
struct WinEvent {
|
||||
uint8_t type; // 0=key, 1=mouse, 2=resize, 3=close, 4=scale
|
||||
|
||||
@@ -15,10 +15,12 @@ namespace Montauk {
|
||||
static void Sys_Print(const char* text) {
|
||||
auto* proc = Sched::GetCurrentProcessPtr();
|
||||
if (proc && proc->redirected) {
|
||||
auto* target = GetRedirTarget(proc);
|
||||
if (target && target->outBuf) {
|
||||
for (int i = 0; text[i]; i++) {
|
||||
RingWrite(target->outBuf, target->outHead, target->outTail, Sched::Process::IoBufSize, (uint8_t)text[i]);
|
||||
Ipc::Stream* stream = GetRedirOutStream(proc);
|
||||
if (stream != nullptr) {
|
||||
int len = 0;
|
||||
while (text[len]) len++;
|
||||
if (len > 0) {
|
||||
WriteAllToStream(stream, (const uint8_t*)text, len);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -31,9 +33,10 @@ namespace Montauk {
|
||||
static void Sys_Putchar(char c) {
|
||||
auto* proc = Sched::GetCurrentProcessPtr();
|
||||
if (proc && proc->redirected) {
|
||||
auto* target = GetRedirTarget(proc);
|
||||
if (target && target->outBuf) {
|
||||
RingWrite(target->outBuf, target->outHead, target->outTail, Sched::Process::IoBufSize, (uint8_t)c);
|
||||
Ipc::Stream* stream = GetRedirOutStream(proc);
|
||||
if (stream != nullptr) {
|
||||
uint8_t byte = (uint8_t)c;
|
||||
WriteAllToStream(stream, &byte, 1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -41,4 +44,4 @@ namespace Montauk {
|
||||
if (Kt::g_suppressKernelLog) return;
|
||||
Kt::Putchar(c);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
+164
-294
@@ -5,118 +5,104 @@
|
||||
*/
|
||||
|
||||
#include "WinServer.hpp"
|
||||
#include <Memory/PageFrameAllocator.hpp>
|
||||
#include <Memory/Paging.hpp>
|
||||
#include <Memory/HHDM.hpp>
|
||||
#include <Libraries/Memory.hpp>
|
||||
#include <Terminal/Terminal.hpp>
|
||||
#include <CppLib/Spinlock.hpp>
|
||||
#include <Sched/Scheduler.hpp>
|
||||
#include <Timekeeping/ApicTimer.hpp>
|
||||
|
||||
namespace WinServer {
|
||||
|
||||
static WindowSlot g_slots[MaxWindows];
|
||||
static int g_uiScale = 1;
|
||||
static kcp::Mutex wsLock;
|
||||
static constexpr uint64_t RetireGraceMs = 2000;
|
||||
static constexpr int MaxRetiredBatches = MaxWindows * 4;
|
||||
static constexpr int MaxRetiredSnapshots = MaxWindows * 4;
|
||||
|
||||
struct RetiredBatch {
|
||||
struct RetiredSnapshot {
|
||||
bool used;
|
||||
int numPages;
|
||||
uint64_t freeAfterMs;
|
||||
uint64_t physPages[MaxPixelPages];
|
||||
int windowId;
|
||||
Ipc::Surface* surface;
|
||||
};
|
||||
|
||||
static RetiredBatch g_retired[MaxRetiredBatches];
|
||||
static RetiredSnapshot g_retiredSnapshots[MaxRetiredSnapshots];
|
||||
|
||||
// RAII lock guard for WinServer operations
|
||||
struct WsGuard {
|
||||
WsGuard() { wsLock.Acquire(); }
|
||||
~WsGuard() { wsLock.Release(); }
|
||||
};
|
||||
|
||||
static void FreePageBatchLocked(uint64_t* physPages, int numPages) {
|
||||
for (int i = 0; i < numPages; i++) {
|
||||
if (physPages[i] != 0) {
|
||||
Memory::g_pfa->Free((void*)Memory::HHDM(physPages[i]));
|
||||
physPages[i] = 0;
|
||||
}
|
||||
}
|
||||
static void ResetSlotLocked(WindowSlot& slot) {
|
||||
memset(&slot, 0, sizeof(WindowSlot));
|
||||
}
|
||||
|
||||
static void ProcessRetiredLocked() {
|
||||
uint64_t now = Timekeeping::GetMilliseconds();
|
||||
for (int i = 0; i < MaxRetiredBatches; i++) {
|
||||
if (!g_retired[i].used) continue;
|
||||
if (now < g_retired[i].freeAfterMs) continue;
|
||||
FreePageBatchLocked(g_retired[i].physPages, g_retired[i].numPages);
|
||||
g_retired[i].used = false;
|
||||
g_retired[i].numPages = 0;
|
||||
g_retired[i].freeAfterMs = 0;
|
||||
}
|
||||
}
|
||||
static void RetireSnapshotLocked(int windowId, Ipc::Surface* surface) {
|
||||
if (surface == nullptr) return;
|
||||
|
||||
static void RetirePageBatchLocked(uint64_t* physPages, int numPages) {
|
||||
if (numPages <= 0) return;
|
||||
|
||||
int retireIdx = -1;
|
||||
for (int i = 0; i < MaxRetiredBatches; i++) {
|
||||
if (!g_retired[i].used) {
|
||||
retireIdx = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (retireIdx < 0) {
|
||||
ProcessRetiredLocked();
|
||||
for (int i = 0; i < MaxRetiredBatches; i++) {
|
||||
if (!g_retired[i].used) {
|
||||
retireIdx = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (retireIdx < 0) {
|
||||
uint64_t oldest = ~0ULL;
|
||||
for (int i = 0; i < MaxRetiredBatches; i++) {
|
||||
if (g_retired[i].freeAfterMs < oldest) {
|
||||
oldest = g_retired[i].freeAfterMs;
|
||||
retireIdx = i;
|
||||
}
|
||||
}
|
||||
if (retireIdx >= 0) {
|
||||
Kt::KernelLogStream(Kt::ERROR, "WinServer")
|
||||
<< "Retire queue full, forcing early free of stale window pages";
|
||||
FreePageBatchLocked(g_retired[retireIdx].physPages, g_retired[retireIdx].numPages);
|
||||
g_retired[retireIdx].used = false;
|
||||
g_retired[retireIdx].numPages = 0;
|
||||
g_retired[retireIdx].freeAfterMs = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (retireIdx < 0) {
|
||||
FreePageBatchLocked(physPages, numPages);
|
||||
for (int i = 0; i < MaxRetiredSnapshots; i++) {
|
||||
if (g_retiredSnapshots[i].used) continue;
|
||||
g_retiredSnapshots[i].used = true;
|
||||
g_retiredSnapshots[i].windowId = windowId;
|
||||
g_retiredSnapshots[i].surface = surface;
|
||||
return;
|
||||
}
|
||||
|
||||
RetiredBatch& batch = g_retired[retireIdx];
|
||||
batch.used = true;
|
||||
batch.numPages = numPages;
|
||||
batch.freeAfterMs = Timekeeping::GetMilliseconds() + RetireGraceMs;
|
||||
for (int i = 0; i < numPages; i++) {
|
||||
batch.physPages[i] = physPages[i];
|
||||
physPages[i] = 0;
|
||||
Ipc::ReleaseSurface(surface);
|
||||
}
|
||||
|
||||
static int UnmapRetiredSnapshotsLocked(int windowId, int callerPid, uint64_t callerPml4) {
|
||||
int result = -1;
|
||||
for (int i = 0; i < MaxRetiredSnapshots; i++) {
|
||||
if (!g_retiredSnapshots[i].used || g_retiredSnapshots[i].windowId != windowId) continue;
|
||||
|
||||
if (g_retiredSnapshots[i].surface != nullptr) {
|
||||
if (Ipc::UnmapSurfaceForPid(g_retiredSnapshots[i].surface, callerPid, callerPml4) == 0) {
|
||||
result = 0;
|
||||
}
|
||||
Ipc::ReleaseSurface(g_retiredSnapshots[i].surface);
|
||||
}
|
||||
|
||||
g_retiredSnapshots[i].used = false;
|
||||
g_retiredSnapshots[i].windowId = -1;
|
||||
g_retiredSnapshots[i].surface = nullptr;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static void ReleaseSlotResourcesLocked(WindowSlot& slot, bool unmapOwner) {
|
||||
if (unmapOwner && slot.liveSurface != nullptr) {
|
||||
auto* owner = Sched::GetProcessByPid(slot.ownerPid);
|
||||
if (owner != nullptr) {
|
||||
Ipc::UnmapSurfaceForPid(slot.liveSurface, slot.ownerPid, owner->pml4Phys);
|
||||
}
|
||||
}
|
||||
|
||||
if (slot.eventMailbox != nullptr) {
|
||||
Ipc::ReleaseMailbox(slot.eventMailbox, true, true);
|
||||
}
|
||||
if (slot.liveSurface != nullptr) {
|
||||
Ipc::ReleaseSurface(slot.liveSurface);
|
||||
}
|
||||
if (slot.snapshotSurface != nullptr) {
|
||||
RetireSnapshotLocked((int)(&slot - g_slots), slot.snapshotSurface);
|
||||
}
|
||||
|
||||
ResetSlotLocked(slot);
|
||||
}
|
||||
|
||||
static int SendEventLocked(int windowId, const Montauk::WinEvent* event) {
|
||||
if (windowId < 0 || windowId >= MaxWindows || event == nullptr) return -1;
|
||||
WindowSlot& slot = g_slots[windowId];
|
||||
if (!slot.used || slot.eventMailbox == nullptr) return -1;
|
||||
|
||||
int rc = Ipc::MailboxSend(slot.eventMailbox, 0, event, sizeof(*event));
|
||||
return rc > 0 ? 0 : -1;
|
||||
}
|
||||
|
||||
int Create(int ownerPid, uint64_t ownerPml4, const char* title, int w, int h,
|
||||
uint64_t& heapNext, uint64_t& outVa) {
|
||||
WsGuard guard;
|
||||
ProcessRetiredLocked();
|
||||
// Find a free slot
|
||||
|
||||
if (w <= 0 || h <= 0 || w > 16384 || h > 16384) return -1;
|
||||
|
||||
int slotIdx = -1;
|
||||
for (int i = 0; i < MaxWindows; i++) {
|
||||
if (!g_slots[i].used) {
|
||||
@@ -126,26 +112,42 @@ namespace WinServer {
|
||||
}
|
||||
if (slotIdx < 0) return -1;
|
||||
|
||||
// Validate dimensions (cap at 16384 to prevent integer overflow in w*h*4)
|
||||
if (w <= 0 || h <= 0 || w > 16384 || h > 16384) return -1;
|
||||
uint64_t bufSize = (uint64_t)w * h * 4;
|
||||
int numPages = (int)((bufSize + 0xFFF) / 0x1000);
|
||||
if (numPages > MaxPixelPages) return -1;
|
||||
uint64_t bufSize = (uint64_t)w * (uint64_t)h * 4ULL;
|
||||
Ipc::Surface* live = Ipc::CreateSurface(bufSize);
|
||||
Ipc::Surface* snapshot = Ipc::CreateSurface(bufSize);
|
||||
Ipc::Mailbox* mailbox = Ipc::CreateMailbox();
|
||||
if (live == nullptr || snapshot == nullptr || mailbox == nullptr) {
|
||||
if (live != nullptr) Ipc::ReleaseSurface(live);
|
||||
if (snapshot != nullptr) Ipc::ReleaseSurface(snapshot);
|
||||
if (mailbox != nullptr) Ipc::ReleaseMailbox(mailbox, false, false);
|
||||
return -1;
|
||||
}
|
||||
|
||||
Ipc::RetainSurface(live);
|
||||
Ipc::RetainSurface(snapshot);
|
||||
Ipc::RetainMailbox(mailbox, true, true);
|
||||
|
||||
uint64_t userVa = 0;
|
||||
if (Ipc::MapSurfaceForPid(live, ownerPid, ownerPml4, heapNext, userVa) != 0) {
|
||||
Ipc::ReleaseMailbox(mailbox, true, true);
|
||||
Ipc::ReleaseSurface(snapshot);
|
||||
Ipc::ReleaseSurface(live);
|
||||
return -1;
|
||||
}
|
||||
|
||||
WindowSlot& slot = g_slots[slotIdx];
|
||||
memset(&slot, 0, sizeof(WindowSlot));
|
||||
ResetSlotLocked(slot);
|
||||
slot.used = true;
|
||||
slot.ownerPid = ownerPid;
|
||||
slot.width = w;
|
||||
slot.height = h;
|
||||
slot.pixelNumPages = numPages;
|
||||
slot.eventHead = 0;
|
||||
slot.eventTail = 0;
|
||||
slot.liveSurface = live;
|
||||
slot.snapshotSurface = snapshot;
|
||||
slot.eventMailbox = mailbox;
|
||||
slot.ownerVa = userVa;
|
||||
slot.dirty = false;
|
||||
slot.desktopVa = 0;
|
||||
slot.desktopPid = 0;
|
||||
slot.cursor = 0;
|
||||
|
||||
// Copy title
|
||||
int tlen = 0;
|
||||
while (title[tlen] && tlen < 63) {
|
||||
slot.title[tlen] = title[tlen];
|
||||
@@ -153,127 +155,55 @@ namespace WinServer {
|
||||
}
|
||||
slot.title[tlen] = '\0';
|
||||
|
||||
// Allocate live pixel pages (app renders here) and snapshot pages
|
||||
// (compositor reads here). Present copies live -> snapshot.
|
||||
uint64_t userVa = heapNext;
|
||||
for (int i = 0; i < numPages; i++) {
|
||||
void* page = Memory::g_pfa->AllocateZeroed();
|
||||
if (page == nullptr) {
|
||||
slot.used = false;
|
||||
return -1;
|
||||
}
|
||||
uint64_t physAddr = Memory::SubHHDM((uint64_t)page);
|
||||
slot.pixelPhysPages[i] = physAddr;
|
||||
if (!Memory::VMM::Paging::MapUserIn(ownerPml4, physAddr, userVa + (uint64_t)i * 0x1000)) {
|
||||
slot.used = false;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Allocate corresponding snapshot page
|
||||
void* snapPage = Memory::g_pfa->AllocateZeroed();
|
||||
if (snapPage == nullptr) {
|
||||
slot.used = false;
|
||||
return -1;
|
||||
}
|
||||
slot.snapshotPhysPages[i] = Memory::SubHHDM((uint64_t)snapPage);
|
||||
}
|
||||
|
||||
slot.ownerVa = userVa;
|
||||
heapNext += (uint64_t)numPages * 0x1000;
|
||||
outVa = userVa;
|
||||
|
||||
Kt::KernelLogStream(Kt::OK, "WinServer") << "Created window " << slotIdx
|
||||
<< " (" << w << "x" << h << ") for PID " << ownerPid;
|
||||
|
||||
return slotIdx;
|
||||
}
|
||||
|
||||
int Destroy(int windowId, int callerPid) {
|
||||
WsGuard guard;
|
||||
ProcessRetiredLocked();
|
||||
if (windowId < 0 || windowId >= MaxWindows) return -1;
|
||||
|
||||
WindowSlot& slot = g_slots[windowId];
|
||||
if (!slot.used || slot.ownerPid != callerPid) return -1;
|
||||
|
||||
// Unmap LIVE pixel pages from the owner's address space so that
|
||||
// FreeUserHalf() won't double-free them when the process exits.
|
||||
// Physical page reclamation is deferred below to avoid stale-TLB
|
||||
// consumers reading freed memory.
|
||||
{
|
||||
auto* ownerProc = Sched::GetProcessByPid(slot.ownerPid);
|
||||
if (ownerProc) {
|
||||
for (int p = 0; p < slot.pixelNumPages; p++) {
|
||||
Memory::VMM::Paging::UnmapUserIn(
|
||||
ownerProc->pml4Phys,
|
||||
slot.ownerVa + (uint64_t)p * 0x1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Retire both the owner-facing live pages and the compositor-facing
|
||||
// snapshot pages. The desktop can still hold stale mappings to the
|
||||
// snapshot pages for a short time after Enumerate notices the window
|
||||
// is gone, so freeing them immediately turns that stale mapping into
|
||||
// a use-after-free.
|
||||
RetirePageBatchLocked(slot.pixelPhysPages, slot.pixelNumPages);
|
||||
RetirePageBatchLocked(slot.snapshotPhysPages, slot.pixelNumPages);
|
||||
|
||||
slot.used = false;
|
||||
slot.pixelNumPages = 0;
|
||||
slot.ownerVa = 0;
|
||||
slot.desktopVa = 0;
|
||||
slot.desktopPid = 0;
|
||||
ReleaseSlotResourcesLocked(slot, true);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Present(int windowId, int callerPid) {
|
||||
// Validate ownership under the lock (brief)
|
||||
wsLock.Acquire();
|
||||
ProcessRetiredLocked();
|
||||
if (windowId < 0 || windowId >= MaxWindows) { wsLock.Release(); return -1; }
|
||||
WindowSlot& slot = g_slots[windowId];
|
||||
if (!slot.used || slot.ownerPid != callerPid) { wsLock.Release(); return -1; }
|
||||
int numPages = slot.pixelNumPages;
|
||||
wsLock.Release();
|
||||
WsGuard guard;
|
||||
if (windowId < 0 || windowId >= MaxWindows) return -1;
|
||||
|
||||
// Snapshot memcpy OUTSIDE the lock. This is safe because only the
|
||||
// owning process calls Present/Resize, and a process runs on one
|
||||
// CPU at a time. Holding wsLock during an 8MB copy would block
|
||||
// ALL other WinServer operations (Poll, Enumerate, SendEvent)
|
||||
// across all CPUs, causing convoy stalls and lockups.
|
||||
for (int i = 0; i < numPages; i++) {
|
||||
void* src = (void*)Memory::HHDM(slot.pixelPhysPages[i]);
|
||||
void* dst = (void*)Memory::HHDM(slot.snapshotPhysPages[i]);
|
||||
memcpy(dst, src, 0x1000);
|
||||
WindowSlot& slot = g_slots[windowId];
|
||||
if (!slot.used || slot.ownerPid != callerPid ||
|
||||
slot.liveSurface == nullptr || slot.snapshotSurface == nullptr) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Set dirty under lock
|
||||
wsLock.Acquire();
|
||||
if (Ipc::CopySurface(slot.snapshotSurface, slot.liveSurface) != 0) return -1;
|
||||
slot.dirty = true;
|
||||
wsLock.Release();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Poll(int windowId, int callerPid, Montauk::WinEvent* outEvent) {
|
||||
WsGuard guard;
|
||||
ProcessRetiredLocked();
|
||||
if (windowId < 0 || windowId >= MaxWindows) return -1;
|
||||
if (windowId < 0 || windowId >= MaxWindows || outEvent == nullptr) return -1;
|
||||
|
||||
WindowSlot& slot = g_slots[windowId];
|
||||
if (!slot.used || slot.ownerPid != callerPid) return -1;
|
||||
if (!slot.used || slot.ownerPid != callerPid || slot.eventMailbox == nullptr) return -1;
|
||||
|
||||
if (slot.eventHead == slot.eventTail) return 0; // no events
|
||||
|
||||
*outEvent = slot.events[slot.eventTail];
|
||||
slot.eventTail = (slot.eventTail + 1) % MaxEvents;
|
||||
return 1;
|
||||
uint16_t len = sizeof(*outEvent);
|
||||
int rc = Ipc::MailboxRecv(slot.eventMailbox, nullptr, outEvent, &len, true);
|
||||
if (rc < 0) return -1;
|
||||
return rc > 0 ? 1 : 0;
|
||||
}
|
||||
|
||||
int Enumerate(Montauk::WinInfo* outArray, int maxCount) {
|
||||
WsGuard guard;
|
||||
ProcessRetiredLocked();
|
||||
int count = 0;
|
||||
for (int i = 0; i < MaxWindows && count < maxCount; i++) {
|
||||
if (!g_slots[i].used) continue;
|
||||
|
||||
Montauk::WinInfo& info = outArray[count];
|
||||
info.id = i;
|
||||
info.ownerPid = g_slots[i].ownerPid;
|
||||
@@ -282,7 +212,7 @@ namespace WinServer {
|
||||
info.height = g_slots[i].height;
|
||||
info.dirty = g_slots[i].dirty ? 1 : 0;
|
||||
info.cursor = g_slots[i].cursor;
|
||||
g_slots[i].dirty = false; // clear dirty after read
|
||||
g_slots[i].dirty = false;
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
@@ -290,59 +220,43 @@ namespace WinServer {
|
||||
|
||||
uint64_t Map(int windowId, int callerPid, uint64_t callerPml4, uint64_t& heapNext) {
|
||||
WsGuard guard;
|
||||
ProcessRetiredLocked();
|
||||
if (windowId < 0 || windowId >= MaxWindows) return 0;
|
||||
|
||||
WindowSlot& slot = g_slots[windowId];
|
||||
if (!slot.used) return 0;
|
||||
if (!slot.used || slot.snapshotSurface == nullptr) return 0;
|
||||
|
||||
// If already mapped into this process, return existing VA
|
||||
if (slot.desktopPid == callerPid && slot.desktopVa != 0) {
|
||||
return slot.desktopVa;
|
||||
uint64_t outVa = 0;
|
||||
if (Ipc::MapSurfaceForPid(slot.snapshotSurface, callerPid, callerPml4, heapNext, outVa) != 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint64_t userVa = heapNext;
|
||||
|
||||
// Map the SNAPSHOT pages (not live pages) so the compositor
|
||||
// always reads a complete frame captured at Present time.
|
||||
for (int i = 0; i < slot.pixelNumPages; i++) {
|
||||
if (!Memory::VMM::Paging::MapUserIn(callerPml4, slot.snapshotPhysPages[i],
|
||||
userVa + (uint64_t)i * 0x1000)) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
slot.desktopVa = userVa;
|
||||
slot.desktopPid = callerPid;
|
||||
heapNext += (uint64_t)slot.pixelNumPages * 0x1000;
|
||||
|
||||
return userVa;
|
||||
return outVa;
|
||||
}
|
||||
|
||||
// Internal: send event without acquiring lock (caller must hold wsLock)
|
||||
static int SendEventLocked(int windowId, const Montauk::WinEvent* event) {
|
||||
if (windowId < 0 || windowId >= MaxWindows) return -1;
|
||||
int Unmap(int windowId, int callerPid, uint64_t callerPml4) {
|
||||
WsGuard guard;
|
||||
if (windowId < 0 || windowId >= MaxWindows) {
|
||||
return UnmapRetiredSnapshotsLocked(windowId, callerPid, callerPml4);
|
||||
}
|
||||
|
||||
int result = UnmapRetiredSnapshotsLocked(windowId, callerPid, callerPml4);
|
||||
WindowSlot& slot = g_slots[windowId];
|
||||
if (!slot.used) return -1;
|
||||
if (!slot.used || slot.snapshotSurface == nullptr) return result;
|
||||
|
||||
int nextHead = (slot.eventHead + 1) % MaxEvents;
|
||||
if (nextHead == slot.eventTail) return -1; // queue full, drop event
|
||||
|
||||
slot.events[slot.eventHead] = *event;
|
||||
slot.eventHead = nextHead;
|
||||
return 0;
|
||||
int current = Ipc::UnmapSurfaceForPid(slot.snapshotSurface, callerPid, callerPml4);
|
||||
if (current == 0) return 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
int SendEvent(int windowId, const Montauk::WinEvent* event) {
|
||||
WsGuard guard;
|
||||
ProcessRetiredLocked();
|
||||
return SendEventLocked(windowId, event);
|
||||
}
|
||||
|
||||
int Resize(int windowId, int callerPid, uint64_t ownerPml4, int newW, int newH,
|
||||
uint64_t& heapNext, uint64_t& outVa) {
|
||||
WsGuard guard;
|
||||
ProcessRetiredLocked();
|
||||
if (windowId < 0 || windowId >= MaxWindows) return -1;
|
||||
|
||||
WindowSlot& slot = g_slots[windowId];
|
||||
if (!slot.used || slot.ownerPid != callerPid) return -1;
|
||||
if (newW <= 0 || newH <= 0 || newW > 16384 || newH > 16384) return -1;
|
||||
@@ -351,56 +265,48 @@ namespace WinServer {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint64_t bufSize = (uint64_t)newW * newH * 4;
|
||||
int numPages = (int)((bufSize + 0xFFF) / 0x1000);
|
||||
if (numPages > MaxPixelPages) return -1;
|
||||
|
||||
// Unmap old LIVE pixel pages from the owner's address space. Actual
|
||||
// physical page reclamation is deferred below for both live and
|
||||
// snapshot pages so stale desktop mappings cannot turn into
|
||||
// use-after-free reads during remap.
|
||||
int oldNumPages = slot.pixelNumPages;
|
||||
for (int i = 0; i < oldNumPages; i++) {
|
||||
Memory::VMM::Paging::UnmapUserIn(
|
||||
ownerPml4, slot.ownerVa + (uint64_t)i * 0x1000);
|
||||
}
|
||||
RetirePageBatchLocked(slot.pixelPhysPages, oldNumPages);
|
||||
RetirePageBatchLocked(slot.snapshotPhysPages, oldNumPages);
|
||||
|
||||
// Allocate new live + snapshot pages and map live into owner's address space
|
||||
uint64_t userVa = heapNext;
|
||||
for (int i = 0; i < numPages; i++) {
|
||||
void* page = Memory::g_pfa->AllocateZeroed();
|
||||
if (page == nullptr) return -1;
|
||||
uint64_t physAddr = Memory::SubHHDM((uint64_t)page);
|
||||
slot.pixelPhysPages[i] = physAddr;
|
||||
if (!Memory::VMM::Paging::MapUserIn(ownerPml4, physAddr, userVa + (uint64_t)i * 0x1000)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
void* snapPage = Memory::g_pfa->AllocateZeroed();
|
||||
if (snapPage == nullptr) return -1;
|
||||
slot.snapshotPhysPages[i] = Memory::SubHHDM((uint64_t)snapPage);
|
||||
uint64_t bufSize = (uint64_t)newW * (uint64_t)newH * 4ULL;
|
||||
Ipc::Surface* newLive = Ipc::CreateSurface(bufSize);
|
||||
Ipc::Surface* newSnapshot = Ipc::CreateSurface(bufSize);
|
||||
if (newLive == nullptr || newSnapshot == nullptr) {
|
||||
if (newLive != nullptr) Ipc::ReleaseSurface(newLive);
|
||||
if (newSnapshot != nullptr) Ipc::ReleaseSurface(newSnapshot);
|
||||
return -1;
|
||||
}
|
||||
|
||||
Ipc::RetainSurface(newLive);
|
||||
Ipc::RetainSurface(newSnapshot);
|
||||
|
||||
uint64_t newOwnerVa = 0;
|
||||
if (Ipc::MapSurfaceForPid(newLive, callerPid, ownerPml4, heapNext, newOwnerVa) != 0) {
|
||||
Ipc::ReleaseSurface(newSnapshot);
|
||||
Ipc::ReleaseSurface(newLive);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (slot.liveSurface != nullptr) {
|
||||
Ipc::UnmapSurfaceForPid(slot.liveSurface, callerPid, ownerPml4);
|
||||
Ipc::ReleaseSurface(slot.liveSurface);
|
||||
}
|
||||
if (slot.snapshotSurface != nullptr) {
|
||||
RetireSnapshotLocked(windowId, slot.snapshotSurface);
|
||||
}
|
||||
|
||||
slot.liveSurface = newLive;
|
||||
slot.snapshotSurface = newSnapshot;
|
||||
slot.width = newW;
|
||||
slot.height = newH;
|
||||
slot.pixelNumPages = numPages;
|
||||
slot.ownerVa = userVa;
|
||||
heapNext += (uint64_t)numPages * 0x1000;
|
||||
slot.ownerVa = newOwnerVa;
|
||||
slot.dirty = true;
|
||||
|
||||
// Invalidate desktop mapping so it re-maps on next enumerate
|
||||
slot.desktopVa = 0;
|
||||
slot.desktopPid = 0;
|
||||
|
||||
outVa = userVa;
|
||||
outVa = newOwnerVa;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int SetCursor(int windowId, int callerPid, int cursor) {
|
||||
WsGuard guard;
|
||||
ProcessRetiredLocked();
|
||||
if (windowId < 0 || windowId >= MaxWindows) return -1;
|
||||
|
||||
WindowSlot& slot = g_slots[windowId];
|
||||
if (!slot.used || slot.ownerPid != callerPid) return -1;
|
||||
slot.cursor = (uint8_t)cursor;
|
||||
@@ -409,14 +315,11 @@ namespace WinServer {
|
||||
|
||||
int SetScale(int scale) {
|
||||
WsGuard guard;
|
||||
ProcessRetiredLocked();
|
||||
if (scale < 0) scale = 0;
|
||||
if (scale > 2) scale = 2;
|
||||
g_uiScale = scale;
|
||||
|
||||
// Broadcast scale event to all active windows
|
||||
Montauk::WinEvent ev;
|
||||
memset(&ev, 0, sizeof(ev));
|
||||
Montauk::WinEvent ev{};
|
||||
ev.type = 4;
|
||||
ev.scale.scale = scale;
|
||||
for (int i = 0; i < MaxWindows; i++) {
|
||||
@@ -433,42 +336,9 @@ namespace WinServer {
|
||||
|
||||
void CleanupProcess(int pid) {
|
||||
WsGuard guard;
|
||||
ProcessRetiredLocked();
|
||||
for (int i = 0; i < MaxWindows; i++) {
|
||||
if (g_slots[i].used && g_slots[i].ownerPid == pid) {
|
||||
Kt::KernelLogStream(Kt::INFO, "WinServer") << "Cleaning up window "
|
||||
<< i << " for exited PID " << pid;
|
||||
|
||||
// Do NOT free live pixel pages here -- they are still mapped
|
||||
// in the owner's page tables and FreeUserHalf() (called right
|
||||
// after CleanupProcess) will free them.
|
||||
|
||||
// Snapshot pages can still be mapped into the desktop, so
|
||||
// retire them instead of freeing them immediately.
|
||||
RetirePageBatchLocked(g_slots[i].snapshotPhysPages, g_slots[i].pixelNumPages);
|
||||
|
||||
g_slots[i].used = false;
|
||||
g_slots[i].pixelNumPages = 0;
|
||||
g_slots[i].ownerVa = 0;
|
||||
g_slots[i].desktopVa = 0;
|
||||
g_slots[i].desktopPid = 0;
|
||||
}
|
||||
|
||||
// If this process had windows mapped INTO it (was the desktop viewer),
|
||||
// unmap those pixel pages so FreeUserHalf() won't free pages owned
|
||||
// by other processes.
|
||||
if (g_slots[i].used && g_slots[i].desktopPid == pid) {
|
||||
auto* proc = Sched::GetProcessByPid(pid);
|
||||
if (proc) {
|
||||
for (int p = 0; p < g_slots[i].pixelNumPages; p++) {
|
||||
Memory::VMM::Paging::UnmapUserIn(
|
||||
proc->pml4Phys,
|
||||
g_slots[i].desktopVa + (uint64_t)p * 0x1000);
|
||||
}
|
||||
}
|
||||
g_slots[i].desktopVa = 0;
|
||||
g_slots[i].desktopPid = 0;
|
||||
}
|
||||
if (!g_slots[i].used || g_slots[i].ownerPid != pid) continue;
|
||||
ReleaseSlotResourcesLocked(g_slots[i], false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,28 +7,23 @@
|
||||
#pragma once
|
||||
#include "Syscall.hpp"
|
||||
#include <cstdint>
|
||||
#include <Ipc/Ipc.hpp>
|
||||
|
||||
namespace WinServer {
|
||||
|
||||
static constexpr int MaxWindows = 8;
|
||||
static constexpr int MaxEvents = 64;
|
||||
static constexpr int MaxPixelPages = 8192; // up to 3840x2160 @ 32bpp = 32MB
|
||||
|
||||
struct WindowSlot {
|
||||
bool used;
|
||||
int ownerPid;
|
||||
char title[64];
|
||||
int width, height;
|
||||
uint64_t pixelPhysPages[MaxPixelPages]; // live buffer (app writes here)
|
||||
uint64_t snapshotPhysPages[MaxPixelPages]; // snapshot (compositor reads here)
|
||||
int pixelNumPages;
|
||||
uint64_t ownerVa; // VA in owner's address space
|
||||
uint64_t desktopVa; // VA in desktop's address space (0 = not yet mapped)
|
||||
int desktopPid; // PID of the process that mapped it
|
||||
Montauk::WinEvent events[MaxEvents];
|
||||
int eventHead, eventTail;
|
||||
Ipc::Surface* liveSurface; // app writes here
|
||||
Ipc::Surface* snapshotSurface; // compositor/viewers read here
|
||||
Ipc::Mailbox* eventMailbox; // input/control events for the app
|
||||
uint64_t ownerVa; // mapped VA of liveSurface in owner
|
||||
bool dirty;
|
||||
uint8_t cursor; // cursor style requested by app (0=arrow, 1=resize_h, 2=resize_v)
|
||||
uint8_t cursor; // 0=arrow, 1=resize_h, 2=resize_v
|
||||
};
|
||||
|
||||
int Create(int ownerPid, uint64_t ownerPml4, const char* title, int w, int h,
|
||||
@@ -38,6 +33,7 @@ namespace WinServer {
|
||||
int Poll(int windowId, int callerPid, Montauk::WinEvent* outEvent);
|
||||
int Enumerate(Montauk::WinInfo* outArray, int maxCount);
|
||||
uint64_t Map(int windowId, int callerPid, uint64_t callerPml4, uint64_t& heapNext);
|
||||
int Unmap(int windowId, int callerPid, uint64_t callerPml4);
|
||||
int SendEvent(int windowId, const Montauk::WinEvent* event);
|
||||
int Resize(int windowId, int callerPid, uint64_t ownerPml4, int newW, int newH,
|
||||
uint64_t& heapNext, uint64_t& outVa);
|
||||
|
||||
@@ -51,6 +51,12 @@ namespace Montauk {
|
||||
return WinServer::Map(windowId, proc->pid, proc->pml4Phys, proc->heapNext);
|
||||
}
|
||||
|
||||
static int Sys_WinUnmap(int windowId) {
|
||||
auto* proc = Sched::GetCurrentProcessPtr();
|
||||
if (proc == nullptr) return -1;
|
||||
return WinServer::Unmap(windowId, proc->pid, proc->pml4Phys);
|
||||
}
|
||||
|
||||
static int Sys_WinSendEvent(int windowId, const WinEvent* event) {
|
||||
if (event == nullptr) return -1;
|
||||
return WinServer::SendEvent(windowId, event);
|
||||
|
||||
Reference in New Issue
Block a user