diff --git a/src/toxic.c b/src/toxic.c index be880b5..98cb1d8 100644 --- a/src/toxic.c +++ b/src/toxic.c @@ -967,6 +967,12 @@ static useconds_t optimal_msleepval(uint64_t *looptimer, uint64_t *loopcount, ui return new_sleep; } +void cb(const char* asdv, DropType dt) +{ + if (dt != DT_plain) + line_info_add(prompt, NULL, NULL, NULL, SYS_MSG, 0, 0, asdv); +} + int main(int argc, char *argv[]) { parse_args(argc, argv); @@ -1004,7 +1010,7 @@ int main(int argc, char *argv[]) int settings_err = settings_load(user_settings, p); #ifdef X11 - xtra_init(); + xtra_init(cb); #endif Tox *m = init_tox(); diff --git a/src/xtra.c b/src/xtra.c index f930b7b..1afbf14 100644 --- a/src/xtra.c +++ b/src/xtra.c @@ -2,96 +2,255 @@ #include #include -// #include -// #include #include #include +#include +#include +#include -static Display *the_d = NULL; -static Window self_window; /* We will assume that during initialization - * we have this window focused */ -Atom XdndAware, - XdndEnter, - XdndLeave, - XdndPosition, - XdndStatus, - XdndDrop, - XdndSelection, - XdndDATA, - XdndActionCopy, - XdndActionAsk, - XdndTypeList; +const Atom XtraTerminate = 1; +const Atom XtraNil = 0; +static Atom XdndAware; +static Atom XdndEnter; +static Atom XdndLeave; +static Atom XdndPosition; +static Atom XdndStatus; +static Atom XdndDrop; +static Atom XdndSelection; +static Atom XdndDATA; +static Atom XdndTypeList; +static Atom XdndActionCopy; +static Atom XdndFinished; + +struct _Xtra { + drop_callback on_drop; + Display *display; + Window terminal_window; + Window proxy_window; + Window source_window; /* When we have a drop */ + Atom handling_version; + Atom expecting_type; +} Xtra; + +typedef struct _Property +{ + unsigned char *data; + int read_format; + unsigned long read_num; + Atom read_type; +} Property; + +Property read_property(Window s, Atom p) +{ + Atom read_type; + int read_format; + unsigned long read_num; + unsigned long left_bytes; + unsigned char *data = NULL; + + int read_bytes = 1024; + + /* Keep trying to read the property until there are no bytes unread */ + do { + if (data) XFree(data); + + XGetWindowProperty(Xtra.display, s, + p, 0, + read_bytes, + False, AnyPropertyType, + &read_type, &read_format, + &read_num, &left_bytes, + &data); + + read_bytes *= 2; + } while (left_bytes != 0); + + Property property = {data, read_format, read_num, read_type}; + return property; +} + +Atom get_dnd_type(long *a, int l) +{ + int i = 0; + for (; i < l; i ++) { + if (a[i] != XtraNil) return a[i]; /* Get first valid */ + } + return XtraNil; +} + +/* TODO maybe support only certain types in the future */ +static void handle_xdnd_enter(XClientMessageEvent* e) +{ + Xtra.handling_version = (e->data.l[1] >> 24); + + if ((e->data.l[1] & 1)) { + // Fetch the list of possible conversions + Property p = read_property(e->data.l[0], XdndTypeList); + Xtra.expecting_type = get_dnd_type((long*)p.data, p.read_num); + XFree(p.data); + } else { + // Use the available list + Xtra.expecting_type = get_dnd_type(e->data.l + 2, 3); + } +} + +static void handle_xdnd_position(XClientMessageEvent* e) +{ + XEvent ev = { + .xclient = { + .type = ClientMessage, + .display = e->display, + .window = e->data.l[0], + .message_type = XdndStatus, + .format = 32, + .data = { + .l = { + Xtra.proxy_window, + (Xtra.expecting_type != XtraNil), + 0, 0, + XdndActionCopy + } + } + } + }; + + XSendEvent(Xtra.display, e->data.l[0], False, NoEventMask, &ev); + XFlush(Xtra.display); +} + +static void handle_xdnd_drop(XClientMessageEvent* e) +{ + /* Not expecting any type */ + if (Xtra.expecting_type == XtraNil) { + XEvent ev = { + .xclient = { + .type = ClientMessage, + .display = e->display, + .window = e->data.l[0], + .message_type = XdndFinished, + .format = 32, + .data = { + .l = {Xtra.proxy_window, 0, 0} + } + } + }; + + XSendEvent(Xtra.display, e->data.l[0], False, NoEventMask, &ev); + } else { + Xtra.source_window = e->data.l[0]; + XConvertSelection(Xtra.display, + XdndSelection, + Xtra.expecting_type, + XdndSelection, + Xtra.proxy_window, + Xtra.handling_version >= 1 ? e->data.l[2] : CurrentTime); + } +} + +static void handle_xdnd_selection(XSelectionEvent* e) +{ + /* DnD succesfully finished, send finished and call callback */ + XEvent ev = { + .xclient = { + .type = ClientMessage, + .display = Xtra.display, + .window = Xtra.source_window, + .message_type = XdndFinished, + .format = 32, + .data = { + .l = {Xtra.proxy_window, 1, XdndActionCopy} + } + } + }; + XSendEvent(Xtra.display, Xtra.source_window, False, NoEventMask, &ev); + + Property p = read_property(Xtra.proxy_window, XdndSelection); + DropType dt; + + if (strcmp(XGetAtomName(Xtra.display, p.read_type), "text/uri-list") == 0) + dt = DT_file_list; + else /* text/uri-list */ + dt = DT_plain; + + + /* Call callback for every entry */ + if (Xtra.on_drop && p.read_num) + { + char *sptr; + char *str = strtok_r((char*)p.data, "\n\r", &sptr); + + if (str) Xtra.on_drop(str, dt); + while ((str = strtok_r(NULL, "\n\r", &sptr))) + Xtra.on_drop(str, dt); + } + + if (p.data) XFree(p.data); +} void *event_loop(void* p) { - (void) p; + /* Handle events like a real nigga */ + + (void) p; /* DINDUNOTHIN */ XEvent event; - while (the_d) + int pending; + + while (Xtra.display) { - XNextEvent(the_d, &event); + /* NEEDMOEVENTSFODEMPROGRAMS */ - switch (event.type) + XLockDisplay(Xtra.display); + if((pending = XPending(Xtra.display))) XNextEvent(Xtra.display, &event); + + if (!pending) { - case ClientMessage: - { - assert(0); - if(event.xclient.message_type == XdndEnter) { - } else if(event.xclient.message_type == XdndPosition) { - Window src = event.xclient.data.l[0]; - XEvent event = { - .xclient = { - .type = ClientMessage, - .display = the_d, - .window = src, - .message_type = XdndStatus, - .format = 32, - .data = { - .l = {self_window, 1, 0, 0, XdndActionCopy} - } - } - }; - XSendEvent(the_d, src, 0, 0, &event); - } else if(event.xclient.message_type == XdndStatus) { - } else if(event.xclient.message_type == XdndDrop) { - XConvertSelection(the_d, XdndSelection, XA_STRING, XdndDATA, self_window, CurrentTime); - } else if(event.xclient.message_type == XdndLeave) { - } else { - goto exit; - } - - } break; - - default: -// XSendEvent(the_d, self_window, 0, 0, &event); - break; + XUnlockDisplay(Xtra.display); + usleep(10000); + continue; } + + if (event.type == ClientMessage) + { + Atom type = event.xclient.message_type; + + if (type == XdndEnter) handle_xdnd_enter(&event.xclient); + else if (type == XdndPosition) handle_xdnd_position(&event.xclient); + else if (type == XdndDrop) handle_xdnd_drop(&event.xclient); + else if (type == XtraTerminate) break; + } + else if (event.type == SelectionNotify) handle_xdnd_selection(&event.xselection); + /* AINNOBODYCANHANDLEDEMEVENTS*/ + else XSendEvent(Xtra.display, Xtra.terminal_window, 0, 0, &event); + + XUnlockDisplay(Xtra.display); } -exit: /* Actual XTRA termination * Please call xtra_terminate() at exit * otherwise bad stuff happens */ - if (the_d) XCloseDisplay(the_d); - return NULL; + if (Xtra.display) XCloseDisplay(Xtra.display); + return (Xtra.display = NULL); } -void xtra_init() +int xtra_init(drop_callback d) { - the_d = XOpenDisplay(NULL); - self_window = xtra_focused_window_id(); + memset(&Xtra, 0, sizeof(Xtra)); - Window m_wndProxy; + if (!d) return -1; + else Xtra.on_drop = d; + + XInitThreads(); + if ( !(Xtra.display = XOpenDisplay(NULL))) return -1; + + Xtra.terminal_window = xtra_focused_window_id(); { - /* Create an invisible window which will act as proxy for the DnD - * operation. This window will be used for both the GH and HG - * direction. - */ + /* Create an invisible window which will act as proxy for the DnD operation. */ XSetWindowAttributes attr = {0}; attr.event_mask = EnterWindowMask | LeaveWindowMask | @@ -99,9 +258,9 @@ void xtra_init() ButtonPressMask | ButtonReleaseMask | ResizeRedirectMask; - + attr.do_not_propagate_mask = NoEventMask; - + Window root; int x, y; unsigned int wht, hht, b, d; @@ -109,71 +268,88 @@ void xtra_init() /* Since we cannot capture resize events for parent window we will have to create * this window to have maximum size as defined in root window */ - XGetGeometry(the_d, XDefaultRootWindow(the_d), &root, &x, &y, &wht, &hht, &b, &d); + XGetGeometry(Xtra.display, + XDefaultRootWindow(Xtra.display), + &root, &x, &y, &wht, &hht, &b, &d); - m_wndProxy = XCreateWindow(the_d, self_window /* Parent */, - 0, 0, /* Position */ - wht, hht, /* Width + height */ - 0, /* Border width */ - CopyFromParent, /* Depth */ - InputOnly, /* Class */ - CopyFromParent, /* Visual */ - CWDontPropagate | CWEventMask | CWCursor, /* Value mask */ - &attr /* Attributes for value mask */); - if (!m_wndProxy) - { - //TODO return status - assert(0); - } + if (! (Xtra.proxy_window = XCreateWindow + (Xtra.display, Xtra.terminal_window, /* Parent */ + 0, 0, /* Position */ + wht, hht, /* Width + height */ + 0, /* Border width */ + CopyFromParent, /* Depth */ + InputOnly, /* Class */ + CopyFromParent, /* Visual */ + CWEventMask | CWCursor, /* Value mask */ + &attr)) ) /* Attributes for value mask */ + return -1; } - XMapWindow(the_d, m_wndProxy); - XLowerWindow(the_d, m_wndProxy); /* Don't interfere with parent lmao */ + XMapWindow(Xtra.display, Xtra.proxy_window); /* Show window (sandwich) */ + XLowerWindow(Xtra.display, Xtra.proxy_window); /* Don't interfere with parent lmao */ + + XdndAware = XInternAtom(Xtra.display, "XdndAware", False); + XdndEnter = XInternAtom(Xtra.display, "XdndEnter", False); + XdndLeave = XInternAtom(Xtra.display, "XdndLeave", False); + XdndPosition = XInternAtom(Xtra.display, "XdndPosition", False); + XdndStatus = XInternAtom(Xtra.display, "XdndStatus", False); + XdndDrop = XInternAtom(Xtra.display, "XdndDrop", False); + XdndSelection = XInternAtom(Xtra.display, "XdndSelection", False); + XdndDATA = XInternAtom(Xtra.display, "XdndDATA", False); + XdndTypeList = XInternAtom(Xtra.display, "XdndTypeList", False); + XdndActionCopy = XInternAtom(Xtra.display, "XdndActionCopy", False); + XdndFinished = XInternAtom(Xtra.display, "XdndFinished", False); - self_window = m_wndProxy; - - XdndAware = XInternAtom(the_d, "XdndAware", False); - XdndEnter = XInternAtom(the_d, "XdndEnter", False); - XdndLeave = XInternAtom(the_d, "XdndLeave", False); - XdndPosition = XInternAtom(the_d, "XdndPosition", False); - XdndStatus = XInternAtom(the_d, "XdndStatus", False); - XdndDrop = XInternAtom(the_d, "XdndDrop", False); - XdndSelection = XInternAtom(the_d, "XdndSelection", False); - XdndDATA = XInternAtom(the_d, "XdndDATA", False); - XdndActionCopy = XInternAtom(the_d, "XdndActionCopy", False); - XdndActionAsk = XInternAtom(the_d, "XdndActionAsk", False); - XdndTypeList = XInternAtom(the_d, "XdndTypeList", False); - - Atom Xdndversion = 3; - XChangeProperty(the_d, m_wndProxy, XdndAware, XA_ATOM, 32, PropModeReplace, (unsigned char*)&Xdndversion, 1); + /* Inform my nigga windows that we are aware of dnd */ + Atom XdndVersion = 3; + XChangeProperty(Xtra.display, + Xtra.proxy_window, + XdndAware, + XA_ATOM, + 32, + PropModeReplace, + (unsigned char*)&XdndVersion, 1); pthread_t id; pthread_create(&id, NULL, event_loop, NULL); pthread_detach(id); + + return 0; } void xtra_terminate() { - XEvent terminate_event; - terminate_event.xclient.display = the_d; - terminate_event.type = ClientMessage; + if (!Xtra.display) return; + XEvent terminate = { + .xclient = { + .type = ClientMessage, + .display = Xtra.display, + .message_type = XtraTerminate, + } + }; - XDeleteProperty(the_d, self_window, XdndAware); - XSendEvent(the_d, self_window, 0, NoEventMask, &terminate_event); + XLockDisplay(Xtra.display); + XDeleteProperty(Xtra.display, Xtra.proxy_window, XdndAware); + XSendEvent(Xtra.display, Xtra.proxy_window, 0, NoEventMask, &terminate); + XUnlockDisplay(Xtra.display); + + while (Xtra.display); /* Wait for termination */ } long unsigned int xtra_focused_window_id() { - if (!the_d) return 0; + if (!Xtra.display) return 0; Window focus; int revert; - XGetInputFocus(the_d, &focus, &revert); + XLockDisplay(Xtra.display); + XGetInputFocus(Xtra.display, &focus, &revert); + XUnlockDisplay(Xtra.display); return focus; } int xtra_is_this_focused() { - return self_window == xtra_focused_window_id(); + return Xtra.proxy_window == xtra_focused_window_id(); } diff --git a/src/xtra.h b/src/xtra.h index 1159adf..89a096d 100644 --- a/src/xtra.h +++ b/src/xtra.h @@ -3,10 +3,17 @@ /* NOTE: If no xlib present don't compile */ -void xtra_init(); -void xtra_terminate(); +typedef enum { + DT_plain, + DT_file_list +} +DropType; +typedef void (*drop_callback) (const char*, DropType); + +int xtra_init(drop_callback d); +void xtra_terminate(); long unsigned int xtra_focused_window_id(); -int xtra_is_this_focused(); /* returns bool */ +int xtra_is_this_focused(); /* returns bool */ -#endif /* DND_H */ \ No newline at end of file +#endif /* XTRA_H */ \ No newline at end of file