add threading functions to platform, make inputs async
This commit is contained in:
parent
29a98de0e0
commit
9c6f08d2a0
455
platform.d
455
platform.d
@ -12,6 +12,8 @@ import std.stdio;
|
|||||||
import core.memory;
|
import core.memory;
|
||||||
import core.thread.osthread;
|
import core.thread.osthread;
|
||||||
import core.time;
|
import core.time;
|
||||||
|
import core.volatile;
|
||||||
|
import core.atomic;
|
||||||
|
|
||||||
const WINDOW_EDGE_BUFFER = 50;
|
const WINDOW_EDGE_BUFFER = 50;
|
||||||
|
|
||||||
@ -45,16 +47,6 @@ enum Input
|
|||||||
|
|
||||||
alias KBI = Input;
|
alias KBI = Input;
|
||||||
|
|
||||||
version(linux)
|
|
||||||
{
|
|
||||||
import core.sys.posix.dlfcn;
|
|
||||||
import core.sys.posix.sys.mman;
|
|
||||||
import core.sys.linux.sys.inotify;
|
|
||||||
import core.sys.linux.fcntl;
|
|
||||||
import core.sys.posix.unistd;
|
|
||||||
|
|
||||||
import core.stdc.string : strlen;
|
|
||||||
|
|
||||||
struct InputEvent
|
struct InputEvent
|
||||||
{
|
{
|
||||||
Input key;
|
Input key;
|
||||||
@ -65,12 +57,249 @@ struct InputEvent
|
|||||||
i32 rel_y;
|
i32 rel_y;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
struct Inputs
|
struct Inputs
|
||||||
{
|
{
|
||||||
DLList!(InputEvent) list;
|
DLList!(InputEvent) list;
|
||||||
Arena arena;
|
Arena arena;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Inputs*
|
||||||
|
GetEvents(PlatformWindow* window)
|
||||||
|
{
|
||||||
|
Lock(&window.input_mutex);
|
||||||
|
|
||||||
|
Inputs* inputs = &window.inputs[window.input_idx];
|
||||||
|
window.input_idx = (window.input_idx + 1) % 2;
|
||||||
|
ResetInputs(&window.inputs[window.input_idx]);
|
||||||
|
|
||||||
|
Unlock(&window.input_mutex);
|
||||||
|
|
||||||
|
return inputs;
|
||||||
|
}
|
||||||
|
|
||||||
|
Inputs*
|
||||||
|
GetInputs(PlatformWindow* window)
|
||||||
|
{
|
||||||
|
Lock(&window.input_mutex);
|
||||||
|
|
||||||
|
Inputs* inputs = window.inputs.ptr + window.input_idx;
|
||||||
|
|
||||||
|
return inputs;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ReturnInputs(PlatformWindow* window)
|
||||||
|
{
|
||||||
|
Unlock(&window.input_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
enum SysMessageType
|
||||||
|
{
|
||||||
|
None,
|
||||||
|
Quit,
|
||||||
|
LockCursor,
|
||||||
|
UnlockCursor,
|
||||||
|
HideCursor,
|
||||||
|
ShowCursor,
|
||||||
|
}
|
||||||
|
|
||||||
|
alias SMT = SysMessageType;
|
||||||
|
|
||||||
|
struct Mut
|
||||||
|
{
|
||||||
|
bool locked;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct TicketMut
|
||||||
|
{
|
||||||
|
u64 ticket;
|
||||||
|
u64 next_ticket;
|
||||||
|
}
|
||||||
|
|
||||||
|
Mut
|
||||||
|
CreateMut()
|
||||||
|
{
|
||||||
|
return Mut(locked: false);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
TryLock(Mut* mut)
|
||||||
|
{
|
||||||
|
return cas!(MemoryOrder.acq, MemoryOrder.raw)(&mut.locked, false, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Unlock(Mut* mut)
|
||||||
|
{
|
||||||
|
atomicStore!(MemoryOrder.rel, bool)(mut.locked, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
TicketMut
|
||||||
|
CreateTicketMut()
|
||||||
|
{
|
||||||
|
return TicketMut(ticket: 0, next_ticket: 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Lock(TicketMut* mut)
|
||||||
|
{
|
||||||
|
u64 ticket = atomicFetchAdd!(MemoryOrder.acq, u64)(mut.ticket, 1);
|
||||||
|
while (ticket != volatileLoad(&mut.next_ticket)) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Unlock(TicketMut* mut)
|
||||||
|
{
|
||||||
|
atomicFetchAdd!(MemoryOrder.rel, u64)(mut.next_ticket, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const DNode!(SysMessage) g_sys_message;
|
||||||
|
DNode!(SysMessage)* g_NIL_MSG;
|
||||||
|
|
||||||
|
struct SysMessage
|
||||||
|
{
|
||||||
|
SysMessageType type;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct MessageQueue
|
||||||
|
{
|
||||||
|
DLList!(SysMessage) list;
|
||||||
|
DLList!(SysMessage) free_list;
|
||||||
|
TicketMut mut;
|
||||||
|
Arena arena;
|
||||||
|
}
|
||||||
|
|
||||||
|
MessageQueue
|
||||||
|
CreateMessageQueue()
|
||||||
|
{
|
||||||
|
if (g_NIL_MSG == null)
|
||||||
|
{
|
||||||
|
g_NIL_MSG = cast(DNode!(SysMessage)*)&g_sys_message;
|
||||||
|
}
|
||||||
|
|
||||||
|
MessageQueue queue = {
|
||||||
|
list: {
|
||||||
|
first: g_NIL_MSG,
|
||||||
|
last: g_NIL_MSG,
|
||||||
|
},
|
||||||
|
free_list: {
|
||||||
|
first: g_NIL_MSG,
|
||||||
|
last: g_NIL_MSG,
|
||||||
|
},
|
||||||
|
mut: CreateTicketMut(),
|
||||||
|
arena: CreateArena(MB(1)),
|
||||||
|
};
|
||||||
|
|
||||||
|
return queue;
|
||||||
|
}
|
||||||
|
|
||||||
|
pragma(inline) bool
|
||||||
|
Nil(DNode!(SysMessage)* msg)
|
||||||
|
{
|
||||||
|
return msg == null || msg == g_NIL_MSG;
|
||||||
|
}
|
||||||
|
|
||||||
|
DNode!(SysMessage)*
|
||||||
|
Pop(MessageQueue* queue)
|
||||||
|
{
|
||||||
|
Lock(&queue.mut);
|
||||||
|
|
||||||
|
DNode!(SysMessage)* node = DLLPop(&queue.list, g_NIL_MSG);
|
||||||
|
|
||||||
|
Unlock(&queue.mut);
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Push(PlatformWindow* w, SysMessageType msg)
|
||||||
|
{
|
||||||
|
Lock(&w.msg_queue.mut);
|
||||||
|
|
||||||
|
DNode!(SysMessage)* node = g_NIL_MSG;
|
||||||
|
if (Nil(w.msg_queue.free_list.first))
|
||||||
|
{
|
||||||
|
node = Alloc!(DNode!(SysMessage))(&w.msg_queue.arena);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
node = DLLPop(&w.msg_queue.free_list, g_NIL_MSG);
|
||||||
|
}
|
||||||
|
|
||||||
|
node.value.type = msg;
|
||||||
|
DLLPush(&w.msg_queue.list, node, g_NIL_MSG);
|
||||||
|
|
||||||
|
Unlock(&w.msg_queue.mut);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
PushFree(PlatformWindow* w, DNode!(SysMessage)* node)
|
||||||
|
{
|
||||||
|
DLLPush(&w.msg_queue.list, node, g_NIL_MSG);
|
||||||
|
}
|
||||||
|
|
||||||
|
version(linux)
|
||||||
|
{
|
||||||
|
import core.sys.posix.dlfcn;
|
||||||
|
import core.sys.posix.sys.mman;
|
||||||
|
import core.sys.linux.sys.inotify;
|
||||||
|
import core.sys.linux.fcntl;
|
||||||
|
import core.sys.posix.unistd;
|
||||||
|
import core.sys.posix.pthread : PThread = pthread_t,
|
||||||
|
PThreadCond = pthread_cond_t,
|
||||||
|
PThreadMutex = pthread_mutex_t,
|
||||||
|
PThreadMutexInit = pthread_mutex_init,
|
||||||
|
PThreadCondInit = pthread_cond_init,
|
||||||
|
PThreadCreate = pthread_create,
|
||||||
|
PThreadMutexLock = pthread_mutex_lock,
|
||||||
|
PThreadCondWait = pthread_cond_wait,
|
||||||
|
PThreadMutexUnlock = pthread_mutex_unlock,
|
||||||
|
PThreadCondSignal = pthread_cond_signal,
|
||||||
|
PThreadExit = pthread_exit;
|
||||||
|
|
||||||
|
import core.stdc.string : strlen;
|
||||||
|
|
||||||
|
struct SysThread
|
||||||
|
{
|
||||||
|
PThread handle;
|
||||||
|
PThreadCond cond;
|
||||||
|
PThreadMutex mut;
|
||||||
|
}
|
||||||
|
|
||||||
|
alias PThreadProc = extern (C) void* function(void*);
|
||||||
|
|
||||||
|
SysThread
|
||||||
|
CreateThread(void* proc, void* param)
|
||||||
|
{
|
||||||
|
SysThread thread;
|
||||||
|
assert(!PThreadMutexInit(&thread.mut, null));
|
||||||
|
assert(!PThreadCondInit(&thread.cond, null));
|
||||||
|
assert(!PThreadCreate(&thread.handle, null, cast(PThreadProc)proc, param));
|
||||||
|
return thread;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Suspend(SysThread* thread)
|
||||||
|
{
|
||||||
|
PThreadMutexLock(&thread.mut);
|
||||||
|
PThreadCondWait(&thread.cond, &thread.mut);
|
||||||
|
PThreadMutexUnlock(&thread.mut);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Wake(SysThread* thread)
|
||||||
|
{
|
||||||
|
PThreadCondSignal(&thread.cond);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Kill()
|
||||||
|
{
|
||||||
|
PThreadExit(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
ResetInputs(Inputs* inputs)
|
ResetInputs(Inputs* inputs)
|
||||||
{
|
{
|
||||||
@ -118,7 +347,12 @@ struct PlatformWindow
|
|||||||
i32 mouse_prev_y;
|
i32 mouse_prev_y;
|
||||||
bool locked_cursor;
|
bool locked_cursor;
|
||||||
bool close;
|
bool close;
|
||||||
Inputs inputs;
|
|
||||||
|
MessageQueue msg_queue;
|
||||||
|
|
||||||
|
u32 input_idx;
|
||||||
|
Inputs[2] inputs;
|
||||||
|
TicketMut input_mutex;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Library
|
struct Library
|
||||||
@ -131,7 +365,16 @@ struct Function
|
|||||||
void* ptr;
|
void* ptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
void CheckErr(PlatformWindow *window, xcb_void_cookie_t *cookie, xcb_generic_error_t *err, string msg)
|
pragma(inline) bool
|
||||||
|
NilErr(PlatformWindow* window, xcb_void_cookie_t* cookie, xcb_generic_error_t* err)
|
||||||
|
{
|
||||||
|
bool result = err == null;
|
||||||
|
pureFree(err);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
pragma(inline) void
|
||||||
|
CheckErr(PlatformWindow *window, xcb_void_cookie_t *cookie, xcb_generic_error_t *err, string msg)
|
||||||
{
|
{
|
||||||
assert(err == null, msg);
|
assert(err == null, msg);
|
||||||
pureFree(err);
|
pureFree(err);
|
||||||
@ -143,9 +386,12 @@ CreateWindow(string name, u16 width, u16 height)
|
|||||||
PlatformWindow window = {
|
PlatformWindow window = {
|
||||||
w: width,
|
w: width,
|
||||||
h: height,
|
h: height,
|
||||||
inputs: {
|
input_mutex: CreateTicketMut(),
|
||||||
arena: CreateArena(MB(1)),
|
msg_queue: CreateMessageQueue(),
|
||||||
},
|
inputs: [
|
||||||
|
{ arena: CreateArena(MB(1)) },
|
||||||
|
{ arena: CreateArena(MB(1)) },
|
||||||
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
assert(width > 0 && height > 0, "CreateWindow error: width and height must be above 0");
|
assert(width > 0 && height > 0, "CreateWindow error: width and height must be above 0");
|
||||||
@ -257,15 +503,14 @@ CreateWindow(string name, u16 width, u16 height)
|
|||||||
|
|
||||||
xcb_xfixes_query_version(window.conn, 4, 0);
|
xcb_xfixes_query_version(window.conn, 4, 0);
|
||||||
|
|
||||||
LockCursor(&window);
|
|
||||||
UnlockCursor(&window);
|
|
||||||
|
|
||||||
return window;
|
return window;
|
||||||
};
|
};
|
||||||
|
|
||||||
void
|
bool
|
||||||
LockCursor(PlatformWindow* window)
|
LockCursor(PlatformWindow* window)
|
||||||
{
|
{
|
||||||
|
bool result = window.locked_cursor;
|
||||||
|
|
||||||
if (!window.locked_cursor)
|
if (!window.locked_cursor)
|
||||||
{
|
{
|
||||||
u32 counter = 0;
|
u32 counter = 0;
|
||||||
@ -282,18 +527,25 @@ LockCursor(PlatformWindow* window)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (grab_reply.status == XCB_GRAB_STATUS_SUCCESS)
|
if (grab_reply.status == XCB_GRAB_STATUS_SUCCESS)
|
||||||
|
{
|
||||||
|
result = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (counter > 5)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(counter < 5, "Unable to grab cursor");
|
|
||||||
counter += 1;
|
counter += 1;
|
||||||
Thread.sleep(dur!("msecs")(50));
|
Thread.sleep(dur!("msecs")(50));
|
||||||
}
|
}
|
||||||
|
|
||||||
HideCursor(window);
|
HideCursor(window);
|
||||||
window.locked_cursor = true;
|
window.locked_cursor = result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -307,23 +559,20 @@ UnlockCursor(PlatformWindow* window)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: improve error handling for show/hide cursor
|
bool
|
||||||
void
|
|
||||||
HideCursor(PlatformWindow* window)
|
HideCursor(PlatformWindow* window)
|
||||||
{
|
{
|
||||||
xcb_void_cookie_t hide_cursor_cookie = xcb_xfixes_hide_cursor_checked(window.conn, window.window);
|
xcb_void_cookie_t hide_cursor_cookie = xcb_xfixes_hide_cursor_checked(window.conn, window.window);
|
||||||
xcb_generic_error_t *error = xcb_request_check(window.conn, hide_cursor_cookie);
|
xcb_generic_error_t *error = xcb_request_check(window.conn, hide_cursor_cookie);
|
||||||
CheckErr(window, &hide_cursor_cookie, error, "xcb_xfixes_hide_cursor_checked failure");
|
return NilErr(window, &hide_cursor_cookie, error);
|
||||||
pureFree(error);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
bool
|
||||||
ShowCursor(PlatformWindow* window)
|
ShowCursor(PlatformWindow* window)
|
||||||
{
|
{
|
||||||
xcb_void_cookie_t show_cursor_cookie = xcb_xfixes_show_cursor_checked(window.conn, window.window);
|
xcb_void_cookie_t show_cursor_cookie = xcb_xfixes_show_cursor_checked(window.conn, window.window);
|
||||||
xcb_generic_error_t *error = xcb_request_check(window.conn, show_cursor_cookie);
|
xcb_generic_error_t *error = xcb_request_check(window.conn, show_cursor_cookie);
|
||||||
CheckErr(window, &show_cursor_cookie, error, "xcb_xfixes_show_cursor_checked failure");
|
return NilErr(window, &show_cursor_cookie, error);
|
||||||
pureFree(error);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -337,34 +586,91 @@ FlushEvents(PlatformWindow* window)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
HandleEvents(PlatformWindow* window)
|
HandleEvents(void* window_ptr)
|
||||||
{
|
{
|
||||||
|
PlatformWindow* w = cast(PlatformWindow*)window_ptr;
|
||||||
|
|
||||||
|
DNode!(SysMessage)* sys_msg = g_NIL_MSG;
|
||||||
xcb_generic_event_t* e;
|
xcb_generic_event_t* e;
|
||||||
|
|
||||||
bool ignore_mouse_events = false;
|
bool ignore_mouse_events = false;
|
||||||
|
|
||||||
Inputs* inputs = &window.inputs;
|
for(;;)
|
||||||
ResetInputs(inputs);
|
|
||||||
|
|
||||||
do
|
|
||||||
{
|
{
|
||||||
e = xcb_poll_for_event(window.conn);
|
if (w.close)
|
||||||
|
{
|
||||||
|
Kill();
|
||||||
|
}
|
||||||
|
|
||||||
|
sys_msg = Pop(&w.msg_queue);
|
||||||
|
|
||||||
|
if (!Nil(sys_msg))
|
||||||
|
{
|
||||||
|
SysMessage msg = sys_msg.value;
|
||||||
|
|
||||||
|
switch (msg.type)
|
||||||
|
{
|
||||||
|
case SMT.Quit:
|
||||||
|
{
|
||||||
|
w.close = true;
|
||||||
|
} break;
|
||||||
|
case SMT.LockCursor:
|
||||||
|
{
|
||||||
|
for(u64 i = 0; i < 5; i += 1)
|
||||||
|
{
|
||||||
|
if (LockCursor(w))
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case SMT.UnlockCursor:
|
||||||
|
{
|
||||||
|
UnlockCursor(w);
|
||||||
|
} break;
|
||||||
|
case SMT.HideCursor:
|
||||||
|
{
|
||||||
|
for (u64 i = 0; i < 5; i += 1)
|
||||||
|
{
|
||||||
|
if (HideCursor(w))
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case SMT.ShowCursor:
|
||||||
|
{
|
||||||
|
for(u64 i = 0; i < 5; i += 1)
|
||||||
|
{
|
||||||
|
if (ShowCursor(w))
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
e = xcb_poll_for_event(w.conn);
|
||||||
|
|
||||||
if (e)
|
if (e)
|
||||||
{
|
{
|
||||||
|
Inputs* inputs = GetInputs(w);
|
||||||
|
|
||||||
switch (e.response_type & ~0x80)
|
switch (e.response_type & ~0x80)
|
||||||
{
|
{
|
||||||
case XCB_CLIENT_MESSAGE:
|
case XCB_CLIENT_MESSAGE:
|
||||||
{
|
{
|
||||||
xcb_client_message_event_t* msg = cast(xcb_client_message_event_t*)e;
|
xcb_client_message_event_t* msg = cast(xcb_client_message_event_t*)e;
|
||||||
if (msg.window != window.window)
|
if (msg.window != w.window)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (msg.data.data32[0] == window.close_event)
|
if (msg.data.data32[0] == w.close_event)
|
||||||
{
|
{
|
||||||
window.close = true;
|
w.close = true;
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
case XCB_KEY_RELEASE:
|
case XCB_KEY_RELEASE:
|
||||||
@ -374,7 +680,7 @@ HandleEvents(PlatformWindow* window)
|
|||||||
|
|
||||||
bool pressed = e.response_type == XCB_KEY_PRESS;
|
bool pressed = e.response_type == XCB_KEY_PRESS;
|
||||||
xcb_keycode_t code = keyboard_event.detail;
|
xcb_keycode_t code = keyboard_event.detail;
|
||||||
KeySym key_sym = XkbKeycodeToKeysym(window.display, cast(KeyCode)code, 0, 0);
|
KeySym key_sym = XkbKeycodeToKeysym(w.display, cast(KeyCode)code, 0, 0);
|
||||||
KBI input = ConvertInput(key_sym);
|
KBI input = ConvertInput(key_sym);
|
||||||
|
|
||||||
if (input != KBI.None)
|
if (input != KBI.None)
|
||||||
@ -414,48 +720,50 @@ HandleEvents(PlatformWindow* window)
|
|||||||
static bool first = true;
|
static bool first = true;
|
||||||
if (first)
|
if (first)
|
||||||
{
|
{
|
||||||
window.mouse_prev_x = x;
|
w.mouse_prev_x = x;
|
||||||
window.mouse_prev_y = y;
|
w.mouse_prev_y = y;
|
||||||
first = false;
|
first = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (x > 0 || y > 0)
|
if (x > 0 || y > 0)
|
||||||
{
|
{
|
||||||
PushMotion(inputs, window.mouse_prev_x-x, window.mouse_prev_y-y, x, y);
|
PushMotion(inputs, w.mouse_prev_x-x, w.mouse_prev_y-y, x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
window.mouse_prev_x = x;
|
w.mouse_prev_x = x;
|
||||||
window.mouse_prev_y = y;
|
w.mouse_prev_y = y;
|
||||||
|
|
||||||
if (window.locked_cursor &&
|
if (w.locked_cursor &&
|
||||||
(x < WINDOW_EDGE_BUFFER || y < WINDOW_EDGE_BUFFER || x > window.w - WINDOW_EDGE_BUFFER || y > window.h - WINDOW_EDGE_BUFFER))
|
(x < WINDOW_EDGE_BUFFER || y < WINDOW_EDGE_BUFFER || x > w.w - WINDOW_EDGE_BUFFER || y > w.h - WINDOW_EDGE_BUFFER))
|
||||||
{
|
{
|
||||||
i16 new_x = cast(i16)(window.w / 2);
|
i16 new_x = cast(i16)(w.w / 2);
|
||||||
i16 new_y = cast(i16)(window.h / 2);
|
i16 new_y = cast(i16)(w.h / 2);
|
||||||
xcb_warp_pointer(window.conn, window.window, window.window, 0, 0, cast(i16)window.w, cast(i16)window.h, new_x, new_y);
|
xcb_warp_pointer(w.conn, w.window, w.window, 0, 0, cast(i16)w.w, cast(i16)w.h, new_x, new_y);
|
||||||
window.mouse_prev_x = new_x;
|
w.mouse_prev_x = new_x;
|
||||||
window.mouse_prev_y = new_y;
|
w.mouse_prev_y = new_y;
|
||||||
ignore_mouse_events = true;
|
ignore_mouse_events = true;
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
case XCB_CONFIGURE_NOTIFY:
|
case XCB_CONFIGURE_NOTIFY:
|
||||||
{
|
{
|
||||||
xcb_configure_notify_event_t* config_event = cast(xcb_configure_notify_event_t*)e;
|
xcb_configure_notify_event_t* config_event = cast(xcb_configure_notify_event_t*)e;
|
||||||
if (window.w != config_event.width || window.h != config_event.height)
|
if (w.w != config_event.width || w.h != config_event.height)
|
||||||
{
|
{
|
||||||
window.w = config_event.width;
|
w.w = config_event.width;
|
||||||
window.h = config_event.height;
|
w.h = config_event.height;
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ReturnInputs(w);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
while (e);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Library LoadLibrary(string name)
|
Library
|
||||||
|
LoadLibrary(string name)
|
||||||
{
|
{
|
||||||
Library lib = {
|
Library lib = {
|
||||||
ptr: null,
|
ptr: null,
|
||||||
@ -466,7 +774,8 @@ Library LoadLibrary(string name)
|
|||||||
return lib;
|
return lib;
|
||||||
};
|
};
|
||||||
|
|
||||||
Function LoadFunction(Library lib, string name)
|
Function
|
||||||
|
LoadFunction(Library lib, string name)
|
||||||
{
|
{
|
||||||
Function fn = {
|
Function fn = {
|
||||||
ptr: null,
|
ptr: null,
|
||||||
@ -915,5 +1224,39 @@ struct WatchEvent
|
|||||||
|
|
||||||
version(Windows)
|
version(Windows)
|
||||||
{
|
{
|
||||||
|
import core.sys.windows.windows;
|
||||||
|
|
||||||
|
struct SysThread
|
||||||
|
{
|
||||||
|
HANDLE handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
alias Win32ThreadProc = extern (C) u32 function(void*);
|
||||||
|
|
||||||
|
SysThread
|
||||||
|
CreateThread(void* proc, void* param)
|
||||||
|
{
|
||||||
|
SysThread thread;
|
||||||
|
CreateThread(null, 0, cast(Win32ThreadProc)proc, param, 0, null);
|
||||||
|
return thread;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Suspend(SysThread* thread)
|
||||||
|
{
|
||||||
|
SuspendThread(thread.handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Wake(SysThread* thread)
|
||||||
|
{
|
||||||
|
ResumeThread(thread.handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Kill()
|
||||||
|
{
|
||||||
|
ExitThread(0);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
4
util.d
4
util.d
@ -135,13 +135,13 @@ ConcatInPlace(T)(T* list, T* to_concat)
|
|||||||
}
|
}
|
||||||
|
|
||||||
U*
|
U*
|
||||||
DLLPop(T, U)(T* list, T* nil)
|
DLLPop(T, U)(T* list, U* nil)
|
||||||
{
|
{
|
||||||
U* node = list.first;
|
U* node = list.first;
|
||||||
|
|
||||||
if (list.first == list.last)
|
if (list.first == list.last)
|
||||||
{
|
{
|
||||||
list.first = list.last = list.nil;
|
list.first = list.last = nil;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user