summaryrefslogtreecommitdiffstats
path: root/video/out/x11_common.c
diff options
context:
space:
mode:
Diffstat (limited to 'video/out/x11_common.c')
-rw-r--r--video/out/x11_common.c625
1 files changed, 450 insertions, 175 deletions
diff --git a/video/out/x11_common.c b/video/out/x11_common.c
index 92b3539aca..fa2f2ba38c 100644
--- a/video/out/x11_common.c
+++ b/video/out/x11_common.c
@@ -29,16 +29,17 @@
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
+#include <X11/Xresource.h>
#include <X11/keysym.h>
#include <X11/XKBlib.h>
#include <X11/XF86keysym.h>
#include <X11/extensions/scrnsaver.h>
#include <X11/extensions/dpms.h>
-#include <X11/extensions/Xinerama.h>
+#include <X11/extensions/shape.h>
+#include <X11/extensions/Xpresent.h>
#include <X11/extensions/Xrandr.h>
-#include "config.h"
#include "misc/bstr.h"
#include "options/options.h"
#include "options/m_config.h"
@@ -48,12 +49,14 @@
#include "input/event.h"
#include "video/image_loader.h"
#include "video/mp_image.h"
+#include "present_sync.h"
#include "x11_common.h"
#include "mpv_talloc.h"
#include "vo.h"
#include "win_state.h"
#include "osdep/io.h"
+#include "osdep/poll_wrapper.h"
#include "osdep/timer.h"
#include "osdep/subprocess.h"
@@ -65,6 +68,7 @@
#define vo_wm_STAYS_ON_TOP 4
#define vo_wm_ABOVE 8
#define vo_wm_BELOW 16
+#define vo_wm_STICKY 32
/* EWMH state actions, see
http://freedesktop.org/Standards/wm-spec/index.html#id2768769 */
@@ -95,7 +99,9 @@
#define MWM_FUNC_MAXIMIZE (1L << 4)
#define MWM_FUNC_CLOSE (1L << 5)
-#define MWM_DECOR_ALL (1L << 0)
+// Equals to all MWM_DECOR_* OR'd together.
+#define MWM_DECOR_ALL 126
+#define MWM_DECOR_TITLE (1L << 3)
typedef struct
{
@@ -107,19 +113,19 @@ typedef struct
} MotifWmHints;
static const char x11_icon_16[] =
-#include "generated/etc/mpv-icon-8bit-16x16.png.inc"
+#include "etc/mpv-icon-8bit-16x16.png.inc"
;
static const char x11_icon_32[] =
-#include "generated/etc/mpv-icon-8bit-32x32.png.inc"
+#include "etc/mpv-icon-8bit-32x32.png.inc"
;
static const char x11_icon_64[] =
-#include "generated/etc/mpv-icon-8bit-64x64.png.inc"
+#include "etc/mpv-icon-8bit-64x64.png.inc"
;
static const char x11_icon_128[] =
-#include "generated/etc/mpv-icon-8bit-128x128.png.inc"
+#include "etc/mpv-icon-8bit-128x128.png.inc"
;
#define ICON_ENTRY(var) { (char *)var, sizeof(var) }
@@ -134,6 +140,7 @@ static const struct bstr x11_icons[] = {
static struct mp_log *x11_error_output;
static atomic_int x11_error_silence;
+static bool rc_overlaps(struct mp_rect rc1, struct mp_rect rc2);
static void vo_x11_update_geometry(struct vo *vo);
static void vo_x11_fullscreen(struct vo *vo);
static void xscreensaver_heartbeat(struct vo_x11_state *x11);
@@ -147,6 +154,8 @@ static void vo_x11_move_resize(struct vo *vo, bool move, bool resize,
struct mp_rect rc);
static void vo_x11_maximize(struct vo *vo);
static void vo_x11_minimize(struct vo *vo);
+static void vo_x11_set_input_region(struct vo *vo, bool passthrough);
+static void vo_x11_sticky(struct vo *vo, bool sticky);
#define XA(x11, s) (XInternAtom((x11)->display, # s, False))
#define XAs(x11, s) XInternAtom((x11)->display, s, False)
@@ -325,6 +334,7 @@ static int net_wm_support_state_test(struct vo_x11_state *x11, Atom atom)
NET_WM_STATE_TEST(ABOVE);
NET_WM_STATE_TEST(STAYS_ON_TOP);
NET_WM_STATE_TEST(BELOW);
+ NET_WM_STATE_TEST(STICKY);
return 0;
}
@@ -375,6 +385,19 @@ static int vo_wm_detect(struct vo *vo)
return wm;
}
+static void xpresent_set(struct vo_x11_state *x11)
+{
+ int present = x11->opts->x11_present;
+ x11->use_present = x11->present_code &&
+ ((x11->has_mesa && !x11->has_nvidia && present) ||
+ present == 2);
+ if (x11->use_present) {
+ MP_VERBOSE(x11, "XPresent enabled.\n");
+ } else {
+ MP_VERBOSE(x11, "XPresent disabled.\n");
+ }
+}
+
static void xrandr_read(struct vo_x11_state *x11)
{
for(int i = 0; i < x11->num_displays; i++)
@@ -399,6 +422,32 @@ static void xrandr_read(struct vo_x11_state *x11)
return;
}
+ /* Look at the available providers on the current screen and try to determine
+ * the driver. If amd/intel/radeon, assume this is mesa. If nvidia is found,
+ * assume nvidia. Because the same screen can have multiple providers (e.g.
+ * a laptop with switchable graphics), we need to know both of these things.
+ * In practice, this is used for determining whether or not to use XPresent
+ * (i.e. needs to be Mesa and not Nvidia). Requires Randr 1.4. */
+ XRRProviderResources *pr = XRRGetProviderResources(x11->display, x11->rootwin);
+ for (int i = 0; i < pr->nproviders; i++) {
+ XRRProviderInfo *info = XRRGetProviderInfo(x11->display, r, pr->providers[i]);
+ struct bstr provider_name = bstrdup(x11, bstr0(info->name));
+ bstr_lower(provider_name);
+ int amd = bstr_find0(provider_name, "amd");
+ int intel = bstr_find0(provider_name, "intel");
+ int modesetting = bstr_find0(provider_name, "modesetting");
+ int nouveau = bstr_find0(provider_name, "nouveau");
+ int nvidia = bstr_find0(provider_name, "nvidia");
+ int radeon = bstr_find0(provider_name, "radeon");
+ x11->has_mesa = x11->has_mesa || amd >= 0 || intel >= 0 ||
+ modesetting >= 0 || nouveau >= 0 || radeon >= 0;
+ x11->has_nvidia = x11->has_nvidia || nvidia >= 0;
+ XRRFreeProviderInfo(info);
+ }
+ if (x11->present_code)
+ xpresent_set(x11);
+ XRRFreeProviderResources(pr);
+
int primary_id = -1;
RROutput primary = XRRGetOutputPrimary(x11->display, x11->rootwin);
for (int o = 0; o < r->noutput; o++) {
@@ -446,6 +495,7 @@ static void xrandr_read(struct vo_x11_state *x11)
for (int i = 0; i < x11->num_displays; i++) {
struct xrandr_display *d = &(x11->displays[i]);
+ d->screen = i;
if (i == primary_id) {
d->atom_id = 0;
@@ -461,47 +511,81 @@ static void xrandr_read(struct vo_x11_state *x11)
XRRFreeScreenResources(r);
}
-static void vo_x11_update_screeninfo(struct vo *vo)
+static int vo_x11_select_screen(struct vo *vo)
{
struct vo_x11_state *x11 = vo->x11;
struct mp_vo_opts *opts = x11->opts;
- bool all_screens = opts->fullscreen && opts->fsscreen_id == -2;
- x11->screenrc = (struct mp_rect){.x1 = x11->ws_width, .y1 = x11->ws_height};
- if (opts->screen_id >= -1 && XineramaIsActive(x11->display) && !all_screens)
- {
- int screen = opts->fullscreen ? opts->fsscreen_id : opts->screen_id;
- XineramaScreenInfo *screens;
- int num_screens;
-
+ int screen = -2; // all displays
+ if (!opts->fullscreen || opts->fsscreen_id != -2) {
+ screen = opts->fullscreen ? opts->fsscreen_id : opts->screen_id;
if (opts->fullscreen && opts->fsscreen_id == -1)
screen = opts->screen_id;
- screens = XineramaQueryScreens(x11->display, &num_screens);
- if (screen >= num_screens)
- screen = num_screens - 1;
+ if (screen == -1 && (opts->fsscreen_name || opts->screen_name)) {
+ char *screen_name = opts->fullscreen ? opts->fsscreen_name : opts->screen_name;
+ if (screen_name) {
+ bool screen_found = false;
+ for (int n = 0; n < x11->num_displays; n++) {
+ char *display_name = x11->displays[n].name;
+ if (!strcmp(display_name, screen_name)) {
+ screen = n;
+ screen_found = true;
+ break;
+ }
+ }
+ if (!screen_found)
+ MP_WARN(x11, "Screen name %s not found!\n", screen_name);
+ }
+ }
+
+ if (screen >= x11->num_displays)
+ screen = x11->num_displays - 1;
+ }
+ return screen;
+}
+
+static void vo_x11_update_screeninfo(struct vo *vo)
+{
+ struct vo_x11_state *x11 = vo->x11;
+ x11->screenrc = (struct mp_rect){.x1 = x11->ws_width, .y1 = x11->ws_height};
+ int screen = vo_x11_select_screen(vo);
+ if (screen >= -1) {
if (screen == -1) {
int x = x11->winrc.x0 + RC_W(x11->winrc) / 2;
int y = x11->winrc.y0 + RC_H(x11->winrc) / 2;
- for (screen = num_screens - 1; screen > 0; screen--) {
- int left = screens[screen].x_org;
- int right = left + screens[screen].width;
- int top = screens[screen].y_org;
- int bottom = top + screens[screen].height;
+ for (screen = x11->num_displays - 1; screen > 0; screen--) {
+ struct xrandr_display *disp = &x11->displays[screen];
+ int left = disp->rc.x0;
+ int right = disp->rc.x1;
+ int top = disp->rc.y0;
+ int bottom = disp->rc.y1;
if (left <= x && x <= right && top <= y && y <= bottom)
break;
}
}
+
if (screen < 0)
screen = 0;
x11->screenrc = (struct mp_rect){
- .x0 = screens[screen].x_org,
- .y0 = screens[screen].y_org,
- .x1 = screens[screen].x_org + screens[screen].width,
- .y1 = screens[screen].y_org + screens[screen].height,
+ .x0 = x11->displays[screen].rc.x0,
+ .y0 = x11->displays[screen].rc.y0,
+ .x1 = x11->displays[screen].rc.x1,
+ .y1 = x11->displays[screen].rc.y1,
};
+ }
+}
- XFree(screens);
+static struct xrandr_display *get_current_display(struct vo *vo)
+{
+ struct vo_x11_state *x11 = vo->x11;
+ struct xrandr_display *selected_disp = NULL;
+ for (int n = 0; n < x11->num_displays; n++) {
+ struct xrandr_display *disp = &x11->displays[n];
+ disp->overlaps = rc_overlaps(disp->rc, x11->winrc);
+ if (disp->overlaps && (!selected_disp || disp->fps < selected_disp->fps))
+ selected_disp = disp;
}
+ return selected_disp;
}
// Get the monitors for the 4 edges of the rectangle spanning all screens.
@@ -509,25 +593,84 @@ static void vo_x11_get_bounding_monitors(struct vo_x11_state *x11, long b[4])
{
//top bottom left right
b[0] = b[1] = b[2] = b[3] = 0;
- int num_screens = 0;
- XineramaScreenInfo *screens = XineramaQueryScreens(x11->display, &num_screens);
- if (!screens)
- return;
- for (int n = 0; n < num_screens; n++) {
- XineramaScreenInfo *s = &screens[n];
- if (s->y_org < screens[b[0]].y_org)
+ for (int n = 0; n < x11->num_displays; n++) {
+ struct xrandr_display *d = &x11->displays[n];
+ if (d->rc.y0 < x11->displays[b[0]].rc.y0)
b[0] = n;
- if (s->y_org + s->height > screens[b[1]].y_org + screens[b[1]].height)
+ if (d->rc.y1 < x11->displays[b[1]].rc.y1)
b[1] = n;
- if (s->x_org < screens[b[2]].x_org)
+ if (d->rc.x0 < x11->displays[b[2]].rc.x0)
b[2] = n;
- if (s->x_org + s->width > screens[b[3]].x_org + screens[b[3]].width)
+ if (d->rc.x1 < x11->displays[b[3]].rc.x1)
b[3] = n;
}
- XFree(screens);
}
-int vo_x11_init(struct vo *vo)
+// Get the dpi scale of the x11 screen. Almost no GUI programs use this value
+// nowadays, and it has inconsistent behavior for different drivers.
+// (it returns the physical display DPI for proprietary NVIDIA driver only,
+// but essentially a user-set prefrence value everywhere else)
+static void vo_x11_get_x11_screen_dpi_scale(struct vo_x11_state *x11)
+{
+ int w_mm = DisplayWidthMM(x11->display, x11->screen);
+ int h_mm = DisplayHeightMM(x11->display, x11->screen);
+ double dpi_x = x11->ws_width * 25.4 / w_mm;
+ double dpi_y = x11->ws_height * 25.4 / h_mm;
+ double base_dpi = 96;
+ if (isfinite(dpi_x) && isfinite(dpi_y)) {
+ int s_x = lrint(MPCLAMP(2 * dpi_x / base_dpi, 0, 20));
+ int s_y = lrint(MPCLAMP(2 * dpi_y / base_dpi, 0, 20));
+ if (s_x == s_y && s_x > 2 && s_x < 20) {
+ x11->dpi_scale = s_x / 2.0;
+ MP_VERBOSE(x11, "Using X11 screen DPI scale: %g", x11->dpi_scale);
+ }
+ }
+}
+
+// Get the dpi scale from the Xft.dpi resource. In practice, this value is much more
+// commonly used by GUI programs for scaling compared to the x11 screen dpi.
+// This is always a preference value so it's also more consistent.
+static bool vo_x11_get_xft_dpi_scale(struct vo_x11_state *x11)
+{
+ XrmInitialize();
+ char *resman = XResourceManagerString(x11->display);
+ if (!resman)
+ return false;
+
+ XrmDatabase db = XrmGetStringDatabase(resman);
+ if (!db)
+ return false;
+
+ XrmValue ret;
+ char *type;
+ double base_dpi = 96;
+ bool success = false;
+ if (XrmGetResource(db, "Xft.dpi", "String", &type, &ret) == True &&
+ ret.addr && !strcmp("String", type))
+ {
+ char *end;
+ long value = strtol(ret.addr, &end, 10);
+ if (*ret.addr && *end == '\0') {
+ int s = lrint(MPCLAMP(2 * value / base_dpi, 0, 20));
+ if (s > 2 && s < 20) {
+ x11->dpi_scale = s / 2.0;
+ MP_VERBOSE(x11, "Using Xft.dpi scale: %g", x11->dpi_scale);
+ success = true;
+ }
+ }
+ }
+ XrmDestroyDatabase(db);
+ return success;
+}
+
+static void vo_x11_get_dpi_scale(struct vo_x11_state *x11)
+{
+ if (!vo_x11_get_xft_dpi_scale(x11))
+ vo_x11_get_x11_screen_dpi_scale(x11);
+ x11->pending_vo_events |= VO_EVENT_DPI;
+}
+
+bool vo_x11_init(struct vo *vo)
{
char *dispName;
@@ -550,6 +693,7 @@ int vo_x11_init(struct vo *vo)
x11_error_output = x11->log;
XSetErrorHandler(x11_errorhandler);
+ x11->present = mp_present_initialize(x11, x11->opts, VO_MAX_SWAPCHAIN_DEPTH);
dispName = XDisplayName(NULL);
@@ -590,21 +734,7 @@ int vo_x11_init(struct vo *vo)
x11->ws_width, x11->ws_height, dispName,
x11->display_is_local ? "local" : "remote");
- int w_mm = DisplayWidthMM(x11->display, x11->screen);
- int h_mm = DisplayHeightMM(x11->display, x11->screen);
- double dpi_x = x11->ws_width * 25.4 / w_mm;
- double dpi_y = x11->ws_height * 25.4 / h_mm;
- double base_dpi = 96;
- if (isfinite(dpi_x) && isfinite(dpi_y) && x11->opts->hidpi_window_scale) {
- int s_x = lrint(MPCLAMP(dpi_x / base_dpi, 0, 10));
- int s_y = lrint(MPCLAMP(dpi_y / base_dpi, 0, 10));
- if (s_x == s_y && s_x > 1 && s_x < 10) {
- x11->dpi_scale = s_x;
- MP_VERBOSE(x11, "Assuming DPI scale %d for prescaling. This can "
- "be disabled with --hidpi-window-scale=no.\n",
- x11->dpi_scale);
- }
- }
+ vo_x11_get_dpi_scale(x11);
x11->wm_type = vo_wm_detect(vo);
@@ -615,11 +745,11 @@ int vo_x11_init(struct vo *vo)
vo_x11_update_geometry(vo);
- return 1;
+ return true;
error:
vo_x11_uninit(vo);
- return 0;
+ return false;
}
static const struct mp_keymap keymap[] = {
@@ -643,6 +773,10 @@ static const struct mp_keymap keymap[] = {
{XK_F4, MP_KEY_F+4}, {XK_F5, MP_KEY_F+5}, {XK_F6, MP_KEY_F+6},
{XK_F7, MP_KEY_F+7}, {XK_F8, MP_KEY_F+8}, {XK_F9, MP_KEY_F+9},
{XK_F10, MP_KEY_F+10}, {XK_F11, MP_KEY_F+11}, {XK_F12, MP_KEY_F+12},
+ {XK_F13, MP_KEY_F+13}, {XK_F14, MP_KEY_F+14}, {XK_F15, MP_KEY_F+15},
+ {XK_F16, MP_KEY_F+16}, {XK_F17, MP_KEY_F+17}, {XK_F18, MP_KEY_F+18},
+ {XK_F19, MP_KEY_F+19}, {XK_F20, MP_KEY_F+20}, {XK_F21, MP_KEY_F+21},
+ {XK_F22, MP_KEY_F+22}, {XK_F23, MP_KEY_F+23}, {XK_F24, MP_KEY_F+24},
// numpad independent of numlock
{XK_KP_Subtract, '-'}, {XK_KP_Add, '+'}, {XK_KP_Multiply, '*'},
@@ -656,11 +790,11 @@ static const struct mp_keymap keymap[] = {
{XK_KP_Separator, MP_KEY_KPDEC},
// numpad without numlock
- {XK_KP_Insert, MP_KEY_KPINS}, {XK_KP_End, MP_KEY_KP1},
- {XK_KP_Down, MP_KEY_KP2}, {XK_KP_Page_Down, MP_KEY_KP3},
- {XK_KP_Left, MP_KEY_KP4}, {XK_KP_Begin, MP_KEY_KP5},
- {XK_KP_Right, MP_KEY_KP6}, {XK_KP_Home, MP_KEY_KP7}, {XK_KP_Up, MP_KEY_KP8},
- {XK_KP_Page_Up, MP_KEY_KP9}, {XK_KP_Delete, MP_KEY_KPDEL},
+ {XK_KP_Insert, MP_KEY_KPINS}, {XK_KP_End, MP_KEY_KPEND},
+ {XK_KP_Down, MP_KEY_KPDOWN}, {XK_KP_Page_Down, MP_KEY_KPPGDOWN},
+ {XK_KP_Left, MP_KEY_KPLEFT}, {XK_KP_Begin, MP_KEY_KP5},
+ {XK_KP_Right, MP_KEY_KPRIGHT}, {XK_KP_Home, MP_KEY_KPHOME}, {XK_KP_Up, MP_KEY_KPUP},
+ {XK_KP_Page_Up, MP_KEY_KPPGUP}, {XK_KP_Delete, MP_KEY_KPDEL},
{XF86XK_MenuKB, MP_KEY_MENU},
{XF86XK_AudioPlay, MP_KEY_PLAY}, {XF86XK_AudioPause, MP_KEY_PAUSE},
@@ -673,6 +807,9 @@ static const struct mp_keymap keymap[] = {
{XF86XK_HomePage, MP_KEY_HOMEPAGE}, {XF86XK_WWW, MP_KEY_WWW},
{XF86XK_Mail, MP_KEY_MAIL}, {XF86XK_Favorites, MP_KEY_FAVORITES},
{XF86XK_Search, MP_KEY_SEARCH}, {XF86XK_Sleep, MP_KEY_SLEEP},
+ {XF86XK_Back, MP_KEY_GO_BACK}, {XF86XK_Forward, MP_KEY_GO_FORWARD},
+ {XF86XK_Tools, MP_KEY_TOOLS},
+ {XF86XK_ZoomIn, MP_KEY_ZOOMIN}, {XF86XK_ZoomOut, MP_KEY_ZOOMOUT},
{0, 0}
};
@@ -700,7 +837,7 @@ static int vo_x11_lookupkey(int key)
return mpkey;
}
-static void vo_x11_decoration(struct vo *vo, bool d)
+static void vo_x11_decoration(struct vo *vo, bool decorations, bool title_bar)
{
struct vo_x11_state *x11 = vo->x11;
@@ -711,8 +848,9 @@ static void vo_x11_decoration(struct vo *vo, bool d)
MotifWmHints mhints = {0};
bool got = x11_get_property_copy(x11, x11->window, motif_hints,
motif_hints, 32, &mhints, sizeof(mhints));
- // hints weren't set, and decorations requested -> assume WM displays them
- if (!got && d)
+ // If hints weren't set, and decorations and title bar requested,
+ // assume WM displays them.
+ if (!got && decorations && title_bar)
return;
if (!got) {
mhints.flags = MWM_HINTS_FUNCTIONS;
@@ -720,7 +858,8 @@ static void vo_x11_decoration(struct vo *vo, bool d)
MWM_FUNC_MAXIMIZE | MWM_FUNC_RESIZE;
}
mhints.flags |= MWM_HINTS_DECORATIONS;
- mhints.decorations = d ? MWM_DECOR_ALL : 0;
+ mhints.decorations = decorations ? MWM_DECOR_ALL : 0;
+ mhints.decorations &= ~(!title_bar ? MWM_DECOR_TITLE : 0);
XChangeProperty(x11->display, x11->window, motif_hints, motif_hints, 32,
PropModeReplace, (unsigned char *) &mhints, 5);
}
@@ -759,10 +898,8 @@ void vo_x11_uninit(struct vo *vo)
set_screensaver(x11, true);
- if (x11->window != None && x11->window != x11->rootwin) {
- XUnmapWindow(x11->display, x11->window);
+ if (x11->window != None && x11->window != x11->rootwin)
XDestroyWindow(x11->display, x11->window);
- }
if (x11->xic)
XDestroyIC(x11->xic);
if (x11->colormap != None)
@@ -909,15 +1046,20 @@ static void vo_x11_dnd_handle_selection(struct vo *vo, XSelectionEvent *se)
if (se->selection == XA(x11, XdndSelection) &&
se->property == XAs(x11, DND_PROPERTY) &&
- se->target == x11->dnd_requested_format)
+ se->target == x11->dnd_requested_format &&
+ x11->opts->drag_and_drop != -2)
{
int nitems;
void *prop = x11_get_property(x11, x11->window, XAs(x11, DND_PROPERTY),
x11->dnd_requested_format, 8, &nitems);
if (prop) {
- enum mp_dnd_action action =
- x11->dnd_requested_action == XA(x11, XdndActionCopy) ?
- DND_REPLACE : DND_APPEND;
+ enum mp_dnd_action action;
+ if (x11->opts->drag_and_drop >= 0) {
+ action = x11->opts->drag_and_drop;
+ } else {
+ action = x11->dnd_requested_action == XA(x11, XdndActionCopy) ?
+ DND_REPLACE : DND_APPEND;
+ }
char *mime_type = x11_dnd_mime_type(x11, x11->dnd_requested_format);
MP_VERBOSE(x11, "Dropping type: %s (%s)\n",
@@ -1020,6 +1162,7 @@ static void vo_x11_check_net_wm_state_change(struct vo *vo)
}
opts->window_minimized = is_minimized;
+ x11->hidden = is_minimized;
m_config_cache_write_opt(x11->opts_cache, &opts->window_minimized);
opts->window_maximized = is_maximized;
m_config_cache_write_opt(x11->opts_cache, &opts->window_maximized);
@@ -1071,7 +1214,28 @@ static void release_all_keys(struct vo *vo)
if (x11->no_autorepeat)
mp_input_put_key(x11->input_ctx, MP_INPUT_RELEASE_ALL);
- x11->win_drag_button1_down = false;
+}
+
+static void vo_x11_begin_dragging(struct vo *vo)
+{
+ struct vo_x11_state *x11 = vo->x11;
+ XEvent Event = x11->last_button_event;
+ if (Event.type == ButtonPress && !x11->fs &&
+ !mp_input_test_dragging(x11->input_ctx, Event.xmotion.x,
+ Event.xmotion.y))
+ {
+ mp_input_put_key(x11->input_ctx, MP_INPUT_RELEASE_ALL);
+ XUngrabPointer(x11->display, CurrentTime);
+
+ long params[5] = {
+ Event.xmotion.x_root, Event.xmotion.y_root,
+ 8, // _NET_WM_MOVERESIZE_MOVE
+ Event.xbutton.button,
+ 1, // source indication: normal
+ };
+ x11_send_ewmh_msg(x11, "_NET_WM_MOVERESIZE", params);
+ x11->last_button_event = (XEvent){0};
+ }
}
void vo_x11_check_events(struct vo *vo)
@@ -1084,6 +1248,8 @@ void vo_x11_check_events(struct vo *vo)
while (XPending(display)) {
XNextEvent(display, &Event);
+ if (XFilterEvent(&Event, x11->window))
+ continue;
MP_TRACE(x11, "XEvent: %d\n", Event.type);
switch (Event.type) {
case Expose:
@@ -1140,30 +1306,12 @@ void vo_x11_check_events(struct vo *vo)
release_all_keys(vo);
break;
case MotionNotify:
- if (x11->win_drag_button1_down && !x11->fs &&
- !mp_input_test_dragging(x11->input_ctx, Event.xmotion.x,
- Event.xmotion.y))
- {
- mp_input_put_key(x11->input_ctx, MP_INPUT_RELEASE_ALL);
- XUngrabPointer(x11->display, CurrentTime);
-
- long params[5] = {
- Event.xmotion.x_root, Event.xmotion.y_root,
- 8, // _NET_WM_MOVERESIZE_MOVE
- 1, // button 1
- 1, // source indication: normal
- };
- x11_send_ewmh_msg(x11, "_NET_WM_MOVERESIZE", params);
- } else {
- mp_input_set_mouse_pos(x11->input_ctx, Event.xmotion.x,
- Event.xmotion.y);
- }
- x11->win_drag_button1_down = false;
+ mp_input_set_mouse_pos(x11->input_ctx, Event.xmotion.x,
+ Event.xmotion.y);
break;
case LeaveNotify:
if (Event.xcrossing.mode != NotifyNormal)
break;
- x11->win_drag_button1_down = false;
mp_input_put_key(x11->input_ctx, MP_KEY_MOUSE_LEAVE);
break;
case EnterNotify:
@@ -1174,27 +1322,25 @@ void vo_x11_check_events(struct vo *vo)
case ButtonPress:
if (Event.xbutton.button - 1 >= MP_KEY_MOUSE_BTN_COUNT)
break;
- if (Event.xbutton.button == 1)
- x11->win_drag_button1_down = true;
mp_input_put_key(x11->input_ctx,
(MP_MBTN_BASE + Event.xbutton.button - 1) |
get_mods(Event.xbutton.state) | MP_KEY_STATE_DOWN);
long msg[4] = {XEMBED_REQUEST_FOCUS};
vo_x11_xembed_send_message(x11, msg);
+ x11->last_button_event = Event;
break;
case ButtonRelease:
if (Event.xbutton.button - 1 >= MP_KEY_MOUSE_BTN_COUNT)
break;
- if (Event.xbutton.button == 1)
- x11->win_drag_button1_down = false;
mp_input_put_key(x11->input_ctx,
(MP_MBTN_BASE + Event.xbutton.button - 1) |
get_mods(Event.xbutton.state) | MP_KEY_STATE_UP);
+ x11->last_button_event = Event;
break;
case MapNotify:
x11->window_hidden = false;
x11->pseudo_mapped = true;
- x11->current_icc_screen = -1;
+ x11->current_screen = -1;
vo_x11_update_geometry(vo);
break;
case DestroyNotify:
@@ -1228,6 +1374,22 @@ void vo_x11_check_events(struct vo *vo)
x11->pending_vo_events |= VO_EVENT_ICC_PROFILE_CHANGED;
}
break;
+ case GenericEvent: {
+ XGenericEventCookie *cookie = (XGenericEventCookie *)&Event.xcookie;
+ if (cookie->extension == x11->present_code && x11->use_present)
+ {
+ XGetEventData(x11->display, cookie);
+ if (cookie->evtype == PresentCompleteNotify) {
+ XPresentCompleteNotifyEvent *present_event;
+ present_event = (XPresentCompleteNotifyEvent *)cookie->data;
+ present_sync_update_values(x11->present,
+ present_event->ust * 1000,
+ present_event->msc);
+ }
+ }
+ XFreeEventData(x11->display, cookie);
+ break;
+ }
default:
if (Event.type == x11->ShmCompletionEvent) {
if (x11->ShmCompletionWaitCount > 0)
@@ -1235,6 +1397,7 @@ void vo_x11_check_events(struct vo *vo)
}
if (Event.type == x11->xrandr_event) {
xrandr_read(x11);
+ vo_x11_get_dpi_scale(x11);
vo_x11_update_geometry(vo);
}
break;
@@ -1252,15 +1415,19 @@ static void vo_x11_sizehint(struct vo *vo, struct mp_rect rc, bool override_pos)
if (!x11->window || x11->parent)
return;
+ bool screen = opts->screen_id >= 0 || (opts->screen_name &&
+ opts->screen_name[0]);
+ bool fsscreen = opts->fsscreen_id >= 0 || (opts->fsscreen_name &&
+ opts->fsscreen_name[0]);
bool force_pos = opts->geometry.xy_valid || // explicitly forced by user
opts->force_window_position || // resize -> reset position
- opts->screen_id >= 0 || // force onto screen area
+ screen || fsscreen || // force onto screen area
+ opts->screen_name || // also force onto screen area
x11->parent || // force to fill parent
override_pos; // for fullscreen and such
XSizeHints *hint = XAllocSizeHints();
- if (!hint)
- return; // OOM
+ MP_HANDLE_OOM(hint);
hint->flags |= PSize | (force_pos ? PPosition : 0);
hint->x = rc.x0;
@@ -1326,9 +1493,7 @@ static void vo_x11_set_property_string(struct vo *vo, Atom name, const char *t)
// can do this correctly.
vo_x11_set_property_utf8(vo, name, t);
}
-
- if (prop.value)
- XFree(prop.value);
+ XFree(prop.value);
}
static void vo_x11_update_window_title(struct vo *vo)
@@ -1340,8 +1505,13 @@ static void vo_x11_update_window_title(struct vo *vo)
vo_x11_set_property_string(vo, XA_WM_NAME, x11->window_title);
vo_x11_set_property_string(vo, XA_WM_ICON_NAME, x11->window_title);
- vo_x11_set_property_utf8(vo, XA(x11, _NET_WM_NAME), x11->window_title);
- vo_x11_set_property_utf8(vo, XA(x11, _NET_WM_ICON_NAME), x11->window_title);
+
+ /* _NET_WM_NAME and _NET_WM_ICON_NAME must be sanitized to UTF-8. */
+ void *tmp = talloc_new(NULL);
+ struct bstr b_title = bstr_sanitize_utf8_latin1(tmp, bstr0(x11->window_title));
+ vo_x11_set_property_utf8(vo, XA(x11, _NET_WM_NAME), bstrto0(tmp, b_title));
+ vo_x11_set_property_utf8(vo, XA(x11, _NET_WM_ICON_NAME), bstrto0(tmp, b_title));
+ talloc_free(tmp);
}
static void vo_x11_xembed_update(struct vo_x11_state *x11, int flags)
@@ -1449,13 +1619,21 @@ static void vo_x11_create_window(struct vo *vo, XVisualInfo *vis,
Atom protos[1] = {XA(x11, WM_DELETE_WINDOW)};
XSetWMProtocols(x11->display, x11->window, protos, 1);
+ if (!XPresentQueryExtension(x11->display, &x11->present_code, NULL, NULL)) {
+ MP_VERBOSE(x11, "The XPresent extension is not supported.\n");
+ } else {
+ MP_VERBOSE(x11, "The XPresent extension was found.\n");
+ XPresentSelectInput(x11->display, x11->window, PresentCompleteNotifyMask);
+ }
+ xpresent_set(x11);
+
x11->mouse_cursor_set = false;
x11->mouse_cursor_visible = true;
vo_update_cursor(vo);
if (x11->xim) {
x11->xic = XCreateIC(x11->xim,
- XNInputStyle, XIMPreeditNone | XIMStatusNone,
+ XNInputStyle, XIMPreeditNothing | XIMStatusNothing,
XNClientWindow, x11->window,
XNFocusWindow, x11->window,
NULL);
@@ -1464,10 +1642,11 @@ static void vo_x11_create_window(struct vo *vo, XVisualInfo *vis,
if (!x11->parent) {
vo_x11_update_composition_hint(vo);
vo_x11_set_wm_icon(x11);
- vo_x11_update_window_title(vo);
vo_x11_dnd_init_window(vo);
vo_x11_set_property_utf8(vo, XA(x11, _GTK_THEME_VARIANT), "dark");
}
+ if (!x11->parent || x11->opts->x11_wid_title)
+ vo_x11_update_window_title(vo);
vo_x11_xembed_update(x11, 0);
}
@@ -1476,7 +1655,7 @@ static void vo_x11_map_window(struct vo *vo, struct mp_rect rc)
struct vo_x11_state *x11 = vo->x11;
vo_x11_move_resize(vo, true, true, rc);
- vo_x11_decoration(vo, x11->opts->border);
+ vo_x11_decoration(vo, x11->opts->border, x11->opts->title_bar);
if (x11->opts->fullscreen && (x11->wm_type & vo_wm_FULLSCREEN)) {
Atom state = XA(x11, _NET_WM_STATE_FULLSCREEN);
@@ -1500,9 +1679,18 @@ static void vo_x11_map_window(struct vo *vo, struct mp_rect rc)
x11_send_ewmh_msg(x11, "_NET_WM_FULLSCREEN_MONITORS", params);
}
- if (x11->opts->all_workspaces || x11->opts->geometry.ws > 0) {
- long v = x11->opts->all_workspaces
- ? 0xFFFFFFFF : x11->opts->geometry.ws - 1;
+ if (x11->opts->all_workspaces) {
+ if (x11->wm_type & vo_wm_STICKY) {
+ Atom state = XA(x11, _NET_WM_STATE_STICKY);
+ XChangeProperty(x11->display, x11->window, XA(x11, _NET_WM_STATE), XA_ATOM,
+ 32, PropModeReplace, (unsigned char *)&state, 1);
+ } else {
+ long v = 0xFFFFFFFF;
+ XChangeProperty(x11->display, x11->window, XA(x11, _NET_WM_DESKTOP),
+ XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&v, 1);
+ }
+ } else if (x11->opts->geometry.ws > 0) {
+ long v = x11->opts->geometry.ws - 1;
XChangeProperty(x11->display, x11->window, XA(x11, _NET_WM_DESKTOP),
XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&v, 1);
}
@@ -1519,6 +1707,9 @@ static void vo_x11_map_window(struct vo *vo, struct mp_rect rc)
vo_x11_selectinput_witherr(vo, x11->display, x11->window, events);
XMapWindow(x11->display, x11->window);
+ if (x11->opts->cursor_passthrough)
+ vo_x11_set_input_region(vo, true);
+
if (x11->opts->window_maximized) // don't override WM default on "no"
vo_x11_maximize(vo);
if (x11->opts->window_minimized) // don't override WM default on "no"
@@ -1530,12 +1721,12 @@ static void vo_x11_map_window(struct vo *vo, struct mp_rect rc)
vo_x11_xembed_update(x11, XEMBED_MAPPED);
}
-static void vo_x11_highlevel_resize(struct vo *vo, struct mp_rect rc)
+static void vo_x11_highlevel_resize(struct vo *vo, struct mp_rect rc, bool force)
{
struct vo_x11_state *x11 = vo->x11;
struct mp_vo_opts *opts = x11->opts;
- bool reset_pos = opts->force_window_position;
+ bool reset_pos = opts->force_window_position || force;
if (reset_pos) {
x11->nofsrc = rc;
} else {
@@ -1618,17 +1809,25 @@ void vo_x11_config_vo_window(struct vo *vo)
rc = (struct mp_rect){0, 0, RC_W(x11->winrc), RC_H(x11->winrc)};
}
- bool reset_size = x11->old_dw != RC_W(rc) || x11->old_dh != RC_H(rc);
+ bool reset_size = (x11->old_dw != RC_W(rc) || x11->old_dh != RC_H(rc)) &&
+ (opts->auto_window_resize || x11->geometry_change);
+ reset_size |= (x11->old_x != rc.x0 || x11->old_y != rc.y0) &&
+ (x11->geometry_change);
+
x11->old_dw = RC_W(rc);
x11->old_dh = RC_H(rc);
+ x11->old_x = rc.x0;
+ x11->old_y = rc.y0;
if (x11->window_hidden) {
x11->nofsrc = rc;
vo_x11_map_window(vo, rc);
} else if (reset_size) {
- vo_x11_highlevel_resize(vo, rc);
+ vo_x11_highlevel_resize(vo, rc, x11->geometry_change);
}
+ x11->geometry_change = false;
+
if (opts->ontop)
vo_x11_setlayer(vo, opts->ontop);
@@ -1640,6 +1839,23 @@ void vo_x11_config_vo_window(struct vo *vo)
x11->pending_vo_events &= ~VO_EVENT_RESIZE; // implicitly done by the VO
}
+static void vo_x11_sticky(struct vo *vo, bool sticky)
+{
+ struct vo_x11_state *x11 = vo->x11;
+ if (x11->wm_type & vo_wm_STICKY) {
+ x11_set_ewmh_state(x11, "_NET_WM_STATE_STICKY", sticky);
+ } else {
+ long params[5] = {0xFFFFFFFF, 1};
+ if (!sticky) {
+ x11_get_property_copy(x11, x11->rootwin,
+ XA(x11, _NET_CURRENT_DESKTOP),
+ XA_CARDINAL, 32, &params[0],
+ sizeof(params[0]));
+ }
+ x11_send_ewmh_msg(x11, "_NET_WM_DESKTOP", params);
+ }
+}
+
static void vo_x11_setlayer(struct vo *vo, bool ontop)
{
struct vo_x11_state *x11 = vo->x11;
@@ -1679,23 +1895,6 @@ static bool rc_overlaps(struct mp_rect rc1, struct mp_rect rc2)
return mp_rect_intersection(&rc1, &rc2); // changes the first argument
}
-// which screen's ICC profile we're going to use
-static int get_icc_screen(struct vo *vo)
-{
- struct vo_x11_state *x11 = vo->x11;
- int cx = x11->winrc.x0 + (x11->winrc.x1 - x11->winrc.x0)/2,
- cy = x11->winrc.y0 + (x11->winrc.y1 - x11->winrc.y0)/2;
- int screen = x11->current_icc_screen; // xinerama screen number
- for (int n = 0; n < x11->num_displays; n++) {
- struct xrandr_display *disp = &x11->displays[n];
- if (mp_rect_contains(&disp->rc, cx, cy)) {
- screen = n;
- break;
- }
- }
- return screen;
-}
-
// update x11->winrc with current boundaries of vo->x11->window
static void vo_x11_update_geometry(struct vo *vo)
{
@@ -1715,25 +1914,25 @@ static void vo_x11_update_geometry(struct vo *vo)
&x, &y, &dummy_win);
x11->winrc = (struct mp_rect){x, y, x + w, y + h};
}
- double fps = 1000.0;
- for (int n = 0; n < x11->num_displays; n++) {
- struct xrandr_display *disp = &x11->displays[n];
- disp->overlaps = rc_overlaps(disp->rc, x11->winrc);
- if (disp->overlaps)
- fps = MPMIN(fps, disp->fps);
+ struct xrandr_display *disp = get_current_display(vo);
+ // Try to fallback to something reasonable if we have no disp yet
+ if (!disp) {
+ int screen = vo_x11_select_screen(vo);
+ if (screen > -1) {
+ disp = &x11->displays[screen];
+ } else if (x11->current_screen > - 1) {
+ disp = &x11->displays[x11->current_screen];
+ }
}
- double fallback = x11->num_displays > 0 ? x11->displays[0].fps : 0;
- fps = fps < 1000.0 ? fps : fallback;
+ double fps = disp ? disp->fps : 0;
if (fps != x11->current_display_fps)
MP_VERBOSE(x11, "Current display FPS: %f\n", fps);
x11->current_display_fps = fps;
- // might have changed displays
- x11->pending_vo_events |= VO_EVENT_WIN_STATE;
- int icc_screen = get_icc_screen(vo);
- if (x11->current_icc_screen != icc_screen) {
- x11->current_icc_screen = icc_screen;
+ if (disp && x11->current_screen != disp->screen) {
+ x11->current_screen = disp-&g