+void dnd_enter(XClientMessageEvent *e)
+{
+ dnd_atom = None;
+ int more_than_3 = e->data.l[1] & 1;
+ dnd_source_window = e->data.l[0];
+ dnd_version = (e->data.l[1] >> 24);
+
+ fprintf(stderr, "DnD %s:%d: DnDEnter\n", __FILE__, __LINE__);
+ fprintf(stderr, "DnD %s:%d: DnDEnter. Supports > 3 types = %s\n", __FILE__, __LINE__, more_than_3 ? "yes" : "no");
+ fprintf(stderr, "DnD %s:%d: Protocol version = %d\n", __FILE__, __LINE__, dnd_version);
+ fprintf(stderr, "DnD %s:%d: Type 1 = %s\n", __FILE__, __LINE__, GetAtomName(server.dsp, e->data.l[2]));
+ fprintf(stderr, "DnD %s:%d: Type 2 = %s\n", __FILE__, __LINE__, GetAtomName(server.dsp, e->data.l[3]));
+ fprintf(stderr, "DnD %s:%d: Type 3 = %s\n", __FILE__, __LINE__, GetAtomName(server.dsp, e->data.l[4]));
+
+ //Query which conversions are available and pick the best
+
+ if (more_than_3) {
+ //Fetch the list of possible conversions
+ //Notice the similarity to TARGETS with paste.
+ Property p = read_property(server.dsp, dnd_source_window, server.atom.XdndTypeList);
+ dnd_atom = pick_target_from_targets(server.dsp, p);
+ XFree(p.data);
+ } else {
+ //Use the available list
+ dnd_atom = pick_target_from_atoms(server.dsp, e->data.l[2], e->data.l[3], e->data.l[4]);
+ }
+
+ fprintf(stderr, "DnD %s:%d: Requested type = %s\n", __FILE__, __LINE__, GetAtomName(server.dsp, dnd_atom));
+}
+
+void dnd_position(XClientMessageEvent *e)
+{
+ dnd_target_window = e->window;
+ int accept = 0;
+ Panel *panel = get_panel(e->window);
+ int x, y, mapX, mapY;
+ Window child;
+ x = (e->data.l[2] >> 16) & 0xFFFF;
+ y = e->data.l[2] & 0xFFFF;
+ XTranslateCoordinates(server.dsp, server.root_win, e->window, x, y, &mapX, &mapY, &child);
+ Task* task = click_task(panel, mapX, mapY);
+ if (task) {
+ if (task->desktop != server.desktop )
+ set_desktop (task->desktop);
+ window_action(task, TOGGLE);
+ } else {
+ LauncherIcon *icon = click_launcher_icon(panel, mapX, mapY);
+ if (icon) {
+ accept = 1;
+ dnd_launcher_exec = icon->cmd;
+ } else {
+ dnd_launcher_exec = 0;
+ }
+ }
+
+ // send XdndStatus event to get more XdndPosition events
+ XClientMessageEvent se;
+ se.type = ClientMessage;
+ se.window = e->data.l[0];
+ se.message_type = server.atom.XdndStatus;
+ se.format = 32;
+ se.data.l[0] = e->window; // XID of the target window
+ se.data.l[1] = accept ? 1 : 0; // bit 0: accept drop bit 1: send XdndPosition events if inside rectangle
+ se.data.l[2] = 0; // Rectangle x,y for which no more XdndPosition events
+ se.data.l[3] = (1 << 16) | 1; // Rectangle w,h for which no more XdndPosition events
+ if (accept) {
+ se.data.l[4] = dnd_version >= 2 ? e->data.l[4] : server.atom.XdndActionCopy;
+ } else {
+ se.data.l[4] = None; // None = drop will not be accepted
+ }
+
+ XSendEvent(server.dsp, e->data.l[0], False, NoEventMask, (XEvent*)&se);
+}
+
+void dnd_drop(XClientMessageEvent *e)
+{
+ if (dnd_target_window && dnd_launcher_exec) {
+ if (dnd_version >= 1) {
+ XConvertSelection(server.dsp, server.atom.XdndSelection, XA_STRING, dnd_selection, dnd_target_window, e->data.l[2]);
+ } else {
+ XConvertSelection(server.dsp, server.atom.XdndSelection, XA_STRING, dnd_selection, dnd_target_window, CurrentTime);
+ }
+ } else {
+ //The source is sending anyway, despite instructions to the contrary.
+ //So reply that we're not interested.
+ XClientMessageEvent m;
+ memset(&m, sizeof(m), 0);
+ m.type = ClientMessage;
+ m.display = e->display;
+ m.window = e->data.l[0];
+ m.message_type = server.atom.XdndFinished;
+ m.format = 32;
+ m.data.l[0] = dnd_target_window;
+ m.data.l[1] = 0;
+ m.data.l[2] = None; //Failed.
+ XSendEvent(server.dsp, e->data.l[0], False, NoEventMask, (XEvent*)&m);
+ }
+}