Merge pull request #4874 from Hinsbart/x11_dragndrop

x11: Add support for filesystem drag & drop using xdnd
This commit is contained in:
Rémi Verschelde 2016-05-29 18:40:27 +02:00
commit d31696e3db
2 changed files with 177 additions and 0 deletions

View file

@ -105,6 +105,7 @@ void OS_X11::initialize(const VideoMode& p_desired,int p_video_driver,int p_audi
last_timestamp=0; last_timestamp=0;
last_mouse_pos_valid=false; last_mouse_pos_valid=false;
last_keyrelease_time=0; last_keyrelease_time=0;
xdnd_version = 0;
if (get_render_thread_mode()==RENDER_SEPARATE_THREAD) { if (get_render_thread_mode()==RENDER_SEPARATE_THREAD) {
XInitThreads(); XInitThreads();
@ -415,6 +416,19 @@ void OS_X11::initialize(const VideoMode& p_desired,int p_video_driver,int p_audi
} }
set_cursor_shape(CURSOR_BUSY); set_cursor_shape(CURSOR_BUSY);
//Set Xdnd (drag & drop) support
Atom XdndAware = XInternAtom(x11_display, "XdndAware", False);
Atom version=5;
XChangeProperty(x11_display, x11_window, XdndAware, XA_ATOM, 32, PropModeReplace, (unsigned char*)&version, 1);
xdnd_enter = XInternAtom(x11_display, "XdndEnter", False);
xdnd_position = XInternAtom(x11_display, "XdndPosition", False);
xdnd_status = XInternAtom(x11_display, "XdndStatus", False);
xdnd_action_copy = XInternAtom(x11_display, "XdndActionCopy", False);
xdnd_drop = XInternAtom(x11_display, "XdndDrop", False);
xdnd_finished = XInternAtom(x11_display, "XdndFinished", False);
xdnd_selection = XInternAtom(x11_display, "XdndSelection", False);
requested = None;
visual_server->init(); visual_server->init();
// //
@ -1166,6 +1180,72 @@ void OS_X11::handle_key_event(XKeyEvent *p_event, bool p_echo) {
input->parse_input_event( event); input->parse_input_event( event);
} }
struct Property
{
unsigned char *data;
int format, nitems;
Atom type;
};
static Property read_property(Display* p_display, Window p_window, Atom p_property) {
Atom actual_type;
int actual_format;
unsigned long nitems;
unsigned long bytes_after;
unsigned char *ret=0;
int read_bytes = 1024;
//Keep trying to read the property until there are no
//bytes unread.
do
{
if(ret != 0)
XFree(ret);
XGetWindowProperty(p_display, p_window, p_property, 0, read_bytes, False, AnyPropertyType,
&actual_type, &actual_format, &nitems, &bytes_after,
&ret);
read_bytes *= 2;
}while(bytes_after != 0);
Property p = {ret, actual_format, nitems, actual_type};
return p;
}
static Atom pick_target_from_list(Display* p_display, Atom *p_list, int p_count) {
static const char* target_type = "text/uri-list";
for (int i = 0; i < p_count; i++) {
Atom atom = p_list[i];
if (atom != None && String(XGetAtomName(p_display, atom)) == target_type)
return atom;
}
return None;
}
static Atom pick_target_from_atoms(Display* p_disp, Atom p_t1, Atom p_t2, Atom p_t3) {
static const char* target_type = "text/uri-list";
if (p_t1 != None && String(XGetAtomName(p_disp, p_t1)) == target_type)
return p_t1;
if (p_t2 != None && String(XGetAtomName(p_disp, p_t2)) == target_type)
return p_t2;
if (p_t3 != None && String(XGetAtomName(p_disp, p_t3)) == target_type)
return p_t3;
return None;
}
void OS_X11::process_xevents() { void OS_X11::process_xevents() {
//printf("checking events %i\n", XPending(x11_display)); //printf("checking events %i\n", XPending(x11_display));
@ -1456,11 +1536,96 @@ void OS_X11::process_xevents() {
XFlush (x11_display); XFlush (x11_display);
} break; } break;
case SelectionNotify:
if (event.xselection.target == requested) {
Property p = read_property(x11_display, x11_window, XInternAtom(x11_display, "PRIMARY", 0));
Vector<String> files = String((char *)p.data).split("\n", false);
for (int i = 0; i < files.size(); i++) {
files[i] = files[i].replace("file://", "").replace("%20", " ").strip_escapes();
}
main_loop->drop_files(files);
//Reply that all is well.
XClientMessageEvent m;
memset(&m, sizeof(m), 0);
m.type = ClientMessage;
m.display = x11_display;
m.window = xdnd_source_window;
m.message_type = xdnd_finished;
m.format=32;
m.data.l[0] = x11_window;
m.data.l[1] = 1;
m.data.l[2] = xdnd_action_copy; //We only ever copy.
XSendEvent(x11_display, xdnd_source_window, False, NoEventMask, (XEvent*)&m);
}
break;
case ClientMessage: case ClientMessage:
if ((unsigned int)event.xclient.data.l[0]==(unsigned int)wm_delete) if ((unsigned int)event.xclient.data.l[0]==(unsigned int)wm_delete)
main_loop->notification(MainLoop::NOTIFICATION_WM_QUIT_REQUEST); main_loop->notification(MainLoop::NOTIFICATION_WM_QUIT_REQUEST);
else if ((unsigned int)event.xclient.message_type == (unsigned int)xdnd_enter) {
//File(s) have been dragged over the window, check for supported target (text/uri-list)
xdnd_version = ( event.xclient.data.l[1] >> 24);
Window source = event.xclient.data.l[0];
bool more_than_3 = event.xclient.data.l[1] & 1;
if (more_than_3) {
Property p = read_property(x11_display, source, XInternAtom(x11_display, "XdndTypeList", False));
requested = pick_target_from_list(x11_display, (Atom*)p.data, p.nitems);
}
else
requested = pick_target_from_atoms(x11_display, event.xclient.data.l[2],event.xclient.data.l[3], event.xclient.data.l[4]);
}
else if ((unsigned int)event.xclient.message_type == (unsigned int )xdnd_position) {
//xdnd position event, reply with an XDND status message
//just depending on type of data for now
XClientMessageEvent m;
memset(&m, sizeof(m), 0);
m.type = ClientMessage;
m.display = event.xclient.display;
m.window = event.xclient.data.l[0];
m.message_type = xdnd_status;
m.format=32;
m.data.l[0] = x11_window;
m.data.l[1] = (requested != None);
m.data.l[2] = 0; //empty rectangle
m.data.l[3] = 0;
m.data.l[4] = xdnd_action_copy;
XSendEvent(x11_display, event.xclient.data.l[0], False, NoEventMask, (XEvent*)&m);
XFlush(x11_display);
}
else if ((unsigned int)event.xclient.message_type == (unsigned int)xdnd_drop) {
if (requested != None) {
xdnd_source_window = event.xclient.data.l[0];
if(xdnd_version >= 1)
XConvertSelection(x11_display, xdnd_selection, requested, XInternAtom(x11_display, "PRIMARY", 0), x11_window, event.xclient.data.l[2]);
else
XConvertSelection(x11_display, xdnd_selection, requested, XInternAtom(x11_display, "PRIMARY", 0), x11_window, CurrentTime);
}
else {
//Reply that we're not interested.
XClientMessageEvent m;
memset(&m, sizeof(m), 0);
m.type = ClientMessage;
m.display = event.xclient.display;
m.window = event.xclient.data.l[0];
m.message_type = xdnd_finished;
m.format=32;
m.data.l[0] = x11_window;
m.data.l[1] = 0;
m.data.l[2] = None; //Failed.
XSendEvent(x11_display, event.xclient.data.l[0], False, NoEventMask, (XEvent*)&m);
}
}
break; break;
default: default:
break; break;

View file

@ -70,6 +70,17 @@ typedef struct {
class OS_X11 : public OS_Unix { class OS_X11 : public OS_Unix {
Atom wm_delete; Atom wm_delete;
Atom xdnd_enter;
Atom xdnd_position;
Atom xdnd_status;
Atom xdnd_action_copy;
Atom xdnd_drop;
Atom xdnd_finished;
Atom xdnd_selection;
Atom requested;
int xdnd_version;
#if defined(OPENGL_ENABLED) || defined(LEGACYGL_ENABLED) #if defined(OPENGL_ENABLED) || defined(LEGACYGL_ENABLED)
ContextGL_X11 *context_gl; ContextGL_X11 *context_gl;
#endif #endif
@ -78,6 +89,7 @@ class OS_X11 : public OS_Unix {
VideoMode current_videomode; VideoMode current_videomode;
List<String> args; List<String> args;
Window x11_window; Window x11_window;
Window xdnd_source_window;
MainLoop *main_loop; MainLoop *main_loop;
::Display* x11_display; ::Display* x11_display;
char *xmbstring; char *xmbstring;