summaryrefslogtreecommitdiffstats
path: root/video/out/x11_common.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--video/out/x11_common.c203
1 files changed, 133 insertions, 70 deletions
diff --git a/video/out/x11_common.c b/video/out/x11_common.c
index b4605bf..fa2f2ba 100644
--- a/video/out/x11_common.c
+++ b/video/out/x11_common.c
@@ -29,6 +29,7 @@
#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>
@@ -98,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
{
@@ -603,6 +606,70 @@ static void vo_x11_get_bounding_monitors(struct vo_x11_state *x11, long b[4])
}
}
+// 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;
@@ -667,21 +734,7 @@ bool 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);
@@ -754,7 +807,8 @@ 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_BACK}, {XF86XK_Tools, MP_KEY_TOOLS},
+ {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}
@@ -783,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;
@@ -794,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;
@@ -803,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);
}
@@ -1105,11 +1161,6 @@ static void vo_x11_check_net_wm_state_change(struct vo *vo)
XFree(elems);
}
- if (opts->window_maximized && !is_maximized && x11->geometry_change) {
- x11->geometry_change = false;
- vo_x11_config_vo_window(vo);
- }
-
opts->window_minimized = is_minimized;
x11->hidden = is_minimized;
m_config_cache_write_opt(x11->opts_cache, &opts->window_minimized);
@@ -1163,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)
@@ -1176,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:
@@ -1232,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:
@@ -1266,22 +1322,20 @@ 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;
@@ -1343,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;
@@ -1372,8 +1427,7 @@ static void vo_x11_sizehint(struct vo *vo, struct mp_rect rc, bool override_pos)
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;
@@ -1579,7 +1633,7 @@ static void vo_x11_create_window(struct vo *vo, XVisualInfo *vis,
if (x11->xim) {
x11->xic = XCreateIC(x11->xim,
- XNInputStyle, XIMPreeditNone | XIMStatusNone,
+ XNInputStyle, XIMPreeditNothing | XIMStatusNothing,
XNClientWindow, x11->window,
XNFocusWindow, x11->window,
NULL);
@@ -1601,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);
@@ -1667,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 {
@@ -1742,10 +1796,6 @@ void vo_x11_config_vo_window(struct vo *vo)
assert(x11->window);
- // Don't attempt to change autofit/geometry on maximized windows.
- if (x11->geometry_change && opts->window_maximized)
- return;
-
vo_x11_update_screeninfo(vo);
struct vo_win_geometry geo;
@@ -1761,15 +1811,19 @@ void vo_x11_config_vo_window(struct vo *vo)
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;
@@ -1922,7 +1976,7 @@ static void vo_x11_fullscreen(struct vo *vo)
rc = x11->screenrc;
}
- vo_x11_decoration(vo, opts->border && !x11->fs);
+ vo_x11_decoration(vo, opts->border && !x11->fs, opts->title_bar);
vo_x11_sizehint(vo, rc, true);
XMoveResizeWindow(x11->display, x11->window, rc.x0, rc.y0,
@@ -2020,8 +2074,8 @@ int vo_x11_control(struct vo *vo, int *events, int request, void *arg)
vo_x11_fullscreen(vo);
if (opt == &opts->ontop)
vo_x11_setlayer(vo, opts->ontop);
- if (opt == &opts->border)
- vo_x11_decoration(vo, opts->border);
+ if (opt == &opts->border || opt == &opts->title_bar)
+ vo_x11_decoration(vo, opts->border, opts->title_bar);
if (opt == &opts->all_workspaces)
vo_x11_sticky(vo, opts->all_workspaces);
if (opt == &opts->window_minimized)
@@ -2035,6 +2089,12 @@ int vo_x11_control(struct vo *vo, int *events, int request, void *arg)
if (opt == &opts->geometry || opt == &opts->autofit ||
opt == &opts->autofit_smaller || opt == &opts->autofit_larger)
{
+ if (opts->window_maximized && !opts->fullscreen) {
+ x11->opts->window_maximized = false;
+ m_config_cache_write_opt(x11->opts_cache,
+ &x11->opts->window_maximized);
+ vo_x11_maximize(vo);
+ }
vo_x11_set_geometry(vo);
}
}
@@ -2044,16 +2104,16 @@ int vo_x11_control(struct vo *vo, int *events, int request, void *arg)
int *s = arg;
if (!x11->window || x11->parent)
return VO_FALSE;
- s[0] = (x11->fs ? RC_W(x11->nofsrc) : RC_W(x11->winrc)) / x11->dpi_scale;
- s[1] = (x11->fs ? RC_H(x11->nofsrc) : RC_H(x11->winrc)) / x11->dpi_scale;
+ s[0] = x11->fs ? RC_W(x11->nofsrc) : RC_W(x11->winrc);
+ s[1] = x11->fs ? RC_H(x11->nofsrc) : RC_H(x11->winrc);
return VO_TRUE;
}
case VOCTRL_SET_UNFS_WINDOW_SIZE: {
int *s = arg;
if (!x11->window || x11->parent)
return VO_FALSE;
- int w = s[0] * x11->dpi_scale;
- int h = s[1] * x11->dpi_scale;
+ int w = s[0];
+ int h = s[1];
struct mp_rect rc = x11->winrc;
rc.x1 = rc.x0 + w;
rc.y1 = rc.y0 + h;
@@ -2063,7 +2123,7 @@ int vo_x11_control(struct vo *vo, int *events, int request, void *arg)
&x11->opts->window_maximized);
vo_x11_maximize(vo);
}
- vo_x11_highlevel_resize(vo, rc);
+ vo_x11_highlevel_resize(vo, rc, false);
if (!x11->fs) { // guess new window size, instead of waiting for X
x11->winrc.x1 = x11->winrc.x0 + w;
x11->winrc.y1 = x11->winrc.y0 + h;
@@ -2151,6 +2211,9 @@ int vo_x11_control(struct vo *vo, int *events, int request, void *arg)
case VOCTRL_GET_HIDPI_SCALE:
*(double *)arg = x11->dpi_scale;
return VO_TRUE;
+ case VOCTRL_BEGIN_DRAGGING:
+ vo_x11_begin_dragging(vo);
+ return VO_TRUE;
}
return VO_NOTIMPL;
}