add x11 clipboard
This commit is contained in:
parent
2af4a29314
commit
57e3634691
471
platform.d
471
platform.d
@ -159,6 +159,16 @@ struct InputEvent
|
|||||||
i32 rel_y;
|
i32 rel_y;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum ClipboardMode
|
||||||
|
{
|
||||||
|
Clipboard,
|
||||||
|
Primary,
|
||||||
|
Selection = Primary,
|
||||||
|
Secondary,
|
||||||
|
Max
|
||||||
|
}
|
||||||
|
|
||||||
|
alias CBM = ClipboardMode;
|
||||||
|
|
||||||
struct Inputs
|
struct Inputs
|
||||||
{
|
{
|
||||||
@ -166,6 +176,24 @@ struct Inputs
|
|||||||
Arena arena;
|
Arena arena;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pragma(inline) bool
|
||||||
|
Shift(Modifier md)
|
||||||
|
{
|
||||||
|
return cast(bool)(md & (MD.LeftShift | MD.RightShift));
|
||||||
|
}
|
||||||
|
|
||||||
|
pragma(inline) bool
|
||||||
|
Ctrl(Modifier md)
|
||||||
|
{
|
||||||
|
return cast(bool)(md & (MD.LeftCtrl | MD.RightCtrl));
|
||||||
|
}
|
||||||
|
|
||||||
|
pragma(inline) bool
|
||||||
|
Alt(Modifier md)
|
||||||
|
{
|
||||||
|
return cast(bool)(md & (MD.LeftAlt | MD.RightAlt));
|
||||||
|
}
|
||||||
|
|
||||||
Inputs*
|
Inputs*
|
||||||
GetEvents(PlatformWindow* window)
|
GetEvents(PlatformWindow* window)
|
||||||
{
|
{
|
||||||
@ -347,6 +375,7 @@ import core.sys.posix.dlfcn;
|
|||||||
import core.sys.posix.sys.mman;
|
import core.sys.posix.sys.mman;
|
||||||
import core.sys.linux.sys.inotify;
|
import core.sys.linux.sys.inotify;
|
||||||
import core.sys.linux.fcntl;
|
import core.sys.linux.fcntl;
|
||||||
|
import core.sys.posix.sys.time : timespec, timeval, gettimeofday;
|
||||||
import core.sys.posix.unistd;
|
import core.sys.posix.unistd;
|
||||||
import core.sys.posix.pthread : PThread = pthread_t,
|
import core.sys.posix.pthread : PThread = pthread_t,
|
||||||
PThreadCond = pthread_cond_t,
|
PThreadCond = pthread_cond_t,
|
||||||
@ -358,10 +387,14 @@ import core.sys.posix.pthread : PThread = pthread_t,
|
|||||||
PThreadCondWait = pthread_cond_wait,
|
PThreadCondWait = pthread_cond_wait,
|
||||||
PThreadMutexUnlock = pthread_mutex_unlock,
|
PThreadMutexUnlock = pthread_mutex_unlock,
|
||||||
PThreadCondSignal = pthread_cond_signal,
|
PThreadCondSignal = pthread_cond_signal,
|
||||||
PThreadExit = pthread_exit;
|
PThreadExit = pthread_exit,
|
||||||
|
PThreadCondTimedWait = pthread_cond_timedwait;
|
||||||
|
|
||||||
import core.stdc.string : strlen;
|
import core.stdc.string : strlen;
|
||||||
|
|
||||||
|
const u32 X11_CB_TRANSFER_SIZE_DEFAULT = 1048576;
|
||||||
|
const u32 X11_TIMEOUT_DEFAULT = 1500;
|
||||||
|
|
||||||
struct SysThread
|
struct SysThread
|
||||||
{
|
{
|
||||||
PThread handle;
|
PThread handle;
|
||||||
@ -369,6 +402,40 @@ struct SysThread
|
|||||||
PThreadMutex mut;
|
PThreadMutex mut;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct Selection
|
||||||
|
{
|
||||||
|
bool owned;
|
||||||
|
u8[] data;
|
||||||
|
xcb_atom_t target;
|
||||||
|
xcb_atom_t xmode;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Atoms
|
||||||
|
{
|
||||||
|
Targets,
|
||||||
|
Multiple,
|
||||||
|
Timestamp,
|
||||||
|
Incr,
|
||||||
|
Clipboard,
|
||||||
|
Utf8String,
|
||||||
|
WMProtocols,
|
||||||
|
DeleteWindow,
|
||||||
|
StateHidden,
|
||||||
|
Max,
|
||||||
|
}
|
||||||
|
|
||||||
|
const char[][] ATOM_STRS = [
|
||||||
|
CastStr!(char)("TARGETS"),
|
||||||
|
CastStr!(char)("MULTIPLE"),
|
||||||
|
CastStr!(char)("TIMESTAMP"),
|
||||||
|
CastStr!(char)("INCR"),
|
||||||
|
CastStr!(char)("CLIPBOARD"),
|
||||||
|
CastStr!(char)("UTF8_STRING"),
|
||||||
|
CastStr!(char)("WM_PROTOCOLS"),
|
||||||
|
CastStr!(char)("WM_DELETE_WINDOW"),
|
||||||
|
CastStr!(char)("_NET_WM_STATE_HIDDEN"),
|
||||||
|
];
|
||||||
|
|
||||||
alias PThreadProc = extern (C) void* function(void*);
|
alias PThreadProc = extern (C) void* function(void*);
|
||||||
|
|
||||||
SysThread
|
SysThread
|
||||||
@ -442,12 +509,11 @@ PushMotion(Inputs* inputs, i32 rel_x, i32 rel_y, i32 x, i32 y)
|
|||||||
|
|
||||||
struct PlatformWindow
|
struct PlatformWindow
|
||||||
{
|
{
|
||||||
|
xcb_atom_t[Atoms.max] atoms;
|
||||||
Display* display;
|
Display* display;
|
||||||
xcb_connection_t* conn;
|
xcb_connection_t* conn;
|
||||||
xcb_screen_t* screen;
|
xcb_screen_t* screen;
|
||||||
xcb_window_t window;
|
xcb_window_t window;
|
||||||
xcb_atom_t close_event;
|
|
||||||
xcb_atom_t minimize_event;
|
|
||||||
u16 w;
|
u16 w;
|
||||||
u16 h;
|
u16 h;
|
||||||
i32 mouse_prev_x;
|
i32 mouse_prev_x;
|
||||||
@ -461,6 +527,10 @@ struct PlatformWindow
|
|||||||
u32 input_idx;
|
u32 input_idx;
|
||||||
Inputs[2] inputs;
|
Inputs[2] inputs;
|
||||||
TicketMut input_mutex;
|
TicketMut input_mutex;
|
||||||
|
|
||||||
|
Mut cb_mut;
|
||||||
|
Selection[CBM.max] selections;
|
||||||
|
version(linux) u32 cb_transfer_size;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Library
|
struct Library
|
||||||
@ -496,12 +566,18 @@ CreateWindow(string name, u16 width, u16 height)
|
|||||||
h: height,
|
h: height,
|
||||||
input_mutex: CreateTicketMut(),
|
input_mutex: CreateTicketMut(),
|
||||||
msg_queue: CreateMessageQueue(),
|
msg_queue: CreateMessageQueue(),
|
||||||
|
cb_mut: CreateMut(),
|
||||||
inputs: [
|
inputs: [
|
||||||
{ arena: CreateArena(MB(1)) },
|
{ arena: CreateArena(MB(1)) },
|
||||||
{ arena: CreateArena(MB(1)) },
|
{ arena: CreateArena(MB(1)) },
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
version(linux)
|
||||||
|
{
|
||||||
|
window.cb_transfer_size = X11_CB_TRANSFER_SIZE_DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
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");
|
||||||
|
|
||||||
window.display = XOpenDisplay(null);
|
window.display = XOpenDisplay(null);
|
||||||
@ -523,7 +599,8 @@ CreateWindow(string name, u16 width, u16 height)
|
|||||||
XCB_EVENT_MASK_BUTTON_PRESS |
|
XCB_EVENT_MASK_BUTTON_PRESS |
|
||||||
XCB_EVENT_MASK_BUTTON_RELEASE |
|
XCB_EVENT_MASK_BUTTON_RELEASE |
|
||||||
XCB_EVENT_MASK_POINTER_MOTION |
|
XCB_EVENT_MASK_POINTER_MOTION |
|
||||||
XCB_EVENT_MASK_STRUCTURE_NOTIFY;
|
XCB_EVENT_MASK_STRUCTURE_NOTIFY|
|
||||||
|
XCB_EVENT_MASK_PROPERTY_CHANGE;
|
||||||
|
|
||||||
i32[2] val_win = [window.screen.black_pixel, event_mask];
|
i32[2] val_win = [window.screen.black_pixel, event_mask];
|
||||||
i32 val_mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK;
|
i32 val_mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK;
|
||||||
@ -568,42 +645,36 @@ CreateWindow(string name, u16 width, u16 height)
|
|||||||
CheckErr(&window, &cookie, error, "xcb_change_property_checked failure");
|
CheckErr(&window, &cookie, error, "xcb_change_property_checked failure");
|
||||||
pureFree(error);
|
pureFree(error);
|
||||||
|
|
||||||
xcb_intern_atom_cookie_t c_proto = xcb_intern_atom(window.conn, 1, 12, "WM_PROTOCOLS");
|
for(u64 i = 0; i < Atoms.max; i += 1)
|
||||||
xcb_intern_atom_reply_t *r_proto = xcb_intern_atom_reply(window.conn, c_proto, &error);
|
{
|
||||||
CheckErr(&window, &cookie, error, "xcb_intern_atom WM_PROTOCOLS failure");
|
xcb_intern_atom_cookie_t intern = xcb_intern_atom(window.conn, 1, cast(u16)ATOM_STRS[i].length, ATOM_STRS[i].ptr);
|
||||||
pureFree(error);
|
xcb_intern_atom_reply_t* reply = xcb_intern_atom_reply(window.conn, intern, &error);
|
||||||
|
CheckErr(&window, &cookie, error, "xcb_intern_atom failure");
|
||||||
|
|
||||||
xcb_intern_atom_cookie_t c_close = xcb_intern_atom(window.conn, 0, 16, "WM_DELETE_WINDOW");
|
window.atoms[i] = reply.atom;
|
||||||
xcb_intern_atom_reply_t *r_close = xcb_intern_atom_reply(window.conn, c_close, &error);
|
|
||||||
CheckErr(&window, &cookie, error, "xcb_intern_atom WM_DELETE_WINDOW failure");
|
|
||||||
pureFree(error);
|
|
||||||
|
|
||||||
xcb_intern_atom_cookie_t c_minimize = xcb_intern_atom(window.conn, 0, 20, "_NET_WM_STATE_HIDDEN");
|
|
||||||
xcb_intern_atom_reply_t *r_minimize = xcb_intern_atom_reply(window.conn, c_minimize, &error);
|
|
||||||
CheckErr(&window, &cookie, error, "xcb_intern_atom _NET_WM_STATE_HIDDEN failure");
|
|
||||||
pureFree(error);
|
pureFree(error);
|
||||||
|
pureFree(reply);
|
||||||
|
}
|
||||||
|
|
||||||
|
window.selections[CBM.Clipboard].xmode = window.atoms[Atoms.Clipboard];
|
||||||
|
window.selections[CBM.Primary].xmode = XCB_ATOM_PRIMARY;
|
||||||
|
window.selections[CBM.Secondary].xmode = XCB_ATOM_SECONDARY;
|
||||||
|
|
||||||
cookie = xcb_change_property_checked(
|
cookie = xcb_change_property_checked(
|
||||||
window.conn,
|
window.conn,
|
||||||
XCB_PROP_MODE_REPLACE,
|
XCB_PROP_MODE_REPLACE,
|
||||||
window.window,
|
window.window,
|
||||||
r_proto.atom,
|
window.atoms[Atoms.WMProtocols],
|
||||||
XCB_ATOM_ATOM,
|
XCB_ATOM_ATOM,
|
||||||
32,
|
32,
|
||||||
1,
|
1,
|
||||||
&r_close.atom
|
&window.atoms[Atoms.DeleteWindow]
|
||||||
);
|
);
|
||||||
error = xcb_request_check(window.conn, cookie);
|
error = xcb_request_check(window.conn, cookie);
|
||||||
CheckErr(&window, &cookie, error, "xcb_change_property_checked failure");
|
CheckErr(&window, &cookie, error, "xcb_change_property_checked failure");
|
||||||
pureFree(error);
|
pureFree(error);
|
||||||
|
|
||||||
window.close_event = r_close.atom;
|
|
||||||
window.minimize_event = r_minimize.atom;
|
|
||||||
|
|
||||||
pureFree(r_proto);
|
|
||||||
pureFree(r_close);
|
|
||||||
pureFree(r_minimize);
|
|
||||||
|
|
||||||
xcb_map_window(window.conn, window.window);
|
xcb_map_window(window.conn, window.window);
|
||||||
|
|
||||||
i32 stream_result = xcb_flush(window.conn);
|
i32 stream_result = xcb_flush(window.conn);
|
||||||
@ -699,6 +770,333 @@ FlushEvents(PlatformWindow* window)
|
|||||||
} while (e);
|
} while (e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
TransmitSelection(PlatformWindow* w, xcb_selection_request_event_t* ev)
|
||||||
|
{
|
||||||
|
bool result;
|
||||||
|
if(ev.property == XCB_NONE)
|
||||||
|
{
|
||||||
|
ev.property = ev.target;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(ev.target == w.atoms[Atoms.Targets])
|
||||||
|
{
|
||||||
|
xcb_atom_t[3] targets = [
|
||||||
|
w.atoms[Atoms.Timestamp],
|
||||||
|
w.atoms[Atoms.Targets],
|
||||||
|
w.atoms[Atoms.Utf8String]
|
||||||
|
];
|
||||||
|
|
||||||
|
xcb_change_property(
|
||||||
|
w.conn,
|
||||||
|
XCB_PROP_MODE_REPLACE,
|
||||||
|
ev.requestor,
|
||||||
|
ev.property,
|
||||||
|
XCB_ATOM_ATOM,
|
||||||
|
xcb_atom_t.sizeof * 8,
|
||||||
|
targets.length,
|
||||||
|
targets.ptr
|
||||||
|
);
|
||||||
|
|
||||||
|
result = true;
|
||||||
|
}
|
||||||
|
else if(ev.target == w.atoms[Atoms.Timestamp])
|
||||||
|
{
|
||||||
|
xcb_timestamp_t cur = XCB_CURRENT_TIME;
|
||||||
|
xcb_change_property(
|
||||||
|
w.conn,
|
||||||
|
XCB_PROP_MODE_REPLACE,
|
||||||
|
ev.requestor,
|
||||||
|
ev.property,
|
||||||
|
XCB_ATOM_INTEGER,
|
||||||
|
cur.sizeof * 8,
|
||||||
|
1,
|
||||||
|
&cur
|
||||||
|
);
|
||||||
|
|
||||||
|
result = true;
|
||||||
|
}
|
||||||
|
else if(ev.target == w.atoms[Atoms.Utf8String])
|
||||||
|
{
|
||||||
|
Selection* sel = null;
|
||||||
|
if(TryLock(&w.cb_mut))
|
||||||
|
{
|
||||||
|
foreach(i; CBM.min .. CBM.max)
|
||||||
|
{
|
||||||
|
if(w.selections[i].xmode == ev.selection)
|
||||||
|
{
|
||||||
|
sel = &w.selections[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(sel != null && sel.owned && sel.data.length > 0 && sel.target == ev.target)
|
||||||
|
{
|
||||||
|
xcb_change_property(
|
||||||
|
w.conn,
|
||||||
|
XCB_PROP_MODE_REPLACE,
|
||||||
|
ev.requestor,
|
||||||
|
ev.property,
|
||||||
|
ev.target,
|
||||||
|
8,
|
||||||
|
cast(u32)sel.data.length,
|
||||||
|
sel.data.ptr
|
||||||
|
);
|
||||||
|
|
||||||
|
result = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Unlock(&w.cb_mut);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
ClipboardOwns(PlatformWindow* w, ClipboardMode mode)
|
||||||
|
{
|
||||||
|
bool result;
|
||||||
|
|
||||||
|
if(TryLock(&w.cb_mut))
|
||||||
|
{
|
||||||
|
result = w.selections[mode].owned;
|
||||||
|
Unlock(&w.cb_mut);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
u8[]
|
||||||
|
ClipboardText(PlatformWindow* w, ClipboardMode mode)
|
||||||
|
{
|
||||||
|
u8[] buf;
|
||||||
|
|
||||||
|
if(TryLock(&w.cb_mut))
|
||||||
|
{
|
||||||
|
scope(exit) Unlock(&w.cb_mut);
|
||||||
|
|
||||||
|
Selection* sel = &w.selections[mode];
|
||||||
|
|
||||||
|
if(sel.owned)
|
||||||
|
{
|
||||||
|
buf = GetClipboardSelection(w, sel);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
timeval now;
|
||||||
|
timespec timeout;
|
||||||
|
int pret = 0;
|
||||||
|
|
||||||
|
auto owner = xcb_get_selection_owner_reply(w.conn, xcb_get_selection_owner(w.conn, sel.xmode), null);
|
||||||
|
scope(exit) pureFree(owner);
|
||||||
|
|
||||||
|
if(owner != null && owner.owner != 0)
|
||||||
|
{
|
||||||
|
FreeArray(sel.data);
|
||||||
|
sel.data = [];
|
||||||
|
|
||||||
|
sel.target = w.atoms[Atoms.Utf8String];
|
||||||
|
xcb_convert_selection(w.conn, w.window, sel.xmode, sel.target, sel.xmode, XCB_CURRENT_TIME);
|
||||||
|
xcb_flush(w.conn);
|
||||||
|
|
||||||
|
gettimeofday(&now, null);
|
||||||
|
|
||||||
|
timeout.tv_sec = now.tv_sec + (X11_TIMEOUT_DEFAULT / 1000);
|
||||||
|
timeout.tv_nsec = (now.tv_usec * 1000UL) * ((X11_TIMEOUT_DEFAULT % 1000) * 1000000UL);
|
||||||
|
if(timeout.tv_nsec >= 1000000000UL)
|
||||||
|
{
|
||||||
|
timeout.tv_sec += timeout.tv_nsec / 1000000000UL;
|
||||||
|
timeout.tv_nsec = timeout.tv_nsec % 1000000000UL;
|
||||||
|
}
|
||||||
|
|
||||||
|
while(pret == 0 && sel.data.length == 0)
|
||||||
|
{
|
||||||
|
pret = PThreadCondTimedWait(&w.thread.cond, &w.thread.mut, &timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
GetClipboardSelection(w, sel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
u8[]
|
||||||
|
ClipboardText(PlatformWindow* w)
|
||||||
|
{
|
||||||
|
return ClipboardText(w, CBM.Clipboard);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
SetClipboard(PlatformWindow* w, u8[] data, ClipboardMode mode)
|
||||||
|
{
|
||||||
|
bool result = true;
|
||||||
|
|
||||||
|
if(data.length > 0 && TryLock(&w.cb_mut))
|
||||||
|
{
|
||||||
|
Selection* sel = &w.selections[mode];
|
||||||
|
if(sel.data.length > 0)
|
||||||
|
{
|
||||||
|
FreeArray(sel.data);
|
||||||
|
sel.data = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
sel.data = Alloc!(u8)(data.length+1);
|
||||||
|
if(sel.data.length > 0)
|
||||||
|
{
|
||||||
|
MemCpy(sel.data.ptr, data.ptr, data.length);
|
||||||
|
|
||||||
|
sel.data[sel.data.length-1] = '\0';
|
||||||
|
sel.owned = true;
|
||||||
|
sel.target = w.atoms[Atoms.Utf8String];
|
||||||
|
|
||||||
|
xcb_set_selection_owner(w.conn, w.window, sel.xmode, XCB_CURRENT_TIME);
|
||||||
|
xcb_flush(w.conn);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
SetClipboard(PlatformWindow* w, u8[] data)
|
||||||
|
{
|
||||||
|
return SetClipboard(w, data, CBM.Clipboard);
|
||||||
|
}
|
||||||
|
|
||||||
|
u8[]
|
||||||
|
GetClipboardSelection(PlatformWindow* w, Selection* sel)
|
||||||
|
{
|
||||||
|
u8[] buf;
|
||||||
|
|
||||||
|
if(sel.data.length > 0 && sel.target == w.atoms[Atoms.Utf8String])
|
||||||
|
{
|
||||||
|
buf = ScratchAllocCopy(sel.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
RetrieveSelection(PlatformWindow* w, xcb_selection_notify_event_t* ev)
|
||||||
|
{
|
||||||
|
u8[] buf;
|
||||||
|
u64 buf_size;
|
||||||
|
u64 bytes_after = 1;
|
||||||
|
xcb_get_property_reply_t* reply;
|
||||||
|
xcb_atom_t actual_type;
|
||||||
|
u8 actual_format;
|
||||||
|
|
||||||
|
if(ev.property == XCB_ATOM_PRIMARY || ev.property == XCB_ATOM_SECONDARY || ev.property == w.atoms[Atoms.Clipboard])
|
||||||
|
{
|
||||||
|
while(bytes_after > 0)
|
||||||
|
{
|
||||||
|
scope(exit) pureFree(reply);
|
||||||
|
|
||||||
|
xcb_get_property_cookie_t cookie = xcb_get_property(
|
||||||
|
w.conn,
|
||||||
|
true,
|
||||||
|
w.window,
|
||||||
|
ev.property,
|
||||||
|
XCB_ATOM_ANY,
|
||||||
|
cast(u32)(buf_size/4),
|
||||||
|
cast(u32)(w.cb_transfer_size/4)
|
||||||
|
);
|
||||||
|
|
||||||
|
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(buf_size%4 != 0)
|
||||||
|
{
|
||||||
|
Errf("RetrieveSelection failure: Data size is not a multiple of 4");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 unit_size = reply.format/8;
|
||||||
|
buf = Alloc!(u8)(unit_size * (buf_size + nitems));
|
||||||
|
|
||||||
|
MemCpy(buf.ptr + buf_size, xcb_get_property_value(reply), nitems * unit_size);
|
||||||
|
buf_size += nitems * unit_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes_after = reply.bytes_after;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(buf != null && TryLock(&w.cb_mut))
|
||||||
|
{
|
||||||
|
Selection* sel;
|
||||||
|
foreach(i; CBM.min .. CBM.max)
|
||||||
|
{
|
||||||
|
if(w.selections[i].xmode == ev.property)
|
||||||
|
{
|
||||||
|
sel = &w.selections[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(sel != null && sel.target == actual_type)
|
||||||
|
{
|
||||||
|
FreeArray(sel.data);
|
||||||
|
sel.data = buf;
|
||||||
|
buf = [];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Errf("RetrieveSelection failure: mismatched selection actual_type: %s", actual_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
Unlock(&w.cb_mut);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
FreeArray(buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ClearSelection(PlatformWindow* w, xcb_selection_clear_event_t* ev)
|
||||||
|
{
|
||||||
|
if(ev.owner == w.window)
|
||||||
|
{
|
||||||
|
foreach(i; CBM.min .. CBM.max)
|
||||||
|
{
|
||||||
|
Selection* sel = &w.selections[i];
|
||||||
|
if(sel.xmode == ev.selection && TryLock(&w.cb_mut))
|
||||||
|
{
|
||||||
|
FreeArray(sel.data);
|
||||||
|
sel.data = [];
|
||||||
|
sel.owned = false;
|
||||||
|
sel.target = XCB_NONE;
|
||||||
|
|
||||||
|
Unlock(&w.cb_mut);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
HandleEvents(void* window_ptr)
|
HandleEvents(void* window_ptr)
|
||||||
{
|
{
|
||||||
@ -782,7 +1180,7 @@ HandleEvents(void* window_ptr)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(msg.data.data32[0] == w.close_event)
|
if(msg.data.data32[0] == w.atoms[Atoms.DeleteWindow])
|
||||||
{
|
{
|
||||||
w.close = true;
|
w.close = true;
|
||||||
}
|
}
|
||||||
@ -878,6 +1276,29 @@ HandleEvents(void* window_ptr)
|
|||||||
w.h = config_event.height;
|
w.h = config_event.height;
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
|
case XCB_SELECTION_CLEAR:
|
||||||
|
{
|
||||||
|
ClearSelection(w, cast(xcb_selection_clear_event_t*)e);
|
||||||
|
} break;
|
||||||
|
case XCB_SELECTION_NOTIFY:
|
||||||
|
{
|
||||||
|
RetrieveSelection(w, cast(xcb_selection_notify_event_t*)e);
|
||||||
|
} break;
|
||||||
|
case XCB_SELECTION_REQUEST:
|
||||||
|
{
|
||||||
|
auto req = cast(xcb_selection_request_event_t*)e;
|
||||||
|
xcb_selection_notify_event_t notify = {
|
||||||
|
response_type: XCB_SELECTION_NOTIFY,
|
||||||
|
time: XCB_CURRENT_TIME,
|
||||||
|
requestor: req.requestor,
|
||||||
|
selection: req.selection,
|
||||||
|
target: req.target,
|
||||||
|
property: TransmitSelection(w, req) ? req.property : XCB_NONE,
|
||||||
|
};
|
||||||
|
|
||||||
|
xcb_send_event(w.conn, false, req.requestor, XCB_EVENT_MASK_PROPERTY_CHANGE, cast(char*)¬ify);
|
||||||
|
xcb_flush(w.conn);
|
||||||
|
} break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
17
util.d
17
util.d
@ -6,7 +6,7 @@ import dlib.alloc;
|
|||||||
import xxhash3;
|
import xxhash3;
|
||||||
import includes;
|
import includes;
|
||||||
|
|
||||||
import std.stdio;
|
import std.stdio : write, writeln, writefln, stderr;
|
||||||
import std.conv;
|
import std.conv;
|
||||||
import std.string;
|
import std.string;
|
||||||
import std.traits;
|
import std.traits;
|
||||||
@ -43,6 +43,7 @@ Logf(Args...)(string fmt, Args args)
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
write("[INFO]: ");
|
||||||
writefln(fmt, args);
|
writefln(fmt, args);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
@ -51,6 +52,20 @@ Logf(Args...)(string fmt, Args args)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Errf(Args...)(string fmt, Args args)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
stderr.write("[ERROR]: ");
|
||||||
|
stderr.writef(fmt, args, '\n');
|
||||||
|
}
|
||||||
|
catch(Exception e)
|
||||||
|
{
|
||||||
|
assert(false, "Incompatible format type");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
Log(string str)
|
Log(string str)
|
||||||
{
|
{
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user