summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--video/out/x11_common.c181
-rw-r--r--video/out/x11_common.h17
2 files changed, 192 insertions, 6 deletions
diff --git a/video/out/x11_common.c b/video/out/x11_common.c
index 9b5d5cb929..2e8b0917ac 100644
--- a/video/out/x11_common.c
+++ b/video/out/x11_common.c
@@ -93,6 +93,8 @@
#define WIN_LAYER_ONTOP 6
#define WIN_LAYER_ABOVE_DOCK 10
+#define DND_VERSION 5
+
// ----- Motif header: -------
#define MWM_HINTS_FUNCTIONS (1L << 0)
@@ -342,17 +344,17 @@ static int net_wm_support_state_test(struct vo_x11_state *x11, Atom atom)
return 0;
}
-static int x11_get_property(struct vo_x11_state *x11, Atom type, Atom **args,
- unsigned long *nitems)
+static bool x11_get_property(struct vo_x11_state *x11, Window wnd, Atom type,
+ Atom **args, unsigned long *nitems)
{
int format;
unsigned long bytesafter;
return Success ==
- XGetWindowProperty(x11->display, x11->rootwin, type, 0, 16384, False,
+ XGetWindowProperty(x11->display, wnd, type, 0, 16384, False,
AnyPropertyType, &type, &format, nitems,
&bytesafter, (unsigned char **) args)
- && *nitems > 0;
+ && *nitems > 0 && bytesafter == 0;
}
static int vo_wm_detect(struct vo *vo)
@@ -362,12 +364,13 @@ static int vo_wm_detect(struct vo *vo)
int wm = 0;
unsigned long nitems;
Atom *args = NULL;
+ Window win = x11->rootwin;
if (vo->opts->WinID >= 0)
return 0;
// -- supports layers
- if (x11_get_property(x11, x11->XA_WIN_PROTOCOLS, &args, &nitems)) {
+ if (x11_get_property(x11, win, x11->XA_WIN_PROTOCOLS, &args, &nitems)) {
MP_VERBOSE(x11, "Detected wm supports layers.\n");
for (i = 0; i < nitems; i++) {
if (args[i] == x11->XA_WIN_LAYER)
@@ -376,7 +379,7 @@ static int vo_wm_detect(struct vo *vo)
XFree(args);
}
// --- netwm
- if (x11_get_property(x11, x11->XA_NET_SUPPORTED, &args, &nitems)) {
+ if (x11_get_property(x11, win, x11->XA_NET_SUPPORTED, &args, &nitems)) {
MP_VERBOSE(x11, "Detected wm supports NetWM.\n");
for (i = 0; i < nitems; i++)
wm |= net_wm_support_state_test(vo->x11, args[i]);
@@ -407,9 +410,21 @@ static void init_atoms(struct vo_x11_state *x11)
XA_INIT(WM_PROTOCOLS);
XA_INIT(WM_DELETE_WINDOW);
XA_INIT(UTF8_STRING);
+ XA_INIT(TARGETS);
+ XA_INIT(XdndAware);
+ XA_INIT(XdndEnter);
+ XA_INIT(XdndLeave);
+ XA_INIT(XdndPosition);
+ XA_INIT(XdndStatus);
+ XA_INIT(XdndActionCopy);
+ XA_INIT(XdndTypeList);
+ XA_INIT(XdndDrop);
+ XA_INIT(XdndSelection);
+ XA_INIT(XdndFinished);
char buf[50];
sprintf(buf, "_NET_WM_CM_S%d", x11->screen);
x11->XA_NET_WM_CM = XInternAtom(x11->display, buf, False);
+ x11->XA_uri_list = XInternAtom(x11->display, "text/uri-list", False);
}
static void vo_x11_update_screeninfo(struct vo *vo)
@@ -720,6 +735,155 @@ void vo_x11_uninit(struct vo *vo)
vo->x11 = NULL;
}
+// The data is in the form of the mimetype text/uri-list.
+static bool dnd_handle_drop_data(struct vo *vo, bstr data)
+{
+ void *tmp = talloc_new(NULL);
+ int num_files = 0;
+ char **files = NULL;
+ while (data.len) {
+ bstr line = bstr_getline(data, &data);
+ line = bstr_strip_linebreaks(line);
+ if (bstr_startswith0(line, "#"))
+ continue;
+ char *s = bstrto0(tmp, line);
+ MP_TARRAY_APPEND(tmp, files, num_files, s);
+ }
+ vo_drop_files(vo, num_files, files);
+ talloc_free(tmp);
+ return num_files > 0;
+}
+
+static void vo_x11_dnd_init_window(struct vo *vo)
+{
+ struct vo_x11_state *x11 = vo->x11;
+
+ Atom version = DND_VERSION;
+ XChangeProperty(x11->display, x11->window, x11->XAXdndAware, XA_ATOM,
+ 32, PropModeReplace, (unsigned char *)&version, 1);
+
+ x11->dnd_property = XInternAtom(x11->display, "mpv_dnd_selection", False);
+}
+
+static void dnd_select_format(struct vo_x11_state *x11, Atom *args, int items)
+{
+ for (int n = 0; n < items; n++) {
+ // There are other types; possibly not worth supporting.
+ if (args[n] == x11->XA_uri_list)
+ x11->dnd_requested_format = args[n];
+ }
+}
+
+static void dnd_reset(struct vo *vo)
+{
+ struct vo_x11_state *x11 = vo->x11;
+
+ x11->dnd_src_window = 0;
+ x11->dnd_requested_format = 0;
+}
+
+static void vo_x11_dnd_handle_message(struct vo *vo, XClientMessageEvent *ce)
+{
+ struct vo_x11_state *x11 = vo->x11;
+
+ if (!x11->window)
+ return;
+
+ if (ce->message_type == x11->XAXdndEnter) {
+ x11->dnd_requested_format = 0;
+
+ Window src = ce->data.l[0];
+ if (ce->data.l[1] & 1) {
+ unsigned long nitems = 0;
+ Atom *args = NULL;
+ if (x11_get_property(x11, src, x11->XAXdndTypeList, &args, &nitems)) {
+ dnd_select_format(x11, args, nitems);
+ XFree(args);
+ }
+ } else {
+ Atom args[3];
+ for (int n = 2; n <= 4; n++)
+ args[n - 2] = ce->data.l[n];
+ dnd_select_format(x11, args, 3);
+ }
+ } else if (ce->message_type == x11->XAXdndPosition) {
+ Window src = ce->data.l[0];
+ XEvent xev;
+
+ xev.xclient.type = ClientMessage;
+ xev.xclient.serial = 0;
+ xev.xclient.send_event = True;
+ xev.xclient.message_type = x11->XAXdndStatus;
+ xev.xclient.window = src;
+ xev.xclient.format = 32;
+ xev.xclient.data.l[0] = x11->window;
+ xev.xclient.data.l[1] = x11->dnd_requested_format ? 1 : 0;
+ xev.xclient.data.l[2] = 0;
+ xev.xclient.data.l[3] = 0;
+ xev.xclient.data.l[4] = x11->XAXdndActionCopy;
+
+ XSendEvent(x11->display, src, False, 0, &xev);
+ } else if (ce->message_type == x11->XAXdndDrop) {
+ x11->dnd_src_window = ce->data.l[0];
+ XConvertSelection(x11->display, x11->XAXdndSelection,
+ x11->dnd_requested_format, x11->dnd_property,
+ x11->window, ce->data.l[2]);
+ } else if (ce->message_type == x11->XAXdndLeave) {
+ dnd_reset(vo);
+ }
+}
+
+static void vo_x11_dnd_handle_selection(struct vo *vo, XSelectionEvent *se)
+{
+ struct vo_x11_state *x11 = vo->x11;
+
+ if (!x11->window || !x11->dnd_src_window)
+ return;
+
+ bool success = false;
+
+ if (se->selection == x11->XAXdndSelection &&
+ se->property == x11->dnd_property &&
+ se->target == x11->dnd_requested_format)
+ {
+ Atom type;
+ int format;
+ unsigned long nitems;
+ unsigned long bytes_left;
+ unsigned char *prop;
+ if (XGetWindowProperty(x11->display, x11->window, x11->dnd_property,
+ 0, 64 * 1024, False, x11->dnd_requested_format,
+ &type, &format, &nitems, &bytes_left, &prop)
+ == Success)
+ {
+ if (!bytes_left && type == x11->dnd_requested_format && format == 8)
+ {
+ // No idea if this is guaranteed to be \0-padded, so use bstr.
+ success = dnd_handle_drop_data(vo, (bstr){prop, nitems});
+ }
+ XFree(prop);
+ }
+ }
+
+ XEvent xev;
+
+ xev.xclient.type = ClientMessage;
+ xev.xclient.serial = 0;
+ xev.xclient.send_event = True;
+ xev.xclient.message_type = x11->XAXdndFinished;
+ xev.xclient.window = x11->dnd_src_window;
+ xev.xclient.format = 32;
+ xev.xclient.data.l[0] = x11->window;
+ xev.xclient.data.l[1] = success ? 1 : 0;
+ xev.xclient.data.l[2] = success ? x11->XAXdndActionCopy : 0;
+ xev.xclient.data.l[3] = 0;
+ xev.xclient.data.l[4] = 0;
+
+ XSendEvent(x11->display, x11->dnd_src_window, False, 0, &xev);
+
+ dnd_reset(vo);
+}
+
static void update_vo_size(struct vo *vo)
{
struct vo_x11_state *x11 = vo->x11;
@@ -838,6 +1002,10 @@ int vo_x11_check_events(struct vo *vo)
if (Event.xclient.message_type == x11->XAWM_PROTOCOLS &&
Event.xclient.data.l[0] == x11->XAWM_DELETE_WINDOW)
mp_input_put_key(vo->input_ctx, MP_KEY_CLOSE_WIN);
+ vo_x11_dnd_handle_message(vo, &Event.xclient);
+ break;
+ case SelectionNotify:
+ vo_x11_dnd_handle_selection(vo, &Event.xselection);
break;
default:
if (Event.type == x11->ShmCompletionEvent) {
@@ -1129,6 +1297,7 @@ static void vo_x11_create_window(struct vo *vo, XVisualInfo *vis, int x, int y,
vo_x11_set_wm_icon(x11);
vo_x11_update_window_title(vo);
+ vo_x11_dnd_init_window(vo);
}
static void vo_x11_map_window(struct vo *vo, int x, int y, int w, int h)
diff --git a/video/out/x11_common.h b/video/out/x11_common.h
index 2763009755..f65af8e79f 100644
--- a/video/out/x11_common.h
+++ b/video/out/x11_common.h
@@ -102,6 +102,11 @@ struct vo_x11_state {
/* Increment it before XShmPutImage */
int ShmCompletionWaitCount;
+ /* drag and drop */
+ Atom dnd_property;
+ Atom dnd_requested_format;
+ Window dnd_src_window;
+
Atom XA_NET_SUPPORTED;
Atom XA_NET_WM_STATE;
Atom XA_NET_WM_STATE_FULLSCREEN;
@@ -119,6 +124,18 @@ struct vo_x11_state {
Atom XAWM_DELETE_WINDOW;
Atom XAUTF8_STRING;
Atom XA_NET_WM_CM;
+ Atom XATARGETS;
+ Atom XAXdndAware;
+ Atom XAXdndEnter;
+ Atom XAXdndLeave;
+ Atom XAXdndPosition;
+ Atom XAXdndStatus;
+ Atom XAXdndActionCopy;
+ Atom XAXdndTypeList;
+ Atom XAXdndDrop;
+ Atom XAXdndSelection;
+ Atom XAXdndFinished;
+ Atom XA_uri_list;
};
int vo_x11_init(struct vo *vo);