Created
September 1, 2011 19:36
-
-
Save borisfaure/1187059 to your computer and use it in GitHub Desktop.
dummy dnd test
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* gcc -ggdb3 -std=gnu99 dnd.c -o dnd -lX11 */ | |
#include <stdbool.h> | |
#include <stdio.h> | |
#include <stdint.h> | |
#include <X11/Xlib.h> | |
#include <X11/Xutil.h> | |
#include <X11/Xos.h> | |
#include <X11/Xatom.h> | |
#define XDND_VERSION 5 | |
static struct { | |
Display *display; | |
int screen; | |
Window win; | |
Window root; | |
bool done; | |
struct { | |
Atom delete_window; | |
Atom utf8_string; | |
Atom xdnd_aware; | |
Atom xdnd_enter; | |
Atom xdnd_type_list; | |
Atom xdnd_finished; | |
Atom xdnd_selection; | |
Atom xdnd_leave; | |
Atom xdnd_position; | |
} atom; | |
} _G = { | |
.display = NULL, | |
.screen = 0, | |
.win = 0, | |
.done = false, | |
}; | |
static void | |
send_xdnd_finished (const Window *source) | |
{ | |
XEvent event; | |
XClientMessageEvent xclient = event.xclient; | |
memset(xclient.data.l, 0, sizeof(xclient.data.l)); | |
xclient.type = ClientMessage; | |
xclient.display = _G.display; | |
xclient.window = *source; | |
xclient.message_type = _G.atom.xdnd_finished; | |
xclient.format = 32; | |
xclient.data.l[0] = _G.win; | |
XSendEvent(_G.display, *source, false, NoEventMask, &event); | |
} | |
static void | |
handle_drag_enter(const XClientMessageEvent *event) | |
{ | |
const Atom *types = &event->data.l[2]; | |
unsigned long type_count = 3; /* 3 types stored max in msg struct */ | |
unsigned char *raw_data = NULL; | |
Window *source_window = (Window*) &event->data.l[0]; | |
/* more than 3 types? */ | |
if (event->data.l[1] & 1) { | |
Atom actual_type; | |
int format; | |
unsigned long byte_count = UINT32_MAX; | |
printf("source supports more than 3 types.\n"); | |
XGetWindowProperty(_G.display, *source_window, _G.atom.xdnd_type_list, | |
0, 0, False, XA_ATOM, &actual_type, &format, | |
&type_count, &byte_count, &raw_data); | |
XGetWindowProperty(_G.display, *source_window, _G.atom.xdnd_type_list, | |
0, byte_count, False, XA_ATOM, | |
&actual_type, &format, | |
&type_count, &byte_count, &raw_data); | |
if (actual_type == XA_ATOM && type_count > 0) { | |
printf("using raw data: %ju %ju %p\n", | |
type_count, byte_count, raw_data); | |
types = (const Atom *)raw_data; | |
} | |
} | |
printf("Source window has the following %d type(s):\n", (int) type_count); | |
for (unsigned long i = 0; i < type_count; i++) { | |
Atom type = types[i]; | |
char *name; | |
if (type == None) { | |
printf("[%ju] = None\n", i); | |
continue; | |
} | |
name = XGetAtomName(_G.display, type); | |
printf("[%ju] = %s\n", i, name); | |
XFree(name); | |
} | |
XFree(raw_data); | |
send_xdnd_finished(source_window); | |
} | |
static bool | |
is_xdnd_aware(Window w) | |
{ | |
Atom actual; | |
int format; | |
unsigned long count, remaining; | |
unsigned char *data = 0; | |
XGetWindowProperty (_G.display, w, | |
_G.atom.xdnd_aware, 0, 0x8000000L, | |
False, AnyPropertyType, &actual, &format, | |
&count, &remaining, &data); | |
printf("format=%d, count = %ju, data = %p\n", format, count, data); | |
if (format != 32 || count == 0 || !data) { | |
printf("%x is not xdnd aware\n", (unsigned int)w); | |
XFree (data); | |
return false; | |
} | |
XFree (data); | |
printf("%x is xdnd aware!!!\n", (unsigned int)w); | |
return true; | |
} | |
static void | |
send_xdnd_enter(Window w) | |
{ | |
XEvent xevent; | |
memset(&xevent, 0, sizeof (xevent)); | |
xevent.xany.type = ClientMessage; | |
xevent.xany.display = _G.display; | |
xevent.xclient.window = w; | |
xevent.xclient.message_type = _G.atom.xdnd_enter; | |
xevent.xclient.format = 32; | |
xevent.xclient.data.l[0] = _G.win; | |
xevent.xclient.data.l[1] = '5' << 24; | |
xevent.xclient.data.l[2] = _G.atom.utf8_string; | |
XSendEvent(_G.display, w, False, 0, &xevent); | |
} | |
static void | |
handle_drag(XEvent event) | |
{ | |
Window child_orig = None, w; | |
Window root_return, child_return; | |
bool left = false; | |
unsigned int mask_return; | |
printf("drag from %x\n", (unsigned int)_G.win); | |
XSetSelectionOwner(_G.display, _G.atom.xdnd_selection, _G.win, | |
CurrentTime); | |
if (XGetSelectionOwner(_G.display, _G.atom.xdnd_selection) | |
!= _G.win) { | |
printf("failed to set xdnd selection owner\n"); | |
return; | |
} | |
if (XGrabPointer (_G.display, _G.win, False, | |
ButtonMotionMask | PointerMotionMask | |
| ButtonPressMask | ButtonReleaseMask | |
| EnterWindowMask | LeaveWindowMask, | |
GrabModeAsync, GrabModeAsync, None, | |
None, CurrentTime) != GrabSuccess) | |
{ | |
printf("failed grabbing cursor"); | |
} | |
if (XQueryPointer(_G.display, _G.root, | |
&root_return, &child_orig, | |
&event.xmotion.x_root, &event.xmotion.y_root, | |
&event.xmotion.x, &event.xmotion.y, | |
&mask_return)) { | |
} | |
event.xany.type = ButtonPress; | |
w = _G.root; | |
while (event.xany.type != ButtonRelease) { | |
XAllowEvents(_G.display, SyncPointer, CurrentTime); | |
XNextEvent(_G.display, &event); | |
switch (event.type) { | |
case MotionNotify: | |
{ | |
if (!left) | |
break; | |
if (XQueryPointer(_G.display, w, | |
&root_return, &child_return, | |
&event.xmotion.x_root, &event.xmotion.y_root, | |
&event.xmotion.x, &event.xmotion.y, | |
&mask_return)) { | |
if (child_return != None && child_return != child_orig) { | |
if (is_xdnd_aware(child_return)) { | |
send_xdnd_enter(child_return); | |
} | |
w = child_return; | |
} | |
} | |
} | |
break; | |
case EnterNotify: | |
{ | |
left = false; | |
} | |
break; | |
case LeaveNotify: | |
{ | |
left = true; | |
} | |
break; | |
} | |
} | |
XUngrabPointer (_G.display, CurrentTime); | |
} | |
static void | |
handle_event(const XEvent *event) | |
{ | |
switch (event->type) { | |
case ClientMessage: | |
{ | |
const XClientMessageEvent *xclient = &event->xclient; | |
if (xclient->message_type == _G.atom.xdnd_enter) { | |
handle_drag_enter(xclient); | |
} else | |
if (xclient->data.l[0] == _G.atom.delete_window) { | |
_G.done = true; | |
} | |
} | |
break; | |
case ButtonPress: | |
handle_drag(*event); | |
break; | |
} | |
} | |
int | |
main(int argc, char **argv) | |
{ | |
_G.display = XOpenDisplay(NULL); | |
_G.screen = DefaultScreen(_G.display); | |
_G.root = RootWindow(_G.display, _G.screen); | |
_G.win = XCreateSimpleWindow(_G.display, _G.root, 0, 0, 640, 480, 4, | |
BlackPixel(_G.display, _G.screen), | |
WhitePixel(_G.display, _G.screen)); | |
XSetStandardProperties(_G.display, _G.win, "Test Drop", NULL, | |
None, | |
NULL, 0, NULL); | |
_G.atom.delete_window = XInternAtom(_G.display, "WM_DELETE_WINDOW", | |
False); | |
_G.atom.utf8_string = XInternAtom(_G.display, "UTF8_STRING", | |
False); | |
_G.atom.xdnd_aware = XInternAtom(_G.display, "XdndAware", False); | |
_G.atom.xdnd_enter = XInternAtom(_G.display, "XdndEnter", False); | |
_G.atom.xdnd_type_list = XInternAtom(_G.display, "XdndTypeList", False); | |
_G.atom.xdnd_finished = XInternAtom(_G.display, "XdndFinished", False); | |
_G.atom.xdnd_selection = XInternAtom(_G.display, "XdndSelection", False); | |
_G.atom.xdnd_leave = XInternAtom(_G.display, "XdndLeave", False); | |
_G.atom.xdnd_position = XInternAtom(_G.display, "XdndPosition", False); | |
XChangeProperty(_G.display, _G.win, _G.atom.xdnd_aware, XA_ATOM, 32, | |
PropModeReplace, "5", 1); | |
XMapWindow(_G.display, _G.win); | |
XSelectInput(_G.display, _G.win, ButtonMotionMask | PointerMotionMask | |
| ButtonPressMask | ButtonReleaseMask | |
| EnterWindowMask | LeaveWindowMask); | |
while (!_G.done) { | |
XEvent event; | |
XNextEvent(_G.display, &event); | |
handle_event(&event); | |
} | |
XDestroyWindow(_G.display, _G.win); | |
XFlush(_G.display); | |
XCloseDisplay(_G.display); | |
return 0; | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment