1
0
mirror of https://github.com/Tha14/toxic.git synced 2024-07-01 15:07:46 +02:00

Dnd works

This commit is contained in:
mannol 2014-10-11 23:53:02 +02:00
parent 70add920fe
commit c825adc1e3
3 changed files with 300 additions and 111 deletions

View File

@ -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();

View File

@ -2,96 +2,255 @@
#include <X11/Xlib.h>
#include <X11/Xatom.h>
// #include <X11/X.h>
// #include <X11/Xutil.h>
#include <pthread.h>
#include <assert.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>
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();
}

View File

@ -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 */
#endif /* XTRA_H */