port from xcb to x11

This commit is contained in:
Matthew 2025-11-30 16:00:25 +11:00
parent 2ba1ae480c
commit a221c1d5ce
3 changed files with 286 additions and 187 deletions

View File

@ -1,20 +1,22 @@
//#pragma attribute(push, nogc, nothrow) //#pragma attribute(push, nogc, nothrow)
#ifdef __linux__ #ifdef __linux__
# include <xcb/xcb.h> # define XLIB_ILLEGAL_ACCESS
# include <xcb/xfixes.h> # include <X11/Xlib.h>
# include <X11/XKBlib.h> # include <X11/XKBlib.h>
# include <X11/Xlib-xcb.h> # include <X11/Xlib-xcb.h>
# include <X11/Xlib.h>
# include <X11/keysym.h> # include <X11/keysym.h>
# include <X11/extensions/Xfixes.h> # include <X11/extensions/Xfixes.h>
# include <X11/Xatom.h>
# include <X11/Xutil.h>
# include <ft2build.h> # include <ft2build.h>
# include <GL/glx.h>
# include <GL/glxext.h>
# include FT_FREETYPE_H # include FT_FREETYPE_H
# include FT_GLYPH_H # include FT_GLYPH_H
#endif #endif
#include <xmmintrin.h>
#include "external/stb/stb_image.h" #include "external/stb/stb_image.h"
#include "external/stb/stb_image_write.h" #include "external/stb/stb_image_write.h"

View File

@ -408,10 +408,10 @@ struct SysThread
struct Selection struct Selection
{ {
bool owned; bool owned;
u8[] data; u8[] data;
xcb_atom_t target; Atom target;
xcb_atom_t xmode; Atom xmode;
} }
enum Atoms enum Atoms
@ -429,15 +429,15 @@ enum Atoms
} }
const char[][] ATOM_STRS = [ const char[][] ATOM_STRS = [
CastStr!(char)("TARGETS"), Atoms.Targets: CastStr!(char)("TARGETS"),
CastStr!(char)("MULTIPLE"), Atoms.Multiple: CastStr!(char)("MULTIPLE"),
CastStr!(char)("TIMESTAMP"), Atoms.Timestamp: CastStr!(char)("TIMESTAMP"),
CastStr!(char)("INCR"), Atoms.Incr: CastStr!(char)("INCR"),
CastStr!(char)("CLIPBOARD"), Atoms.Clipboard: CastStr!(char)("CLIPBOARD"),
CastStr!(char)("UTF8_STRING"), Atoms.Utf8String: CastStr!(char)("UTF8_STRING"),
CastStr!(char)("WM_PROTOCOLS"), Atoms.WMProtocols: CastStr!(char)("WM_PROTOCOLS"),
CastStr!(char)("WM_DELETE_WINDOW"), Atoms.DeleteWindow: CastStr!(char)("WM_DELETE_WINDOW"),
CastStr!(char)("_NET_WM_STATE_HIDDEN"), Atoms.StateHidden: CastStr!(char)("_NET_WM_STATE_HIDDEN"),
]; ];
alias PThreadProc = extern (C) void* function(void*); alias PThreadProc = extern (C) void* function(void*);
@ -579,13 +579,16 @@ PushMotion(Inputs* inputs, i32 rel_x, i32 rel_y, i32 x, i32 y)
struct PlatformWindow struct PlatformWindow
{ {
xcb_atom_t[Atoms.max] atoms; Atom[Atoms.max] atoms;
Display* display; Display* display;
xcb_connection_t* conn; Window window;
xcb_screen_t* screen; Window root_window;
xcb_window_t window; i32 screen_id;
u16 w; // xcb_connection_t* conn;
u16 h; // xcb_screen_t* screen;
// xcb_window_t window;
u32 w;
u32 h;
i32 mouse_prev_x; i32 mouse_prev_x;
i32 mouse_prev_y; i32 mouse_prev_y;
bool locked_cursor; bool locked_cursor;
@ -614,51 +617,144 @@ struct Function
void* ptr; void* ptr;
}; };
pragma(inline) bool __gshared string WINDOW_ERR_MSG = null;
NilErr(PlatformWindow* window, xcb_void_cookie_t* cookie, xcb_generic_error_t* err)
string
WindowError()
{ {
bool result = err == null; return WINDOW_ERR_MSG;
pureFree(err);
return result;
} }
pragma(inline) void bool
CheckErr(PlatformWindow *window, xcb_void_cookie_t *cookie, xcb_generic_error_t *err, string msg) CreateWindow(PlatformWindow* window, string name, u32 width, u32 height, XVisualInfo* visual_info = null)
{ {
assert(err == null, msg); PlatformWindow wnd = {
pureFree(err);
};
PlatformWindow
CreateWindow(string name, u16 width, u16 height)
{
PlatformWindow window = {
w: width, w: width,
h: height, h: height,
input_mutex: CreateTicketMut(), input_mutex: CreateTicketMut(),
msg_queue: CreateMessageQueue(), msg_queue: CreateMessageQueue(),
cb_mut: CreateTicketMut(), cb_mut: CreateTicketMut(),
cb_msg_mut: CreateMut(), cb_msg_mut: CreateMut(),
inputs: [ inputs: [
{ arena: CreateArena(MB(1)) }, { arena: CreateArena(MB(1)) },
{ arena: CreateArena(MB(1)) }, { arena: CreateArena(MB(1)) },
], ],
}; };
version(linux) *window = wnd;
{
window.cb_transfer_size = X11_CB_TRANSFER_SIZE_DEFAULT;
}
assert(width > 0 && height > 0, "CreateWindow error: width and height must be above 0"); window.cb_transfer_size = X11_CB_TRANSFER_SIZE_DEFAULT;
window.display = XOpenDisplay(null); window.display = XOpenDisplay(null);
assert(window.display != null, "XOpenDisplay failure"); if(!window.display)
{
WINDOW_ERR_MSG = "Unable to open X11 display";
return false;
}
window.root_window = DefaultRootWindow(window.display);
if(window.root_window == None)
{
WINDOW_ERR_MSG = "Unable to retrieve X11 root window";
return false;
}
window.screen_id = XDefaultScreen(window.display);
XSetWindowAttributes attrs = {
background_pixmap: None,
background_pixel: XBlackPixel(window.display, window.screen_id),
};
i64 value_mask = CWBackPixmap | CWBackPixel;
if(visual_info)
{
attrs.colormap = XCreateColormap(window.display, window.root_window, visual_info.visual, AllocNone);
value_mask |= CWColormap;
}
auto copy = CopyFromParent;
window.window = XCreateWindow(
window.display,
window.root_window,
0,
0,
width,
height,
0,
(visual_info ? visual_info.depth : cast(i32)copy),
InputOutput,
(visual_info ? visual_info.visual : cast(Visual*)&copy),
value_mask,
&attrs
);
if(window.window == None)
{
WINDOW_ERR_MSG = "Failed to create X11 window";
return false;
}
if(visual_info)
{
XFree(visual_info);
}
i64 event_mask = KeyPressMask |
KeyReleaseMask |
ExposureMask |
ButtonPressMask |
ButtonReleaseMask |
PointerMotionMask |
StructureNotifyMask |
PropertyChangeMask;
XSelectInput(window.display, window.window, event_mask);
foreach(atom; Atoms.min .. Atoms.max)
{
window.atoms[atom] = XInternAtom(window.display, ATOM_STRS[atom].ptr, false);
}
XSetWMProtocols(window.display, window.window, window.atoms.ptr, window.atoms.length);
XChangeProperty(
window.display,
window.window,
XA_WM_NAME,
XA_STRING,
8,
PropModeReplace,
cast(const(u8)*)name.ptr,
cast(u32)name.length
);
XStoreName(window.display, window.window, name.ptr);
XChangeProperty(
window.display,
window.window,
window.atoms[Atoms.WMProtocols],
XA_ATOM,
32,
PropModeReplace,
cast(const(u8)*)&window.atoms[Atoms.DeleteWindow],
1
);
int major, minor;
XFixesQueryVersion(window.display, &major, &minor);
XMapWindow(window.display, window.window);
XFlush(window.display);
/*
window.conn = XGetXCBConnection(window.display); window.conn = XGetXCBConnection(window.display);
assert(window.conn != null, "XGetXCBConnection failure"); assert(window.conn != null, "XGetXCBConnection failure");
xcb_void_cookie_t cookie; xcb_void_cookie_t cookie;
xcb_generic_error_t *error; xcb_generic_error_t *error;
xcb_setup_t *setup = xcb_get_setup(window.conn); xcb_setup_t *setup = xcb_get_setup(window.conn);
@ -753,8 +849,9 @@ CreateWindow(string name, u16 width, u16 height)
assert(stream_result > 0, "xcb_flush failure"); assert(stream_result > 0, "xcb_flush failure");
xcb_xfixes_query_version(window.conn, 4, 0); xcb_xfixes_query_version(window.conn, 4, 0);
*/
return window; return true;
}; };
void void
@ -773,17 +870,9 @@ LockCursor(PlatformWindow* window)
u32 counter = 0; u32 counter = 0;
for(;;) for(;;)
{ {
xcb_generic_error_t *error; i32 grab_res = XGrabPointer(window.display, window.window, true, 0, GrabModeAsync, GrabModeAsync, window.window, None, CurrentTime);
xcb_grab_pointer_cookie_t grab_cookie = xcb_grab_pointer(window.conn, true, window.window, 0, XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC, window.window, XCB_NONE, XCB_CURRENT_TIME);
xcb_grab_pointer_reply_t* grab_reply = xcb_grab_pointer_reply(window.conn, grab_cookie, &error);
scope(exit) if(grab_res == None)
{
pureFree(error);
pureFree(grab_reply);
}
if(grab_reply.status == XCB_GRAB_STATUS_SUCCESS)
{ {
result = true; result = true;
break; break;
@ -810,7 +899,7 @@ UnlockCursor(PlatformWindow* window)
{ {
if(window.locked_cursor) if(window.locked_cursor)
{ {
xcb_ungrab_pointer(window.conn, XCB_CURRENT_TIME); XUngrabPointer(window.display, CurrentTime);
ShowCursor(window); ShowCursor(window);
window.locked_cursor = false; window.locked_cursor = false;
} }
@ -819,71 +908,78 @@ UnlockCursor(PlatformWindow* window)
bool bool
HideCursor(PlatformWindow* window) HideCursor(PlatformWindow* window)
{ {
xcb_void_cookie_t hide_cursor_cookie = xcb_xfixes_hide_cursor_checked(window.conn, window.window); XFixesHideCursor(window.display, window.window);
xcb_generic_error_t *error = xcb_request_check(window.conn, hide_cursor_cookie); return true;
return NilErr(window, &hide_cursor_cookie, error);
} }
bool bool
ShowCursor(PlatformWindow* window) ShowCursor(PlatformWindow* window)
{ {
xcb_void_cookie_t show_cursor_cookie = xcb_xfixes_show_cursor_checked(window.conn, window.window); XFixesShowCursor(window.display, window.window);
xcb_generic_error_t *error = xcb_request_check(window.conn, show_cursor_cookie); return true;
return NilErr(window, &show_cursor_cookie, error);
} }
void void
FlushEvents(PlatformWindow* window) ClearEvents(PlatformWindow* window)
{ {
xcb_generic_event_t* e; for(;;)
do
{ {
e = xcb_poll_for_event(window.conn); XEvent ev;
} while (e); u32 count = XPending(window.display);
for(u32 i = 0; i < count; i += 1)
{
XNextEvent(window.display, &ev);
}
if(count == 0)
{
break;
}
}
} }
bool bool
TransmitSelection(PlatformWindow* w, xcb_selection_request_event_t* ev) TransmitSelection(PlatformWindow* w, XSelectionRequestEvent* ev)
{ {
bool result; bool result;
if(ev.property == XCB_NONE) if(ev.property == None)
{ {
ev.property = ev.target; ev.property = ev.target;
} }
if(ev.target == w.atoms[Atoms.Targets]) if(ev.target == w.atoms[Atoms.Targets])
{ {
xcb_atom_t[3] targets = [ Atom[3] targets = [
w.atoms[Atoms.Timestamp], w.atoms[Atoms.Timestamp],
w.atoms[Atoms.Targets], w.atoms[Atoms.Targets],
w.atoms[Atoms.Utf8String] w.atoms[Atoms.Utf8String]
]; ];
xcb_change_property( XChangeProperty(
w.conn, w.display,
XCB_PROP_MODE_REPLACE,
ev.requestor, ev.requestor,
ev.property, ev.property,
XCB_ATOM_ATOM, XA_ATOM,
xcb_atom_t.sizeof * 8, Atom.sizeof * 8,
targets.length, PropModeReplace,
targets.ptr cast(const(u8)*)targets.ptr,
targets.length
); );
result = true; result = true;
} }
else if(ev.target == w.atoms[Atoms.Timestamp]) else if(ev.target == w.atoms[Atoms.Timestamp])
{ {
xcb_timestamp_t cur = XCB_CURRENT_TIME; Time cur = CurrentTime;
xcb_change_property( XChangeProperty(
w.conn, w.display,
XCB_PROP_MODE_REPLACE,
ev.requestor, ev.requestor,
ev.property, ev.property,
XCB_ATOM_INTEGER, XA_INTEGER,
cur.sizeof * 8, cur.sizeof * 8,
1, PropModeReplace,
&cur cast(const(u8)*)&cur,
1
); );
result = true; result = true;
@ -904,15 +1000,15 @@ TransmitSelection(PlatformWindow* w, xcb_selection_request_event_t* ev)
if(sel != null && sel.owned && sel.data.length > 0 && sel.target == ev.target) if(sel != null && sel.owned && sel.data.length > 0 && sel.target == ev.target)
{ {
xcb_change_property( XChangeProperty(
w.conn, w.display,
XCB_PROP_MODE_REPLACE,
ev.requestor, ev.requestor,
ev.property, ev.property,
ev.target, ev.target,
8, 8,
cast(u32)sel.data.length, PropModeReplace,
sel.data.ptr cast(const(u8)*)sel.data.ptr,
cast(u32)sel.data.length
); );
result = true; result = true;
@ -952,10 +1048,9 @@ ClipboardText(PlatformWindow* w, ClipboardMode mode)
} }
else else
{ {
auto owner = xcb_get_selection_owner_reply(w.conn, xcb_get_selection_owner(w.conn, sel.xmode), null); Window owner = XGetSelectionOwner(w.display, sel.xmode);
scope(exit) pureFree(owner);
if(owner != null && owner.owner != 0) if(owner == None)
{ {
Free(sel.data); Free(sel.data);
sel.data = []; sel.data = [];
@ -965,8 +1060,8 @@ ClipboardText(PlatformWindow* w, ClipboardMode mode)
u64 ticket = w.cb_mut.next_ticket; u64 ticket = w.cb_mut.next_ticket;
sel.target = w.atoms[Atoms.Utf8String]; sel.target = w.atoms[Atoms.Utf8String];
xcb_convert_selection(w.conn, w.window, sel.xmode, sel.target, sel.xmode, XCB_CURRENT_TIME); XConvertSelection(w.display, sel.xmode, sel.target, sel.xmode, w.window, CurrentTime);
xcb_flush(w.conn); XFlush(w.display);
while(ticket == w.cb_mut.next_ticket) {} while(ticket == w.cb_mut.next_ticket) {}
@ -1009,8 +1104,8 @@ SetClipboard(PlatformWindow* w, u8[] data, ClipboardMode mode)
sel.owned = true; sel.owned = true;
sel.target = w.atoms[Atoms.Utf8String]; sel.target = w.atoms[Atoms.Utf8String];
xcb_set_selection_owner(w.conn, w.window, sel.xmode, XCB_CURRENT_TIME); XSetSelectionOwner(w.display, sel.xmode, w.window, CurrentTime);
xcb_flush(w.conn); XFlush(w.display);
} }
else else
{ {
@ -1041,49 +1136,40 @@ GetClipboardSelection(PlatformWindow* w, Selection* sel)
} }
void void
RetrieveSelection(PlatformWindow* w, xcb_selection_notify_event_t* ev) RetrieveSelection(PlatformWindow* w, XSelectionEvent* ev)
{ {
Lock(&w.cb_mut); Lock(&w.cb_mut);
scope(exit) Unlock(&w.cb_mut); scope(exit) Unlock(&w.cb_mut);
u8[] buf; u8[] buf;
u64 buf_size; u64 buf_size;
u64 bytes_after = 1; u64 bytes_after = 1;
xcb_get_property_reply_t* reply; Atom actual_type;
xcb_atom_t actual_type; i32 actual_format;
u8 actual_format; u64 nitems;
u8* prop_data;
if(ev.property == XCB_ATOM_PRIMARY || ev.property == XCB_ATOM_SECONDARY || ev.property == w.atoms[Atoms.Clipboard]) if(ev.property == XA_PRIMARY || ev.property == XA_SECONDARY || ev.property == w.atoms[Atoms.Clipboard])
{ {
while(bytes_after > 0) while(bytes_after > 0)
{ {
scope(exit) pureFree(reply); scope(exit) XFree(prop_data);
xcb_get_property_cookie_t cookie = xcb_get_property( i32 get_res = XGetWindowProperty(
w.conn, w.display,
true,
w.window, w.window,
ev.property, ev.property,
XCB_ATOM_ANY,
cast(u32)(buf_size/4), cast(u32)(buf_size/4),
cast(u32)(w.cb_transfer_size/4) cast(u32)(w.cb_transfer_size/4),
true,
AnyPropertyType,
&actual_type,
&actual_format,
&nitems,
&bytes_after,
&prop_data
); );
reply = xcb_get_property_reply(w.conn, cookie, null);
if(reply == null || (buf_size > 0 && (reply.format != actual_format || reply.type != actual_type)) || reply.format%8 != 0)
{
Errf("RetrieveSelection failure: Invalid return value from xcb_get_property_reply");
break;
}
if(buf_size == 0)
{
actual_type = reply.type;
actual_format = reply.format;
}
int nitems = xcb_get_property_value_length(reply);
if(nitems > 0) if(nitems > 0)
{ {
if(buf_size%4 != 0) if(buf_size%4 != 0)
@ -1092,14 +1178,13 @@ RetrieveSelection(PlatformWindow* w, xcb_selection_notify_event_t* ev)
break; break;
} }
u64 unit_size = reply.format/8; u64 unit_size = actual_format/8;
buf = Alloc!(u8)(unit_size * (buf_size + nitems)); u64 data_len = unit_size * nitems;
buf = Realloc!(u8)(buf, data_len + buf_size);
MemCpy(buf.ptr + buf_size, xcb_get_property_value(reply), nitems * unit_size); MemCpy(buf.ptr + buf_size, prop_data, data_len);
buf_size += nitems * unit_size; buf_size += data_len;
} }
bytes_after = reply.bytes_after;
} }
} }
@ -1133,9 +1218,9 @@ RetrieveSelection(PlatformWindow* w, xcb_selection_notify_event_t* ev)
} }
void void
ClearSelection(PlatformWindow* w, xcb_selection_clear_event_t* ev) ClearSelection(PlatformWindow* w, XSelectionClearEvent* ev)
{ {
if(ev.owner == w.window) if(ev.window == w.window)
{ {
foreach(i; CBM.min .. CBM.max) foreach(i; CBM.min .. CBM.max)
{ {
@ -1163,7 +1248,7 @@ HandleEvents(void* window_ptr)
PlatformWindow* w = cast(PlatformWindow*)window_ptr; PlatformWindow* w = cast(PlatformWindow*)window_ptr;
DNode!(SysMessage)* sys_msg = g_NIL_MSG; DNode!(SysMessage)* sys_msg = g_NIL_MSG;
xcb_generic_event_t* e; XEvent e;
bool ignore_mouse_events = false; bool ignore_mouse_events = false;
@ -1225,39 +1310,38 @@ HandleEvents(void* window_ptr)
} }
} }
e = xcb_poll_for_event(w.conn); if(XPending(w.display))
if(e)
{ {
XNextEvent(w.display, &e);
Inputs* inputs = GetInputs(w); Inputs* inputs = GetInputs(w);
switch (e.response_type & ~0x80) switch (e.type)
{ {
case XCB_CLIENT_MESSAGE: case ClientMessage:
{ {
xcb_client_message_event_t* msg = cast(xcb_client_message_event_t*)e; XClientMessageEvent* msg = &e.xclient;
if(msg.window != w.window) if(msg.window != w.window)
{ {
break; break;
} }
if(msg.data.data32[0] == w.atoms[Atoms.DeleteWindow]) if(msg.data.l[0] == w.atoms[Atoms.DeleteWindow])
{ {
w.close = true; w.close = true;
} }
} break; } break;
case XCB_KEY_RELEASE: case KeyRelease:
case XCB_KEY_PRESS: case KeyPress:
{ {
xcb_key_press_event_t* keyboard_event = cast(xcb_key_press_event_t*)e; XKeyEvent* kb_ev = &e.xkey;
bool pressed = e.response_type == XCB_KEY_PRESS; bool pressed = e.type == KeyPress;
xcb_keycode_t code = keyboard_event.detail; u32 code = kb_ev.keycode;
KeySym key_sym = XkbKeycodeToKeysym(w.display, cast(KeyCode)code, 0, 0); KeySym key_sym = XkbKeycodeToKeysym(w.display, cast(KeyCode)code, 0, 0);
Input input = ConvertInput(key_sym); Input input = ConvertInput(key_sym);
enum modifier_inputs = [Input.LeftShift, Input.RightShift, Input.LeftCtrl, Input.RightCtrl, Input.LeftAlt, Input.RightAlt]; enum modifier_inputs = [Input.LeftShift, Input.RightShift, Input.LeftCtrl, Input.RightCtrl, Input.LeftAlt, Input.RightAlt];
enum modifiers = [MD.LeftShift, MD.RightShift, MD.LeftCtrl, MD.RightCtrl, MD.LeftAlt, MD.RightAlt]; enum modifiers = [MD.LeftShift, MD.RightShift, MD.LeftCtrl, MD.RightCtrl, MD.LeftAlt, MD.RightAlt];
static foreach(i, md; modifier_inputs) static foreach(i, md; modifier_inputs)
{ {
@ -1269,37 +1353,38 @@ HandleEvents(void* window_ptr)
if(input != Input.None) if(input != Input.None)
{ {
Push(inputs, input, keyboard_event.event_x, keyboard_event.event_y, pressed, w.modifier); Push(inputs, input, kb_ev.x, kb_ev.y, pressed, w.modifier);
} }
} break; } break;
case XCB_BUTTON_PRESS: case ButtonPress:
case XCB_BUTTON_RELEASE: case ButtonRelease:
{ {
xcb_button_press_event_t* mouse_event = cast(xcb_button_press_event_t*)e; XButtonEvent* mouse_event = &e.xbutton;
bool pressed = e.response_type == XCB_BUTTON_PRESS;
Input input = Input.None;
switch (mouse_event.detail) bool pressed = e.type == ButtonPress;
Input input = Input.None;
switch (mouse_event.button)
{ {
case XCB_BUTTON_INDEX_1: input = Input.LeftClick; break; case Button1: input = Input.LeftClick; break;
case XCB_BUTTON_INDEX_2: input = Input.MiddleClick; break; case Button2: input = Input.MiddleClick; break;
case XCB_BUTTON_INDEX_3: input = Input.RightClick; break; case Button3: input = Input.RightClick; break;
default: break; default: break;
} }
if(input != Input.None) if(input != Input.None)
{ {
Push(inputs, input, mouse_event.event_x, mouse_event.event_y, pressed, w.modifier); Push(inputs, input, mouse_event.x, mouse_event.y, pressed, w.modifier);
} }
} break; } break;
case XCB_MOTION_NOTIFY: case MotionNotify:
{ {
if(ignore_mouse_events) continue; if(ignore_mouse_events) continue;
xcb_motion_notify_event_t* move_event = cast(xcb_motion_notify_event_t*)e; XMotionEvent* move_event = &e.xmotion;
i16 x = move_event.event_x; i32 x = move_event.x;
i16 y = move_event.event_y; i32 y = move_event.y;
static bool first = true; static bool first = true;
if(first) if(first)
@ -1320,45 +1405,48 @@ HandleEvents(void* window_ptr)
if(w.locked_cursor && if(w.locked_cursor &&
(x < WINDOW_EDGE_BUFFER || y < WINDOW_EDGE_BUFFER || x > w.w - WINDOW_EDGE_BUFFER || y > w.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)(w.w / 2); i32 new_x = cast(i32)(w.w / 2);
i16 new_y = cast(i16)(w.h / 2); i32 new_y = cast(i32)(w.h / 2);
xcb_warp_pointer(w.conn, w.window, w.window, 0, 0, cast(i16)w.w, cast(i16)w.h, new_x, new_y); XWarpPointer(w.display, w.window, w.window, 0, 0, cast(i16)w.w, cast(i16)w.h, new_x, new_y);
w.mouse_prev_x = new_x; w.mouse_prev_x = new_x;
w.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 ConfigureNotify:
{ {
xcb_configure_notify_event_t* config_event = cast(xcb_configure_notify_event_t*)e; XConfigureEvent* config_event = &e.xconfigure;
if(w.w != config_event.width || w.h != config_event.height) if(w.w != config_event.width || w.h != config_event.height)
{ {
w.w = config_event.width; w.w = config_event.width;
w.h = config_event.height; w.h = config_event.height;
} }
} break; } break;
case XCB_SELECTION_CLEAR: case SelectionClear:
{ {
ClearSelection(w, cast(xcb_selection_clear_event_t*)e); ClearSelection(w, &e.xselectionclear);
} break; } break;
case XCB_SELECTION_NOTIFY: case SelectionNotify:
{ {
RetrieveSelection(w, cast(xcb_selection_notify_event_t*)e); RetrieveSelection(w, &e.xselection);
} break; } break;
case XCB_SELECTION_REQUEST: case SelectionRequest:
{ {
auto req = cast(xcb_selection_request_event_t*)e; auto req = &e.xselectionrequest;
xcb_selection_notify_event_t notify = { XEvent notify = {
response_type: XCB_SELECTION_NOTIFY, xselection: {
time: XCB_CURRENT_TIME, type: SelectionNotify,
requestor: req.requestor, time: CurrentTime,
selection: req.selection, display: w.display,
target: req.target, requestor: req.requestor,
property: TransmitSelection(w, req) ? req.property : XCB_NONE, selection: req.selection,
target: req.target,
property: TransmitSelection(w, req) ? req.property : None,
},
}; };
xcb_send_event(w.conn, false, req.requestor, XCB_EVENT_MASK_PROPERTY_CHANGE, cast(char*)&notify); XSendEvent(w.display, req.requestor, false, PropertyChangeMask, &notify);
xcb_flush(w.conn); XFlush(w.display);
} break; } break;
default: break; default: break;
} }

9
util.d
View File

@ -33,6 +33,15 @@ Pause()
} }
} }
void
Assert(T)(T cond, string msg)
{
if(!cond)
{
assert(false, msg);
}
}
string string
ConvToStr(T)(T[] arr) ConvToStr(T)(T[] arr)
{ {