From 4ab7b1574723eb74c7ea739e7b3629c30a945154 Mon Sep 17 00:00:00 2001 From: IoIxD Date: Mon, 14 Sep 2026 16:11:01 -0700 Subject: [PATCH 1/2] xdnd --- include/Mw/LowLevel/X11.h | 44 +++++++---- src/backend/x11.c | 149 +++++++++++++++++++++++++++++++++++--- 2 files changed, 169 insertions(+), 24 deletions(-) diff --git a/include/Mw/LowLevel/X11.h b/include/Mw/LowLevel/X11.h index e1b74169..96f49833 100644 --- a/include/Mw/LowLevel/X11.h +++ b/include/Mw/LowLevel/X11.h @@ -27,20 +27,36 @@ struct _MwLLX11 { unsigned int width; unsigned int height; - Display* display; - Window window; - Pixmap pixmap; - GC gc; - Colormap colormap; - Atom wm_delete; - Atom wm_protocols; - Atom utf8_string; - Atom compound_text; - Atom text; - Atom clipboard; - Atom selection; - XIM xim; - XIC xic; + Display* display; + Window window; + Pixmap pixmap; + GC gc; + Colormap colormap; + Atom wm_delete; + Atom wm_protocols; + Atom utf8_string; + Atom compound_text; + Atom text; + Atom clipboard; + Atom selection; + Atom xdnd_type_list; + Atom xdnd_selection; + Atom xdnd_enter; + Atom xdnd_position; + Atom xdnd_status; + Atom xdnd_leave; + Atom xdnd_drop; + Atom xdnd_finished; + Atom xdnd_action_copy; + Atom xdnd_text_uri_list; + Atom xdnd_text_plain; + Atom xdnd_aware; + int xdnd_source; + unsigned char xdnd_version; + int format; + + XIM xim; + XIC xic; long clipboard_time; int clipboard_pending; /* 1 if UTF8_STRING, 2 if COMPOUND_TEXT, 3 if TEXT, 4 if STRING, otherwise 0 */ diff --git a/src/backend/x11.c b/src/backend/x11.c index 8b908e47..295c5145 100644 --- a/src/backend/x11.c +++ b/src/backend/x11.c @@ -82,6 +82,8 @@ static struct symtbl { void (*XSetICFocus)(XIC); XImage* (*XGetImage)(Display*, Drawable, int, int, unsigned int, unsigned int, unsigned long, int); int (*XRaiseWindow)(Display*, Window); + int (*XFlush)(Display*); + char* (*XGetAtomName)(Display*, Atom); XSizeHints* (*XAllocSizeHints)(void); XVisualInfo* (*XGetVisualInfo)(Display*, long, XVisualInfo*, int*); @@ -186,6 +188,8 @@ static struct symtbl { #define XChangeWindowAttributes xsymtbl.XChangeWindowAttributes #define XGetImage xsymtbl.XGetImage #define XRaiseWindow xsymtbl.XRaiseWindow +#define XFlush xsymtbl.XFlush +#define XGetAtomName xsymtbl.XGetAtomName #if USE_XRENDER #define XRenderFindStandardFormat xsymtbl.XRenderFindStandardFormat @@ -425,6 +429,7 @@ static MwLL MwLLCreateImpl(MwLL parent, int x, int y, int width, int height) { r->x11.colormap = DefaultColormap(r->x11.display, XDefaultScreen(r->x11.display)); r->x11.wm_delete = XInternAtom(r->x11.display, "WM_DELETE_WINDOW", False); r->x11.wm_protocols = XInternAtom(r->x11.display, "WM_PROTOCOLS", False); + XSetWMProtocols(r->x11.display, r->x11.window, &r->x11.wm_delete, 1); r->x11.utf8_string = XInternAtom(r->x11.display, "UTF8_STRING", False); @@ -433,6 +438,22 @@ static MwLL MwLLCreateImpl(MwLL parent, int x, int y, int width, int height) { r->x11.clipboard = XInternAtom(r->x11.display, "CLIPBOARD", False); r->x11.selection = XInternAtom(r->x11.display, "_MILSKO_SELECTION_", False); + r->x11.xdnd_type_list = XInternAtom(r->x11.display, "XdndTypeList", False); + r->x11.xdnd_selection = XInternAtom(r->x11.display, "XdndSelection", False); + r->x11.xdnd_enter = XInternAtom(r->x11.display, "XdndEnter", False); + r->x11.xdnd_position = XInternAtom(r->x11.display, "XdndPosition", False); + r->x11.xdnd_status = XInternAtom(r->x11.display, "XdndStatus", False); + r->x11.xdnd_leave = XInternAtom(r->x11.display, "XdndLeave", False); + r->x11.xdnd_drop = XInternAtom(r->x11.display, "XdndDrop", False); + r->x11.xdnd_finished = XInternAtom(r->x11.display, "XdndFinished", False); + r->x11.xdnd_action_copy = XInternAtom(r->x11.display, "XdndActionCopy", False); + r->x11.xdnd_text_uri_list = XInternAtom(r->x11.display, "text/uri-list", False); + r->x11.xdnd_text_plain = XInternAtom(r->x11.display, "text/plain", False); + r->x11.xdnd_aware = XInternAtom(r->x11.display, "XdndAware", False); + r->x11.xdnd_version = 5; + r->x11.format = None; + XChangeProperty(r->x11.display, r->x11.window, r->x11.xdnd_aware, 4, 32, PropModeReplace, &r->x11.xdnd_version, 1); + r->x11.clipboard_pending = 0; r->x11.gc = XCreateGC(r->x11.display, r->x11.window, 0, NULL); @@ -704,10 +725,15 @@ static void MwLLNextEventImpl(MwLL handle) { while(XCheckTypedWindowEvent(handle->x11.display, handle->x11.window, ClientMessage, &ev) || XCheckTypedWindowEvent(handle->x11.display, handle->x11.window, SelectionNotify, &ev) || XCheckWindowEvent(handle->x11.display, handle->x11.window, mask, &ev)) { int render = 0; - if(ev.type == Expose) { + switch(ev.type) { + case Expose: + { handle->x11.force_render = 0; render = 1; - } else if(ev.type == ButtonPress) { + break; + }; + case ButtonPress: + { MwMouse p; p.point.x = ev.xbutton.x; p.point.y = ev.xbutton.y; @@ -728,7 +754,10 @@ static void MwLLNextEventImpl(MwLL handle) { #endif MwLLDispatch(handle, down, &p); - } else if(ev.type == ButtonRelease) { + break; + }; + case ButtonRelease: + { MwMouse p; p.point.x = ev.xbutton.x; p.point.y = ev.xbutton.y; @@ -745,24 +774,114 @@ static void MwLLNextEventImpl(MwLL handle) { } MwLLDispatch(handle, up, &p); - } else if(ev.type == ConfigureNotify) { + break; + }; + case ConfigureNotify: + { if(handle->x11.width != (unsigned int)ev.xconfigure.width || handle->x11.height != (unsigned int)ev.xconfigure.height) { MwLLDispatch(handle, resize, NULL); destroy_pixmap(handle); create_pixmap(handle); render = 1; + break; } handle->x11.width = ev.xconfigure.width; handle->x11.height = ev.xconfigure.height; - } else if(ev.type == ClientMessage) { + }; + case ClientMessage: + { + if(ev.xclient.message_type == handle->x11.wm_protocols && ev.xclient.data.l[0] == (long)handle->x11.wm_delete) { MwLLDispatch(handle, close, NULL); } - } else if(ev.type == FocusIn) { + + if( + ev.xclient.message_type == handle->x11.xdnd_enter || + ev.xclient.message_type == handle->x11.xdnd_selection || + ev.xclient.message_type == handle->x11.xdnd_drop) { + char* name = XGetAtomName(handle->x11.display, ev.xclient.message_type); + + printf("%s\n", name); + + if(ev.xclient.message_type == handle->x11.xdnd_enter) { + Bool list = ev.xclient.data.l[1] & 1; + int version = ev.xclient.data.l[1] >> 24; + unsigned long count = 0; + unsigned char* formats = NULL; + handle->x11.xdnd_source = ev.xclient.data.l[0]; + + unsigned long i; + if(list) { + Atom actualType; + int32_t actualFormat; + unsigned long bytesAfter; + + XGetWindowProperty((Display*)handle->x11.display, + handle->x11.xdnd_source, + handle->x11.xdnd_type_list, + 0, + LONG_MAX, + False, + 4, + &actualType, + &actualFormat, + &count, + &bytesAfter, + (unsigned char**)&formats); + } else { + count = 0; + + if(ev.xclient.data.l[2] != None) + formats[count++] = ev.xclient.data.l[2]; + if(ev.xclient.data.l[3] != None) + formats[count++] = ev.xclient.data.l[3]; + if(ev.xclient.data.l[4] != None) + formats[count++] = ev.xclient.data.l[4]; + } + + for(i = 0; i < count; i++) { + if(formats[i] == handle->x11.xdnd_text_uri_list || formats[i] == handle->x11.xdnd_text_plain) { + handle->x11.format = formats[i]; + break; + } + } + + if(list) { + XFree(formats); + } + } + if(ev.xclient.message_type == handle->x11.xdnd_selection && handle->x11.xdnd_version >= 5) { + XEvent reply; + reply.xclient.message_type = handle->x11.xdnd_status; + + if(handle->x11.format) { + reply.xclient.data.l[1] = 1; + if(handle->x11.xdnd_version >= 2) + reply.xclient.data.l[4] = handle->x11.xdnd_action_copy; + } + XSendEvent(handle->x11.display, handle->x11.xdnd_source, False, NoEventMask, &reply); + XFlush(handle->x11.display); + } + if(ev.xclient.message_type == handle->x11.xdnd_drop && handle->x11.xdnd_version >= 5) { + printf("whar\n"); + } + + XFree(name); + } + break; + }; + case FocusIn: + { MwLLDispatch(handle, focus_in, NULL); - } else if(ev.type == FocusOut) { + break; + }; + case FocusOut: + { MwLLDispatch(handle, focus_out, NULL); - } else if(ev.type == MotionNotify) { + break; + }; + case MotionNotify: + { MwPoint p; XWindowAttributes attr; @@ -780,7 +899,11 @@ static void MwLLNextEventImpl(MwLL handle) { if(handle->x11.grabbed && (p.x != 0 || p.y != 0)) { XWarpPointer(handle->x11.display, None, handle->x11.window, 0, 0, 0, 0, attr.width / 2, attr.height / 2); } - } else if(ev.type == KeyPress || ev.type == KeyRelease) { + break; + }; + case KeyPress: + case KeyRelease: + { int n = -1; char str[512]; KeySym sym; @@ -844,7 +967,10 @@ static void MwLLNextEventImpl(MwLL handle) { MwLLDispatch(handle, key_released, &n); } } - } else if(ev.type == SelectionNotify) { + break; + }; + case SelectionNotify: + { handle->x11.clipboard_pending = 0; if(ev.xselection.property != None) { @@ -887,6 +1013,7 @@ static void MwLLNextEventImpl(MwLL handle) { XDeleteProperty(handle->x11.display, handle->x11.window, handle->x11.selection); } + } break; } if(render) { int x, y; @@ -1512,6 +1639,8 @@ static int MwLLX11CallInitImpl(void) { X11_FUNC_LOAD(XSetICFocus); X11_FUNC_LOAD(XGetImage); X11_FUNC_LOAD(XRaiseWindow); + X11_FUNC_LOAD(XFlush); + X11_FUNC_LOAD(XGetAtomName); X11_FUNC_LOAD(XAllocSizeHints); X11_FUNC_LOAD(XGetVisualInfo); From 4bdbfffb78522aafc29cb39ab2066da8b7f0c82e Mon Sep 17 00:00:00 2001 From: IoIxD Date: Mon, 14 Sep 2026 16:45:58 -0700 Subject: [PATCH 2/2] xdnd code that i can't really test but i think it might work? --- include/Mw/LowLevel/X11.h | 2 +- src/backend/x11.c | 90 ++++++++++++++++++++++++++++++--------- 2 files changed, 71 insertions(+), 21 deletions(-) diff --git a/include/Mw/LowLevel/X11.h b/include/Mw/LowLevel/X11.h index 96f49833..9663097c 100644 --- a/include/Mw/LowLevel/X11.h +++ b/include/Mw/LowLevel/X11.h @@ -53,7 +53,7 @@ struct _MwLLX11 { Atom xdnd_aware; int xdnd_source; unsigned char xdnd_version; - int format; + int xdnd_format; XIM xim; XIC xic; diff --git a/src/backend/x11.c b/src/backend/x11.c index 295c5145..c6db2b84 100644 --- a/src/backend/x11.c +++ b/src/backend/x11.c @@ -451,8 +451,7 @@ static MwLL MwLLCreateImpl(MwLL parent, int x, int y, int width, int height) { r->x11.xdnd_text_plain = XInternAtom(r->x11.display, "text/plain", False); r->x11.xdnd_aware = XInternAtom(r->x11.display, "XdndAware", False); r->x11.xdnd_version = 5; - r->x11.format = None; - XChangeProperty(r->x11.display, r->x11.window, r->x11.xdnd_aware, 4, 32, PropModeReplace, &r->x11.xdnd_version, 1); + r->x11.xdnd_format = None; r->x11.clipboard_pending = 0; @@ -797,20 +796,17 @@ static void MwLLNextEventImpl(MwLL handle) { if( ev.xclient.message_type == handle->x11.xdnd_enter || - ev.xclient.message_type == handle->x11.xdnd_selection || + ev.xclient.message_type == handle->x11.xdnd_position || ev.xclient.message_type == handle->x11.xdnd_drop) { char* name = XGetAtomName(handle->x11.display, ev.xclient.message_type); - printf("%s\n", name); - if(ev.xclient.message_type == handle->x11.xdnd_enter) { - Bool list = ev.xclient.data.l[1] & 1; - int version = ev.xclient.data.l[1] >> 24; - unsigned long count = 0; - unsigned char* formats = NULL; + Bool list = ev.xclient.data.l[1] & 1; + int version = ev.xclient.data.l[1] >> 24; + unsigned long count = 0; + unsigned char* formats = NULL; + unsigned long i; handle->x11.xdnd_source = ev.xclient.data.l[0]; - - unsigned long i; if(list) { Atom actualType; int32_t actualFormat; @@ -841,7 +837,8 @@ static void MwLLNextEventImpl(MwLL handle) { for(i = 0; i < count; i++) { if(formats[i] == handle->x11.xdnd_text_uri_list || formats[i] == handle->x11.xdnd_text_plain) { - handle->x11.format = formats[i]; + handle->x11.xdnd_format = formats[i]; + printf("format %d\n", handle->x11.xdnd_format); break; } } @@ -850,20 +847,30 @@ static void MwLLNextEventImpl(MwLL handle) { XFree(formats); } } - if(ev.xclient.message_type == handle->x11.xdnd_selection && handle->x11.xdnd_version >= 5) { + if(ev.xclient.message_type == handle->x11.xdnd_position && handle->x11.xdnd_version >= 5) { XEvent reply; reply.xclient.message_type = handle->x11.xdnd_status; - if(handle->x11.format) { + if(handle->x11.xdnd_format) { reply.xclient.data.l[1] = 1; - if(handle->x11.xdnd_version >= 2) - reply.xclient.data.l[4] = handle->x11.xdnd_action_copy; + if(handle->x11.xdnd_version >= 2) reply.xclient.data.l[4] = handle->x11.xdnd_action_copy; } XSendEvent(handle->x11.display, handle->x11.xdnd_source, False, NoEventMask, &reply); XFlush(handle->x11.display); } if(ev.xclient.message_type == handle->x11.xdnd_drop && handle->x11.xdnd_version >= 5) { - printf("whar\n"); + Time time = CurrentTime; + if(handle->x11.xdnd_version >= 1) + time = ev.xclient.data.l[2]; + + if(handle->x11.xdnd_format) { + XConvertSelection((Display*)handle->x11.display, + handle->x11.xdnd_selection, + handle->x11.xdnd_format, + handle->x11.xdnd_selection, + (Window)handle->x11.window, + time); + } } XFree(name); @@ -973,7 +980,51 @@ static void MwLLNextEventImpl(MwLL handle) { { handle->x11.clipboard_pending = 0; - if(ev.xselection.property != None) { + if(ev.xselection.property == handle->x11.xdnd_selection) { + XEvent reply; + char* data; + unsigned long result; + Atom actualType; + int32_t actualFormat; + unsigned long bytesAfter; + + XGetWindowProperty(handle->x11.display, + ev.xselection.requestor, ev.xselection.property, + 0, LONG_MAX, False, ev.xselection.target, + &actualType, &actualFormat, &result, &bytesAfter, + (unsigned char**)&data); + + if(result == 0) + break; + + /* !! untested lmao !! */ + printf("File(s) dropped: %s\n", data); + if(strstr(data, "\n")) { + char* data_ptr = data; + while(data_ptr) { + if(strstr(data_ptr, "file://")) { + MwLLDispatch(handle, drag_and_drop, data_ptr + sizeof("file://") - 1); + } else { + MwLLDispatch(handle, drag_and_drop, data_ptr); + } + data_ptr = strstr(data, "\n"); + } + } else { + MwLLDispatch(handle, drag_and_drop, data); + } + + if(data) + XFree(data); + + if(handle->x11.xdnd_version >= 2) { + reply.xclient.message_type = handle->x11.xdnd_finished; + reply.xclient.data.l[1] = result; + reply.xclient.data.l[2] = handle->x11.xdnd_action_copy; + + XSendEvent((Display*)handle->x11.display, handle->x11.xdnd_source, False, NoEventMask, &reply); + XFlush((Display*)handle->x11.display); + } + } else if(ev.xselection.property != None) { Atom type; int format; unsigned long nitems, after, size; @@ -1010,7 +1061,6 @@ static void MwLLNextEventImpl(MwLL handle) { free(buf); } } - XDeleteProperty(handle->x11.display, handle->x11.window, handle->x11.selection); } } break; @@ -1492,7 +1542,7 @@ static void MwLLGetScreenSizeImpl(MwLL handle, MwRect* rect) { } static void MwLLSetupDragAndDropImpl(MwLL handle) { - (void)handle; + XChangeProperty(handle->x11.display, handle->x11.window, handle->x11.xdnd_aware, 4, 32, PropModeReplace, &handle->x11.xdnd_version, 1); } static void MwLLBeginStateChangeImpl(MwLL handle) {