Skip to content

Instantly share code, notes, and snippets.

@borisfaure
Created September 1, 2011 19:36
Show Gist options
  • Save borisfaure/1187059 to your computer and use it in GitHub Desktop.
Save borisfaure/1187059 to your computer and use it in GitHub Desktop.
dummy dnd test
/* 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