2009-02-08 03:27:30 +00:00
|
|
|
/*
|
2015-04-13 07:36:54 +00:00
|
|
|
* This file is part of mpv.
|
2009-02-08 03:27:30 +00:00
|
|
|
*
|
2015-04-13 07:36:54 +00:00
|
|
|
* mpv is free software; you can redistribute it and/or modify
|
2009-02-08 03:27:30 +00:00
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
2015-04-13 07:36:54 +00:00
|
|
|
* mpv is distributed in the hope that it will be useful,
|
2009-02-08 03:27:30 +00:00
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License along
|
2015-04-13 07:36:54 +00:00
|
|
|
* with mpv. If not, see <http://www.gnu.org/licenses/>.
|
2009-02-08 03:27:30 +00:00
|
|
|
*/
|
2001-04-15 20:31:58 +00:00
|
|
|
|
2001-03-03 21:47:37 +00:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
2002-10-29 20:27:47 +00:00
|
|
|
#include <math.h>
|
2002-06-12 22:57:58 +00:00
|
|
|
#include <inttypes.h>
|
2008-11-15 19:06:25 +00:00
|
|
|
#include <limits.h>
|
2016-07-20 18:52:08 +00:00
|
|
|
#include <unistd.h>
|
|
|
|
#include <poll.h>
|
2017-04-21 05:30:03 +00:00
|
|
|
#include <string.h>
|
|
|
|
#include <assert.h>
|
|
|
|
|
|
|
|
#include <X11/Xmd.h>
|
|
|
|
#include <X11/Xlib.h>
|
|
|
|
#include <X11/Xutil.h>
|
|
|
|
#include <X11/Xatom.h>
|
2024-01-05 05:58:15 +00:00
|
|
|
#include <X11/Xresource.h>
|
2017-04-21 05:30:03 +00:00
|
|
|
#include <X11/keysym.h>
|
|
|
|
#include <X11/XKBlib.h>
|
|
|
|
#include <X11/XF86keysym.h>
|
|
|
|
|
2017-08-20 07:11:26 +00:00
|
|
|
#include <X11/extensions/scrnsaver.h>
|
2017-04-21 05:30:03 +00:00
|
|
|
#include <X11/extensions/dpms.h>
|
2022-05-12 21:26:49 +00:00
|
|
|
#include <X11/extensions/shape.h>
|
2017-04-21 05:30:03 +00:00
|
|
|
#include <X11/extensions/Xrandr.h>
|
|
|
|
|
2024-06-04 21:02:57 +00:00
|
|
|
#ifndef PRESENT_FUTURE_VERSION
|
|
|
|
#define PRESENT_FUTURE_VERSION 0
|
|
|
|
#endif
|
|
|
|
#include <X11/extensions/Xpresent.h>
|
|
|
|
|
2014-08-29 10:09:04 +00:00
|
|
|
#include "misc/bstr.h"
|
2013-12-17 01:02:25 +00:00
|
|
|
#include "options/options.h"
|
2016-09-02 13:50:54 +00:00
|
|
|
#include "options/m_config.h"
|
2014-04-19 15:18:10 +00:00
|
|
|
#include "common/common.h"
|
2013-12-17 01:39:45 +00:00
|
|
|
#include "common/msg.h"
|
2013-12-17 00:23:09 +00:00
|
|
|
#include "input/input.h"
|
2014-01-04 15:59:22 +00:00
|
|
|
#include "input/event.h"
|
2017-07-01 13:56:12 +00:00
|
|
|
#include "video/image_loader.h"
|
|
|
|
#include "video/mp_image.h"
|
x11: support xorg present extension
This builds off of present_sync which was introduced in a previous
commit to support xorg's present extension in all of the X11 backends
(sans vdpau) in mpv. It turns out there is an Xpresent library that
integrates the xorg present extention with Xlib (which barely anyone
seems to use), so this can be added without too much trouble. The
workflow is to first setup the event by telling Xorg we would like to
receive PresentCompleteNotify (there are others in the extension but
this is the only one we really care about). After that, just call
XPresentNotifyMSC after every buffer swap with a target_msc of 0. Xorg
then returns the last presentation through its usual event loop and we
go ahead and use that information to update mpv's values for vsync
timing purposes. One theoretical weakness of this approach is that the
present event is put on the same queue as the rest of the XEvents. It
would be nicer for it be placed somewhere else so we could just wait
on that queue without having to deal with other possible events in
there. In theory, xcb could do that with special events, but it doesn't
really matter in practice.
Unsurprisingly, this doesn't work on NVIDIA. Well NVIDIA does actually
receive presentation events, but for whatever the calculations used make
timings worse which defeats the purpose. This works perfectly fine on
Mesa however. Utilizing the previous commit that detects Xrandr
providers, we can enable this mechanism for users that have both Mesa
and not NVIDIA (to avoid messing up anyone that has a switchable
graphics system or such). Patches welcome if anyone figures out how to
fix this on NVIDIA.
Unlike the EGL/GLX sync extensions, the present extension works with any
graphics API (good for vulkan since its timing extension has been in
development hell). NVIDIA also happens to have zero support for the
EGL/GLX sync extensions, so we can just remove it with no loss. Only
Xorg ever used it and other backends already have their own present
methods. vo_vdpau VO is a special case that has its own fancying timing
code in its flip_page. This presumably works well, and I have no way of
testing it so just leave it as it is.
2022-06-10 16:49:38 +00:00
|
|
|
#include "present_sync.h"
|
2002-06-12 22:57:58 +00:00
|
|
|
#include "x11_common.h"
|
2016-01-11 18:03:40 +00:00
|
|
|
#include "mpv_talloc.h"
|
2001-04-15 20:31:58 +00:00
|
|
|
|
2012-11-09 00:06:43 +00:00
|
|
|
#include "vo.h"
|
2014-05-06 18:24:35 +00:00
|
|
|
#include "win_state.h"
|
2016-07-20 18:52:08 +00:00
|
|
|
#include "osdep/io.h"
|
2023-09-18 03:27:12 +00:00
|
|
|
#include "osdep/poll_wrapper.h"
|
2004-10-28 01:15:53 +00:00
|
|
|
#include "osdep/timer.h"
|
2015-04-15 20:39:01 +00:00
|
|
|
#include "osdep/subprocess.h"
|
|
|
|
|
2013-12-17 00:23:09 +00:00
|
|
|
#include "input/input.h"
|
|
|
|
#include "input/keycodes.h"
|
2002-02-11 11:44:50 +00:00
|
|
|
|
2013-01-26 21:37:47 +00:00
|
|
|
#define vo_wm_LAYER 1
|
|
|
|
#define vo_wm_FULLSCREEN 2
|
|
|
|
#define vo_wm_STAYS_ON_TOP 4
|
|
|
|
#define vo_wm_ABOVE 8
|
|
|
|
#define vo_wm_BELOW 16
|
2022-10-09 23:20:32 +00:00
|
|
|
#define vo_wm_STICKY 32
|
2024-04-13 03:13:17 +00:00
|
|
|
#define vo_wm_SKIP_TASKBAR 64
|
2013-01-26 21:37:47 +00:00
|
|
|
|
|
|
|
/* EWMH state actions, see
|
|
|
|
http://freedesktop.org/Standards/wm-spec/index.html#id2768769 */
|
2014-05-23 18:22:00 +00:00
|
|
|
#define NET_WM_STATE_REMOVE 0 /* remove/unset property */
|
|
|
|
#define NET_WM_STATE_ADD 1 /* add/set property */
|
|
|
|
#define NET_WM_STATE_TOGGLE 2 /* toggle property */
|
2013-01-26 21:37:47 +00:00
|
|
|
|
2002-12-30 18:50:15 +00:00
|
|
|
#define WIN_LAYER_ONBOTTOM 2
|
|
|
|
#define WIN_LAYER_NORMAL 4
|
|
|
|
#define WIN_LAYER_ONTOP 6
|
This time the patch is fixing much more that breaking :-)
It includes ugly hack for OpenBox, swaps checking for netwm
with layers (and makes fvwm work -> they have also broken
_NET_WM_STATE_FULLSCREEN but also supports layers) and changes
back fullscreen layer to 10, afterstep doesn't like 12 :-)
Tested with: sawfish, metacity, kwin, blackbox, openbox, waimea,
windowmaker, afterstep, icewm, mwm, fvwm
Filip Kalinski <filon@pld.org.pl>
git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@8670 b3059339-0415-0410-9bf9-f77b7e298cf2
2002-12-30 23:59:53 +00:00
|
|
|
#define WIN_LAYER_ABOVE_DOCK 10
|
2004-06-14 04:53:03 +00:00
|
|
|
|
2014-01-04 00:16:44 +00:00
|
|
|
#define DND_VERSION 5
|
|
|
|
|
2015-01-12 11:08:48 +00:00
|
|
|
#define XEMBED_VERSION 0
|
|
|
|
#define XEMBED_MAPPED (1 << 0)
|
|
|
|
#define XEMBED_EMBEDDED_NOTIFY 0
|
|
|
|
#define XEMBED_REQUEST_FOCUS 3
|
|
|
|
|
2013-01-26 21:37:47 +00:00
|
|
|
// ----- Motif header: -------
|
|
|
|
|
|
|
|
#define MWM_HINTS_FUNCTIONS (1L << 0)
|
|
|
|
#define MWM_HINTS_DECORATIONS (1L << 1)
|
|
|
|
|
|
|
|
#define MWM_FUNC_RESIZE (1L << 1)
|
|
|
|
#define MWM_FUNC_MOVE (1L << 2)
|
|
|
|
#define MWM_FUNC_MINIMIZE (1L << 3)
|
|
|
|
#define MWM_FUNC_MAXIMIZE (1L << 4)
|
|
|
|
#define MWM_FUNC_CLOSE (1L << 5)
|
2003-01-09 21:23:54 +00:00
|
|
|
|
2024-02-01 10:32:08 +00:00
|
|
|
// Equals to all MWM_DECOR_* OR'd together.
|
|
|
|
#define MWM_DECOR_ALL 126
|
|
|
|
#define MWM_DECOR_TITLE (1L << 3)
|
2001-04-13 00:54:05 +00:00
|
|
|
|
2013-01-26 21:37:47 +00:00
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
long flags;
|
|
|
|
long functions;
|
|
|
|
long decorations;
|
|
|
|
long input_mode;
|
|
|
|
long state;
|
|
|
|
} MotifWmHints;
|
2003-01-07 20:58:25 +00:00
|
|
|
|
2017-07-01 13:56:12 +00:00
|
|
|
static const char x11_icon_16[] =
|
2023-07-23 22:54:35 +00:00
|
|
|
#include "etc/mpv-icon-8bit-16x16.png.inc"
|
2017-07-01 13:56:12 +00:00
|
|
|
;
|
|
|
|
|
|
|
|
static const char x11_icon_32[] =
|
2023-07-23 22:54:35 +00:00
|
|
|
#include "etc/mpv-icon-8bit-32x32.png.inc"
|
2017-07-01 13:56:12 +00:00
|
|
|
;
|
|
|
|
|
|
|
|
static const char x11_icon_64[] =
|
2023-07-23 22:54:35 +00:00
|
|
|
#include "etc/mpv-icon-8bit-64x64.png.inc"
|
x11: add window icon
The png file added to etc/ are taken from the link mentioned in commit
303096b, except that they have been converted to 16 bit, sRGB (with
color profile info dropped, if there was one), and transparent pixels
reset for better compression.
The file x11_icon.bin is generated by gen-x11-icon.sh. I'm adding it to
the git repo directly, because the script requires ImageMagick, and we
don't want to make building even more complicated.
The way how this is done is basically a compromise between effort
required in x11_common.c and in gen-x11-icon.sh. Ideally, x11_icon.bin
would be directly in the format as required by _NET_WM_ICON, but trying
to write the binary width/height values from shell would probably be a
nightmare, so here we go.
The zlib code in x11_common.c is lifted from demux_mkv.c, with some
modifications (like accepting a gzip header, because I don't know how to
make gzip write raw compressed data).
2013-09-01 21:26:44 +00:00
|
|
|
;
|
|
|
|
|
2017-07-02 03:04:20 +00:00
|
|
|
static const char x11_icon_128[] =
|
2023-07-23 22:54:35 +00:00
|
|
|
#include "etc/mpv-icon-8bit-128x128.png.inc"
|
2017-07-02 03:04:20 +00:00
|
|
|
;
|
|
|
|
|
2017-07-01 13:56:12 +00:00
|
|
|
#define ICON_ENTRY(var) { (char *)var, sizeof(var) }
|
|
|
|
static const struct bstr x11_icons[] = {
|
|
|
|
ICON_ENTRY(x11_icon_16),
|
|
|
|
ICON_ENTRY(x11_icon_32),
|
|
|
|
ICON_ENTRY(x11_icon_64),
|
2017-07-02 03:04:20 +00:00
|
|
|
ICON_ENTRY(x11_icon_128),
|
2017-07-01 13:56:12 +00:00
|
|
|
{0}
|
|
|
|
};
|
|
|
|
|
2013-12-27 22:07:57 +00:00
|
|
|
static struct mp_log *x11_error_output;
|
2015-11-06 20:20:23 +00:00
|
|
|
static atomic_int x11_error_silence;
|
2013-12-27 22:07:57 +00:00
|
|
|
|
2023-08-13 00:30:11 +00:00
|
|
|
static bool rc_overlaps(struct mp_rect rc1, struct mp_rect rc2);
|
x11_common: fix window mapping, refactor window creation/resize handling
vo_opengl creates an invisible window with VOFLAG_HIDDEN in order to
test whether the OpenGL context is useable. The visible window is
created at a later point. This has been broken forever (in vo_gl,
now called vo_opengl_old, it could be avoided by disabling auto-
detection explicitly using the "yuv" sub-option). Avoiding
VOFLAG_HIDDEN only mitigates the issue, and a related bug can still
happen with some window managers (see below).
As a hack, code was added to vo_gl to destroy the (hidden) window so
that all state was destroyed. Later, more hacks were added to deal with
some caveats that came with recreating the window, such as probing for
the context up to 4 times.
Attempt to fix the problem properly. There were two problems: first,
the window was not resized to video size before mapping. This was the
main cause for the placement issue, e.g. mapping the window as 320x200,
and then resizing it. Second, mpv tried to force the window position
with XSetWMNormalHints and PPosition with values it just read with
XGetGeometry. This messes up the window manager's default placement.
It seems to be a race condition, and behavior is different across
various WMs too: with IceWM, the window manager's placement is usually
preferred, and with Fluxbox, mpv's position is preferred. mpv's default
position is centering the window on the screen, which is much nicer for
video in general than typical WM default placement, so it's possible
that this bug was perceived as a feature. (Users who want this have to
use --geometry="50%:50%", doing this by default is probably not safe
with all WMs.)
Since the old code was hard to follow and full of issues, it's easier
to redo it. Move general window creation stuff out of the
vo_x11_config_vo_window function, and move the resize logic into it.
This has been tested on IcwWM, Fluxbox, awesome, Unity/Compiz.
2013-03-02 16:08:37 +00:00
|
|
|
static void vo_x11_update_geometry(struct vo *vo);
|
2013-05-16 12:02:28 +00:00
|
|
|
static void vo_x11_fullscreen(struct vo *vo);
|
2017-08-20 07:11:07 +00:00
|
|
|
static void xscreensaver_heartbeat(struct vo_x11_state *x11);
|
2014-05-17 19:32:11 +00:00
|
|
|
static void set_screensaver(struct vo_x11_state *x11, bool enabled);
|
2013-03-04 16:40:21 +00:00
|
|
|
static void vo_x11_selectinput_witherr(struct vo *vo, Display *display,
|
|
|
|
Window w, long event_mask);
|
2014-05-23 18:27:14 +00:00
|
|
|
static void vo_x11_setlayer(struct vo *vo, bool ontop);
|
2024-04-13 04:00:40 +00:00
|
|
|
static void vo_x11_set_in_taskbar(struct vo *vo, bool in);
|
2015-01-12 11:08:48 +00:00
|
|
|
static void vo_x11_xembed_handle_message(struct vo *vo, XClientMessageEvent *ce);
|
|
|
|
static void vo_x11_xembed_send_message(struct vo_x11_state *x11, long m[4]);
|
2015-06-28 16:10:17 +00:00
|
|
|
static void vo_x11_move_resize(struct vo *vo, bool move, bool resize,
|
|
|
|
struct mp_rect rc);
|
2019-11-29 12:52:37 +00:00
|
|
|
static void vo_x11_maximize(struct vo *vo);
|
|
|
|
static void vo_x11_minimize(struct vo *vo);
|
2022-05-12 21:26:49 +00:00
|
|
|
static void vo_x11_set_input_region(struct vo *vo, bool passthrough);
|
2022-10-09 23:20:32 +00:00
|
|
|
static void vo_x11_sticky(struct vo *vo, bool sticky);
|
2003-05-30 18:05:49 +00:00
|
|
|
|
2014-05-16 21:17:09 +00:00
|
|
|
#define XA(x11, s) (XInternAtom((x11)->display, # s, False))
|
2014-11-03 22:01:27 +00:00
|
|
|
#define XAs(x11, s) XInternAtom((x11)->display, s, False)
|
2014-05-16 21:17:09 +00:00
|
|
|
|
2014-05-17 01:29:36 +00:00
|
|
|
#define RC_W(rc) ((rc).x1 - (rc).x0)
|
|
|
|
#define RC_H(rc) ((rc).y1 - (rc).y0)
|
|
|
|
|
2016-03-15 21:42:06 +00:00
|
|
|
static char *x11_atom_name_buf(struct vo_x11_state *x11, Atom atom,
|
|
|
|
char *buf, size_t buf_size)
|
|
|
|
{
|
|
|
|
buf[0] = '\0';
|
|
|
|
|
|
|
|
char *new_name = XGetAtomName(x11->display, atom);
|
|
|
|
if (new_name) {
|
|
|
|
snprintf(buf, buf_size, "%s", new_name);
|
|
|
|
XFree(new_name);
|
|
|
|
}
|
|
|
|
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
|
|
|
#define x11_atom_name(x11, atom) x11_atom_name_buf(x11, atom, (char[80]){0}, 80)
|
|
|
|
|
2014-05-17 14:58:28 +00:00
|
|
|
// format = 8 (unsigned char), 16 (short), 32 (long, even on LP64 systems)
|
|
|
|
// *out_nitems = returned number of items of requested format
|
|
|
|
static void *x11_get_property(struct vo_x11_state *x11, Window w, Atom property,
|
|
|
|
Atom type, int format, int *out_nitems)
|
|
|
|
{
|
|
|
|
assert(format == 8 || format == 16 || format == 32);
|
|
|
|
*out_nitems = 0;
|
2014-05-18 23:06:43 +00:00
|
|
|
if (!w)
|
|
|
|
return NULL;
|
2015-01-26 01:18:47 +00:00
|
|
|
long max_len = 128 * 1024 * 1024; // static maximum limit
|
2014-05-17 14:58:28 +00:00
|
|
|
Atom ret_type = 0;
|
|
|
|
int ret_format = 0;
|
|
|
|
unsigned long ret_nitems = 0;
|
|
|
|
unsigned long ret_bytesleft = 0;
|
|
|
|
unsigned char *ret_prop = NULL;
|
|
|
|
if (XGetWindowProperty(x11->display, w, property, 0, max_len, False, type,
|
|
|
|
&ret_type, &ret_format, &ret_nitems, &ret_bytesleft,
|
|
|
|
&ret_prop) != Success)
|
|
|
|
return NULL;
|
|
|
|
if (ret_format != format || ret_nitems < 1 || ret_bytesleft) {
|
|
|
|
XFree(ret_prop);
|
|
|
|
ret_prop = NULL;
|
|
|
|
ret_nitems = 0;
|
|
|
|
}
|
|
|
|
*out_nitems = ret_nitems;
|
|
|
|
return ret_prop;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool x11_get_property_copy(struct vo_x11_state *x11, Window w,
|
|
|
|
Atom property, Atom type, int format,
|
|
|
|
void *dst, size_t dst_size)
|
|
|
|
{
|
|
|
|
bool ret = false;
|
|
|
|
int len;
|
|
|
|
void *ptr = x11_get_property(x11, w, property, type, format, &len);
|
|
|
|
if (ptr) {
|
|
|
|
size_t ib = format == 32 ? sizeof(long) : format / 8;
|
2019-12-18 06:12:53 +00:00
|
|
|
if (dst_size <= len * ib) {
|
2014-05-17 14:58:28 +00:00
|
|
|
memcpy(dst, ptr, dst_size);
|
|
|
|
ret = true;
|
|
|
|
}
|
|
|
|
XFree(ptr);
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2014-05-17 16:03:28 +00:00
|
|
|
static void x11_send_ewmh_msg(struct vo_x11_state *x11, char *message_type,
|
|
|
|
long params[5])
|
|
|
|
{
|
2014-05-18 23:06:43 +00:00
|
|
|
if (!x11->window)
|
|
|
|
return;
|
|
|
|
|
2014-05-17 16:03:28 +00:00
|
|
|
XEvent xev = {
|
|
|
|
.xclient = {
|
|
|
|
.type = ClientMessage,
|
|
|
|
.send_event = True,
|
|
|
|
.message_type = XInternAtom(x11->display, message_type, False),
|
|
|
|
.window = x11->window,
|
|
|
|
.format = 32,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
for (int n = 0; n < 5; n++)
|
|
|
|
xev.xclient.data.l[n] = params[n];
|
|
|
|
|
|
|
|
if (!XSendEvent(x11->display, x11->rootwin, False,
|
|
|
|
SubstructureRedirectMask | SubstructureNotifyMask,
|
|
|
|
&xev))
|
|
|
|
MP_ERR(x11, "Couldn't send EWMH %s message!\n", message_type);
|
|
|
|
}
|
|
|
|
|
2014-05-23 18:05:24 +00:00
|
|
|
// change the _NET_WM_STATE hint. Remove or add the state according to "set".
|
|
|
|
static void x11_set_ewmh_state(struct vo_x11_state *x11, char *state, bool set)
|
2004-06-02 02:34:39 +00:00
|
|
|
{
|
2014-05-23 18:05:24 +00:00
|
|
|
long params[5] = {
|
2014-05-23 18:22:00 +00:00
|
|
|
set ? NET_WM_STATE_ADD : NET_WM_STATE_REMOVE,
|
2014-05-23 18:05:24 +00:00
|
|
|
XInternAtom(x11->display, state, False),
|
2019-11-29 08:51:53 +00:00
|
|
|
0, // No second state
|
|
|
|
1, // source indication: normal
|
2014-05-23 18:05:24 +00:00
|
|
|
};
|
|
|
|
x11_send_ewmh_msg(x11, "_NET_WM_STATE", params);
|
2004-06-02 02:34:39 +00:00
|
|
|
}
|
|
|
|
|
2018-10-11 04:38:57 +00:00
|
|
|
static void vo_update_cursor(struct vo *vo)
|
2001-04-13 00:54:05 +00:00
|
|
|
{
|
2004-06-14 04:53:03 +00:00
|
|
|
Cursor no_ptr;
|
|
|
|
Pixmap bm_no;
|
|
|
|
XColor black, dummy;
|
|
|
|
Colormap colormap;
|
2013-02-13 15:33:01 +00:00
|
|
|
const char bm_no_data[] = {0, 0, 0, 0, 0, 0, 0, 0};
|
2013-05-16 21:24:56 +00:00
|
|
|
struct vo_x11_state *x11 = vo->x11;
|
|
|
|
Display *disp = x11->display;
|
|
|
|
Window win = x11->window;
|
2018-10-11 04:38:57 +00:00
|
|
|
bool should_hide = x11->has_focus && !x11->mouse_cursor_visible;
|
2004-06-14 04:53:03 +00:00
|
|
|
|
2018-10-11 04:38:57 +00:00
|
|
|
if (should_hide == x11->mouse_cursor_set)
|
2013-05-16 21:24:56 +00:00
|
|
|
return;
|
2004-06-14 04:53:03 +00:00
|
|
|
|
2018-10-11 04:38:57 +00:00
|
|
|
x11->mouse_cursor_set = should_hide;
|
2001-04-13 00:54:05 +00:00
|
|
|
|
2014-12-09 20:28:07 +00:00
|
|
|
if (x11->parent == x11->rootwin || !win)
|
2013-05-16 21:24:56 +00:00
|
|
|
return; // do not hide if playing on the root window
|
|
|
|
|
2018-10-11 04:38:57 +00:00
|
|
|
if (x11->mouse_cursor_set) {
|
2013-05-16 21:24:56 +00:00
|
|
|
colormap = DefaultColormap(disp, DefaultScreen(disp));
|
|
|
|
if (!XAllocNamedColor(disp, colormap, "black", &black, &dummy))
|
|
|
|
return; // color alloc failed, give up
|
|
|
|
bm_no = XCreateBitmapFromData(disp, win, bm_no_data, 8, 8);
|
|
|
|
no_ptr = XCreatePixmapCursor(disp, bm_no, bm_no, &black, &black, 0, 0);
|
|
|
|
XDefineCursor(disp, win, no_ptr);
|
|
|
|
XFreeCursor(disp, no_ptr);
|
|
|
|
if (bm_no != None)
|
|
|
|
XFreePixmap(disp, bm_no);
|
|
|
|
XFreeColors(disp, colormap, &black.pixel, 1, 0);
|
|
|
|
} else {
|
|
|
|
XDefineCursor(x11->display, x11->window, 0);
|
|
|
|
}
|
2002-05-03 20:57:13 +00:00
|
|
|
}
|
2001-04-13 00:54:05 +00:00
|
|
|
|
2013-12-27 22:07:57 +00:00
|
|
|
static int x11_errorhandler(Display *display, XErrorEvent *event)
|
|
|
|
{
|
|
|
|
struct mp_log *log = x11_error_output;
|
2016-05-08 09:04:44 +00:00
|
|
|
if (!log)
|
|
|
|
return 0;
|
|
|
|
|
2015-11-06 20:20:23 +00:00
|
|
|
char msg[60];
|
2013-12-27 22:07:57 +00:00
|
|
|
XGetErrorText(display, event->error_code, (char *) &msg, sizeof(msg));
|
|
|
|
|
2015-11-07 16:43:10 +00:00
|
|
|
int lev = atomic_load(&x11_error_silence) ? MSGL_V : MSGL_ERR;
|
|
|
|
mp_msg(log, lev, "X11 error: %s\n", msg);
|
|
|
|
mp_msg(log, lev, "Type: %x, display: %p, resourceid: %lx, serial: %lx\n",
|
2013-12-27 22:07:57 +00:00
|
|
|
event->type, event->display, event->resourceid, event->serial);
|
2015-11-07 16:43:10 +00:00
|
|
|
mp_msg(log, lev, "Error code: %x, request code: %x, minor code: %x\n",
|
|
|
|
event->error_code, event->request_code, event->minor_code);
|
2013-12-27 22:07:57 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-11-06 20:20:23 +00:00
|
|
|
void vo_x11_silence_xlib(int dir)
|
|
|
|
{
|
|
|
|
atomic_fetch_add(&x11_error_silence, dir);
|
|
|
|
}
|
|
|
|
|
2008-04-20 23:50:40 +00:00
|
|
|
static int net_wm_support_state_test(struct vo_x11_state *x11, Atom atom)
|
2002-12-30 00:47:43 +00:00
|
|
|
{
|
2013-02-13 15:33:01 +00:00
|
|
|
#define NET_WM_STATE_TEST(x) { \
|
2014-05-16 21:17:09 +00:00
|
|
|
if (atom == XA(x11, _NET_WM_STATE_##x)) { \
|
2019-11-01 00:40:22 +00:00
|
|
|
MP_DBG(x11, "Detected wm supports " #x " state.\n" ); \
|
2013-02-13 15:33:01 +00:00
|
|
|
return vo_wm_##x; \
|
|
|
|
} \
|
|
|
|
}
|
2004-06-14 04:53:03 +00:00
|
|
|
|
|
|
|
NET_WM_STATE_TEST(FULLSCREEN);
|
|
|
|
NET_WM_STATE_TEST(ABOVE);
|
|
|
|
NET_WM_STATE_TEST(STAYS_ON_TOP);
|
|
|
|
NET_WM_STATE_TEST(BELOW);
|
2022-10-09 23:20:32 +00:00
|
|
|
NET_WM_STATE_TEST(STICKY);
|
2024-04-13 03:13:17 +00:00
|
|
|
NET_WM_STATE_TEST(SKIP_TASKBAR);
|
2004-06-14 04:53:03 +00:00
|
|
|
return 0;
|
2002-12-30 00:47:43 +00:00
|
|
|
}
|
|
|
|
|
2008-04-20 06:34:27 +00:00
|
|
|
static int vo_wm_detect(struct vo *vo)
|
2002-05-07 13:31:29 +00:00
|
|
|
{
|
2008-04-20 23:50:40 +00:00
|
|
|
struct vo_x11_state *x11 = vo->x11;
|
2004-06-14 04:53:03 +00:00
|
|
|
int i;
|
|
|
|
int wm = 0;
|
2014-05-17 14:58:28 +00:00
|
|
|
int nitems;
|
2004-06-14 04:53:03 +00:00
|
|
|
Atom *args = NULL;
|
2014-01-04 00:16:44 +00:00
|
|
|
Window win = x11->rootwin;
|
2004-06-14 04:53:03 +00:00
|
|
|
|
2014-12-09 20:28:07 +00:00
|
|
|
if (x11->parent)
|
2004-06-14 04:53:03 +00:00
|
|
|
return 0;
|
This time the patch is fixing much more that breaking :-)
It includes ugly hack for OpenBox, swaps checking for netwm
with layers (and makes fvwm work -> they have also broken
_NET_WM_STATE_FULLSCREEN but also supports layers) and changes
back fullscreen layer to 10, afterstep doesn't like 12 :-)
Tested with: sawfish, metacity, kwin, blackbox, openbox, waimea,
windowmaker, afterstep, icewm, mwm, fvwm
Filip Kalinski <filon@pld.org.pl>
git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@8670 b3059339-0415-0410-9bf9-f77b7e298cf2
2002-12-30 23:59:53 +00:00
|
|
|
|
2004-06-14 04:53:03 +00:00
|
|
|
// -- supports layers
|
2014-05-17 14:58:28 +00:00
|
|
|
args = x11_get_property(x11, win, XA(x11, _WIN_PROTOCOLS), XA_ATOM, 32,
|
|
|
|
&nitems);
|
|
|
|
if (args) {
|
2013-02-13 15:33:01 +00:00
|
|
|
for (i = 0; i < nitems; i++) {
|
2014-05-17 14:58:28 +00:00
|
|
|
if (args[i] == XA(x11, _WIN_LAYER)) {
|
2019-11-01 00:40:22 +00:00
|
|
|
MP_DBG(x11, "Detected wm supports layers.\n");
|
2004-06-14 04:53:03 +00:00
|
|
|
wm |= vo_wm_LAYER;
|
2014-05-17 14:58:28 +00:00
|
|
|
}
|
2004-06-14 04:53:03 +00:00
|
|
|
}
|
|
|
|
XFree(args);
|
|
|
|
}
|
2009-07-06 23:26:13 +00:00
|
|
|
// --- netwm
|
2014-05-17 14:58:28 +00:00
|
|
|
args = x11_get_property(x11, win, XA(x11, _NET_SUPPORTED), XA_ATOM, 32,
|
|
|
|
&nitems);
|
|
|
|
if (args) {
|
2019-11-01 00:40:22 +00:00
|
|
|
MP_DBG(x11, "Detected wm supports NetWM.\n");
|
2019-11-27 19:30:13 +00:00
|
|
|
if (x11->opts->x11_netwm >= 0) {
|
2014-05-15 22:47:13 +00:00
|
|
|
for (i = 0; i < nitems; i++)
|
|
|
|
wm |= net_wm_support_state_test(vo->x11, args[i]);
|
|
|
|
} else {
|
2019-11-01 00:40:22 +00:00
|
|
|
MP_DBG(x11, "NetWM usage disabled by user.\n");
|
2014-05-15 22:47:13 +00:00
|
|
|
}
|
2004-06-14 04:53:03 +00:00
|
|
|
XFree(args);
|
|
|
|
}
|
2003-02-07 19:38:39 +00:00
|
|
|
|
2004-06-14 04:53:03 +00:00
|
|
|
if (wm == 0)
|
2019-11-01 00:40:22 +00:00
|
|
|
MP_DBG(x11, "Unknown wm type...\n");
|
2019-11-27 19:30:13 +00:00
|
|
|
if (x11->opts->x11_netwm > 0 && !(wm & vo_wm_FULLSCREEN)) {
|
2014-10-04 13:03:02 +00:00
|
|
|
MP_WARN(x11, "Forcing NetWM FULLSCREEN support.\n");
|
|
|
|
wm |= vo_wm_FULLSCREEN;
|
|
|
|
}
|
2004-06-14 04:53:03 +00:00
|
|
|
return wm;
|
|
|
|
}
|
2002-05-07 13:31:29 +00:00
|
|
|
|
2022-06-22 04:13:44 +00:00
|
|
|
static void xpresent_set(struct vo_x11_state *x11)
|
|
|
|
{
|
|
|
|
int present = x11->opts->x11_present;
|
|
|
|
x11->use_present = x11->present_code &&
|
2024-10-16 14:38:22 +00:00
|
|
|
((x11->has_mesa && present) ||
|
2022-06-22 04:13:44 +00:00
|
|
|
present == 2);
|
|
|
|
if (x11->use_present) {
|
|
|
|
MP_VERBOSE(x11, "XPresent enabled.\n");
|
|
|
|
} else {
|
|
|
|
MP_VERBOSE(x11, "XPresent disabled.\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-08-16 19:43:13 +00:00
|
|
|
static void xrandr_read(struct vo_x11_state *x11)
|
|
|
|
{
|
2014-11-05 23:58:24 +00:00
|
|
|
for(int i = 0; i < x11->num_displays; i++)
|
|
|
|
talloc_free(x11->displays[i].name);
|
|
|
|
|
2014-08-16 19:43:13 +00:00
|
|
|
x11->num_displays = 0;
|
|
|
|
|
2014-08-17 13:38:47 +00:00
|
|
|
if (x11->xrandr_event < 0) {
|
|
|
|
int event_base, error_base;
|
|
|
|
if (!XRRQueryExtension(x11->display, &event_base, &error_base)) {
|
|
|
|
MP_VERBOSE(x11, "Couldn't init Xrandr.\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
x11->xrandr_event = event_base + RRNotify;
|
|
|
|
XRRSelectInput(x11->display, x11->rootwin, RRScreenChangeNotifyMask |
|
|
|
|
RRCrtcChangeNotifyMask | RROutputChangeNotifyMask);
|
|
|
|
}
|
|
|
|
|
2016-01-20 19:06:46 +00:00
|
|
|
XRRScreenResources *r = XRRGetScreenResourcesCurrent(x11->display, x11->rootwin);
|
2014-08-16 19:43:13 +00:00
|
|
|
if (!r) {
|
|
|
|
MP_VERBOSE(x11, "Xrandr doesn't work.\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-06-16 22:39:14 +00:00
|
|
|
/* Look at the available providers on the current screen and try to determine
|
2024-10-16 14:38:22 +00:00
|
|
|
* the driver. If amd/intel/radeon, assume this is mesa. For any of the mesa
|
|
|
|
* drivers, enable XPresent. */
|
2023-08-12 22:54:11 +00:00
|
|
|
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 radeon = bstr_find0(provider_name, "radeon");
|
|
|
|
x11->has_mesa = x11->has_mesa || amd >= 0 || intel >= 0 ||
|
|
|
|
modesetting >= 0 || nouveau >= 0 || radeon >= 0;
|
|
|
|
XRRFreeProviderInfo(info);
|
2022-06-16 22:39:14 +00:00
|
|
|
}
|
2023-08-12 22:54:11 +00:00
|
|
|
if (x11->present_code)
|
|
|
|
xpresent_set(x11);
|
|
|
|
XRRFreeProviderResources(pr);
|
2022-06-16 22:39:14 +00:00
|
|
|
|
2018-11-01 01:00:03 +00:00
|
|
|
int primary_id = -1;
|
|
|
|
RROutput primary = XRRGetOutputPrimary(x11->display, x11->rootwin);
|
2014-08-16 19:43:13 +00:00
|
|
|
for (int o = 0; o < r->noutput; o++) {
|
|
|
|
RROutput output = r->outputs[o];
|
2014-08-17 00:47:44 +00:00
|
|
|
XRRCrtcInfo *crtc = NULL;
|
2014-08-16 19:43:13 +00:00
|
|
|
XRROutputInfo *out = XRRGetOutputInfo(x11->display, r, output);
|
|
|
|
if (!out || !out->crtc)
|
2014-08-17 00:47:44 +00:00
|
|
|
goto next;
|
|
|
|
crtc = XRRGetCrtcInfo(x11->display, r, out->crtc);
|
2014-08-16 19:43:13 +00:00
|
|
|
if (!crtc)
|
2014-08-17 00:47:44 +00:00
|
|
|
goto next;
|
2014-08-16 19:43:13 +00:00
|
|
|
for (int om = 0; om < out->nmode; om++) {
|
|
|
|
RRMode xm = out->modes[om];
|
|
|
|
for (int n = 0; n < r->nmode; n++) {
|
|
|
|
XRRModeInfo m = r->modes[n];
|
|
|
|
if (m.id != xm || crtc->mode != xm)
|
|
|
|
continue;
|
|
|
|
if (x11->num_displays >= MAX_DISPLAYS)
|
|
|
|
continue;
|
2014-10-27 15:17:46 +00:00
|
|
|
double vTotal = m.vTotal;
|
|
|
|
if (m.modeFlags & RR_DoubleScan)
|
|
|
|
vTotal *= 2;
|
|
|
|
if (m.modeFlags & RR_Interlace)
|
|
|
|
vTotal /= 2;
|
2014-08-16 19:43:13 +00:00
|
|
|
struct xrandr_display d = {
|
|
|
|
.rc = { crtc->x, crtc->y,
|
|
|
|
crtc->x + crtc->width, crtc->y + crtc->height },
|
2014-10-27 15:17:46 +00:00
|
|
|
.fps = m.dotClock / (m.hTotal * vTotal),
|
2014-11-05 23:58:24 +00:00
|
|
|
.name = talloc_strdup(x11, out->name),
|
2014-08-16 19:43:13 +00:00
|
|
|
};
|
|
|
|
int num = x11->num_displays++;
|
2014-11-05 23:58:24 +00:00
|
|
|
MP_VERBOSE(x11, "Display %d (%s): [%d, %d, %d, %d] @ %f FPS\n",
|
|
|
|
num, d.name, d.rc.x0, d.rc.y0, d.rc.x1, d.rc.y1, d.fps);
|
2014-08-16 19:43:13 +00:00
|
|
|
x11->displays[num] = d;
|
2018-11-01 01:00:03 +00:00
|
|
|
if (output == primary)
|
|
|
|
primary_id = num;
|
2014-08-16 19:43:13 +00:00
|
|
|
}
|
|
|
|
}
|
2014-08-17 00:47:44 +00:00
|
|
|
next:
|
|
|
|
if (crtc)
|
|
|
|
XRRFreeCrtcInfo(crtc);
|
|
|
|
if (out)
|
|
|
|
XRRFreeOutputInfo(out);
|
2014-08-16 19:43:13 +00:00
|
|
|
}
|
|
|
|
|
2018-11-01 01:00:03 +00:00
|
|
|
for (int i = 0; i < x11->num_displays; i++) {
|
|
|
|
struct xrandr_display *d = &(x11->displays[i]);
|
2023-08-13 00:30:11 +00:00
|
|
|
d->screen = i;
|
2018-11-01 01:00:03 +00:00
|
|
|
|
|
|
|
if (i == primary_id) {
|
|
|
|
d->atom_id = 0;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (primary_id > 0 && i < primary_id) {
|
|
|
|
d->atom_id = i+1;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
d->atom_id = i;
|
|
|
|
}
|
|
|
|
|
2014-08-16 19:43:13 +00:00
|
|
|
XRRFreeScreenResources(r);
|
|
|
|
}
|
|
|
|
|
2023-08-13 00:30:11 +00:00
|
|
|
static int vo_x11_select_screen(struct vo *vo)
|
2013-02-13 15:33:01 +00:00
|
|
|
{
|
2013-02-15 18:47:34 +00:00
|
|
|
struct vo_x11_state *x11 = vo->x11;
|
2019-11-27 19:30:13 +00:00
|
|
|
struct mp_vo_opts *opts = x11->opts;
|
2023-08-13 00:30:11 +00:00
|
|
|
int screen = -2; // all displays
|
|
|
|
if (!opts->fullscreen || opts->fsscreen_id != -2) {
|
|
|
|
screen = opts->fullscreen ? opts->fsscreen_id : opts->screen_id;
|
2019-12-22 00:19:53 +00:00
|
|
|
if (opts->fullscreen && opts->fsscreen_id == -1)
|
|
|
|
screen = opts->screen_id;
|
|
|
|
|
2020-11-23 20:01:23 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-13 00:30:11 +00:00
|
|
|
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) {
|
2008-04-19 09:58:01 +00:00
|
|
|
if (screen == -1) {
|
2014-05-17 01:29:36 +00:00
|
|
|
int x = x11->winrc.x0 + RC_W(x11->winrc) / 2;
|
|
|
|
int y = x11->winrc.y0 + RC_H(x11->winrc) / 2;
|
2023-08-13 00:30:11 +00:00
|
|
|
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;
|
2013-02-13 15:33:01 +00:00
|
|
|
if (left <= x && x <= right && top <= y && y <= bottom)
|
|
|
|
break;
|
2008-04-19 09:58:01 +00:00
|
|
|
}
|
2008-04-13 11:15:08 +00:00
|
|
|
}
|
2023-08-13 00:30:11 +00:00
|
|
|
|
2006-04-16 13:38:28 +00:00
|
|
|
if (screen < 0)
|
|
|
|
screen = 0;
|
2014-05-06 18:24:35 +00:00
|
|
|
x11->screenrc = (struct mp_rect){
|
2023-08-13 00:30:11 +00:00
|
|
|
.x0 = x11->displays[screen].rc.x0,
|
|
|
|
.y0 = x11->displays[screen].rc.y0,
|
|
|
|
.x1 = x11->displays[screen].rc.x1,
|
|
|
|
.y1 = x11->displays[screen].rc.y1,
|
2014-05-06 18:24:35 +00:00
|
|
|
};
|
2023-08-13 00:30:11 +00:00
|
|
|
}
|
|
|
|
}
|
2006-04-16 13:38:28 +00:00
|
|
|
|
2024-05-25 18:49:05 +00:00
|
|
|
static struct xrandr_display *get_xrandr_display(struct vo *vo, struct mp_rect rc)
|
2023-08-13 00:30:11 +00:00
|
|
|
{
|
|
|
|
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];
|
2024-05-25 18:49:05 +00:00
|
|
|
disp->overlaps = rc_overlaps(disp->rc, rc);
|
2023-08-13 00:30:11 +00:00
|
|
|
if (disp->overlaps && (!selected_disp || disp->fps < selected_disp->fps))
|
|
|
|
selected_disp = disp;
|
2006-04-16 13:38:28 +00:00
|
|
|
}
|
2023-08-13 00:30:11 +00:00
|
|
|
return selected_disp;
|
2006-04-16 13:38:28 +00:00
|
|
|
}
|
|
|
|
|
2014-05-16 23:14:59 +00:00
|
|
|
// Get the monitors for the 4 edges of the rectangle spanning all screens.
|
|
|
|
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;
|
2023-08-13 00:30:11 +00:00
|
|
|
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)
|
2014-05-16 23:14:59 +00:00
|
|
|
b[0] = n;
|
2024-10-23 01:09:24 +00:00
|
|
|
if (d->rc.y1 > x11->displays[b[1]].rc.y1)
|
2014-05-16 23:14:59 +00:00
|
|
|
b[1] = n;
|
2023-08-13 00:30:11 +00:00
|
|
|
if (d->rc.x0 < x11->displays[b[2]].rc.x0)
|
2014-05-16 23:14:59 +00:00
|
|
|
b[2] = n;
|
2024-10-23 01:09:24 +00:00
|
|
|
if (d->rc.x1 > x11->displays[b[3]].rc.x1)
|
2014-05-16 23:14:59 +00:00
|
|
|
b[3] = n;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-05 05:25:28 +00:00
|
|
|
// 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;
|
2024-02-12 12:51:25 +00:00
|
|
|
if (isfinite(dpi_x) && isfinite(dpi_y)) {
|
2024-01-05 05:25:28 +00:00
|
|
|
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;
|
2024-02-13 23:20:08 +00:00
|
|
|
MP_VERBOSE(x11, "Using X11 screen DPI scale: %g", x11->dpi_scale);
|
2024-01-05 05:25:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-05 05:58:15 +00:00
|
|
|
// 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;
|
2024-02-13 23:20:08 +00:00
|
|
|
MP_VERBOSE(x11, "Using Xft.dpi scale: %g", x11->dpi_scale);
|
2024-01-05 05:58:15 +00:00
|
|
|
success = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
XrmDestroyDatabase(db);
|
|
|
|
return success;
|
|
|
|
}
|
|
|
|
|
2024-02-13 23:20:08 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2023-01-08 19:16:47 +00:00
|
|
|
bool vo_x11_init(struct vo *vo)
|
2001-03-03 21:47:37 +00:00
|
|
|
{
|
2004-06-14 04:53:03 +00:00
|
|
|
char *dispName;
|
2009-07-06 23:26:13 +00:00
|
|
|
|
2013-01-26 21:37:47 +00:00
|
|
|
assert(!vo->x11);
|
2012-08-03 03:55:02 +00:00
|
|
|
|
2014-08-12 22:26:13 +00:00
|
|
|
XInitThreads();
|
|
|
|
|
2013-01-26 21:37:47 +00:00
|
|
|
struct vo_x11_state *x11 = talloc_ptrtype(NULL, x11);
|
|
|
|
*x11 = (struct vo_x11_state){
|
2013-09-11 22:56:41 +00:00
|
|
|
.log = mp_log_new(x11, vo->log, "x11"),
|
2016-03-15 21:11:31 +00:00
|
|
|
.input_ctx = vo->input_ctx,
|
2014-05-17 19:32:11 +00:00
|
|
|
.screensaver_enabled = true,
|
2014-08-17 13:38:47 +00:00
|
|
|
.xrandr_event = -1,
|
2016-07-21 13:14:37 +00:00
|
|
|
.wakeup_pipe = {-1, -1},
|
2017-01-19 15:30:38 +00:00
|
|
|
.dpi_scale = 1,
|
2019-11-29 11:50:50 +00:00
|
|
|
.opts_cache = m_config_cache_alloc(x11, vo->global, &vo_sub_opts),
|
2013-01-26 21:37:47 +00:00
|
|
|
};
|
2019-11-29 11:50:50 +00:00
|
|
|
x11->opts = x11->opts_cache->opts;
|
2013-01-26 21:37:47 +00:00
|
|
|
vo->x11 = x11;
|
2012-08-03 03:55:02 +00:00
|
|
|
|
2013-12-27 22:07:57 +00:00
|
|
|
x11_error_output = x11->log;
|
|
|
|
XSetErrorHandler(x11_errorhandler);
|
2023-11-06 22:35:04 +00:00
|
|
|
x11->present = mp_present_initialize(x11, x11->opts, VO_MAX_SWAPCHAIN_DEPTH);
|
2013-12-27 22:07:57 +00:00
|
|
|
|
2012-11-15 14:38:02 +00:00
|
|
|
dispName = XDisplayName(NULL);
|
2002-02-22 19:36:21 +00:00
|
|
|
|
2013-09-11 22:56:41 +00:00
|
|
|
MP_VERBOSE(x11, "X11 opening display: %s\n", dispName);
|
2004-06-14 04:53:03 +00:00
|
|
|
|
2008-04-20 06:34:27 +00:00
|
|
|
x11->display = XOpenDisplay(dispName);
|
2013-02-13 15:33:01 +00:00
|
|
|
if (!x11->display) {
|
2013-09-10 16:33:43 +00:00
|
|
|
MP_MSG(x11, vo->probing ? MSGL_V : MSGL_ERR,
|
2013-09-19 22:02:49 +00:00
|
|
|
"couldn't open the X11 display (%s)!\n", dispName);
|
2015-04-15 20:39:01 +00:00
|
|
|
goto error;
|
2004-06-14 04:53:03 +00:00
|
|
|
}
|
2008-04-21 01:59:00 +00:00
|
|
|
x11->screen = DefaultScreen(x11->display); // screen ID
|
|
|
|
x11->rootwin = RootWindow(x11->display, x11->screen); // root window ID
|
2001-05-31 22:32:58 +00:00
|
|
|
|
2019-11-27 19:30:13 +00:00
|
|
|
if (x11->opts->WinID >= 0)
|
|
|
|
x11->parent = x11->opts->WinID ? x11->opts->WinID : x11->rootwin;
|
2014-12-09 20:28:07 +00:00
|
|
|
|
2019-11-27 19:30:13 +00:00
|
|
|
if (!x11->opts->native_keyrepeat) {
|
2013-04-03 23:13:56 +00:00
|
|
|
Bool ok = False;
|
|
|
|
XkbSetDetectableAutoRepeat(x11->display, True, &ok);
|
|
|
|
x11->no_autorepeat = ok;
|
|
|
|
}
|
|
|
|
|
2012-01-14 13:09:44 +00:00
|
|
|
x11->xim = XOpenIM(x11->display, NULL, NULL, NULL);
|
2013-09-20 12:45:49 +00:00
|
|
|
if (!x11->xim)
|
|
|
|
MP_WARN(x11, "XOpenIM() failed. Unicode input will not work.\n");
|
2012-01-14 13:09:44 +00:00
|
|
|
|
2014-01-11 17:57:02 +00:00
|
|
|
x11->ws_width = DisplayWidth(x11->display, x11->screen);
|
|
|
|
x11->ws_height = DisplayHeight(x11->display, x11->screen);
|
2004-06-14 04:53:03 +00:00
|
|
|
|
|
|
|
if (strncmp(dispName, "unix:", 5) == 0)
|
|
|
|
dispName += 4;
|
|
|
|
else if (strncmp(dispName, "localhost:", 10) == 0)
|
|
|
|
dispName += 9;
|
2018-10-17 14:59:36 +00:00
|
|
|
x11->display_is_local = dispName[0] == ':' &&
|
|
|
|
strtoul(dispName + 1, NULL, 10) < 10;
|
2019-11-01 00:40:22 +00:00
|
|
|
MP_DBG(x11, "X11 running at %dx%d (\"%s\" => %s display)\n",
|
|
|
|
x11->ws_width, x11->ws_height, dispName,
|
|
|
|
x11->display_is_local ? "local" : "remote");
|
2004-06-14 04:53:03 +00:00
|
|
|
|
2024-02-13 23:20:08 +00:00
|
|
|
vo_x11_get_dpi_scale(x11);
|
2017-01-19 15:30:38 +00:00
|
|
|
|
2008-04-21 01:50:29 +00:00
|
|
|
x11->wm_type = vo_wm_detect(vo);
|
2004-06-14 04:53:03 +00:00
|
|
|
|
2016-07-20 18:52:08 +00:00
|
|
|
x11->event_fd = ConnectionNumber(x11->display);
|
|
|
|
mp_make_wakeup_pipe(x11->wakeup_pipe);
|
x11_common: fix window mapping, refactor window creation/resize handling
vo_opengl creates an invisible window with VOFLAG_HIDDEN in order to
test whether the OpenGL context is useable. The visible window is
created at a later point. This has been broken forever (in vo_gl,
now called vo_opengl_old, it could be avoided by disabling auto-
detection explicitly using the "yuv" sub-option). Avoiding
VOFLAG_HIDDEN only mitigates the issue, and a related bug can still
happen with some window managers (see below).
As a hack, code was added to vo_gl to destroy the (hidden) window so
that all state was destroyed. Later, more hacks were added to deal with
some caveats that came with recreating the window, such as probing for
the context up to 4 times.
Attempt to fix the problem properly. There were two problems: first,
the window was not resized to video size before mapping. This was the
main cause for the placement issue, e.g. mapping the window as 320x200,
and then resizing it. Second, mpv tried to force the window position
with XSetWMNormalHints and PPosition with values it just read with
XGetGeometry. This messes up the window manager's default placement.
It seems to be a race condition, and behavior is different across
various WMs too: with IceWM, the window manager's placement is usually
preferred, and with Fluxbox, mpv's position is preferred. mpv's default
position is centering the window on the screen, which is much nicer for
video in general than typical WM default placement, so it's possible
that this bug was perceived as a feature. (Users who want this have to
use --geometry="50%:50%", doing this by default is probably not safe
with all WMs.)
Since the old code was hard to follow and full of issues, it's easier
to redo it. Move general window creation stuff out of the
vo_x11_config_vo_window function, and move the resize logic into it.
This has been tested on IcwWM, Fluxbox, awesome, Unity/Compiz.
2013-03-02 16:08:37 +00:00
|
|
|
|
2014-08-16 19:43:13 +00:00
|
|
|
xrandr_read(x11);
|
|
|
|
|
2015-02-13 21:34:26 +00:00
|
|
|
vo_x11_update_geometry(vo);
|
|
|
|
|
2023-01-08 19:16:47 +00:00
|
|
|
return true;
|
2015-04-15 20:39:01 +00:00
|
|
|
|
|
|
|
error:
|
|
|
|
vo_x11_uninit(vo);
|
2023-01-08 19:16:47 +00:00
|
|
|
return false;
|
2002-05-08 16:41:44 +00:00
|
|
|
}
|
2002-01-31 11:04:41 +00:00
|
|
|
|
2010-04-23 10:22:44 +00:00
|
|
|
static const struct mp_keymap keymap[] = {
|
2008-12-20 11:12:45 +00:00
|
|
|
// special keys
|
2013-02-12 00:34:04 +00:00
|
|
|
{XK_Pause, MP_KEY_PAUSE}, {XK_Escape, MP_KEY_ESC},
|
|
|
|
{XK_BackSpace, MP_KEY_BS}, {XK_Tab, MP_KEY_TAB}, {XK_Return, MP_KEY_ENTER},
|
|
|
|
{XK_Menu, MP_KEY_MENU}, {XK_Print, MP_KEY_PRINT},
|
2018-05-23 17:37:47 +00:00
|
|
|
{XK_Cancel, MP_KEY_CANCEL}, {XK_ISO_Left_Tab, MP_KEY_TAB},
|
2008-12-20 11:12:45 +00:00
|
|
|
|
|
|
|
// cursor keys
|
2013-02-12 00:34:04 +00:00
|
|
|
{XK_Left, MP_KEY_LEFT}, {XK_Right, MP_KEY_RIGHT}, {XK_Up, MP_KEY_UP},
|
|
|
|
{XK_Down, MP_KEY_DOWN},
|
2008-12-20 11:12:45 +00:00
|
|
|
|
|
|
|
// navigation block
|
2013-02-12 00:34:04 +00:00
|
|
|
{XK_Insert, MP_KEY_INSERT}, {XK_Delete, MP_KEY_DELETE},
|
|
|
|
{XK_Home, MP_KEY_HOME}, {XK_End, MP_KEY_END}, {XK_Page_Up, MP_KEY_PAGE_UP},
|
|
|
|
{XK_Page_Down, MP_KEY_PAGE_DOWN},
|
2008-12-20 11:12:45 +00:00
|
|
|
|
|
|
|
// F-keys
|
2013-02-12 00:34:04 +00:00
|
|
|
{XK_F1, MP_KEY_F+1}, {XK_F2, MP_KEY_F+2}, {XK_F3, MP_KEY_F+3},
|
|
|
|
{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},
|
2022-09-02 10:53:37 +00:00
|
|
|
{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},
|
2008-12-20 11:12:45 +00:00
|
|
|
|
|
|
|
// numpad independent of numlock
|
2024-08-11 04:05:05 +00:00
|
|
|
{XK_KP_Subtract, MP_KEY_KPSUBTRACT}, {XK_KP_Add, MP_KEY_KPADD},
|
|
|
|
{XK_KP_Multiply, MP_KEY_KPMULTIPLY}, {XK_KP_Divide, MP_KEY_KPDIVIDE},
|
|
|
|
{XK_KP_Enter, MP_KEY_KPENTER},
|
2008-12-20 11:12:45 +00:00
|
|
|
|
|
|
|
// numpad with numlock
|
2013-02-12 00:34:04 +00:00
|
|
|
{XK_KP_0, MP_KEY_KP0}, {XK_KP_1, MP_KEY_KP1}, {XK_KP_2, MP_KEY_KP2},
|
|
|
|
{XK_KP_3, MP_KEY_KP3}, {XK_KP_4, MP_KEY_KP4}, {XK_KP_5, MP_KEY_KP5},
|
|
|
|
{XK_KP_6, MP_KEY_KP6}, {XK_KP_7, MP_KEY_KP7}, {XK_KP_8, MP_KEY_KP8},
|
|
|
|
{XK_KP_9, MP_KEY_KP9}, {XK_KP_Decimal, MP_KEY_KPDEC},
|
|
|
|
{XK_KP_Separator, MP_KEY_KPDEC},
|
2008-12-20 11:12:45 +00:00
|
|
|
|
|
|
|
// numpad without numlock
|
2023-08-24 23:48:45 +00:00
|
|
|
{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},
|
2024-06-13 02:17:10 +00:00
|
|
|
{XK_KP_Left, MP_KEY_KPLEFT}, {XK_KP_Begin, MP_KEY_KPBEGIN},
|
2023-08-24 23:48:45 +00:00
|
|
|
{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},
|
2008-12-20 11:12:45 +00:00
|
|
|
|
2013-02-12 00:34:04 +00:00
|
|
|
{XF86XK_MenuKB, MP_KEY_MENU},
|
|
|
|
{XF86XK_AudioPlay, MP_KEY_PLAY}, {XF86XK_AudioPause, MP_KEY_PAUSE},
|
2014-09-03 18:58:13 +00:00
|
|
|
{XF86XK_AudioStop, MP_KEY_STOP},
|
|
|
|
{XF86XK_AudioPrev, MP_KEY_PREV}, {XF86XK_AudioNext, MP_KEY_NEXT},
|
|
|
|
{XF86XK_AudioRewind, MP_KEY_REWIND}, {XF86XK_AudioForward, MP_KEY_FORWARD},
|
|
|
|
{XF86XK_AudioMute, MP_KEY_MUTE},
|
2013-02-12 00:34:04 +00:00
|
|
|
{XF86XK_AudioLowerVolume, MP_KEY_VOLUME_DOWN},
|
|
|
|
{XF86XK_AudioRaiseVolume, MP_KEY_VOLUME_UP},
|
2013-09-01 17:59:38 +00:00
|
|
|
{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},
|
2023-11-27 06:04:10 +00:00
|
|
|
{XF86XK_Back, MP_KEY_GO_BACK}, {XF86XK_Forward, MP_KEY_GO_FORWARD},
|
|
|
|
{XF86XK_Tools, MP_KEY_TOOLS},
|
2023-08-15 11:27:03 +00:00
|
|
|
{XF86XK_ZoomIn, MP_KEY_ZOOMIN}, {XF86XK_ZoomOut, MP_KEY_ZOOMOUT},
|
2008-12-20 11:12:45 +00:00
|
|
|
|
2009-07-06 23:26:13 +00:00
|
|
|
{0, 0}
|
2008-12-20 11:12:45 +00:00
|
|
|
};
|
|
|
|
|
2012-01-14 13:09:44 +00:00
|
|
|
static int vo_x11_lookupkey(int key)
|
2004-06-14 04:53:03 +00:00
|
|
|
{
|
2014-06-10 21:56:05 +00:00
|
|
|
const char *passthrough_keys = " -+*/<>`~!@#$%^&()_{}:;\"\',.?\\|=[]";
|
2008-12-20 11:12:45 +00:00
|
|
|
int mpkey = 0;
|
2008-12-20 10:25:13 +00:00
|
|
|
if ((key >= 'a' && key <= 'z') ||
|
|
|
|
(key >= 'A' && key <= 'Z') ||
|
|
|
|
(key >= '0' && key <= '9') ||
|
2013-02-13 15:33:01 +00:00
|
|
|
(key > 0 && key < 256 && strchr(passthrough_keys, key)))
|
2008-12-20 11:12:45 +00:00
|
|
|
mpkey = key;
|
2008-12-20 10:25:13 +00:00
|
|
|
|
2008-12-20 11:52:11 +00:00
|
|
|
if (!mpkey)
|
2008-12-21 04:11:51 +00:00
|
|
|
mpkey = lookup_keymap_table(keymap, key);
|
2001-04-12 00:05:41 +00:00
|
|
|
|
2015-02-13 20:42:39 +00:00
|
|
|
// XFree86 keysym range; typically contains obscure "extra" keys
|
|
|
|
if (!mpkey && key >= 0x10080001 && key <= 0x1008FFFF) {
|
|
|
|
mpkey = MP_KEY_UNKNOWN_RESERVED_START + (key - 0x10080000);
|
|
|
|
if (mpkey > MP_KEY_UNKNOWN_RESERVED_LAST)
|
|
|
|
mpkey = 0;
|
|
|
|
}
|
|
|
|
|
2012-01-14 13:09:44 +00:00
|
|
|
return mpkey;
|
2001-03-03 21:47:37 +00:00
|
|
|
}
|
|
|
|
|
2024-02-01 10:32:08 +00:00
|
|
|
static void vo_x11_decoration(struct vo *vo, bool decorations, bool title_bar)
|
2001-03-03 21:47:37 +00:00
|
|
|
{
|
2008-04-21 01:09:26 +00:00
|
|
|
struct vo_x11_state *x11 = vo->x11;
|
2004-06-14 04:53:03 +00:00
|
|
|
|
2014-12-09 20:28:07 +00:00
|
|
|
if (x11->parent || !x11->window)
|
2004-06-14 04:53:03 +00:00
|
|
|
return;
|
|
|
|
|
2014-06-06 16:44:57 +00:00
|
|
|
Atom motif_hints = XA(x11, _MOTIF_WM_HINTS);
|
|
|
|
MotifWmHints mhints = {0};
|
|
|
|
bool got = x11_get_property_copy(x11, x11->window, motif_hints,
|
|
|
|
motif_hints, 32, &mhints, sizeof(mhints));
|
2024-02-01 10:32:08 +00:00
|
|
|
// If hints weren't set, and decorations and title bar requested,
|
|
|
|
// assume WM displays them.
|
|
|
|
if (!got && decorations && title_bar)
|
2014-06-06 16:44:57 +00:00
|
|
|
return;
|
|
|
|
if (!got) {
|
|
|
|
mhints.flags = MWM_HINTS_FUNCTIONS;
|
|
|
|
mhints.functions = MWM_FUNC_MOVE | MWM_FUNC_CLOSE | MWM_FUNC_MINIMIZE |
|
|
|
|
MWM_FUNC_MAXIMIZE | MWM_FUNC_RESIZE;
|
2004-06-14 04:53:03 +00:00
|
|
|
}
|
2014-06-06 16:44:57 +00:00
|
|
|
mhints.flags |= MWM_HINTS_DECORATIONS;
|
2024-02-01 10:32:08 +00:00
|
|
|
mhints.decorations = decorations ? MWM_DECOR_ALL : 0;
|
|
|
|
mhints.decorations &= ~(!title_bar ? MWM_DECOR_TITLE : 0);
|
2014-06-06 16:44:57 +00:00
|
|
|
XChangeProperty(x11->display, x11->window, motif_hints, motif_hints, 32,
|
|
|
|
PropModeReplace, (unsigned char *) &mhints, 5);
|
2001-03-03 21:47:37 +00:00
|
|
|
}
|
|
|
|
|
2020-05-23 04:25:52 +00:00
|
|
|
static void vo_x11_wm_hints(struct vo *vo, Window window)
|
|
|
|
{
|
|
|
|
struct vo_x11_state *x11 = vo->x11;
|
|
|
|
XWMHints hints = {0};
|
|
|
|
hints.flags = InputHint | StateHint;
|
|
|
|
hints.input = 1;
|
|
|
|
hints.initial_state = NormalState;
|
|
|
|
XSetWMHints(x11->display, window, &hints);
|
|
|
|
}
|
|
|
|
|
2013-01-26 21:37:47 +00:00
|
|
|
static void vo_x11_classhint(struct vo *vo, Window window, const char *name)
|
2004-06-14 04:53:03 +00:00
|
|
|
{
|
2008-04-20 23:50:40 +00:00
|
|
|
struct vo_x11_state *x11 = vo->x11;
|
2019-11-27 19:30:13 +00:00
|
|
|
struct mp_vo_opts *opts = x11->opts;
|
2004-06-14 04:53:03 +00:00
|
|
|
XClassHint wmClass;
|
2014-05-26 19:53:20 +00:00
|
|
|
long pid = getpid();
|
2004-06-14 04:53:03 +00:00
|
|
|
|
2013-03-04 21:41:27 +00:00
|
|
|
wmClass.res_name = opts->winname ? opts->winname : (char *)name;
|
2012-10-11 00:04:08 +00:00
|
|
|
wmClass.res_class = "mpv";
|
2008-04-20 23:50:40 +00:00
|
|
|
XSetClassHint(x11->display, window, &wmClass);
|
2014-05-16 21:17:09 +00:00
|
|
|
XChangeProperty(x11->display, window, XA(x11, _NET_WM_PID), XA_CARDINAL,
|
2008-04-20 23:50:40 +00:00
|
|
|
32, PropModeReplace, (unsigned char *) &pid, 1);
|
2001-06-17 01:22:09 +00:00
|
|
|
}
|
|
|
|
|
2008-04-20 06:34:27 +00:00
|
|
|
void vo_x11_uninit(struct vo *vo)
|
2002-01-31 11:15:54 +00:00
|
|
|
{
|
2008-04-21 00:14:37 +00:00
|
|
|
struct vo_x11_state *x11 = vo->x11;
|
2014-05-27 23:07:24 +00:00
|
|
|
if (!x11)
|
|
|
|
return;
|
2013-01-26 21:37:47 +00:00
|
|
|
|
2016-03-15 21:11:31 +00:00
|
|
|
mp_input_put_key(x11->input_ctx, MP_INPUT_RELEASE_ALL);
|
2013-04-03 23:13:56 +00:00
|
|
|
|
2014-05-17 19:32:11 +00:00
|
|
|
set_screensaver(x11, true);
|
|
|
|
|
2023-06-28 21:02:22 +00:00
|
|
|
if (x11->window != None && x11->window != x11->rootwin)
|
2013-02-24 22:35:13 +00:00
|
|
|
XDestroyWindow(x11->display, x11->window);
|
2013-01-26 21:37:47 +00:00
|
|
|
if (x11->xic)
|
|
|
|
XDestroyIC(x11->xic);
|
2013-01-27 11:01:08 +00:00
|
|
|
if (x11->colormap != None)
|
|
|
|
XFreeColormap(vo->x11->display, x11->colormap);
|
2004-06-14 04:53:03 +00:00
|
|
|
|
2019-11-01 00:40:22 +00:00
|
|
|
MP_DBG(x11, "uninit ...\n");
|
2013-01-26 21:37:47 +00:00
|
|
|
if (x11->xim)
|
|
|
|
XCloseIM(x11->xim);
|
2015-04-15 20:39:01 +00:00
|
|
|
if (x11->display) {
|
|
|
|
XSetErrorHandler(NULL);
|
2016-05-08 09:04:44 +00:00
|
|
|
x11_error_output = NULL;
|
2015-04-15 20:39:01 +00:00
|
|
|
XCloseDisplay(x11->display);
|
|
|
|
}
|
|
|
|
|
2017-06-27 12:22:28 +00:00
|
|
|
if (x11->wakeup_pipe[0] >= 0) {
|
|
|
|
close(x11->wakeup_pipe[0]);
|
|
|
|
close(x11->wakeup_pipe[1]);
|
|
|
|
}
|
2016-07-20 18:52:08 +00:00
|
|
|
|
2013-01-26 21:37:47 +00:00
|
|
|
talloc_free(x11);
|
2012-08-03 03:55:02 +00:00
|
|
|
vo->x11 = NULL;
|
2002-01-31 11:15:54 +00:00
|
|
|
}
|
|
|
|
|
2014-11-03 22:01:27 +00:00
|
|
|
#define DND_PROPERTY "mpv_dnd_selection"
|
|
|
|
|
2014-01-04 00:16:44 +00:00
|
|
|
static void vo_x11_dnd_init_window(struct vo *vo)
|
|
|
|
{
|
|
|
|
struct vo_x11_state *x11 = vo->x11;
|
|
|
|
|
|
|
|
Atom version = DND_VERSION;
|
2014-05-16 21:17:09 +00:00
|
|
|
XChangeProperty(x11->display, x11->window, XA(x11, XdndAware), XA_ATOM,
|
2014-01-04 00:16:44 +00:00
|
|
|
32, PropModeReplace, (unsigned char *)&version, 1);
|
|
|
|
}
|
|
|
|
|
2016-03-15 21:42:06 +00:00
|
|
|
// The Atom does not always map to a mime type, but often.
|
|
|
|
static char *x11_dnd_mime_type_buf(struct vo_x11_state *x11, Atom atom,
|
|
|
|
char *buf, size_t buf_size)
|
|
|
|
{
|
|
|
|
if (atom == XInternAtom(x11->display, "UTF8_STRING", False))
|
|
|
|
return "text";
|
|
|
|
return x11_atom_name_buf(x11, atom, buf, buf_size);
|
|
|
|
}
|
|
|
|
|
|
|
|
#define x11_dnd_mime_type(x11, atom) \
|
|
|
|
x11_dnd_mime_type_buf(x11, atom, (char[80]){0}, 80)
|
|
|
|
|
|
|
|
static bool dnd_format_is_better(struct vo_x11_state *x11, Atom cur, Atom new)
|
|
|
|
{
|
|
|
|
int new_score = mp_event_get_mime_type_score(x11->input_ctx,
|
|
|
|
x11_dnd_mime_type(x11, new));
|
|
|
|
int cur_score = -1;
|
|
|
|
if (cur) {
|
|
|
|
cur_score = mp_event_get_mime_type_score(x11->input_ctx,
|
|
|
|
x11_dnd_mime_type(x11, cur));
|
|
|
|
}
|
|
|
|
return new_score >= 0 && new_score > cur_score;
|
|
|
|
}
|
|
|
|
|
2014-01-04 00:16:44 +00:00
|
|
|
static void dnd_select_format(struct vo_x11_state *x11, Atom *args, int items)
|
|
|
|
{
|
2016-03-15 21:42:06 +00:00
|
|
|
x11->dnd_requested_format = 0;
|
|
|
|
|
2014-01-04 00:16:44 +00:00
|
|
|
for (int n = 0; n < items; n++) {
|
2016-03-15 21:42:06 +00:00
|
|
|
MP_VERBOSE(x11, "DnD type: '%s'\n", x11_atom_name(x11, args[n]));
|
2014-01-04 00:16:44 +00:00
|
|
|
// There are other types; possibly not worth supporting.
|
2016-03-15 21:42:06 +00:00
|
|
|
if (dnd_format_is_better(x11, x11->dnd_requested_format, args[n]))
|
2014-01-04 00:16:44 +00:00
|
|
|
x11->dnd_requested_format = args[n];
|
|
|
|
}
|
2016-03-15 21:42:06 +00:00
|
|
|
|
|
|
|
MP_VERBOSE(x11, "Selected DnD type: %s\n", x11->dnd_requested_format ?
|
|
|
|
x11_atom_name(x11, x11->dnd_requested_format) : "(none)");
|
2014-01-04 00:16:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
2014-05-16 21:17:09 +00:00
|
|
|
if (ce->message_type == XA(x11, XdndEnter)) {
|
2014-01-04 00:16:44 +00:00
|
|
|
x11->dnd_requested_format = 0;
|
|
|
|
|
|
|
|
Window src = ce->data.l[0];
|
|
|
|
if (ce->data.l[1] & 1) {
|
2014-05-17 14:58:28 +00:00
|
|
|
int nitems;
|
|
|
|
Atom *args = x11_get_property(x11, src, XA(x11, XdndTypeList),
|
|
|
|
XA_ATOM, 32, &nitems);
|
|
|
|
if (args) {
|
2014-01-04 00:16:44 +00:00
|
|
|
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);
|
|
|
|
}
|
2014-05-16 21:17:09 +00:00
|
|
|
} else if (ce->message_type == XA(x11, XdndPosition)) {
|
2015-08-29 09:13:22 +00:00
|
|
|
x11->dnd_requested_action = ce->data.l[4];
|
|
|
|
|
2014-01-04 00:16:44 +00:00
|
|
|
Window src = ce->data.l[0];
|
|
|
|
XEvent xev;
|
|
|
|
|
|
|
|
xev.xclient.type = ClientMessage;
|
|
|
|
xev.xclient.serial = 0;
|
|
|
|
xev.xclient.send_event = True;
|
2014-05-16 21:17:09 +00:00
|
|
|
xev.xclient.message_type = XA(x11, XdndStatus);
|
2014-01-04 00:16:44 +00:00
|
|
|
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;
|
2014-05-16 21:17:09 +00:00
|
|
|
xev.xclient.data.l[4] = XA(x11, XdndActionCopy);
|
2014-01-04 00:16:44 +00:00
|
|
|
|
|
|
|
XSendEvent(x11->display, src, False, 0, &xev);
|
2014-05-16 21:17:09 +00:00
|
|
|
} else if (ce->message_type == XA(x11, XdndDrop)) {
|
2014-01-04 00:16:44 +00:00
|
|
|
x11->dnd_src_window = ce->data.l[0];
|
2014-05-16 21:17:09 +00:00
|
|
|
XConvertSelection(x11->display, XA(x11, XdndSelection),
|
2014-11-03 22:01:27 +00:00
|
|
|
x11->dnd_requested_format, XAs(x11, DND_PROPERTY),
|
2014-01-04 00:16:44 +00:00
|
|
|
x11->window, ce->data.l[2]);
|
2014-05-16 21:17:09 +00:00
|
|
|
} else if (ce->message_type == XA(x11, XdndLeave)) {
|
2014-01-04 00:16:44 +00:00
|
|
|
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;
|
|
|
|
|
2014-05-16 21:17:09 +00:00
|
|
|
if (se->selection == XA(x11, XdndSelection) &&
|
2014-11-03 22:01:27 +00:00
|
|
|
se->property == XAs(x11, DND_PROPERTY) &&
|
2023-06-12 23:09:21 +00:00
|
|
|
se->target == x11->dnd_requested_format &&
|
|
|
|
x11->opts->drag_and_drop != -2)
|
2014-01-04 00:16:44 +00:00
|
|
|
{
|
2014-05-17 14:58:28 +00:00
|
|
|
int nitems;
|
2014-11-03 22:01:27 +00:00
|
|
|
void *prop = x11_get_property(x11, x11->window, XAs(x11, DND_PROPERTY),
|
2014-05-17 14:58:28 +00:00
|
|
|
x11->dnd_requested_format, 8, &nitems);
|
|
|
|
if (prop) {
|
2023-05-20 21:51:12 +00:00
|
|
|
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;
|
|
|
|
}
|
2015-08-29 09:13:22 +00:00
|
|
|
|
2016-03-15 21:42:06 +00:00
|
|
|
char *mime_type = x11_dnd_mime_type(x11, x11->dnd_requested_format);
|
|
|
|
MP_VERBOSE(x11, "Dropping type: %s (%s)\n",
|
|
|
|
x11_atom_name(x11, x11->dnd_requested_format), mime_type);
|
|
|
|
|
2014-05-17 14:58:28 +00:00
|
|
|
// No idea if this is guaranteed to be \0-padded, so use bstr.
|
2016-03-15 21:42:06 +00:00
|
|
|
success = mp_event_drop_mime_data(x11->input_ctx, mime_type,
|
2015-08-29 09:13:22 +00:00
|
|
|
(bstr){prop, nitems}, action) > 0;
|
2014-01-04 00:16:44 +00:00
|
|
|
XFree(prop);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
XEvent xev;
|
|
|
|
|
|
|
|
xev.xclient.type = ClientMessage;
|
|
|
|
xev.xclient.serial = 0;
|
|
|
|
xev.xclient.send_event = True;
|
2014-05-16 21:17:09 +00:00
|
|
|
xev.xclient.message_type = XA(x11, XdndFinished);
|
2014-01-04 00:16:44 +00:00
|
|
|
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;
|
2014-05-16 21:17:09 +00:00
|
|
|
xev.xclient.data.l[2] = success ? XA(x11, XdndActionCopy) : 0;
|
2014-01-04 00:16:44 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2013-02-15 18:47:34 +00:00
|
|
|
static void update_vo_size(struct vo *vo)
|
2010-07-01 19:48:00 +00:00
|
|
|
{
|
2013-02-15 18:47:34 +00:00
|
|
|
struct vo_x11_state *x11 = vo->x11;
|
|
|
|
|
2014-05-17 01:29:36 +00:00
|
|
|
if (RC_W(x11->winrc) != vo->dwidth || RC_H(x11->winrc) != vo->dheight) {
|
|
|
|
vo->dwidth = RC_W(x11->winrc);
|
|
|
|
vo->dheight = RC_H(x11->winrc);
|
2013-02-15 18:47:34 +00:00
|
|
|
x11->pending_vo_events |= VO_EVENT_RESIZE;
|
|
|
|
}
|
2010-07-01 19:48:00 +00:00
|
|
|
}
|
|
|
|
|
2013-07-21 16:07:09 +00:00
|
|
|
static int get_mods(unsigned int state)
|
|
|
|
{
|
|
|
|
int modifiers = 0;
|
|
|
|
if (state & ShiftMask)
|
|
|
|
modifiers |= MP_KEY_MODIFIER_SHIFT;
|
|
|
|
if (state & ControlMask)
|
|
|
|
modifiers |= MP_KEY_MODIFIER_CTRL;
|
|
|
|
if (state & Mod1Mask)
|
|
|
|
modifiers |= MP_KEY_MODIFIER_ALT;
|
|
|
|
if (state & Mod4Mask)
|
|
|
|
modifiers |= MP_KEY_MODIFIER_META;
|
|
|
|
return modifiers;
|
|
|
|
}
|
|
|
|
|
2016-05-02 19:21:53 +00:00
|
|
|
static void vo_x11_update_composition_hint(struct vo *vo)
|
|
|
|
{
|
|
|
|
struct vo_x11_state *x11 = vo->x11;
|
|
|
|
|
|
|
|
long hint = 0;
|
2019-11-27 19:30:13 +00:00
|
|
|
switch (x11->opts->x11_bypass_compositor) {
|
2016-05-02 19:21:53 +00:00
|
|
|
case 0: hint = 0; break; // leave default
|
|
|
|
case 1: hint = 1; break; // always bypass
|
|
|
|
case 2: hint = x11->fs ? 1 : 0; break; // bypass in FS
|
2016-05-06 17:57:17 +00:00
|
|
|
case 3: hint = 2; break; // always enable
|
2016-05-02 19:21:53 +00:00
|
|
|
}
|
|
|
|
|
2024-03-20 03:05:37 +00:00
|
|
|
XChangeProperty(x11->display, x11->window, XA(x11, _NET_WM_BYPASS_COMPOSITOR),
|
2016-05-02 19:21:53 +00:00
|
|
|
XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&hint, 1);
|
|
|
|
}
|
|
|
|
|
2019-11-29 12:52:37 +00:00
|
|
|
static void vo_x11_check_net_wm_state_change(struct vo *vo)
|
2015-06-28 16:10:17 +00:00
|
|
|
{
|
|
|
|
struct vo_x11_state *x11 = vo->x11;
|
2019-11-29 12:52:37 +00:00
|
|
|
struct mp_vo_opts *opts = x11->opts;
|
2015-06-28 16:10:17 +00:00
|
|
|
|
2017-08-21 13:14:55 +00:00
|
|
|
if (x11->parent)
|
|
|
|
return;
|
|
|
|
|
2015-06-28 16:10:17 +00:00
|
|
|
if (x11->wm_type & vo_wm_FULLSCREEN) {
|
|
|
|
int num_elems;
|
|
|
|
long *elems = x11_get_property(x11, x11->window, XA(x11, _NET_WM_STATE),
|
|
|
|
XA_ATOM, 32, &num_elems);
|
2019-11-29 12:52:37 +00:00
|
|
|
int is_fullscreen = 0, is_minimized = 0, is_maximized = 0;
|
2015-06-28 16:10:17 +00:00
|
|
|
if (elems) {
|
|
|
|
Atom fullscreen_prop = XA(x11, _NET_WM_STATE_FULLSCREEN);
|
2019-11-29 12:52:37 +00:00
|
|
|
Atom hidden = XA(x11, _NET_WM_STATE_HIDDEN);
|
|
|
|
Atom max_vert = XA(x11, _NET_WM_STATE_MAXIMIZED_VERT);
|
|
|
|
Atom max_horiz = XA(x11, _NET_WM_STATE_MAXIMIZED_HORZ);
|
2015-06-28 16:10:17 +00:00
|
|
|
for (int n = 0; n < num_elems; n++) {
|
2019-11-29 12:52:37 +00:00
|
|
|
if (elems[n] == fullscreen_prop)
|
2015-06-28 16:10:17 +00:00
|
|
|
is_fullscreen = 1;
|
2019-11-29 12:52:37 +00:00
|
|
|
if (elems[n] == hidden)
|
|
|
|
is_minimized = 1;
|
|
|
|
if (elems[n] == max_vert || elems[n] == max_horiz)
|
|
|
|
is_maximized = 1;
|
2015-06-28 16:10:17 +00:00
|
|
|
}
|
|
|
|
XFree(elems);
|
|
|
|
}
|
|
|
|
|
2019-11-29 12:52:37 +00:00
|
|
|
opts->window_minimized = is_minimized;
|
x11: avoid wasteful rendering when possible
Because wayland is a special snowflake, mpv wound up incorporating a lot
of logic into its render loop where visibilty checks are performed
before rendering anything (in the name of efficiency of course). Only
wayland actually uses this, but there's no reason why other backends
(x11 in this commit) can't be smarter. It's far easier on xorg since we
can just query _NET_WM_STATE_HIDDEN directly and not have to do silly
callback dances.
The function, vo_x11_check_net_wm_state_change, already tracks net wm
changes, including _NET_WM_STATE_HIDDEN. There is an already existing
window_hidden variable but that is actually just for checking if the
window was mapped and has nothing to do with this particular atom. mpv
also currently assumes that a _NET_WM_STATE_HIDDEN is exactly the same
as being minimized but according to the spec, that's not neccesarily
true (in practice, it's likely that these are the same though). Anyways,
just keep track of this state in a new variable (hidden) and use that
for determing if mpv should render or not.
There is one catch though: this cannot work if a display sync mode is
used. This is why the previous commit is needed. The display sync modes
in mpv require a blocking vsync implementation since its render loop is
directly driven by vsync. In xorg, if nothing is actually rendered, then
there's nothing for eglSwapBuffers (or FIFO for vulkan) to block on so
it returns immediately. This, of course, results in completely broken
video. We just need to check to make sure that we aren't in a display
sync mode before trying to be smart about rendering. Display sync is
power inefficient anyways, so no one is really being hurt here. As an
aside, this happens to work in wayland because there's basically a
custom (and ugly) vsync blocking function + timeout but that's off
topic.
2022-04-07 03:36:30 +00:00
|
|
|
x11->hidden = is_minimized;
|
2019-11-29 12:52:37 +00:00
|
|
|
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);
|
|
|
|
|
2019-11-27 19:30:13 +00:00
|
|
|
if ((x11->opts->fullscreen && !is_fullscreen) ||
|
|
|
|
(!x11->opts->fullscreen && is_fullscreen))
|
2015-06-28 16:10:17 +00:00
|
|
|
{
|
2019-11-27 19:30:13 +00:00
|
|
|
x11->opts->fullscreen = is_fullscreen;
|
2015-06-28 16:10:17 +00:00
|
|
|
x11->fs = is_fullscreen;
|
2019-11-29 11:50:50 +00:00
|
|
|
m_config_cache_write_opt(x11->opts_cache, &x11->opts->fullscreen);
|
2015-06-28 16:10:17 +00:00
|
|
|
|
|
|
|
if (!is_fullscreen && (x11->pos_changed_during_fs ||
|
|
|
|
x11->size_changed_during_fs))
|
|
|
|
{
|
|
|
|
vo_x11_move_resize(vo, x11->pos_changed_during_fs,
|
|
|
|
x11->size_changed_during_fs,
|
|
|
|
x11->nofsrc);
|
|
|
|
}
|
2015-06-29 13:09:28 +00:00
|
|
|
|
2015-06-28 16:10:17 +00:00
|
|
|
x11->size_changed_during_fs = false;
|
|
|
|
x11->pos_changed_during_fs = false;
|
2016-05-02 19:21:53 +00:00
|
|
|
|
|
|
|
vo_x11_update_composition_hint(vo);
|
2015-06-28 16:10:17 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-29 12:16:31 +00:00
|
|
|
static void vo_x11_check_net_wm_desktop_change(struct vo *vo)
|
|
|
|
{
|
|
|
|
struct vo_x11_state *x11 = vo->x11;
|
|
|
|
|
|
|
|
if (x11->parent)
|
|
|
|
return;
|
|
|
|
|
2019-12-18 06:12:53 +00:00
|
|
|
long params[1] = {0};
|
2019-11-29 12:16:31 +00:00
|
|
|
if (x11_get_property_copy(x11, x11->window, XA(x11, _NET_WM_DESKTOP),
|
|
|
|
XA_CARDINAL, 32, params, sizeof(params)))
|
|
|
|
{
|
|
|
|
x11->opts->all_workspaces = params[0] == -1; // (gets sign-extended?)
|
|
|
|
m_config_cache_write_opt(x11->opts_cache, &x11->opts->all_workspaces);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-11 04:38:57 +00:00
|
|
|
// Releasing all keys on key-up or defocus is simpler and ensures no keys can
|
|
|
|
// get "stuck".
|
|
|
|
static void release_all_keys(struct vo *vo)
|
|
|
|
{
|
|
|
|
struct vo_x11_state *x11 = vo->x11;
|
|
|
|
|
|
|
|
if (x11->no_autorepeat)
|
|
|
|
mp_input_put_key(x11->input_ctx, MP_INPUT_RELEASE_ALL);
|
|
|
|
}
|
|
|
|
|
2024-02-26 23:37:12 +00:00
|
|
|
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};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-07-25 09:28:06 +00:00
|
|
|
void vo_x11_check_events(struct vo *vo)
|
2004-06-14 04:53:03 +00:00
|
|
|
{
|
2008-04-20 23:50:40 +00:00
|
|
|
struct vo_x11_state *x11 = vo->x11;
|
2008-04-20 06:34:27 +00:00
|
|
|
Display *display = vo->x11->display;
|
2004-06-14 04:53:03 +00:00
|
|
|
XEvent Event;
|
2002-04-16 17:41:29 +00:00
|
|
|
|
2017-08-20 07:11:07 +00:00
|
|
|
xscreensaver_heartbeat(vo->x11);
|
|
|
|
|
2013-02-13 15:33:01 +00:00
|
|
|
while (XPending(display)) {
|
2008-04-20 06:34:27 +00:00
|
|
|
XNextEvent(display, &Event);
|
video/out/x11_common: make IME work (for libmpv usage)
The implementation is minimal since it already uses XIM for UTF-8 text
input. The only changes needed are small fixes: setting the IC focus and
correct the usage of window param for XFilterEvent, and IME will
"just work"...
...after setting 2 process-wide global values before VO creation:
LC_CTYPE and X locale modifiers. So this usage is currently limited to
libmpv clients which don't want to do their own IME processing.
locale strikes once again:
XIM input methods require them to be set before creating the XIM context to
work, even though they don't matter after the context is created. Both of
these values being process-wide global means that the only time to safely
set these values is before the process creates any thread.
In theory, mpv could set LC_CTYPE in the main when running standalone,
but locale being locale means something is always messed up. In this case,
setting LC_CTYPE changes "Character classification and case conversion"
behavior. While mpv uses its own ASCII-only is*() and to*() functions to
avoid some of the issues, it also uses strcasecmp() and POSIX regex which
behaviors are affected by LC_CTYPE.
In summary, to avoid unavoidable race conditions mpv won't do this in
standalone mode right now.
It's now libmpv users' responsibility to:
- set LC_CTYPE to a locale usable for XIM, such as a UTF-8 locale
- set X locale modifiers with XSetLocaleModifiers
- hope that all stars align if trying to switch locale back afterwards
On most setups, both of these can be empty strings, In this case, the usual
XMODIFIERS environment variable must be set to enable IME support.
(e.g. XMODIFIERS=@im=fcitx)
2024-09-02 19:57:50 +00:00
|
|
|
if (XFilterEvent(&Event, None))
|
2023-12-10 06:31:12 +00:00
|
|
|
continue;
|
2014-12-18 15:30:13 +00:00
|
|
|
MP_TRACE(x11, "XEvent: %d\n", Event.type);
|
2013-02-13 15:33:01 +00:00
|
|
|
switch (Event.type) {
|
|
|
|
case Expose:
|
2013-02-15 18:47:34 +00:00
|
|
|
x11->pending_vo_events |= VO_EVENT_EXPOSE;
|
2013-02-13 15:33:01 +00:00
|
|
|
break;
|
|
|
|
case ConfigureNotify:
|
|
|
|
if (x11->window == None)
|
2004-06-14 04:53:03 +00:00
|
|
|
break;
|
x11_common: fix window mapping, refactor window creation/resize handling
vo_opengl creates an invisible window with VOFLAG_HIDDEN in order to
test whether the OpenGL context is useable. The visible window is
created at a later point. This has been broken forever (in vo_gl,
now called vo_opengl_old, it could be avoided by disabling auto-
detection explicitly using the "yuv" sub-option). Avoiding
VOFLAG_HIDDEN only mitigates the issue, and a related bug can still
happen with some window managers (see below).
As a hack, code was added to vo_gl to destroy the (hidden) window so
that all state was destroyed. Later, more hacks were added to deal with
some caveats that came with recreating the window, such as probing for
the context up to 4 times.
Attempt to fix the problem properly. There were two problems: first,
the window was not resized to video size before mapping. This was the
main cause for the placement issue, e.g. mapping the window as 320x200,
and then resizing it. Second, mpv tried to force the window position
with XSetWMNormalHints and PPosition with values it just read with
XGetGeometry. This messes up the window manager's default placement.
It seems to be a race condition, and behavior is different across
various WMs too: with IceWM, the window manager's placement is usually
preferred, and with Fluxbox, mpv's position is preferred. mpv's default
position is centering the window on the screen, which is much nicer for
video in general than typical WM default placement, so it's possible
that this bug was perceived as a feature. (Users who want this have to
use --geometry="50%:50%", doing this by default is probably not safe
with all WMs.)
Since the old code was hard to follow and full of issues, it's easier
to redo it. Move general window creation stuff out of the
vo_x11_config_vo_window function, and move the resize logic into it.
This has been tested on IcwWM, Fluxbox, awesome, Unity/Compiz.
2013-03-02 16:08:37 +00:00
|
|
|
vo_x11_update_geometry(vo);
|
2024-05-25 18:49:05 +00:00
|
|
|
vo_x11_update_screeninfo(vo);
|
2014-12-18 15:30:13 +00:00
|
|
|
if (x11->parent && Event.xconfigure.window == x11->parent) {
|
|
|
|
MP_TRACE(x11, "adjusting embedded window position\n");
|
2014-05-18 22:08:42 +00:00
|
|
|
XMoveResizeWindow(x11->display, x11->window,
|
2014-12-18 15:30:13 +00:00
|
|
|
0, 0, RC_W(x11->winrc), RC_H(x11->winrc));
|
2014-05-18 22:08:42 +00:00
|
|
|
}
|
2013-02-13 15:33:01 +00:00
|
|
|
break;
|
|
|
|
case KeyPress: {
|
|
|
|
char buf[100];
|
|
|
|
KeySym keySym = 0;
|
2013-07-21 16:07:09 +00:00
|
|
|
int modifiers = get_mods(Event.xkey.state);
|
2013-04-03 23:13:56 +00:00
|
|
|
if (x11->no_autorepeat)
|
|
|
|
modifiers |= MP_KEY_STATE_DOWN;
|
2013-02-13 15:33:01 +00:00
|
|
|
if (x11->xic) {
|
|
|
|
Status status;
|
|
|
|
int len = Xutf8LookupString(x11->xic, &Event.xkey, buf,
|
|
|
|
sizeof(buf), &keySym, &status);
|
|
|
|
int mpkey = vo_x11_lookupkey(keySym);
|
|
|
|
if (mpkey) {
|
2016-03-15 21:11:31 +00:00
|
|
|
mp_input_put_key(x11->input_ctx, mpkey | modifiers);
|
2013-02-13 15:33:01 +00:00
|
|
|
} else if (status == XLookupChars || status == XLookupBoth) {
|
|
|
|
struct bstr t = { buf, len };
|
2016-03-15 21:11:31 +00:00
|
|
|
mp_input_put_key_utf8(x11->input_ctx, modifiers, t);
|
2004-06-14 04:53:03 +00:00
|
|
|
}
|
2013-02-13 15:33:01 +00:00
|
|
|
} else {
|
|
|
|
XLookupString(&Event.xkey, buf, sizeof(buf), &keySym,
|
|
|
|
&x11->compose_status);
|
|
|
|
int mpkey = vo_x11_lookupkey(keySym);
|
|
|
|
if (mpkey)
|
2016-03-15 21:11:31 +00:00
|
|
|
mp_input_put_key(x11->input_ctx, mpkey | modifiers);
|
2013-02-13 15:33:01 +00:00
|
|
|
}
|
2013-04-03 23:13:56 +00:00
|
|
|
break;
|
|
|
|
}
|
2018-10-11 04:38:57 +00:00
|
|
|
case FocusIn:
|
|
|
|
x11->has_focus = true;
|
|
|
|
vo_update_cursor(vo);
|
2020-09-07 16:22:25 +00:00
|
|
|
x11->pending_vo_events |= VO_EVENT_FOCUS;
|
2018-10-11 04:38:57 +00:00
|
|
|
break;
|
2013-04-03 23:13:56 +00:00
|
|
|
case FocusOut:
|
2018-10-11 04:38:57 +00:00
|
|
|
release_all_keys(vo);
|
|
|
|
x11->has_focus = false;
|
|
|
|
vo_update_cursor(vo);
|
2020-09-07 16:22:25 +00:00
|
|
|
x11->pending_vo_events |= VO_EVENT_FOCUS;
|
2018-10-11 04:38:57 +00:00
|
|
|
break;
|
2013-04-03 23:13:56 +00:00
|
|
|
case KeyRelease:
|
2018-10-11 04:38:57 +00:00
|
|
|
release_all_keys(vo);
|
2013-04-03 23:13:56 +00:00
|
|
|
break;
|
2013-02-13 15:33:01 +00:00
|
|
|
case MotionNotify:
|
2024-02-10 19:43:50 +00:00
|
|
|
mp_input_set_mouse_pos(x11->input_ctx, Event.xmotion.x,
|
|
|
|
Event.xmotion.y);
|
2013-02-13 15:33:01 +00:00
|
|
|
break;
|
2013-06-19 16:19:45 +00:00
|
|
|
case LeaveNotify:
|
2015-03-11 10:52:13 +00:00
|
|
|
if (Event.xcrossing.mode != NotifyNormal)
|
|
|
|
break;
|
2016-03-15 21:11:31 +00:00
|
|
|
mp_input_put_key(x11->input_ctx, MP_KEY_MOUSE_LEAVE);
|
2013-06-19 16:19:45 +00:00
|
|
|
break;
|
2015-02-17 05:50:57 +00:00
|
|
|
case EnterNotify:
|
2015-03-11 10:52:13 +00:00
|
|
|
if (Event.xcrossing.mode != NotifyNormal)
|
|
|
|
break;
|
2016-03-15 21:11:31 +00:00
|
|
|
mp_input_put_key(x11->input_ctx, MP_KEY_MOUSE_ENTER);
|
2015-02-17 05:50:57 +00:00
|
|
|
break;
|
2013-02-13 15:33:01 +00:00
|
|
|
case ButtonPress:
|
2018-05-10 13:57:36 +00:00
|
|
|
if (Event.xbutton.button - 1 >= MP_KEY_MOUSE_BTN_COUNT)
|
|
|
|
break;
|
2016-03-15 21:11:31 +00:00
|
|
|
mp_input_put_key(x11->input_ctx,
|
2017-09-02 14:00:52 +00:00
|
|
|
(MP_MBTN_BASE + Event.xbutton.button - 1) |
|
2013-07-21 16:07:09 +00:00
|
|
|
get_mods(Event.xbutton.state) | MP_KEY_STATE_DOWN);
|
2015-01-12 11:08:48 +00:00
|
|
|
long msg[4] = {XEMBED_REQUEST_FOCUS};
|
|
|
|
vo_x11_xembed_send_message(x11, msg);
|
2024-02-26 23:37:12 +00:00
|
|
|
x11->last_button_event = Event;
|
2013-02-13 15:33:01 +00:00
|
|
|
break;
|
|
|
|
case ButtonRelease:
|
2018-05-10 13:57:36 +00:00
|
|
|
if (Event.xbutton.button - 1 >= MP_KEY_MOUSE_BTN_COUNT)
|
|
|
|
break;
|
2016-03-15 21:11:31 +00:00
|
|
|
mp_input_put_key(x11->input_ctx,
|
2017-09-02 14:00:52 +00:00
|
|
|
(MP_MBTN_BASE + Event.xbutton.button - 1) |
|
2013-07-21 16:07:09 +00:00
|
|
|
get_mods(Event.xbutton.state) | MP_KEY_STATE_UP);
|
2024-02-26 23:37:12 +00:00
|
|
|
x11->last_button_event = Event;
|
2013-02-13 15:33:01 +00:00
|
|
|
break;
|
|
|
|
case MapNotify:
|
x11: fix race condition when setting aspect when leaving fullscreen
vo_opengl creates a hidden X11 window to probe the OpenGL context. It
must do that before creating a visible window, because VO creation and
VO config are separate phases.
There's a race condition involving the hidden window: when starting with
--fs, and then leaving fullscreen, the unfullscreened window is
sometimes set to the aspect ratio of the hidden window. I'm not sure why
the window size itself uses the correct size (but corrupted by the wrong
aspect), but that's perhaps because the window manager is free to ignore
the size hint while honoring the aspect, or something equally messed up.
It turns out this happens because x11_common.c thinks the size of the
hidden window is the size of the unfullscreened window. This in turn
happens because vo_x11_update_geometry() reads the size of the hidden
window when called in vo_x11_fullscreen() (called from
vo_x11_config_vo_window()) when mapping the fullscreen window. At that
point, the window could be mapped, but not necessarily. If it's not
mapped, it will get the size of the unfullscreened window... I think.
One could fix this by actively waiting until the window is mapped. Try
to pick a less hacky approach instead, and never read the window size
until MapNotify is received.
vo_x11_create_window() needs a hack, because we'd possibly set the VO's
size to 0, resulting e.g. in vdpau to fail initialization. (It'll print
error messages until a proper resize is received.)
2014-02-02 00:35:46 +00:00
|
|
|
x11->window_hidden = false;
|
x11: if the WM supports _NET_FRAME_EXTENTS, don't wait for map
Some window managers can prevent mapping of a window as a feature. i3
can put new windows on a certain workspace (with "assign"), so if mpv is
started on a different workspace, the window will never be mapped.
mpv currently waits until the window is mapped (blocking almost all of
the player), in order to avoid race conditions regarding the window
size. We don't want to remove this, but on the other hand we also don't
want to block the player forever in these situations.
So what we need is a way to know when the window manager is "done" with
processing the map request. Unfortunately, there doesn't seem to be a
standard way for this. So, instead we could do some arbitrary
communication with the WM, that may act as "barrier" after map request
and the "immediate" mapping of the window. If the window is not mapped
after this barrier, it means the window manager decided to delay the
mapping indefinitely. Use the _NET_REQUEST_FRAME_EXTENTS message as such
a barrier. WMs supporting this message must set the _NET_FRAME_EXTENTS
property on the mpv window, and we receive a PropertyNotify event. If
that happens, we always continue and cancel waiting for the MapNotify
event.
I don't know if this is sane or if there's a better mechanism. Also,
this works only for WMs which support this message, which are not many.
But at least it appears to work on i3. It may reintroduce flickering on
fullscreen with other WMs, though.
2014-09-15 16:42:09 +00:00
|
|
|
x11->pseudo_mapped = true;
|
2023-08-13 00:30:11 +00:00
|
|
|
x11->current_screen = -1;
|
x11: fix race condition when setting aspect when leaving fullscreen
vo_opengl creates a hidden X11 window to probe the OpenGL context. It
must do that before creating a visible window, because VO creation and
VO config are separate phases.
There's a race condition involving the hidden window: when starting with
--fs, and then leaving fullscreen, the unfullscreened window is
sometimes set to the aspect ratio of the hidden window. I'm not sure why
the window size itself uses the correct size (but corrupted by the wrong
aspect), but that's perhaps because the window manager is free to ignore
the size hint while honoring the aspect, or something equally messed up.
It turns out this happens because x11_common.c thinks the size of the
hidden window is the size of the unfullscreened window. This in turn
happens because vo_x11_update_geometry() reads the size of the hidden
window when called in vo_x11_fullscreen() (called from
vo_x11_config_vo_window()) when mapping the fullscreen window. At that
point, the window could be mapped, but not necessarily. If it's not
mapped, it will get the size of the unfullscreened window... I think.
One could fix this by actively waiting until the window is mapped. Try
to pick a less hacky approach instead, and never read the window size
until MapNotify is received.
vo_x11_create_window() needs a hack, because we'd possibly set the VO's
size to 0, resulting e.g. in vdpau to fail initialization. (It'll print
error messages until a proper resize is received.)
2014-02-02 00:35:46 +00:00
|
|
|
vo_x11_update_geometry(vo);
|
2013-02-13 15:33:01 +00:00
|
|
|
break;
|
|
|
|
case DestroyNotify:
|
2013-09-11 22:56:41 +00:00
|
|
|
MP_WARN(x11, "Our window was destroyed, exiting\n");
|
2016-03-15 21:11:31 +00:00
|
|
|
mp_input_put_key(x11->input_ctx, MP_KEY_CLOSE_WIN);
|
2014-05-18 23:06:43 +00:00
|
|
|
x11->window = 0;
|
2013-02-13 15:33:01 +00:00
|
|
|
break;
|
|
|
|
case ClientMessage:
|
2014-05-16 21:17:09 +00:00
|
|
|
if (Event.xclient.message_type == XA(x11, WM_PROTOCOLS) &&
|
|
|
|
Event.xclient.data.l[0] == XA(x11, WM_DELETE_WINDOW))
|
2016-03-15 21:11:31 +00:00
|
|
|
mp_input_put_key(x11->input_ctx, MP_KEY_CLOSE_WIN);
|
2014-01-04 00:16:44 +00:00
|
|
|
vo_x11_dnd_handle_message(vo, &Event.xclient);
|
2015-01-12 11:08:48 +00:00
|
|
|
vo_x11_xembed_handle_message(vo, &Event.xclient);
|
2014-01-04 00:16:44 +00:00
|
|
|
break;
|
|
|
|
case SelectionNotify:
|
|
|
|
vo_x11_dnd_handle_selection(vo, &Event.xselection);
|
2013-02-13 15:33:01 +00:00
|
|
|
break;
|
x11: if the WM supports _NET_FRAME_EXTENTS, don't wait for map
Some window managers can prevent mapping of a window as a feature. i3
can put new windows on a certain workspace (with "assign"), so if mpv is
started on a different workspace, the window will never be mapped.
mpv currently waits until the window is mapped (blocking almost all of
the player), in order to avoid race conditions regarding the window
size. We don't want to remove this, but on the other hand we also don't
want to block the player forever in these situations.
So what we need is a way to know when the window manager is "done" with
processing the map request. Unfortunately, there doesn't seem to be a
standard way for this. So, instead we could do some arbitrary
communication with the WM, that may act as "barrier" after map request
and the "immediate" mapping of the window. If the window is not mapped
after this barrier, it means the window manager decided to delay the
mapping indefinitely. Use the _NET_REQUEST_FRAME_EXTENTS message as such
a barrier. WMs supporting this message must set the _NET_FRAME_EXTENTS
property on the mpv window, and we receive a PropertyNotify event. If
that happens, we always continue and cancel waiting for the MapNotify
event.
I don't know if this is sane or if there's a better mechanism. Also,
this works only for WMs which support this message, which are not many.
But at least it appears to work on i3. It may reintroduce flickering on
fullscreen with other WMs, though.
2014-09-15 16:42:09 +00:00
|
|
|
case PropertyNotify:
|
2016-01-30 23:05:58 +00:00
|
|
|
if (Event.xproperty.atom == XA(x11, _NET_FRAME_EXTENTS) ||
|
|
|
|
Event.xproperty.atom == XA(x11, WM_STATE))
|
|
|
|
{
|
2014-12-09 20:28:07 +00:00
|
|
|
if (!x11->pseudo_mapped && !x11->parent) {
|
x11: if the WM supports _NET_FRAME_EXTENTS, don't wait for map
Some window managers can prevent mapping of a window as a feature. i3
can put new windows on a certain workspace (with "assign"), so if mpv is
started on a different workspace, the window will never be mapped.
mpv currently waits until the window is mapped (blocking almost all of
the player), in order to avoid race conditions regarding the window
size. We don't want to remove this, but on the other hand we also don't
want to block the player forever in these situations.
So what we need is a way to know when the window manager is "done" with
processing the map request. Unfortunately, there doesn't seem to be a
standard way for this. So, instead we could do some arbitrary
communication with the WM, that may act as "barrier" after map request
and the "immediate" mapping of the window. If the window is not mapped
after this barrier, it means the window manager decided to delay the
mapping indefinitely. Use the _NET_REQUEST_FRAME_EXTENTS message as such
a barrier. WMs supporting this message must set the _NET_FRAME_EXTENTS
property on the mpv window, and we receive a PropertyNotify event. If
that happens, we always continue and cancel waiting for the MapNotify
event.
I don't know if this is sane or if there's a better mechanism. Also,
this works only for WMs which support this message, which are not many.
But at least it appears to work on i3. It may reintroduce flickering on
fullscreen with other WMs, though.
2014-09-15 16:42:09 +00:00
|
|
|
MP_VERBOSE(x11, "not waiting for MapNotify\n");
|
2014-09-29 16:05:52 +00:00
|
|
|
x11->pseudo_mapped = true;
|
|
|
|
}
|
2014-11-03 22:01:27 +00:00
|
|
|
} else if (Event.xproperty.atom == XA(x11, _NET_WM_STATE)) {
|
2019-11-29 12:52:37 +00:00
|
|
|
vo_x11_check_net_wm_state_change(vo);
|
2019-11-29 12:16:31 +00:00
|
|
|
} else if (Event.xproperty.atom == XA(x11, _NET_WM_DESKTOP)) {
|
|
|
|
vo_x11_check_net_wm_desktop_change(vo);
|
2015-01-26 01:18:47 +00:00
|
|
|
} else if (Event.xproperty.atom == x11->icc_profile_property) {
|
2015-01-26 01:21:00 +00:00
|
|
|
x11->pending_vo_events |= VO_EVENT_ICC_PROFILE_CHANGED;
|
x11: if the WM supports _NET_FRAME_EXTENTS, don't wait for map
Some window managers can prevent mapping of a window as a feature. i3
can put new windows on a certain workspace (with "assign"), so if mpv is
started on a different workspace, the window will never be mapped.
mpv currently waits until the window is mapped (blocking almost all of
the player), in order to avoid race conditions regarding the window
size. We don't want to remove this, but on the other hand we also don't
want to block the player forever in these situations.
So what we need is a way to know when the window manager is "done" with
processing the map request. Unfortunately, there doesn't seem to be a
standard way for this. So, instead we could do some arbitrary
communication with the WM, that may act as "barrier" after map request
and the "immediate" mapping of the window. If the window is not mapped
after this barrier, it means the window manager decided to delay the
mapping indefinitely. Use the _NET_REQUEST_FRAME_EXTENTS message as such
a barrier. WMs supporting this message must set the _NET_FRAME_EXTENTS
property on the mpv window, and we receive a PropertyNotify event. If
that happens, we always continue and cancel waiting for the MapNotify
event.
I don't know if this is sane or if there's a better mechanism. Also,
this works only for WMs which support this message, which are not many.
But at least it appears to work on i3. It may reintroduce flickering on
fullscreen with other WMs, though.
2014-09-15 16:42:09 +00:00
|
|
|
}
|
|
|
|
break;
|
x11: support xorg present extension
This builds off of present_sync which was introduced in a previous
commit to support xorg's present extension in all of the X11 backends
(sans vdpau) in mpv. It turns out there is an Xpresent library that
integrates the xorg present extention with Xlib (which barely anyone
seems to use), so this can be added without too much trouble. The
workflow is to first setup the event by telling Xorg we would like to
receive PresentCompleteNotify (there are others in the extension but
this is the only one we really care about). After that, just call
XPresentNotifyMSC after every buffer swap with a target_msc of 0. Xorg
then returns the last presentation through its usual event loop and we
go ahead and use that information to update mpv's values for vsync
timing purposes. One theoretical weakness of this approach is that the
present event is put on the same queue as the rest of the XEvents. It
would be nicer for it be placed somewhere else so we could just wait
on that queue without having to deal with other possible events in
there. In theory, xcb could do that with special events, but it doesn't
really matter in practice.
Unsurprisingly, this doesn't work on NVIDIA. Well NVIDIA does actually
receive presentation events, but for whatever the calculations used make
timings worse which defeats the purpose. This works perfectly fine on
Mesa however. Utilizing the previous commit that detects Xrandr
providers, we can enable this mechanism for users that have both Mesa
and not NVIDIA (to avoid messing up anyone that has a switchable
graphics system or such). Patches welcome if anyone figures out how to
fix this on NVIDIA.
Unlike the EGL/GLX sync extensions, the present extension works with any
graphics API (good for vulkan since its timing extension has been in
development hell). NVIDIA also happens to have zero support for the
EGL/GLX sync extensions, so we can just remove it with no loss. Only
Xorg ever used it and other backends already have their own present
methods. vo_vdpau VO is a special case that has its own fancying timing
code in its flip_page. This presumably works well, and I have no way of
testing it so just leave it as it is.
2022-06-10 16:49:38 +00:00
|
|
|
case GenericEvent: {
|
|
|
|
XGenericEventCookie *cookie = (XGenericEventCookie *)&Event.xcookie;
|
2022-06-22 04:13:44 +00:00
|
|
|
if (cookie->extension == x11->present_code && x11->use_present)
|
x11: support xorg present extension
This builds off of present_sync which was introduced in a previous
commit to support xorg's present extension in all of the X11 backends
(sans vdpau) in mpv. It turns out there is an Xpresent library that
integrates the xorg present extention with Xlib (which barely anyone
seems to use), so this can be added without too much trouble. The
workflow is to first setup the event by telling Xorg we would like to
receive PresentCompleteNotify (there are others in the extension but
this is the only one we really care about). After that, just call
XPresentNotifyMSC after every buffer swap with a target_msc of 0. Xorg
then returns the last presentation through its usual event loop and we
go ahead and use that information to update mpv's values for vsync
timing purposes. One theoretical weakness of this approach is that the
present event is put on the same queue as the rest of the XEvents. It
would be nicer for it be placed somewhere else so we could just wait
on that queue without having to deal with other possible events in
there. In theory, xcb could do that with special events, but it doesn't
really matter in practice.
Unsurprisingly, this doesn't work on NVIDIA. Well NVIDIA does actually
receive presentation events, but for whatever the calculations used make
timings worse which defeats the purpose. This works perfectly fine on
Mesa however. Utilizing the previous commit that detects Xrandr
providers, we can enable this mechanism for users that have both Mesa
and not NVIDIA (to avoid messing up anyone that has a switchable
graphics system or such). Patches welcome if anyone figures out how to
fix this on NVIDIA.
Unlike the EGL/GLX sync extensions, the present extension works with any
graphics API (good for vulkan since its timing extension has been in
development hell). NVIDIA also happens to have zero support for the
EGL/GLX sync extensions, so we can just remove it with no loss. Only
Xorg ever used it and other backends already have their own present
methods. vo_vdpau VO is a special case that has its own fancying timing
code in its flip_page. This presumably works well, and I have no way of
testing it so just leave it as it is.
2022-06-10 16:49:38 +00:00
|
|
|
{
|
|
|
|
XGetEventData(x11->display, cookie);
|
2022-06-22 04:13:44 +00:00
|
|
|
if (cookie->evtype == PresentCompleteNotify) {
|
x11: support xorg present extension
This builds off of present_sync which was introduced in a previous
commit to support xorg's present extension in all of the X11 backends
(sans vdpau) in mpv. It turns out there is an Xpresent library that
integrates the xorg present extention with Xlib (which barely anyone
seems to use), so this can be added without too much trouble. The
workflow is to first setup the event by telling Xorg we would like to
receive PresentCompleteNotify (there are others in the extension but
this is the only one we really care about). After that, just call
XPresentNotifyMSC after every buffer swap with a target_msc of 0. Xorg
then returns the last presentation through its usual event loop and we
go ahead and use that information to update mpv's values for vsync
timing purposes. One theoretical weakness of this approach is that the
present event is put on the same queue as the rest of the XEvents. It
would be nicer for it be placed somewhere else so we could just wait
on that queue without having to deal with other possible events in
there. In theory, xcb could do that with special events, but it doesn't
really matter in practice.
Unsurprisingly, this doesn't work on NVIDIA. Well NVIDIA does actually
receive presentation events, but for whatever the calculations used make
timings worse which defeats the purpose. This works perfectly fine on
Mesa however. Utilizing the previous commit that detects Xrandr
providers, we can enable this mechanism for users that have both Mesa
and not NVIDIA (to avoid messing up anyone that has a switchable
graphics system or such). Patches welcome if anyone figures out how to
fix this on NVIDIA.
Unlike the EGL/GLX sync extensions, the present extension works with any
graphics API (good for vulkan since its timing extension has been in
development hell). NVIDIA also happens to have zero support for the
EGL/GLX sync extensions, so we can just remove it with no loss. Only
Xorg ever used it and other backends already have their own present
methods. vo_vdpau VO is a special case that has its own fancying timing
code in its flip_page. This presumably works well, and I have no way of
testing it so just leave it as it is.
2022-06-10 16:49:38 +00:00
|
|
|
XPresentCompleteNotifyEvent *present_event;
|
|
|
|
present_event = (XPresentCompleteNotifyEvent *)cookie->data;
|
2023-11-02 21:03:03 +00:00
|
|
|
present_sync_update_values(x11->present,
|
2023-09-16 02:45:24 +00:00
|
|
|
present_event->ust * 1000,
|
x11: support xorg present extension
This builds off of present_sync which was introduced in a previous
commit to support xorg's present extension in all of the X11 backends
(sans vdpau) in mpv. It turns out there is an Xpresent library that
integrates the xorg present extention with Xlib (which barely anyone
seems to use), so this can be added without too much trouble. The
workflow is to first setup the event by telling Xorg we would like to
receive PresentCompleteNotify (there are others in the extension but
this is the only one we really care about). After that, just call
XPresentNotifyMSC after every buffer swap with a target_msc of 0. Xorg
then returns the last presentation through its usual event loop and we
go ahead and use that information to update mpv's values for vsync
timing purposes. One theoretical weakness of this approach is that the
present event is put on the same queue as the rest of the XEvents. It
would be nicer for it be placed somewhere else so we could just wait
on that queue without having to deal with other possible events in
there. In theory, xcb could do that with special events, but it doesn't
really matter in practice.
Unsurprisingly, this doesn't work on NVIDIA. Well NVIDIA does actually
receive presentation events, but for whatever the calculations used make
timings worse which defeats the purpose. This works perfectly fine on
Mesa however. Utilizing the previous commit that detects Xrandr
providers, we can enable this mechanism for users that have both Mesa
and not NVIDIA (to avoid messing up anyone that has a switchable
graphics system or such). Patches welcome if anyone figures out how to
fix this on NVIDIA.
Unlike the EGL/GLX sync extensions, the present extension works with any
graphics API (good for vulkan since its timing extension has been in
development hell). NVIDIA also happens to have zero support for the
EGL/GLX sync extensions, so we can just remove it with no loss. Only
Xorg ever used it and other backends already have their own present
methods. vo_vdpau VO is a special case that has its own fancying timing
code in its flip_page. This presumably works well, and I have no way of
testing it so just leave it as it is.
2022-06-10 16:49:38 +00:00
|
|
|
present_event->msc);
|
|
|
|
}
|
|
|
|
}
|
2022-10-09 04:42:00 +00:00
|
|
|
XFreeEventData(x11->display, cookie);
|
x11: support xorg present extension
This builds off of present_sync which was introduced in a previous
commit to support xorg's present extension in all of the X11 backends
(sans vdpau) in mpv. It turns out there is an Xpresent library that
integrates the xorg present extention with Xlib (which barely anyone
seems to use), so this can be added without too much trouble. The
workflow is to first setup the event by telling Xorg we would like to
receive PresentCompleteNotify (there are others in the extension but
this is the only one we really care about). After that, just call
XPresentNotifyMSC after every buffer swap with a target_msc of 0. Xorg
then returns the last presentation through its usual event loop and we
go ahead and use that information to update mpv's values for vsync
timing purposes. One theoretical weakness of this approach is that the
present event is put on the same queue as the rest of the XEvents. It
would be nicer for it be placed somewhere else so we could just wait
on that queue without having to deal with other possible events in
there. In theory, xcb could do that with special events, but it doesn't
really matter in practice.
Unsurprisingly, this doesn't work on NVIDIA. Well NVIDIA does actually
receive presentation events, but for whatever the calculations used make
timings worse which defeats the purpose. This works perfectly fine on
Mesa however. Utilizing the previous commit that detects Xrandr
providers, we can enable this mechanism for users that have both Mesa
and not NVIDIA (to avoid messing up anyone that has a switchable
graphics system or such). Patches welcome if anyone figures out how to
fix this on NVIDIA.
Unlike the EGL/GLX sync extensions, the present extension works with any
graphics API (good for vulkan since its timing extension has been in
development hell). NVIDIA also happens to have zero support for the
EGL/GLX sync extensions, so we can just remove it with no loss. Only
Xorg ever used it and other backends already have their own present
methods. vo_vdpau VO is a special case that has its own fancying timing
code in its flip_page. This presumably works well, and I have no way of
testing it so just leave it as it is.
2022-06-10 16:49:38 +00:00
|
|
|
break;
|
|
|
|
}
|
2013-01-02 11:55:52 +00:00
|
|
|
default:
|
2013-02-13 15:33:01 +00:00
|
|
|
if (Event.type == x11->ShmCompletionEvent) {
|
|
|
|
if (x11->ShmCompletionWaitCount > 0)
|
|
|
|
x11->ShmCompletionWaitCount--;
|
|
|
|
}
|
2014-08-17 13:38:47 +00:00
|
|
|
if (Event.type == x11->xrandr_event) {
|
|
|
|
xrandr_read(x11);
|
2024-02-13 23:20:08 +00:00
|
|
|
vo_x11_get_dpi_scale(x11);
|
2014-08-17 13:38:47 +00:00
|
|
|
vo_x11_update_geometry(vo);
|
|
|
|
}
|
2013-02-13 15:33:01 +00:00
|
|
|
break;
|
2004-06-14 04:53:03 +00:00
|
|
|
}
|
|
|
|
}
|
2013-03-02 21:50:09 +00:00
|
|
|
|
2013-02-15 18:47:34 +00:00
|
|
|
update_vo_size(vo);
|
2001-03-03 21:47:37 +00:00
|
|
|
}
|
|
|
|
|
2014-05-17 01:29:36 +00:00
|
|
|
static void vo_x11_sizehint(struct vo *vo, struct mp_rect rc, bool override_pos)
|
2002-03-08 20:14:08 +00:00
|
|
|
{
|
2008-04-21 00:14:37 +00:00
|
|
|
struct vo_x11_state *x11 = vo->x11;
|
2019-11-27 19:30:13 +00:00
|
|
|
struct mp_vo_opts *opts = x11->opts;
|
x11_common: fix window mapping, refactor window creation/resize handling
vo_opengl creates an invisible window with VOFLAG_HIDDEN in order to
test whether the OpenGL context is useable. The visible window is
created at a later point. This has been broken forever (in vo_gl,
now called vo_opengl_old, it could be avoided by disabling auto-
detection explicitly using the "yuv" sub-option). Avoiding
VOFLAG_HIDDEN only mitigates the issue, and a related bug can still
happen with some window managers (see below).
As a hack, code was added to vo_gl to destroy the (hidden) window so
that all state was destroyed. Later, more hacks were added to deal with
some caveats that came with recreating the window, such as probing for
the context up to 4 times.
Attempt to fix the problem properly. There were two problems: first,
the window was not resized to video size before mapping. This was the
main cause for the placement issue, e.g. mapping the window as 320x200,
and then resizing it. Second, mpv tried to force the window position
with XSetWMNormalHints and PPosition with values it just read with
XGetGeometry. This messes up the window manager's default placement.
It seems to be a race condition, and behavior is different across
various WMs too: with IceWM, the window manager's placement is usually
preferred, and with Fluxbox, mpv's position is preferred. mpv's default
position is centering the window on the screen, which is much nicer for
video in general than typical WM default placement, so it's possible
that this bug was perceived as a feature. (Users who want this have to
use --geometry="50%:50%", doing this by default is probably not safe
with all WMs.)
Since the old code was hard to follow and full of issues, it's easier
to redo it. Move general window creation stuff out of the
vo_x11_config_vo_window function, and move the resize logic into it.
This has been tested on IcwWM, Fluxbox, awesome, Unity/Compiz.
2013-03-02 16:08:37 +00:00
|
|
|
|
2014-12-18 15:45:55 +00:00
|
|
|
if (!x11->window || x11->parent)
|
2014-05-18 23:06:43 +00:00
|
|
|
return;
|
|
|
|
|
2023-08-13 20:31:51 +00:00
|
|
|
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]);
|
2013-03-04 21:41:27 +00:00
|
|
|
bool force_pos = opts->geometry.xy_valid || // explicitly forced by user
|
|
|
|
opts->force_window_position || // resize -> reset position
|
2023-08-13 20:31:51 +00:00
|
|
|
screen || fsscreen || // force onto screen area
|
2022-04-08 02:23:19 +00:00
|
|
|
opts->screen_name || // also force onto screen area
|
2014-12-09 20:28:07 +00:00
|
|
|
x11->parent || // force to fill parent
|
2013-03-04 21:41:27 +00:00
|
|
|
override_pos; // for fullscreen and such
|
x11_common: fix window mapping, refactor window creation/resize handling
vo_opengl creates an invisible window with VOFLAG_HIDDEN in order to
test whether the OpenGL context is useable. The visible window is
created at a later point. This has been broken forever (in vo_gl,
now called vo_opengl_old, it could be avoided by disabling auto-
detection explicitly using the "yuv" sub-option). Avoiding
VOFLAG_HIDDEN only mitigates the issue, and a related bug can still
happen with some window managers (see below).
As a hack, code was added to vo_gl to destroy the (hidden) window so
that all state was destroyed. Later, more hacks were added to deal with
some caveats that came with recreating the window, such as probing for
the context up to 4 times.
Attempt to fix the problem properly. There were two problems: first,
the window was not resized to video size before mapping. This was the
main cause for the placement issue, e.g. mapping the window as 320x200,
and then resizing it. Second, mpv tried to force the window position
with XSetWMNormalHints and PPosition with values it just read with
XGetGeometry. This messes up the window manager's default placement.
It seems to be a race condition, and behavior is different across
various WMs too: with IceWM, the window manager's placement is usually
preferred, and with Fluxbox, mpv's position is preferred. mpv's default
position is centering the window on the screen, which is much nicer for
video in general than typical WM default placement, so it's possible
that this bug was perceived as a feature. (Users who want this have to
use --geometry="50%:50%", doing this by default is probably not safe
with all WMs.)
Since the old code was hard to follow and full of issues, it's easier
to redo it. Move general window creation stuff out of the
vo_x11_config_vo_window function, and move the resize logic into it.
This has been tested on IcwWM, Fluxbox, awesome, Unity/Compiz.
2013-03-02 16:08:37 +00:00
|
|
|
|
2014-05-16 23:59:08 +00:00
|
|
|
XSizeHints *hint = XAllocSizeHints();
|
2023-11-23 19:46:25 +00:00
|
|
|
MP_HANDLE_OOM(hint);
|
2014-05-16 23:59:08 +00:00
|
|
|
|
|
|
|
hint->flags |= PSize | (force_pos ? PPosition : 0);
|
2014-05-17 01:29:36 +00:00
|
|
|
hint->x = rc.x0;
|
|
|
|
hint->y = rc.y0;
|
|
|
|
hint->width = RC_W(rc);
|
|
|
|
hint->height = RC_H(rc);
|
2014-05-16 23:59:08 +00:00
|
|
|
hint->max_width = 0;
|
|
|
|
hint->max_height = 0;
|
2004-06-14 04:53:03 +00:00
|
|
|
|
2014-10-04 20:17:36 +00:00
|
|
|
if (opts->keepaspect && opts->keepaspect_window) {
|
2014-05-17 01:29:36 +00:00
|
|
|
hint->flags |= PAspect;
|
|
|
|
hint->min_aspect.x = hint->width;
|
|
|
|
hint->min_aspect.y = hint->height;
|
|
|
|
hint->max_aspect.x = hint->width;
|
|
|
|
hint->max_aspect.y = hint->height;
|
|
|
|
}
|
|
|
|
|
libvo, libao: remove useless video and audio output drivers
Some of these have only limited use, and some of these have no use at
all. Remove them. They make maintainance harder and nobody needs them.
It's possible that many of the removed drivers were very useful a dozen
of years ago, but now it's 2012.
Note that some of these could be added back, in case they were more
useful than I thought. But right now, they are just a burden.
Reason for removal for each module:
vo_3dfx, vo_dfbmga, vo_dxr3, vo_ivtv, vo_mga, vo_s3fb,
vo_tdfxfb, vo_xmga, vo_tdfx_vid:
All of these are for very specific and outdated hardware. Some
of them require non-standard kernel drivers or do direct HW
access.
vo_dga: the most crappy and ancient way to get fast output on X.
vo_aa: there's vo_caca for the same purpose.
vo_ggi: this never lived, and is entirely useless.
vo_mpegpes: for DVB cards, I can't test this and it's crappy.
vo_fbdev, vo_fbdev2: there's vo_directfb2
vo_bl: what is this even? But it's neither important, nor alive.
vo_svga, vo_vesa: you want to use this? You can't be serious.
vo_wii: I can't test this, and who the hell uses this?
vo_xvr100: some Sun thing.
vo_xover: only useful in connection with xvr100.
ao_nas: still alive, but I doubt it has any meaning today.
ao_sun: Sun.
ao_win32: use ao_dsound or ao_portaudio instead.
ao_ivtv: removed along vo_ivtv.
Also get rid of anything SDL related. SDL 1.x is total crap for video
output, and will be replaced with SDL 2.x soon (perhaps), so if you
want to use SDL, write output drivers for SDL 2.x.
Additionally, I accidentally damaged Sun support, which made me
completely remove Sun/Solaris support. Nobody cares about this anyway.
Some left overs from previous commits removing modules were cleaned up.
2012-07-28 18:20:17 +00:00
|
|
|
// Set minimum height/width to 4 to avoid off-by-one errors.
|
2014-05-16 23:59:08 +00:00
|
|
|
hint->flags |= PMinSize;
|
|
|
|
hint->min_width = hint->min_height = 4;
|
2004-06-14 04:53:03 +00:00
|
|
|
|
2014-05-16 23:59:08 +00:00
|
|
|
hint->flags |= PWinGravity;
|
2020-02-22 00:35:12 +00:00
|
|
|
hint->win_gravity = StaticGravity;
|
2014-05-17 12:26:49 +00:00
|
|
|
|
2014-05-16 23:59:08 +00:00
|
|
|
XSetWMNormalHints(x11->display, x11->window, hint);
|
|
|
|
XFree(hint);
|
2002-03-08 20:14:08 +00:00
|
|
|
}
|
|
|
|
|
x11_common: fix window mapping, refactor window creation/resize handling
vo_opengl creates an invisible window with VOFLAG_HIDDEN in order to
test whether the OpenGL context is useable. The visible window is
created at a later point. This has been broken forever (in vo_gl,
now called vo_opengl_old, it could be avoided by disabling auto-
detection explicitly using the "yuv" sub-option). Avoiding
VOFLAG_HIDDEN only mitigates the issue, and a related bug can still
happen with some window managers (see below).
As a hack, code was added to vo_gl to destroy the (hidden) window so
that all state was destroyed. Later, more hacks were added to deal with
some caveats that came with recreating the window, such as probing for
the context up to 4 times.
Attempt to fix the problem properly. There were two problems: first,
the window was not resized to video size before mapping. This was the
main cause for the placement issue, e.g. mapping the window as 320x200,
and then resizing it. Second, mpv tried to force the window position
with XSetWMNormalHints and PPosition with values it just read with
XGetGeometry. This messes up the window manager's default placement.
It seems to be a race condition, and behavior is different across
various WMs too: with IceWM, the window manager's placement is usually
preferred, and with Fluxbox, mpv's position is preferred. mpv's default
position is centering the window on the screen, which is much nicer for
video in general than typical WM default placement, so it's possible
that this bug was perceived as a feature. (Users who want this have to
use --geometry="50%:50%", doing this by default is probably not safe
with all WMs.)
Since the old code was hard to follow and full of issues, it's easier
to redo it. Move general window creation stuff out of the
vo_x11_config_vo_window function, and move the resize logic into it.
This has been tested on IcwWM, Fluxbox, awesome, Unity/Compiz.
2013-03-02 16:08:37 +00:00
|
|
|
static void vo_x11_move_resize(struct vo *vo, bool move, bool resize,
|
2014-05-17 01:29:36 +00:00
|
|
|
struct mp_rect rc)
|
2013-01-26 21:37:47 +00:00
|
|
|
{
|
2014-05-18 23:06:43 +00:00
|
|
|
if (!vo->x11->window)
|
|
|
|
return;
|
2014-05-17 01:29:36 +00:00
|
|
|
int w = RC_W(rc), h = RC_H(rc);
|
|
|
|
XWindowChanges req = {.x = rc.x0, .y = rc.y0, .width = w, .height = h};
|
x11_common: fix window mapping, refactor window creation/resize handling
vo_opengl creates an invisible window with VOFLAG_HIDDEN in order to
test whether the OpenGL context is useable. The visible window is
created at a later point. This has been broken forever (in vo_gl,
now called vo_opengl_old, it could be avoided by disabling auto-
detection explicitly using the "yuv" sub-option). Avoiding
VOFLAG_HIDDEN only mitigates the issue, and a related bug can still
happen with some window managers (see below).
As a hack, code was added to vo_gl to destroy the (hidden) window so
that all state was destroyed. Later, more hacks were added to deal with
some caveats that came with recreating the window, such as probing for
the context up to 4 times.
Attempt to fix the problem properly. There were two problems: first,
the window was not resized to video size before mapping. This was the
main cause for the placement issue, e.g. mapping the window as 320x200,
and then resizing it. Second, mpv tried to force the window position
with XSetWMNormalHints and PPosition with values it just read with
XGetGeometry. This messes up the window manager's default placement.
It seems to be a race condition, and behavior is different across
various WMs too: with IceWM, the window manager's placement is usually
preferred, and with Fluxbox, mpv's position is preferred. mpv's default
position is centering the window on the screen, which is much nicer for
video in general than typical WM default placement, so it's possible
that this bug was perceived as a feature. (Users who want this have to
use --geometry="50%:50%", doing this by default is probably not safe
with all WMs.)
Since the old code was hard to follow and full of issues, it's easier
to redo it. Move general window creation stuff out of the
vo_x11_config_vo_window function, and move the resize logic into it.
This has been tested on IcwWM, Fluxbox, awesome, Unity/Compiz.
2013-03-02 16:08:37 +00:00
|
|
|
unsigned mask = (move ? CWX | CWY : 0) | (resize ? CWWidth | CWHeight : 0);
|
|
|
|
if (mask)
|
|
|
|
XConfigureWindow(vo->x11->display, vo->x11->window, mask, &req);
|
2014-05-17 01:29:36 +00:00
|
|
|
vo_x11_sizehint(vo, rc, false);
|
2013-01-26 21:37:47 +00:00
|
|
|
}
|
|
|
|
|
2012-01-25 01:01:22 +00:00
|
|
|
// set a X text property that expects a UTF8_STRING type
|
|
|
|
static void vo_x11_set_property_utf8(struct vo *vo, Atom name, const char *t)
|
|
|
|
{
|
|
|
|
struct vo_x11_state *x11 = vo->x11;
|
|
|
|
|
2014-05-16 21:17:09 +00:00
|
|
|
XChangeProperty(x11->display, x11->window, name, XA(x11, UTF8_STRING), 8,
|
2012-01-25 01:01:22 +00:00
|
|
|
PropModeReplace, t, strlen(t));
|
|
|
|
}
|
|
|
|
|
2011-12-26 16:15:11 +00:00
|
|
|
// set a X text property that expects a STRING or COMPOUND_TEXT type
|
2011-12-06 17:48:31 +00:00
|
|
|
static void vo_x11_set_property_string(struct vo *vo, Atom name, const char *t)
|
|
|
|
{
|
|
|
|
struct vo_x11_state *x11 = vo->x11;
|
|
|
|
XTextProperty prop = {0};
|
|
|
|
|
2011-12-26 16:15:11 +00:00
|
|
|
if (Xutf8TextListToTextProperty(x11->display, (char **)&t, 1,
|
|
|
|
XStdICCTextStyle, &prop) == Success)
|
|
|
|
{
|
2011-12-06 17:48:31 +00:00
|
|
|
XSetTextProperty(x11->display, x11->window, &prop, name);
|
2012-01-25 01:01:22 +00:00
|
|
|
} else {
|
|
|
|
// Strictly speaking this violates the ICCCM, but there's no way we
|
|
|
|
// can do this correctly.
|
|
|
|
vo_x11_set_property_utf8(vo, name, t);
|
2011-12-26 16:15:11 +00:00
|
|
|
}
|
2023-08-11 18:24:33 +00:00
|
|
|
XFree(prop.value);
|
2011-12-06 17:48:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void vo_x11_update_window_title(struct vo *vo)
|
|
|
|
{
|
|
|
|
struct vo_x11_state *x11 = vo->x11;
|
|
|
|
|
2015-12-06 17:39:09 +00:00
|
|
|
if (!x11->window || !x11->window_title)
|
2013-06-15 17:04:20 +00:00
|
|
|
return;
|
|
|
|
|
2015-12-06 17:39:09 +00:00
|
|
|
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);
|
2022-01-20 19:25:46 +00:00
|
|
|
|
|
|
|
/* _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));
|
2022-11-09 14:03:18 +00:00
|
|
|
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));
|
2022-01-20 19:25:46 +00:00
|
|
|
talloc_free(tmp);
|
2011-12-06 17:48:31 +00:00
|
|
|
}
|
|
|
|
|
2015-01-12 11:08:48 +00:00
|
|
|
static void vo_x11_xembed_update(struct vo_x11_state *x11, int flags)
|
|
|
|
{
|
|
|
|
if (!x11->window || !x11->parent)
|
|
|
|
return;
|
|
|
|
|
|
|
|
long xembed_info[] = {XEMBED_VERSION, flags};
|
|
|
|
Atom name = XA(x11, _XEMBED_INFO);
|
|
|
|
XChangeProperty(x11->display, x11->window, name, name, 32,
|
|
|
|
PropModeReplace, (char *)xembed_info, 2);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void vo_x11_xembed_handle_message(struct vo *vo, XClientMessageEvent *ce)
|
|
|
|
{
|
|
|
|
struct vo_x11_state *x11 = vo->x11;
|
|
|
|
if (!x11->window || !x11->parent || ce->message_type != XA(x11, _XEMBED))
|
|
|
|
return;
|
|
|
|
|
|
|
|
long msg = ce->data.l[1];
|
|
|
|
if (msg == XEMBED_EMBEDDED_NOTIFY)
|
|
|
|
MP_VERBOSE(x11, "Parent windows supports XEmbed.\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
static void vo_x11_xembed_send_message(struct vo_x11_state *x11, long m[4])
|
|
|
|
{
|
|
|
|
if (!x11->window || !x11->parent)
|
|
|
|
return;
|
|
|
|
XEvent ev = {.xclient = {
|
|
|
|
.type = ClientMessage,
|
|
|
|
.window = x11->parent,
|
|
|
|
.message_type = XA(x11, _XEMBED),
|
|
|
|
.format = 32,
|
|
|
|
.data = {.l = { CurrentTime, m[0], m[1], m[2], m[3] }},
|
|
|
|
} };
|
|
|
|
XSendEvent(x11->display, x11->parent, False, NoEventMask, &ev);
|
|
|
|
}
|
|
|
|
|
x11: add window icon
The png file added to etc/ are taken from the link mentioned in commit
303096b, except that they have been converted to 16 bit, sRGB (with
color profile info dropped, if there was one), and transparent pixels
reset for better compression.
The file x11_icon.bin is generated by gen-x11-icon.sh. I'm adding it to
the git repo directly, because the script requires ImageMagick, and we
don't want to make building even more complicated.
The way how this is done is basically a compromise between effort
required in x11_common.c and in gen-x11-icon.sh. Ideally, x11_icon.bin
would be directly in the format as required by _NET_WM_ICON, but trying
to write the binary width/height values from shell would probably be a
nightmare, so here we go.
The zlib code in x11_common.c is lifted from demux_mkv.c, with some
modifications (like accepting a gzip header, because I don't know how to
make gzip write raw compressed data).
2013-09-01 21:26:44 +00:00
|
|
|
static void vo_x11_set_wm_icon(struct vo_x11_state *x11)
|
|
|
|
{
|
2017-07-01 13:56:12 +00:00
|
|
|
int icon_size = 0;
|
|
|
|
long *icon = talloc_array(NULL, long, 0);
|
|
|
|
|
|
|
|
for (int n = 0; x11_icons[n].start; n++) {
|
|
|
|
struct mp_image *img =
|
|
|
|
load_image_png_buf(x11_icons[n].start, x11_icons[n].len, IMGFMT_RGBA);
|
|
|
|
if (!img)
|
|
|
|
continue;
|
|
|
|
int new_size = 2 + img->w * img->h;
|
|
|
|
MP_RESIZE_ARRAY(NULL, icon, icon_size + new_size);
|
|
|
|
long *cur = icon + icon_size;
|
|
|
|
icon_size += new_size;
|
|
|
|
*cur++ = img->w;
|
|
|
|
*cur++ = img->h;
|
|
|
|
for (int y = 0; y < img->h; y++) {
|
|
|
|
uint8_t *s = (uint8_t *)img->planes[0] + img->stride[0] * y;
|
|
|
|
for (int x = 0; x < img->w; x++) {
|
|
|
|
*cur++ = s[x * 4 + 0] | (s[x * 4 + 1] << 8) |
|
|
|
|
(s[x * 4 + 2] << 16) | ((unsigned)s[x * 4 + 3] << 24);
|
x11: add window icon
The png file added to etc/ are taken from the link mentioned in commit
303096b, except that they have been converted to 16 bit, sRGB (with
color profile info dropped, if there was one), and transparent pixels
reset for better compression.
The file x11_icon.bin is generated by gen-x11-icon.sh. I'm adding it to
the git repo directly, because the script requires ImageMagick, and we
don't want to make building even more complicated.
The way how this is done is basically a compromise between effort
required in x11_common.c and in gen-x11-icon.sh. Ideally, x11_icon.bin
would be directly in the format as required by _NET_WM_ICON, but trying
to write the binary width/height values from shell would probably be a
nightmare, so here we go.
The zlib code in x11_common.c is lifted from demux_mkv.c, with some
modifications (like accepting a gzip header, because I don't know how to
make gzip write raw compressed data).
2013-09-01 21:26:44 +00:00
|
|
|
}
|
|
|
|
}
|
2017-07-01 13:56:12 +00:00
|
|
|
talloc_free(img);
|
x11: add window icon
The png file added to etc/ are taken from the link mentioned in commit
303096b, except that they have been converted to 16 bit, sRGB (with
color profile info dropped, if there was one), and transparent pixels
reset for better compression.
The file x11_icon.bin is generated by gen-x11-icon.sh. I'm adding it to
the git repo directly, because the script requires ImageMagick, and we
don't want to make building even more complicated.
The way how this is done is basically a compromise between effort
required in x11_common.c and in gen-x11-icon.sh. Ideally, x11_icon.bin
would be directly in the format as required by _NET_WM_ICON, but trying
to write the binary width/height values from shell would probably be a
nightmare, so here we go.
The zlib code in x11_common.c is lifted from demux_mkv.c, with some
modifications (like accepting a gzip header, because I don't know how to
make gzip write raw compressed data).
2013-09-01 21:26:44 +00:00
|
|
|
}
|
|
|
|
|
2014-05-16 21:17:09 +00:00
|
|
|
XChangeProperty(x11->display, x11->window, XA(x11, _NET_WM_ICON),
|
2013-09-04 11:37:38 +00:00
|
|
|
XA_CARDINAL, 32, PropModeReplace,
|
|
|
|
(unsigned char *)icon, icon_size);
|
x11: add window icon
The png file added to etc/ are taken from the link mentioned in commit
303096b, except that they have been converted to 16 bit, sRGB (with
color profile info dropped, if there was one), and transparent pixels
reset for better compression.
The file x11_icon.bin is generated by gen-x11-icon.sh. I'm adding it to
the git repo directly, because the script requires ImageMagick, and we
don't want to make building even more complicated.
The way how this is done is basically a compromise between effort
required in x11_common.c and in gen-x11-icon.sh. Ideally, x11_icon.bin
would be directly in the format as required by _NET_WM_ICON, but trying
to write the binary width/height values from shell would probably be a
nightmare, so here we go.
The zlib code in x11_common.c is lifted from demux_mkv.c, with some
modifications (like accepting a gzip header, because I don't know how to
make gzip write raw compressed data).
2013-09-01 21:26:44 +00:00
|
|
|
talloc_free(icon);
|
|
|
|
}
|
|
|
|
|
2014-05-17 01:29:36 +00:00
|
|
|
static void vo_x11_create_window(struct vo *vo, XVisualInfo *vis,
|
|
|
|
struct mp_rect rc)
|
2013-01-27 11:01:08 +00:00
|
|
|
{
|
x11_common: fix window mapping, refactor window creation/resize handling
vo_opengl creates an invisible window with VOFLAG_HIDDEN in order to
test whether the OpenGL context is useable. The visible window is
created at a later point. This has been broken forever (in vo_gl,
now called vo_opengl_old, it could be avoided by disabling auto-
detection explicitly using the "yuv" sub-option). Avoiding
VOFLAG_HIDDEN only mitigates the issue, and a related bug can still
happen with some window managers (see below).
As a hack, code was added to vo_gl to destroy the (hidden) window so
that all state was destroyed. Later, more hacks were added to deal with
some caveats that came with recreating the window, such as probing for
the context up to 4 times.
Attempt to fix the problem properly. There were two problems: first,
the window was not resized to video size before mapping. This was the
main cause for the placement issue, e.g. mapping the window as 320x200,
and then resizing it. Second, mpv tried to force the window position
with XSetWMNormalHints and PPosition with values it just read with
XGetGeometry. This messes up the window manager's default placement.
It seems to be a race condition, and behavior is different across
various WMs too: with IceWM, the window manager's placement is usually
preferred, and with Fluxbox, mpv's position is preferred. mpv's default
position is centering the window on the screen, which is much nicer for
video in general than typical WM default placement, so it's possible
that this bug was perceived as a feature. (Users who want this have to
use --geometry="50%:50%", doing this by default is probably not safe
with all WMs.)
Since the old code was hard to follow and full of issues, it's easier
to redo it. Move general window creation stuff out of the
vo_x11_config_vo_window function, and move the resize logic into it.
This has been tested on IcwWM, Fluxbox, awesome, Unity/Compiz.
2013-03-02 16:08:37 +00:00
|
|
|
struct vo_x11_state *x11 = vo->x11;
|
|
|
|
|
|
|
|
assert(x11->window == None);
|
|
|
|
assert(!x11->xic);
|
|
|
|
|
|
|
|
XVisualInfo vinfo_storage;
|
|
|
|
if (!vis) {
|
|
|
|
vis = &vinfo_storage;
|
2014-05-16 21:17:02 +00:00
|
|
|
XWindowAttributes att;
|
|
|
|
XGetWindowAttributes(x11->display, x11->rootwin, &att);
|
|
|
|
XMatchVisualInfo(x11->display, x11->screen, att.depth, TrueColor, vis);
|
x11_common: fix window mapping, refactor window creation/resize handling
vo_opengl creates an invisible window with VOFLAG_HIDDEN in order to
test whether the OpenGL context is useable. The visible window is
created at a later point. This has been broken forever (in vo_gl,
now called vo_opengl_old, it could be avoided by disabling auto-
detection explicitly using the "yuv" sub-option). Avoiding
VOFLAG_HIDDEN only mitigates the issue, and a related bug can still
happen with some window managers (see below).
As a hack, code was added to vo_gl to destroy the (hidden) window so
that all state was destroyed. Later, more hacks were added to deal with
some caveats that came with recreating the window, such as probing for
the context up to 4 times.
Attempt to fix the problem properly. There were two problems: first,
the window was not resized to video size before mapping. This was the
main cause for the placement issue, e.g. mapping the window as 320x200,
and then resizing it. Second, mpv tried to force the window position
with XSetWMNormalHints and PPosition with values it just read with
XGetGeometry. This messes up the window manager's default placement.
It seems to be a race condition, and behavior is different across
various WMs too: with IceWM, the window manager's placement is usually
preferred, and with Fluxbox, mpv's position is preferred. mpv's default
position is centering the window on the screen, which is much nicer for
video in general than typical WM default placement, so it's possible
that this bug was perceived as a feature. (Users who want this have to
use --geometry="50%:50%", doing this by default is probably not safe
with all WMs.)
Since the old code was hard to follow and full of issues, it's easier
to redo it. Move general window creation stuff out of the
vo_x11_config_vo_window function, and move the resize logic into it.
This has been tested on IcwWM, Fluxbox, awesome, Unity/Compiz.
2013-03-02 16:08:37 +00:00
|
|
|
}
|
|
|
|
|
2013-09-29 22:37:44 +00:00
|
|
|
if (x11->colormap == None) {
|
|
|
|
x11->colormap = XCreateColormap(x11->display, x11->rootwin,
|
|
|
|
vis->visual, AllocNone);
|
|
|
|
}
|
|
|
|
|
2013-04-15 19:18:46 +00:00
|
|
|
unsigned long xswamask = CWBorderPixel | CWColormap;
|
|
|
|
XSetWindowAttributes xswa = {
|
|
|
|
.border_pixel = 0,
|
|
|
|
.colormap = x11->colormap,
|
|
|
|
};
|
2004-06-14 04:53:03 +00:00
|
|
|
|
2014-12-09 20:28:07 +00:00
|
|
|
Window parent = x11->parent;
|
|
|
|
if (!parent)
|
|
|
|
parent = x11->rootwin;
|
2013-02-24 22:35:13 +00:00
|
|
|
|
x11_common: fix window mapping, refactor window creation/resize handling
vo_opengl creates an invisible window with VOFLAG_HIDDEN in order to
test whether the OpenGL context is useable. The visible window is
created at a later point. This has been broken forever (in vo_gl,
now called vo_opengl_old, it could be avoided by disabling auto-
detection explicitly using the "yuv" sub-option). Avoiding
VOFLAG_HIDDEN only mitigates the issue, and a related bug can still
happen with some window managers (see below).
As a hack, code was added to vo_gl to destroy the (hidden) window so
that all state was destroyed. Later, more hacks were added to deal with
some caveats that came with recreating the window, such as probing for
the context up to 4 times.
Attempt to fix the problem properly. There were two problems: first,
the window was not resized to video size before mapping. This was the
main cause for the placement issue, e.g. mapping the window as 320x200,
and then resizing it. Second, mpv tried to force the window position
with XSetWMNormalHints and PPosition with values it just read with
XGetGeometry. This messes up the window manager's default placement.
It seems to be a race condition, and behavior is different across
various WMs too: with IceWM, the window manager's placement is usually
preferred, and with Fluxbox, mpv's position is preferred. mpv's default
position is centering the window on the screen, which is much nicer for
video in general than typical WM default placement, so it's possible
that this bug was perceived as a feature. (Users who want this have to
use --geometry="50%:50%", doing this by default is probably not safe
with all WMs.)
Since the old code was hard to follow and full of issues, it's easier
to redo it. Move general window creation stuff out of the
vo_x11_config_vo_window function, and move the resize logic into it.
This has been tested on IcwWM, Fluxbox, awesome, Unity/Compiz.
2013-03-02 16:08:37 +00:00
|
|
|
x11->window =
|
2014-05-17 01:29:36 +00:00
|
|
|
XCreateWindow(x11->display, parent, rc.x0, rc.y0, RC_W(rc), RC_H(rc), 0,
|
|
|
|
vis->depth, CopyFromParent, vis->visual, xswamask, &xswa);
|
2014-05-16 21:17:09 +00:00
|
|
|
Atom protos[1] = {XA(x11, WM_DELETE_WINDOW)};
|
|
|
|
XSetWMProtocols(x11->display, x11->window, protos, 1);
|
2004-06-14 04:53:03 +00:00
|
|
|
|
x11: support xorg present extension
This builds off of present_sync which was introduced in a previous
commit to support xorg's present extension in all of the X11 backends
(sans vdpau) in mpv. It turns out there is an Xpresent library that
integrates the xorg present extention with Xlib (which barely anyone
seems to use), so this can be added without too much trouble. The
workflow is to first setup the event by telling Xorg we would like to
receive PresentCompleteNotify (there are others in the extension but
this is the only one we really care about). After that, just call
XPresentNotifyMSC after every buffer swap with a target_msc of 0. Xorg
then returns the last presentation through its usual event loop and we
go ahead and use that information to update mpv's values for vsync
timing purposes. One theoretical weakness of this approach is that the
present event is put on the same queue as the rest of the XEvents. It
would be nicer for it be placed somewhere else so we could just wait
on that queue without having to deal with other possible events in
there. In theory, xcb could do that with special events, but it doesn't
really matter in practice.
Unsurprisingly, this doesn't work on NVIDIA. Well NVIDIA does actually
receive presentation events, but for whatever the calculations used make
timings worse which defeats the purpose. This works perfectly fine on
Mesa however. Utilizing the previous commit that detects Xrandr
providers, we can enable this mechanism for users that have both Mesa
and not NVIDIA (to avoid messing up anyone that has a switchable
graphics system or such). Patches welcome if anyone figures out how to
fix this on NVIDIA.
Unlike the EGL/GLX sync extensions, the present extension works with any
graphics API (good for vulkan since its timing extension has been in
development hell). NVIDIA also happens to have zero support for the
EGL/GLX sync extensions, so we can just remove it with no loss. Only
Xorg ever used it and other backends already have their own present
methods. vo_vdpau VO is a special case that has its own fancying timing
code in its flip_page. This presumably works well, and I have no way of
testing it so just leave it as it is.
2022-06-10 16:49:38 +00:00
|
|
|
if (!XPresentQueryExtension(x11->display, &x11->present_code, NULL, NULL)) {
|
|
|
|
MP_VERBOSE(x11, "The XPresent extension is not supported.\n");
|
|
|
|
} else {
|
2022-06-22 04:13:44 +00:00
|
|
|
MP_VERBOSE(x11, "The XPresent extension was found.\n");
|
x11: support xorg present extension
This builds off of present_sync which was introduced in a previous
commit to support xorg's present extension in all of the X11 backends
(sans vdpau) in mpv. It turns out there is an Xpresent library that
integrates the xorg present extention with Xlib (which barely anyone
seems to use), so this can be added without too much trouble. The
workflow is to first setup the event by telling Xorg we would like to
receive PresentCompleteNotify (there are others in the extension but
this is the only one we really care about). After that, just call
XPresentNotifyMSC after every buffer swap with a target_msc of 0. Xorg
then returns the last presentation through its usual event loop and we
go ahead and use that information to update mpv's values for vsync
timing purposes. One theoretical weakness of this approach is that the
present event is put on the same queue as the rest of the XEvents. It
would be nicer for it be placed somewhere else so we could just wait
on that queue without having to deal with other possible events in
there. In theory, xcb could do that with special events, but it doesn't
really matter in practice.
Unsurprisingly, this doesn't work on NVIDIA. Well NVIDIA does actually
receive presentation events, but for whatever the calculations used make
timings worse which defeats the purpose. This works perfectly fine on
Mesa however. Utilizing the previous commit that detects Xrandr
providers, we can enable this mechanism for users that have both Mesa
and not NVIDIA (to avoid messing up anyone that has a switchable
graphics system or such). Patches welcome if anyone figures out how to
fix this on NVIDIA.
Unlike the EGL/GLX sync extensions, the present extension works with any
graphics API (good for vulkan since its timing extension has been in
development hell). NVIDIA also happens to have zero support for the
EGL/GLX sync extensions, so we can just remove it with no loss. Only
Xorg ever used it and other backends already have their own present
methods. vo_vdpau VO is a special case that has its own fancying timing
code in its flip_page. This presumably works well, and I have no way of
testing it so just leave it as it is.
2022-06-10 16:49:38 +00:00
|
|
|
XPresentSelectInput(x11->display, x11->window, PresentCompleteNotifyMask);
|
|
|
|
}
|
2022-06-22 04:13:44 +00:00
|
|
|
xpresent_set(x11);
|
x11: support xorg present extension
This builds off of present_sync which was introduced in a previous
commit to support xorg's present extension in all of the X11 backends
(sans vdpau) in mpv. It turns out there is an Xpresent library that
integrates the xorg present extention with Xlib (which barely anyone
seems to use), so this can be added without too much trouble. The
workflow is to first setup the event by telling Xorg we would like to
receive PresentCompleteNotify (there are others in the extension but
this is the only one we really care about). After that, just call
XPresentNotifyMSC after every buffer swap with a target_msc of 0. Xorg
then returns the last presentation through its usual event loop and we
go ahead and use that information to update mpv's values for vsync
timing purposes. One theoretical weakness of this approach is that the
present event is put on the same queue as the rest of the XEvents. It
would be nicer for it be placed somewhere else so we could just wait
on that queue without having to deal with other possible events in
there. In theory, xcb could do that with special events, but it doesn't
really matter in practice.
Unsurprisingly, this doesn't work on NVIDIA. Well NVIDIA does actually
receive presentation events, but for whatever the calculations used make
timings worse which defeats the purpose. This works perfectly fine on
Mesa however. Utilizing the previous commit that detects Xrandr
providers, we can enable this mechanism for users that have both Mesa
and not NVIDIA (to avoid messing up anyone that has a switchable
graphics system or such). Patches welcome if anyone figures out how to
fix this on NVIDIA.
Unlike the EGL/GLX sync extensions, the present extension works with any
graphics API (good for vulkan since its timing extension has been in
development hell). NVIDIA also happens to have zero support for the
EGL/GLX sync extensions, so we can just remove it with no loss. Only
Xorg ever used it and other backends already have their own present
methods. vo_vdpau VO is a special case that has its own fancying timing
code in its flip_page. This presumably works well, and I have no way of
testing it so just leave it as it is.
2022-06-10 16:49:38 +00:00
|
|
|
|
2018-10-11 04:38:57 +00:00
|
|
|
x11->mouse_cursor_set = false;
|
2019-03-11 04:37:24 +00:00
|
|
|
x11->mouse_cursor_visible = true;
|
2018-10-11 04:38:57 +00:00
|
|
|
vo_update_cursor(vo);
|
|
|
|
|
2013-09-20 12:45:49 +00:00
|
|
|
if (x11->xim) {
|
|
|
|
x11->xic = XCreateIC(x11->xim,
|
2023-12-10 06:31:12 +00:00
|
|
|
XNInputStyle, XIMPreeditNothing | XIMStatusNothing,
|
2013-09-20 12:45:49 +00:00
|
|
|
XNClientWindow, x11->window,
|
|
|
|
XNFocusWindow, x11->window,
|
|
|
|
NULL);
|
video/out/x11_common: make IME work (for libmpv usage)
The implementation is minimal since it already uses XIM for UTF-8 text
input. The only changes needed are small fixes: setting the IC focus and
correct the usage of window param for XFilterEvent, and IME will
"just work"...
...after setting 2 process-wide global values before VO creation:
LC_CTYPE and X locale modifiers. So this usage is currently limited to
libmpv clients which don't want to do their own IME processing.
locale strikes once again:
XIM input methods require them to be set before creating the XIM context to
work, even though they don't matter after the context is created. Both of
these values being process-wide global means that the only time to safely
set these values is before the process creates any thread.
In theory, mpv could set LC_CTYPE in the main when running standalone,
but locale being locale means something is always messed up. In this case,
setting LC_CTYPE changes "Character classification and case conversion"
behavior. While mpv uses its own ASCII-only is*() and to*() functions to
avoid some of the issues, it also uses strcasecmp() and POSIX regex which
behaviors are affected by LC_CTYPE.
In summary, to avoid unavoidable race conditions mpv won't do this in
standalone mode right now.
It's now libmpv users' responsibility to:
- set LC_CTYPE to a locale usable for XIM, such as a UTF-8 locale
- set X locale modifiers with XSetLocaleModifiers
- hope that all stars align if trying to switch locale back afterwards
On most setups, both of these can be empty strings, In this case, the usual
XMODIFIERS environment variable must be set to enable IME support.
(e.g. XMODIFIERS=@im=fcitx)
2024-09-02 19:57:50 +00:00
|
|
|
if (x11->xic)
|
|
|
|
XSetICFocus(x11->xic);
|
2013-09-20 12:45:49 +00:00
|
|
|
}
|
2013-06-15 17:04:20 +00:00
|
|
|
|
2014-12-09 20:28:07 +00:00
|
|
|
if (!x11->parent) {
|
2016-05-02 19:21:53 +00:00
|
|
|
vo_x11_update_composition_hint(vo);
|
2014-10-07 21:37:47 +00:00
|
|
|
vo_x11_set_wm_icon(x11);
|
|
|
|
vo_x11_dnd_init_window(vo);
|
2016-05-07 15:10:57 +00:00
|
|
|
vo_x11_set_property_utf8(vo, XA(x11, _GTK_THEME_VARIANT), "dark");
|
2014-10-07 21:37:47 +00:00
|
|
|
}
|
2023-08-10 02:11:53 +00:00
|
|
|
if (!x11->parent || x11->opts->x11_wid_title)
|
|
|
|
vo_x11_update_window_title(vo);
|
2015-01-12 11:08:48 +00:00
|
|
|
vo_x11_xembed_update(x11, 0);
|
x11_common: fix window mapping, refactor window creation/resize handling
vo_opengl creates an invisible window with VOFLAG_HIDDEN in order to
test whether the OpenGL context is useable. The visible window is
created at a later point. This has been broken forever (in vo_gl,
now called vo_opengl_old, it could be avoided by disabling auto-
detection explicitly using the "yuv" sub-option). Avoiding
VOFLAG_HIDDEN only mitigates the issue, and a related bug can still
happen with some window managers (see below).
As a hack, code was added to vo_gl to destroy the (hidden) window so
that all state was destroyed. Later, more hacks were added to deal with
some caveats that came with recreating the window, such as probing for
the context up to 4 times.
Attempt to fix the problem properly. There were two problems: first,
the window was not resized to video size before mapping. This was the
main cause for the placement issue, e.g. mapping the window as 320x200,
and then resizing it. Second, mpv tried to force the window position
with XSetWMNormalHints and PPosition with values it just read with
XGetGeometry. This messes up the window manager's default placement.
It seems to be a race condition, and behavior is different across
various WMs too: with IceWM, the window manager's placement is usually
preferred, and with Fluxbox, mpv's position is preferred. mpv's default
position is centering the window on the screen, which is much nicer for
video in general than typical WM default placement, so it's possible
that this bug was perceived as a feature. (Users who want this have to
use --geometry="50%:50%", doing this by default is probably not safe
with all WMs.)
Since the old code was hard to follow and full of issues, it's easier
to redo it. Move general window creation stuff out of the
vo_x11_config_vo_window function, and move the resize logic into it.
This has been tested on IcwWM, Fluxbox, awesome, Unity/Compiz.
2013-03-02 16:08:37 +00:00
|
|
|
}
|
|
|
|
|
2014-05-17 01:29:36 +00:00
|
|
|
static void vo_x11_map_window(struct vo *vo, struct mp_rect rc)
|
x11_common: fix window mapping, refactor window creation/resize handling
vo_opengl creates an invisible window with VOFLAG_HIDDEN in order to
test whether the OpenGL context is useable. The visible window is
created at a later point. This has been broken forever (in vo_gl,
now called vo_opengl_old, it could be avoided by disabling auto-
detection explicitly using the "yuv" sub-option). Avoiding
VOFLAG_HIDDEN only mitigates the issue, and a related bug can still
happen with some window managers (see below).
As a hack, code was added to vo_gl to destroy the (hidden) window so
that all state was destroyed. Later, more hacks were added to deal with
some caveats that came with recreating the window, such as probing for
the context up to 4 times.
Attempt to fix the problem properly. There were two problems: first,
the window was not resized to video size before mapping. This was the
main cause for the placement issue, e.g. mapping the window as 320x200,
and then resizing it. Second, mpv tried to force the window position
with XSetWMNormalHints and PPosition with values it just read with
XGetGeometry. This messes up the window manager's default placement.
It seems to be a race condition, and behavior is different across
various WMs too: with IceWM, the window manager's placement is usually
preferred, and with Fluxbox, mpv's position is preferred. mpv's default
position is centering the window on the screen, which is much nicer for
video in general than typical WM default placement, so it's possible
that this bug was perceived as a feature. (Users who want this have to
use --geometry="50%:50%", doing this by default is probably not safe
with all WMs.)
Since the old code was hard to follow and full of issues, it's easier
to redo it. Move general window creation stuff out of the
vo_x11_config_vo_window function, and move the resize logic into it.
This has been tested on IcwWM, Fluxbox, awesome, Unity/Compiz.
2013-03-02 16:08:37 +00:00
|
|
|
{
|
|
|
|
struct vo_x11_state *x11 = vo->x11;
|
|
|
|
|
2014-05-17 01:29:36 +00:00
|
|
|
vo_x11_move_resize(vo, true, true, rc);
|
2024-02-01 10:32:08 +00:00
|
|
|
vo_x11_decoration(vo, x11->opts->border, x11->opts->title_bar);
|
2014-05-15 21:55:27 +00:00
|
|
|
|
2019-11-27 19:30:13 +00:00
|
|
|
if (x11->opts->fullscreen && (x11->wm_type & vo_wm_FULLSCREEN)) {
|
2014-05-16 21:17:09 +00:00
|
|
|
Atom state = XA(x11, _NET_WM_STATE_FULLSCREEN);
|
|
|
|
XChangeProperty(x11->display, x11->window, XA(x11, _NET_WM_STATE), XA_ATOM,
|
2014-05-15 21:55:27 +00:00
|
|
|
32, PropModeAppend, (unsigned char *)&state, 1);
|
|
|
|
x11->fs = 1;
|
2024-05-25 18:49:05 +00:00
|
|
|
x11->init_fs = true;
|
2014-05-15 21:55:27 +00:00
|
|
|
// The "saved" positions are bogus, so reset them when leaving FS again.
|
|
|
|
x11->size_changed_during_fs = true;
|
|
|
|
x11->pos_changed_during_fs = true;
|
|
|
|
}
|
|
|
|
|
2019-11-27 19:30:13 +00:00
|
|
|
if (x11->opts->fsscreen_id != -1) {
|
2014-05-17 16:03:28 +00:00
|
|
|
long params[5] = {0};
|
2019-11-27 19:30:13 +00:00
|
|
|
if (x11->opts->fsscreen_id >= 0) {
|
2014-05-16 23:14:59 +00:00
|
|
|
for (int n = 0; n < 4; n++)
|
2019-11-27 19:30:13 +00:00
|
|
|
params[n] = x11->opts->fsscreen_id;
|
2014-05-16 23:14:59 +00:00
|
|
|
} else {
|
2014-05-17 16:03:28 +00:00
|
|
|
vo_x11_get_bounding_monitors(x11, ¶ms[0]);
|
2014-05-16 23:14:59 +00:00
|
|
|
}
|
2014-05-17 16:03:28 +00:00
|
|
|
params[4] = 1; // source indication: normal
|
|
|
|
x11_send_ewmh_msg(x11, "_NET_WM_FULLSCREEN_MONITORS", params);
|
2014-05-16 23:14:59 +00:00
|
|
|
}
|
|
|
|
|
2022-10-09 23:20:32 +00:00
|
|
|
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;
|
2015-01-17 18:26:39 +00:00
|
|
|
XChangeProperty(x11->display, x11->window, XA(x11, _NET_WM_DESKTOP),
|
|
|
|
XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&v, 1);
|
|
|
|
}
|
|
|
|
|
2016-05-02 19:21:53 +00:00
|
|
|
vo_x11_update_composition_hint(vo);
|
|
|
|
|
x11_common: fix window mapping, refactor window creation/resize handling
vo_opengl creates an invisible window with VOFLAG_HIDDEN in order to
test whether the OpenGL context is useable. The visible window is
created at a later point. This has been broken forever (in vo_gl,
now called vo_opengl_old, it could be avoided by disabling auto-
detection explicitly using the "yuv" sub-option). Avoiding
VOFLAG_HIDDEN only mitigates the issue, and a related bug can still
happen with some window managers (see below).
As a hack, code was added to vo_gl to destroy the (hidden) window so
that all state was destroyed. Later, more hacks were added to deal with
some caveats that came with recreating the window, such as probing for
the context up to 4 times.
Attempt to fix the problem properly. There were two problems: first,
the window was not resized to video size before mapping. This was the
main cause for the placement issue, e.g. mapping the window as 320x200,
and then resizing it. Second, mpv tried to force the window position
with XSetWMNormalHints and PPosition with values it just read with
XGetGeometry. This messes up the window manager's default placement.
It seems to be a race condition, and behavior is different across
various WMs too: with IceWM, the window manager's placement is usually
preferred, and with Fluxbox, mpv's position is preferred. mpv's default
position is centering the window on the screen, which is much nicer for
video in general than typical WM default placement, so it's possible
that this bug was perceived as a feature. (Users who want this have to
use --geometry="50%:50%", doing this by default is probably not safe
with all WMs.)
Since the old code was hard to follow and full of issues, it's easier
to redo it. Move general window creation stuff out of the
vo_x11_config_vo_window function, and move the resize logic into it.
This has been tested on IcwWM, Fluxbox, awesome, Unity/Compiz.
2013-03-02 16:08:37 +00:00
|
|
|
// map window
|
client API, X11: change default keyboard input handling again
Commit 64b7811c tried to do the "right thing" with respect to whether
keyboard input should be enabled or not. It turns out that X11 does
something stupid by design. All modern toolkits work around this native
X11 behavior, but embedding breaks these workarounds.
The only way to handle this correctly is the XEmbed protocol. It needs
to be supported by the toolkit, and probably also some mpv support. But
Qt has inconsistent support for it. In Qt 4, a X11 specific embedding
widget was needed. Qt 5.0 doesn't support it at all. Qt 5.1 apparently
supports it via QWindow, but if it really does, I couldn't get it to
work.
So add a hack instead. The new --input-x11-keyboard option controls
whether mpv should enable keyboard input on the X11 window or not. In
the command line player, it's enabled by default, but in libmpv it's
disabled.
This hack has the same problem as all previous embedding had: move the
mouse outside of the window, and you don't get keyboard input anymore.
Likewise, mpv will steal all keyboard input from the parent application
as long as the mouse is inside of the mpv window.
Also see issue #1090.
2014-09-28 18:11:00 +00:00
|
|
|
int events = StructureNotifyMask | ExposureMask | PropertyChangeMask |
|
2016-07-08 14:36:53 +00:00
|
|
|
LeaveWindowMask | EnterWindowMask | FocusChangeMask;
|
2016-03-15 21:11:31 +00:00
|
|
|
if (mp_input_mouse_enabled(x11->input_ctx))
|
client API, X11: change default keyboard input handling again
Commit 64b7811c tried to do the "right thing" with respect to whether
keyboard input should be enabled or not. It turns out that X11 does
something stupid by design. All modern toolkits work around this native
X11 behavior, but embedding breaks these workarounds.
The only way to handle this correctly is the XEmbed protocol. It needs
to be supported by the toolkit, and probably also some mpv support. But
Qt has inconsistent support for it. In Qt 4, a X11 specific embedding
widget was needed. Qt 5.0 doesn't support it at all. Qt 5.1 apparently
supports it via QWindow, but if it really does, I couldn't get it to
work.
So add a hack instead. The new --input-x11-keyboard option controls
whether mpv should enable keyboard input on the X11 window or not. In
the command line player, it's enabled by default, but in libmpv it's
disabled.
This hack has the same problem as all previous embedding had: move the
mouse outside of the window, and you don't get keyboard input anymore.
Likewise, mpv will steal all keyboard input from the parent application
as long as the mouse is inside of the mpv window.
Also see issue #1090.
2014-09-28 18:11:00 +00:00
|
|
|
events |= PointerMotionMask | ButtonPressMask | ButtonReleaseMask;
|
2016-03-15 21:11:31 +00:00
|
|
|
if (mp_input_vo_keyboard_enabled(x11->input_ctx))
|
client API, X11: change default keyboard input handling again
Commit 64b7811c tried to do the "right thing" with respect to whether
keyboard input should be enabled or not. It turns out that X11 does
something stupid by design. All modern toolkits work around this native
X11 behavior, but embedding breaks these workarounds.
The only way to handle this correctly is the XEmbed protocol. It needs
to be supported by the toolkit, and probably also some mpv support. But
Qt has inconsistent support for it. In Qt 4, a X11 specific embedding
widget was needed. Qt 5.0 doesn't support it at all. Qt 5.1 apparently
supports it via QWindow, but if it really does, I couldn't get it to
work.
So add a hack instead. The new --input-x11-keyboard option controls
whether mpv should enable keyboard input on the X11 window or not. In
the command line player, it's enabled by default, but in libmpv it's
disabled.
This hack has the same problem as all previous embedding had: move the
mouse outside of the window, and you don't get keyboard input anymore.
Likewise, mpv will steal all keyboard input from the parent application
as long as the mouse is inside of the mpv window.
Also see issue #1090.
2014-09-28 18:11:00 +00:00
|
|
|
events |= KeyPressMask | KeyReleaseMask;
|
2014-08-31 12:26:31 +00:00
|
|
|
vo_x11_selectinput_witherr(vo, x11->display, x11->window, events);
|
x11_common: fix window mapping, refactor window creation/resize handling
vo_opengl creates an invisible window with VOFLAG_HIDDEN in order to
test whether the OpenGL context is useable. The visible window is
created at a later point. This has been broken forever (in vo_gl,
now called vo_opengl_old, it could be avoided by disabling auto-
detection explicitly using the "yuv" sub-option). Avoiding
VOFLAG_HIDDEN only mitigates the issue, and a related bug can still
happen with some window managers (see below).
As a hack, code was added to vo_gl to destroy the (hidden) window so
that all state was destroyed. Later, more hacks were added to deal with
some caveats that came with recreating the window, such as probing for
the context up to 4 times.
Attempt to fix the problem properly. There were two problems: first,
the window was not resized to video size before mapping. This was the
main cause for the placement issue, e.g. mapping the window as 320x200,
and then resizing it. Second, mpv tried to force the window position
with XSetWMNormalHints and PPosition with values it just read with
XGetGeometry. This messes up the window manager's default placement.
It seems to be a race condition, and behavior is different across
various WMs too: with IceWM, the window manager's placement is usually
preferred, and with Fluxbox, mpv's position is preferred. mpv's default
position is centering the window on the screen, which is much nicer for
video in general than typical WM default placement, so it's possible
that this bug was perceived as a feature. (Users who want this have to
use --geometry="50%:50%", doing this by default is probably not safe
with all WMs.)
Since the old code was hard to follow and full of issues, it's easier
to redo it. Move general window creation stuff out of the
vo_x11_config_vo_window function, and move the resize logic into it.
This has been tested on IcwWM, Fluxbox, awesome, Unity/Compiz.
2013-03-02 16:08:37 +00:00
|
|
|
XMapWindow(x11->display, x11->window);
|
2015-01-12 11:08:48 +00:00
|
|
|
|
2022-05-12 21:26:49 +00:00
|
|
|
if (x11->opts->cursor_passthrough)
|
|
|
|
vo_x11_set_input_region(vo, true);
|
|
|
|
|
2019-11-29 12:52:37 +00:00
|
|
|
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"
|
|
|
|
vo_x11_minimize(vo);
|
|
|
|
|
2019-11-27 19:30:13 +00:00
|
|
|
if (x11->opts->fullscreen && (x11->wm_type & vo_wm_FULLSCREEN))
|
2015-05-15 11:50:44 +00:00
|
|
|
x11_set_ewmh_state(x11, "_NET_WM_STATE_FULLSCREEN", 1);
|
|
|
|
|
2015-01-12 11:08:48 +00:00
|
|
|
vo_x11_xembed_update(x11, XEMBED_MAPPED);
|
2003-08-31 22:27:10 +00:00
|
|
|
}
|
|
|
|
|
2024-03-03 18:27:02 +00:00
|
|
|
static void vo_x11_highlevel_resize(struct vo *vo, struct mp_rect rc, bool force)
|
2013-10-31 18:51:46 +00:00
|
|
|
{
|
|
|
|
struct vo_x11_state *x11 = vo->x11;
|
2019-11-27 19:30:13 +00:00
|
|
|
struct mp_vo_opts *opts = x11->opts;
|
2013-10-31 18:51:46 +00:00
|
|
|
|
2024-03-03 18:27:02 +00:00
|
|
|
bool reset_pos = opts->force_window_position || force;
|
2013-10-31 18:51:46 +00:00
|
|
|
if (reset_pos) {
|
2014-05-17 01:29:36 +00:00
|
|
|
x11->nofsrc = rc;
|
|
|
|
} else {
|
|
|
|
x11->nofsrc.x1 = x11->nofsrc.x0 + RC_W(rc);
|
|
|
|
x11->nofsrc.y1 = x11->nofsrc.y0 + RC_H(rc);
|
2013-10-31 18:51:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (opts->fullscreen) {
|
|
|
|
x11->size_changed_during_fs = true;
|
|
|
|
x11->pos_changed_during_fs = reset_pos;
|
2014-05-17 01:29:36 +00:00
|
|
|
vo_x11_sizehint(vo, rc, false);
|
2013-10-31 18:51:46 +00:00
|
|
|
} else {
|
2014-05-17 01:29:36 +00:00
|
|
|
vo_x11_move_resize(vo, reset_pos, true, rc);
|
2013-10-31 18:51:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-05-15 21:47:00 +00:00
|
|
|
static void wait_until_mapped(struct vo *vo)
|
|
|
|
{
|
|
|
|
struct vo_x11_state *x11 = vo->x11;
|
x11: if the WM supports _NET_FRAME_EXTENTS, don't wait for map
Some window managers can prevent mapping of a window as a feature. i3
can put new windows on a certain workspace (with "assign"), so if mpv is
started on a different workspace, the window will never be mapped.
mpv currently waits until the window is mapped (blocking almost all of
the player), in order to avoid race conditions regarding the window
size. We don't want to remove this, but on the other hand we also don't
want to block the player forever in these situations.
So what we need is a way to know when the window manager is "done" with
processing the map request. Unfortunately, there doesn't seem to be a
standard way for this. So, instead we could do some arbitrary
communication with the WM, that may act as "barrier" after map request
and the "immediate" mapping of the window. If the window is not mapped
after this barrier, it means the window manager decided to delay the
mapping indefinitely. Use the _NET_REQUEST_FRAME_EXTENTS message as such
a barrier. WMs supporting this message must set the _NET_FRAME_EXTENTS
property on the mpv window, and we receive a PropertyNotify event. If
that happens, we always continue and cancel waiting for the MapNotify
event.
I don't know if this is sane or if there's a better mechanism. Also,
this works only for WMs which support this message, which are not many.
But at least it appears to work on i3. It may reintroduce flickering on
fullscreen with other WMs, though.
2014-09-15 16:42:09 +00:00
|
|
|
if (!x11->pseudo_mapped)
|
|
|
|
x11_send_ewmh_msg(x11, "_NET_REQUEST_FRAME_EXTENTS", (long[5]){0});
|
|
|
|
while (!x11->pseudo_mapped && x11->window) {
|
2015-01-12 11:15:27 +00:00
|
|
|
XWindowAttributes att;
|
|
|
|
XGetWindowAttributes(x11->display, x11->window, &att);
|
|
|
|
if (att.map_state != IsUnmapped) {
|
|
|
|
x11->pseudo_mapped = true;
|
|
|
|
break;
|
|
|
|
}
|
2014-05-15 21:47:00 +00:00
|
|
|
XEvent unused;
|
|
|
|
XPeekEvent(x11->display, &unused);
|
|
|
|
vo_x11_check_events(vo);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-09-30 21:31:34 +00:00
|
|
|
// Create the X11 window. There is only 1, and it must be created before
|
|
|
|
// vo_x11_config_vo_window() is called. vis can be NULL for default.
|
|
|
|
bool vo_x11_create_vo_window(struct vo *vo, XVisualInfo *vis,
|
2013-01-27 11:01:08 +00:00
|
|
|
const char *classname)
|
2007-06-27 10:26:13 +00:00
|
|
|
{
|
2013-02-13 15:33:01 +00:00
|
|
|
struct vo_x11_state *x11 = vo->x11;
|
2015-09-30 21:31:34 +00:00
|
|
|
assert(!x11->window);
|
x11_common: fix window mapping, refactor window creation/resize handling
vo_opengl creates an invisible window with VOFLAG_HIDDEN in order to
test whether the OpenGL context is useable. The visible window is
created at a later point. This has been broken forever (in vo_gl,
now called vo_opengl_old, it could be avoided by disabling auto-
detection explicitly using the "yuv" sub-option). Avoiding
VOFLAG_HIDDEN only mitigates the issue, and a related bug can still
happen with some window managers (see below).
As a hack, code was added to vo_gl to destroy the (hidden) window so
that all state was destroyed. Later, more hacks were added to deal with
some caveats that came with recreating the window, such as probing for
the context up to 4 times.
Attempt to fix the problem properly. There were two problems: first,
the window was not resized to video size before mapping. This was the
main cause for the placement issue, e.g. mapping the window as 320x200,
and then resizing it. Second, mpv tried to force the window position
with XSetWMNormalHints and PPosition with values it just read with
XGetGeometry. This messes up the window manager's default placement.
It seems to be a race condition, and behavior is different across
various WMs too: with IceWM, the window manager's placement is usually
preferred, and with Fluxbox, mpv's position is preferred. mpv's default
position is centering the window on the screen, which is much nicer for
video in general than typical WM default placement, so it's possible
that this bug was perceived as a feature. (Users who want this have to
use --geometry="50%:50%", doing this by default is probably not safe
with all WMs.)
Since the old code was hard to follow and full of issues, it's easier
to redo it. Move general window creation stuff out of the
vo_x11_config_vo_window function, and move the resize logic into it.
This has been tested on IcwWM, Fluxbox, awesome, Unity/Compiz.
2013-03-02 16:08:37 +00:00
|
|
|
|
2014-12-09 20:28:07 +00:00
|
|
|
if (x11->parent) {
|
|
|
|
if (x11->parent == x11->rootwin) {
|
2013-10-30 21:09:40 +00:00
|
|
|
x11->window = x11->rootwin;
|
2014-10-02 16:14:30 +00:00
|
|
|
x11->pseudo_mapped = true;
|
2014-05-18 22:08:42 +00:00
|
|
|
XSelectInput(x11->display, x11->window, StructureNotifyMask);
|
|
|
|
} else {
|
2014-12-09 20:28:07 +00:00
|
|
|
XSelectInput(x11->display, x11->parent, StructureNotifyMask);
|
2014-05-18 22:08:42 +00:00
|
|
|
}
|
2013-02-13 15:33:01 +00:00
|
|
|
}
|
|
|
|
if (x11->window == None) {
|
2015-09-30 21:31:34 +00:00
|
|
|
vo_x11_create_window(vo, vis, (struct mp_rect){.x1 = 320, .y1 = 200 });
|
x11_common: fix window mapping, refactor window creation/resize handling
vo_opengl creates an invisible window with VOFLAG_HIDDEN in order to
test whether the OpenGL context is useable. The visible window is
created at a later point. This has been broken forever (in vo_gl,
now called vo_opengl_old, it could be avoided by disabling auto-
detection explicitly using the "yuv" sub-option). Avoiding
VOFLAG_HIDDEN only mitigates the issue, and a related bug can still
happen with some window managers (see below).
As a hack, code was added to vo_gl to destroy the (hidden) window so
that all state was destroyed. Later, more hacks were added to deal with
some caveats that came with recreating the window, such as probing for
the context up to 4 times.
Attempt to fix the problem properly. There were two problems: first,
the window was not resized to video size before mapping. This was the
main cause for the placement issue, e.g. mapping the window as 320x200,
and then resizing it. Second, mpv tried to force the window position
with XSetWMNormalHints and PPosition with values it just read with
XGetGeometry. This messes up the window manager's default placement.
It seems to be a race condition, and behavior is different across
various WMs too: with IceWM, the window manager's placement is usually
preferred, and with Fluxbox, mpv's position is preferred. mpv's default
position is centering the window on the screen, which is much nicer for
video in general than typical WM default placement, so it's possible
that this bug was perceived as a feature. (Users who want this have to
use --geometry="50%:50%", doing this by default is probably not safe
with all WMs.)
Since the old code was hard to follow and full of issues, it's easier
to redo it. Move general window creation stuff out of the
vo_x11_config_vo_window function, and move the resize logic into it.
This has been tested on IcwWM, Fluxbox, awesome, Unity/Compiz.
2013-03-02 16:08:37 +00:00
|
|
|
vo_x11_classhint(vo, x11->window, classname);
|
2020-05-23 04:25:52 +00:00
|
|
|
vo_x11_wm_hints(vo, x11->window);
|
x11_common: fix window mapping, refactor window creation/resize handling
vo_opengl creates an invisible window with VOFLAG_HIDDEN in order to
test whether the OpenGL context is useable. The visible window is
created at a later point. This has been broken forever (in vo_gl,
now called vo_opengl_old, it could be avoided by disabling auto-
detection explicitly using the "yuv" sub-option). Avoiding
VOFLAG_HIDDEN only mitigates the issue, and a related bug can still
happen with some window managers (see below).
As a hack, code was added to vo_gl to destroy the (hidden) window so
that all state was destroyed. Later, more hacks were added to deal with
some caveats that came with recreating the window, such as probing for
the context up to 4 times.
Attempt to fix the problem properly. There were two problems: first,
the window was not resized to video size before mapping. This was the
main cause for the placement issue, e.g. mapping the window as 320x200,
and then resizing it. Second, mpv tried to force the window position
with XSetWMNormalHints and PPosition with values it just read with
XGetGeometry. This messes up the window manager's default placement.
It seems to be a race condition, and behavior is different across
various WMs too: with IceWM, the window manager's placement is usually
preferred, and with Fluxbox, mpv's position is preferred. mpv's default
position is centering the window on the screen, which is much nicer for
video in general than typical WM default placement, so it's possible
that this bug was perceived as a feature. (Users who want this have to
use --geometry="50%:50%", doing this by default is probably not safe
with all WMs.)
Since the old code was hard to follow and full of issues, it's easier
to redo it. Move general window creation stuff out of the
vo_x11_config_vo_window function, and move the resize logic into it.
This has been tested on IcwWM, Fluxbox, awesome, Unity/Compiz.
2013-03-02 16:08:37 +00:00
|
|
|
x11->window_hidden = true;
|
2013-02-13 15:33:01 +00:00
|
|
|
}
|
x11_common: fix window mapping, refactor window creation/resize handling
vo_opengl creates an invisible window with VOFLAG_HIDDEN in order to
test whether the OpenGL context is useable. The visible window is
created at a later point. This has been broken forever (in vo_gl,
now called vo_opengl_old, it could be avoided by disabling auto-
detection explicitly using the "yuv" sub-option). Avoiding
VOFLAG_HIDDEN only mitigates the issue, and a related bug can still
happen with some window managers (see below).
As a hack, code was added to vo_gl to destroy the (hidden) window so
that all state was destroyed. Later, more hacks were added to deal with
some caveats that came with recreating the window, such as probing for
the context up to 4 times.
Attempt to fix the problem properly. There were two problems: first,
the window was not resized to video size before mapping. This was the
main cause for the placement issue, e.g. mapping the window as 320x200,
and then resizing it. Second, mpv tried to force the window position
with XSetWMNormalHints and PPosition with values it just read with
XGetGeometry. This messes up the window manager's default placement.
It seems to be a race condition, and behavior is different across
various WMs too: with IceWM, the window manager's placement is usually
preferred, and with Fluxbox, mpv's position is preferred. mpv's default
position is centering the window on the screen, which is much nicer for
video in general than typical WM default placement, so it's possible
that this bug was perceived as a feature. (Users who want this have to
use --geometry="50%:50%", doing this by default is probably not safe
with all WMs.)
Since the old code was hard to follow and full of issues, it's easier
to redo it. Move general window creation stuff out of the
vo_x11_config_vo_window function, and move the resize logic into it.
This has been tested on IcwWM, Fluxbox, awesome, Unity/Compiz.
2013-03-02 16:08:37 +00:00
|
|
|
|
2015-09-30 21:31:34 +00:00
|
|
|
return !!x11->window;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Resize the window (e.g. new file, or video resolution change)
|
|
|
|
void vo_x11_config_vo_window(struct vo *vo)
|
|
|
|
{
|
|
|
|
struct vo_x11_state *x11 = vo->x11;
|
2019-11-27 19:30:13 +00:00
|
|
|
struct mp_vo_opts *opts = x11->opts;
|
2015-09-30 21:31:34 +00:00
|
|
|
|
|
|
|
assert(x11->window);
|
|
|
|
|
|
|
|
vo_x11_update_screeninfo(vo);
|
|
|
|
|
|
|
|
struct vo_win_geometry geo;
|
2024-09-27 14:19:35 +00:00
|
|
|
vo_calc_window_geometry(vo, &x11->screenrc, &x11->screenrc, x11->dpi_scale,
|
|
|
|
!x11->pseudo_mapped, &geo);
|
2015-09-30 21:31:34 +00:00
|
|
|
vo_apply_window_geometry(vo, &geo);
|
|
|
|
|
|
|
|
struct mp_rect rc = geo.win;
|
|
|
|
|
|
|
|
if (x11->parent) {
|
|
|
|
vo_x11_update_geometry(vo);
|
|
|
|
rc = (struct mp_rect){0, 0, RC_W(x11->winrc), RC_H(x11->winrc)};
|
|
|
|
}
|
x11_common: fix window mapping, refactor window creation/resize handling
vo_opengl creates an invisible window with VOFLAG_HIDDEN in order to
test whether the OpenGL context is useable. The visible window is
created at a later point. This has been broken forever (in vo_gl,
now called vo_opengl_old, it could be avoided by disabling auto-
detection explicitly using the "yuv" sub-option). Avoiding
VOFLAG_HIDDEN only mitigates the issue, and a related bug can still
happen with some window managers (see below).
As a hack, code was added to vo_gl to destroy the (hidden) window so
that all state was destroyed. Later, more hacks were added to deal with
some caveats that came with recreating the window, such as probing for
the context up to 4 times.
Attempt to fix the problem properly. There were two problems: first,
the window was not resized to video size before mapping. This was the
main cause for the placement issue, e.g. mapping the window as 320x200,
and then resizing it. Second, mpv tried to force the window position
with XSetWMNormalHints and PPosition with values it just read with
XGetGeometry. This messes up the window manager's default placement.
It seems to be a race condition, and behavior is different across
various WMs too: with IceWM, the window manager's placement is usually
preferred, and with Fluxbox, mpv's position is preferred. mpv's default
position is centering the window on the screen, which is much nicer for
video in general than typical WM default placement, so it's possible
that this bug was perceived as a feature. (Users who want this have to
use --geometry="50%:50%", doing this by default is probably not safe
with all WMs.)
Since the old code was hard to follow and full of issues, it's easier
to redo it. Move general window creation stuff out of the
vo_x11_config_vo_window function, and move the resize logic into it.
This has been tested on IcwWM, Fluxbox, awesome, Unity/Compiz.
2013-03-02 16:08:37 +00:00
|
|
|
|
2024-03-19 15:09:21 +00:00
|
|
|
bool reset_size = ((x11->old_dw != RC_W(rc) || x11->old_dh != RC_H(rc))
|
|
|
|
&& opts->auto_window_resize) || x11->geometry_change;
|
2024-03-03 18:27:02 +00:00
|
|
|
reset_size |= (x11->old_x != rc.x0 || x11->old_y != rc.y0) &&
|
|
|
|
(x11->geometry_change);
|
2021-10-20 02:47:23 +00:00
|
|
|
|
2014-05-17 01:29:36 +00:00
|
|
|
x11->old_dw = RC_W(rc);
|
|
|
|
x11->old_dh = RC_H(rc);
|
2024-03-03 18:27:02 +00:00
|
|
|
x11->old_x = rc.x0;
|
|
|
|
x11->old_y = rc.y0;
|
x11_common: fix window mapping, refactor window creation/resize handling
vo_opengl creates an invisible window with VOFLAG_HIDDEN in order to
test whether the OpenGL context is useable. The visible window is
created at a later point. This has been broken forever (in vo_gl,
now called vo_opengl_old, it could be avoided by disabling auto-
detection explicitly using the "yuv" sub-option). Avoiding
VOFLAG_HIDDEN only mitigates the issue, and a related bug can still
happen with some window managers (see below).
As a hack, code was added to vo_gl to destroy the (hidden) window so
that all state was destroyed. Later, more hacks were added to deal with
some caveats that came with recreating the window, such as probing for
the context up to 4 times.
Attempt to fix the problem properly. There were two problems: first,
the window was not resized to video size before mapping. This was the
main cause for the placement issue, e.g. mapping the window as 320x200,
and then resizing it. Second, mpv tried to force the window position
with XSetWMNormalHints and PPosition with values it just read with
XGetGeometry. This messes up the window manager's default placement.
It seems to be a race condition, and behavior is different across
various WMs too: with IceWM, the window manager's placement is usually
preferred, and with Fluxbox, mpv's position is preferred. mpv's default
position is centering the window on the screen, which is much nicer for
video in general than typical WM default placement, so it's possible
that this bug was perceived as a feature. (Users who want this have to
use --geometry="50%:50%", doing this by default is probably not safe
with all WMs.)
Since the old code was hard to follow and full of issues, it's easier
to redo it. Move general window creation stuff out of the
vo_x11_config_vo_window function, and move the resize logic into it.
This has been tested on IcwWM, Fluxbox, awesome, Unity/Compiz.
2013-03-02 16:08:37 +00:00
|
|
|
|
2013-10-31 18:51:46 +00:00
|
|
|
if (x11->window_hidden) {
|
2014-05-17 01:29:36 +00:00
|
|
|
x11->nofsrc = rc;
|
|
|
|
vo_x11_map_window(vo, rc);
|
x11_common: fix window mapping, refactor window creation/resize handling
vo_opengl creates an invisible window with VOFLAG_HIDDEN in order to
test whether the OpenGL context is useable. The visible window is
created at a later point. This has been broken forever (in vo_gl,
now called vo_opengl_old, it could be avoided by disabling auto-
detection explicitly using the "yuv" sub-option). Avoiding
VOFLAG_HIDDEN only mitigates the issue, and a related bug can still
happen with some window managers (see below).
As a hack, code was added to vo_gl to destroy the (hidden) window so
that all state was destroyed. Later, more hacks were added to deal with
some caveats that came with recreating the window, such as probing for
the context up to 4 times.
Attempt to fix the problem properly. There were two problems: first,
the window was not resized to video size before mapping. This was the
main cause for the placement issue, e.g. mapping the window as 320x200,
and then resizing it. Second, mpv tried to force the window position
with XSetWMNormalHints and PPosition with values it just read with
XGetGeometry. This messes up the window manager's default placement.
It seems to be a race condition, and behavior is different across
various WMs too: with IceWM, the window manager's placement is usually
preferred, and with Fluxbox, mpv's position is preferred. mpv's default
position is centering the window on the screen, which is much nicer for
video in general than typical WM default placement, so it's possible
that this bug was perceived as a feature. (Users who want this have to
use --geometry="50%:50%", doing this by default is probably not safe
with all WMs.)
Since the old code was hard to follow and full of issues, it's easier
to redo it. Move general window creation stuff out of the
vo_x11_config_vo_window function, and move the resize logic into it.
This has been tested on IcwWM, Fluxbox, awesome, Unity/Compiz.
2013-03-02 16:08:37 +00:00
|
|
|
} else if (reset_size) {
|
2024-09-27 14:19:35 +00:00
|
|
|
vo_x11_highlevel_resize(vo, rc, geo.flags & VO_WIN_FORCE_POS);
|
x11_common: fix window mapping, refactor window creation/resize handling
vo_opengl creates an invisible window with VOFLAG_HIDDEN in order to
test whether the OpenGL context is useable. The visible window is
created at a later point. This has been broken forever (in vo_gl,
now called vo_opengl_old, it could be avoided by disabling auto-
detection explicitly using the "yuv" sub-option). Avoiding
VOFLAG_HIDDEN only mitigates the issue, and a related bug can still
happen with some window managers (see below).
As a hack, code was added to vo_gl to destroy the (hidden) window so
that all state was destroyed. Later, more hacks were added to deal with
some caveats that came with recreating the window, such as probing for
the context up to 4 times.
Attempt to fix the problem properly. There were two problems: first,
the window was not resized to video size before mapping. This was the
main cause for the placement issue, e.g. mapping the window as 320x200,
and then resizing it. Second, mpv tried to force the window position
with XSetWMNormalHints and PPosition with values it just read with
XGetGeometry. This messes up the window manager's default placement.
It seems to be a race condition, and behavior is different across
various WMs too: with IceWM, the window manager's placement is usually
preferred, and with Fluxbox, mpv's position is preferred. mpv's default
position is centering the window on the screen, which is much nicer for
video in general than typical WM default placement, so it's possible
that this bug was perceived as a feature. (Users who want this have to
use --geometry="50%:50%", doing this by default is probably not safe
with all WMs.)
Since the old code was hard to follow and full of issues, it's easier
to redo it. Move general window creation stuff out of the
vo_x11_config_vo_window function, and move the resize logic into it.
This has been tested on IcwWM, Fluxbox, awesome, Unity/Compiz.
2013-03-02 16:08:37 +00:00
|
|
|
}
|
|
|
|
|
2021-10-20 02:47:23 +00:00
|
|
|
x11->geometry_change = false;
|
|
|
|
|
2013-08-13 22:34:49 +00:00
|
|
|
if (opts->ontop)
|
2014-05-23 18:27:14 +00:00
|
|
|
vo_x11_setlayer(vo, opts->ontop);
|
2024-04-13 04:00:40 +00:00
|
|
|
if (!opts->show_in_taskbar)
|
|
|
|
vo_x11_set_in_taskbar(vo, opts->show_in_taskbar);
|
2013-08-13 22:34:49 +00:00
|
|
|
|
2013-07-18 12:05:39 +00:00
|
|
|
vo_x11_fullscreen(vo);
|
2011-05-29 23:05:57 +00:00
|
|
|
|
2014-05-15 21:47:00 +00:00
|
|
|
wait_until_mapped(vo);
|
2014-05-16 23:14:59 +00:00
|
|
|
vo_x11_update_geometry(vo);
|
2013-02-15 18:47:34 +00:00
|
|
|
update_vo_size(vo);
|
|
|
|
x11->pending_vo_events &= ~VO_EVENT_RESIZE; // implicitly done by the VO
|
2007-06-27 10:26:13 +00:00
|
|
|
}
|
2004-06-14 04:53:03 +00:00
|
|
|
|
2022-10-09 23:20:32 +00:00
|
|
|
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, ¶ms[0],
|
|
|
|
sizeof(params[0]));
|
|
|
|
}
|
|
|
|
x11_send_ewmh_msg(x11, "_NET_WM_DESKTOP", params);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-05-23 18:27:14 +00:00
|
|
|
static void vo_x11_setlayer(struct vo *vo, bool ontop)
|
2002-05-06 15:05:07 +00:00
|
|
|
{
|
2008-04-20 23:50:40 +00:00
|
|
|
struct vo_x11_state *x11 = vo->x11;
|
2014-12-09 20:28:07 +00:00
|
|
|
if (x11->parent || !x11->window)
|
2004-06-14 04:53:03 +00:00
|
|
|
return;
|
|
|
|
|
2014-05-23 18:25:29 +00:00
|
|
|
if (x11->wm_type & (vo_wm_STAYS_ON_TOP | vo_wm_ABOVE)) {
|
2014-05-23 18:13:26 +00:00
|
|
|
char *state = "_NET_WM_STATE_ABOVE";
|
2004-06-14 04:53:03 +00:00
|
|
|
|
2014-05-23 18:13:26 +00:00
|
|
|
// Not in EWMH - but the old code preferred this (maybe it is "better")
|
|
|
|
if (x11->wm_type & vo_wm_STAYS_ON_TOP)
|
|
|
|
state = "_NET_WM_STATE_STAYS_ON_TOP";
|
|
|
|
|
|
|
|
x11_set_ewmh_state(x11, state, ontop);
|
|
|
|
|
|
|
|
MP_VERBOSE(x11, "NET style stay on top (%d). Using state %s.\n",
|
|
|
|
ontop, state);
|
2014-05-23 18:25:29 +00:00
|
|
|
} else if (x11->wm_type & vo_wm_LAYER) {
|
2014-05-23 18:27:14 +00:00
|
|
|
if (!x11->orig_layer) {
|
|
|
|
x11->orig_layer = WIN_LAYER_NORMAL;
|
|
|
|
x11_get_property_copy(x11, x11->window, XA(x11, _WIN_LAYER),
|
|
|
|
XA_CARDINAL, 32, &x11->orig_layer, sizeof(long));
|
|
|
|
MP_VERBOSE(x11, "original window layer is %ld.\n", x11->orig_layer);
|
|
|
|
}
|
2014-05-23 18:25:29 +00:00
|
|
|
|
|
|
|
long params[5] = {0};
|
|
|
|
// if not fullscreen, stay on default layer
|
|
|
|
params[0] = ontop ? WIN_LAYER_ABOVE_DOCK : x11->orig_layer;
|
|
|
|
params[1] = CurrentTime;
|
|
|
|
MP_VERBOSE(x11, "Layered style stay on top (layer %ld).\n", params[0]);
|
|
|
|
x11_send_ewmh_msg(x11, "_WIN_LAYER", params);
|
2004-06-14 04:53:03 +00:00
|
|
|
}
|
2002-05-06 15:05:07 +00:00
|
|
|
}
|
|
|
|
|
2024-04-13 04:00:40 +00:00
|
|
|
static void vo_x11_set_in_taskbar(struct vo *vo, bool in)
|
|
|
|
{
|
|
|
|
struct vo_x11_state *x11 = vo->x11;
|
|
|
|
if (x11->parent || !x11->window)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (x11->wm_type & (vo_wm_SKIP_TASKBAR)) {
|
|
|
|
char *state = "_NET_WM_STATE_SKIP_TASKBAR";
|
|
|
|
x11_set_ewmh_state(x11, state, !in);
|
|
|
|
MP_VERBOSE(x11, "NET style set skip taskbar (%d).\n", !in);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-08-16 19:43:13 +00:00
|
|
|
static bool rc_overlaps(struct mp_rect rc1, struct mp_rect rc2)
|
|
|
|
{
|
|
|
|
return mp_rect_intersection(&rc1, &rc2); // changes the first argument
|
|
|
|
}
|
|
|
|
|
2014-05-18 23:06:43 +00:00
|
|
|
// update x11->winrc with current boundaries of vo->x11->window
|
x11_common: fix window mapping, refactor window creation/resize handling
vo_opengl creates an invisible window with VOFLAG_HIDDEN in order to
test whether the OpenGL context is useable. The visible window is
created at a later point. This has been broken forever (in vo_gl,
now called vo_opengl_old, it could be avoided by disabling auto-
detection explicitly using the "yuv" sub-option). Avoiding
VOFLAG_HIDDEN only mitigates the issue, and a related bug can still
happen with some window managers (see below).
As a hack, code was added to vo_gl to destroy the (hidden) window so
that all state was destroyed. Later, more hacks were added to deal with
some caveats that came with recreating the window, such as probing for
the context up to 4 times.
Attempt to fix the problem properly. There were two problems: first,
the window was not resized to video size before mapping. This was the
main cause for the placement issue, e.g. mapping the window as 320x200,
and then resizing it. Second, mpv tried to force the window position
with XSetWMNormalHints and PPosition with values it just read with
XGetGeometry. This messes up the window manager's default placement.
It seems to be a race condition, and behavior is different across
various WMs too: with IceWM, the window manager's placement is usually
preferred, and with Fluxbox, mpv's position is preferred. mpv's default
position is centering the window on the screen, which is much nicer for
video in general than typical WM default placement, so it's possible
that this bug was perceived as a feature. (Users who want this have to
use --geometry="50%:50%", doing this by default is probably not safe
with all WMs.)
Since the old code was hard to follow and full of issues, it's easier
to redo it. Move general window creation stuff out of the
vo_x11_config_vo_window function, and move the resize logic into it.
This has been tested on IcwWM, Fluxbox, awesome, Unity/Compiz.
2013-03-02 16:08:37 +00:00
|
|
|
static void vo_x11_update_geometry(struct vo *vo)
|
2008-11-17 18:50:23 +00:00
|
|
|
{
|
|
|
|
struct vo_x11_state *x11 = vo->x11;
|
2014-05-17 01:29:36 +00:00
|
|
|
int x = 0, y = 0;
|
2013-01-26 21:37:47 +00:00
|
|
|
unsigned w, h, dummy_uint;
|
2008-11-15 17:45:55 +00:00
|
|
|
int dummy_int;
|
|
|
|
Window dummy_win;
|
2014-12-09 20:28:07 +00:00
|
|
|
Window win = x11->parent ? x11->parent : x11->window;
|
2015-02-13 21:34:26 +00:00
|
|
|
x11->winrc = (struct mp_rect){0, 0, 0, 0};
|
|
|
|
if (win) {
|
|
|
|
XGetGeometry(x11->display, win, &dummy_win, &dummy_int, &dummy_int,
|
|
|
|
&w, &h, &dummy_int, &dummy_uint);
|
|
|
|
if (w > INT_MAX || h > INT_MAX)
|
|
|
|
w = h = 0;
|
|
|
|
XTranslateCoordinates(x11->display, win, x11->rootwin, 0, 0,
|
|
|
|
&x, &y, &dummy_win);
|
2015-02-14 15:23:01 +00:00
|
|
|
x11->winrc = (struct mp_rect){x, y, x + w, y + h};
|
2015-02-13 21:34:26 +00:00
|
|
|
}
|
2024-05-25 18:49:05 +00:00
|
|
|
struct xrandr_display *disp = get_xrandr_display(vo, x11->winrc);
|
2023-08-13 00:30:11 +00:00
|
|
|
// 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];
|
|
|
|
}
|
2014-08-16 19:43:13 +00:00
|
|
|
}
|
2023-08-13 00:30:11 +00:00
|
|
|
double fps = disp ? disp->fps : 0;
|
2014-08-16 19:43:13 +00:00
|
|
|
if (fps != x11->current_display_fps)
|
|
|
|
MP_VERBOSE(x11, "Current display FPS: %f\n", fps);
|
|
|
|
x11->current_display_fps = fps;
|
2023-08-13 00:30:11 +00:00
|
|
|
if (disp && x11->current_screen != disp->screen) {
|
|
|
|
x11->current_screen = disp->screen;
|
2016-07-18 11:06:38 +00:00
|
|
|
x11->pending_vo_events |= VO_EVENT_ICC_PROFILE_CHANGED;
|
|
|
|
}
|
2023-08-13 00:30:11 +00:00
|
|
|
x11->pending_vo_events |= VO_EVENT_WIN_STATE;
|
2008-11-15 17:45:55 +00:00
|
|
|
}
|
|
|
|
|
2013-05-16 12:02:28 +00:00
|
|
|
static void vo_x11_fullscreen(struct vo *vo)
|
2002-03-07 17:50:25 +00:00
|
|
|
{
|
2008-04-20 06:34:27 +00:00
|
|
|
struct vo_x11_state *x11 = vo->x11;
|
2019-11-27 19:30:13 +00:00
|
|
|
struct mp_vo_opts *opts = x11->opts;
|
2004-06-14 04:53:03 +00:00
|
|
|
|
2013-07-18 12:05:39 +00:00
|
|
|
if (opts->fullscreen == x11->fs)
|
|
|
|
return;
|
2014-05-16 23:14:59 +00:00
|
|
|
x11->fs = opts->fullscreen; // x11->fs now contains the new state
|
2014-12-09 20:28:07 +00:00
|
|
|
if (x11->parent || !x11->window)
|
2009-08-27 12:03:02 +00:00
|
|
|
return;
|
2013-02-17 15:35:44 +00:00
|
|
|
|
2014-05-16 23:14:59 +00:00
|
|
|
// Save old state before entering fullscreen
|
|
|
|
if (x11->fs) {
|
x11_common: fix window mapping, refactor window creation/resize handling
vo_opengl creates an invisible window with VOFLAG_HIDDEN in order to
test whether the OpenGL context is useable. The visible window is
created at a later point. This has been broken forever (in vo_gl,
now called vo_opengl_old, it could be avoided by disabling auto-
detection explicitly using the "yuv" sub-option). Avoiding
VOFLAG_HIDDEN only mitigates the issue, and a related bug can still
happen with some window managers (see below).
As a hack, code was added to vo_gl to destroy the (hidden) window so
that all state was destroyed. Later, more hacks were added to deal with
some caveats that came with recreating the window, such as probing for
the context up to 4 times.
Attempt to fix the problem properly. There were two problems: first,
the window was not resized to video size before mapping. This was the
main cause for the placement issue, e.g. mapping the window as 320x200,
and then resizing it. Second, mpv tried to force the window position
with XSetWMNormalHints and PPosition with values it just read with
XGetGeometry. This messes up the window manager's default placement.
It seems to be a race condition, and behavior is different across
various WMs too: with IceWM, the window manager's placement is usually
preferred, and with Fluxbox, mpv's position is preferred. mpv's default
position is centering the window on the screen, which is much nicer for
video in general than typical WM default placement, so it's possible
that this bug was perceived as a feature. (Users who want this have to
use --geometry="50%:50%", doing this by default is probably not safe
with all WMs.)
Since the old code was hard to follow and full of issues, it's easier
to redo it. Move general window creation stuff out of the
vo_x11_config_vo_window function, and move the resize logic into it.
This has been tested on IcwWM, Fluxbox, awesome, Unity/Compiz.
2013-03-02 16:08:37 +00:00
|
|
|
vo_x11_update_geometry(vo);
|
2014-05-17 01:29:36 +00:00
|
|
|
x11->nofsrc = x11->winrc;
|
2014-05-16 23:14:59 +00:00
|
|
|
}
|
2013-02-17 15:35:44 +00:00
|
|
|
|
2016-08-25 12:16:10 +00:00
|
|
|
struct mp_rect rc = x11->nofsrc;
|
|
|
|
|
2014-05-16 23:14:59 +00:00
|
|
|
if (x11->wm_type & vo_wm_FULLSCREEN) {
|
2014-05-23 18:05:24 +00:00
|
|
|
x11_set_ewmh_state(x11, "_NET_WM_STATE_FULLSCREEN", x11->fs);
|
2014-05-26 19:54:00 +00:00
|
|
|
if (!x11->fs && (x11->pos_changed_during_fs ||
|
|
|
|
x11->size_changed_during_fs))
|
2014-05-23 18:05:24 +00:00
|
|
|
{
|
2016-08-25 12:16:10 +00:00
|
|
|
if (x11->screenrc.x0 == rc.x0 && x11->screenrc.x1 == rc.x1 &&
|
|
|
|
x11->screenrc.y0 == rc.y0 && x11->screenrc.y1 == rc.y1)
|
|
|
|
{
|
|
|
|
// Workaround for some WMs switching back to FS in this case.
|
|
|
|
MP_VERBOSE(x11, "avoiding triggering old-style fullscreen\n");
|
|
|
|
rc.x1 -= 1;
|
|
|
|
rc.y1 -= 1;
|
|
|
|
}
|
2024-05-25 18:49:05 +00:00
|
|
|
|
|
|
|
// If launched with --fs and the fs screen is different than
|
|
|
|
// nofsrc, try to translate nofsrc to the fs screen.
|
|
|
|
if (x11->init_fs) {
|
|
|
|
struct xrandr_display *fs_disp = get_xrandr_display(vo, x11->winrc);
|
|
|
|
struct xrandr_display *nofs_disp = get_xrandr_display(vo, x11->nofsrc);
|
|
|
|
if (fs_disp && nofs_disp && fs_disp->screen != nofs_disp->screen) {
|
|
|
|
int old_w = mp_rect_w(x11->nofsrc);
|
|
|
|
int old_h = mp_rect_h(x11->nofsrc);
|
|
|
|
int new_x = (mp_rect_w(fs_disp->rc) - old_w) / 2 + fs_disp->rc.x0;
|
|
|
|
int new_y = (mp_rect_h(fs_disp->rc) - old_h) / 2 + fs_disp->rc.y0;
|
|
|
|
x11->nofsrc.x0 = new_x;
|
|
|
|
x11->nofsrc.x1 = new_x + old_w;
|
|
|
|
x11->nofsrc.y0 = new_y;
|
|
|
|
x11->nofsrc.y1 = new_y + old_h;
|
|
|
|
rc = x11->nofsrc;
|
|
|
|
}
|
|
|
|
x11->init_fs = false;
|
|
|
|
}
|
|
|
|
|
2014-05-23 18:05:24 +00:00
|
|
|
vo_x11_move_resize(vo, x11->pos_changed_during_fs,
|
2016-08-25 12:16:10 +00:00
|
|
|
x11->size_changed_during_fs, rc);
|
2014-05-16 23:14:59 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (x11->fs) {
|
|
|
|
vo_x11_update_screeninfo(vo);
|
2014-05-17 01:29:36 +00:00
|
|
|
rc = x11->screenrc;
|
2013-02-17 15:35:44 +00:00
|
|
|
}
|
|
|
|
|
2024-02-01 10:32:08 +00:00
|
|
|
vo_x11_decoration(vo, opts->border && !x11->fs, opts->title_bar);
|
2014-05-17 01:29:36 +00:00
|
|
|
vo_x11_sizehint(vo, rc, true);
|
2004-06-14 04:53:03 +00:00
|
|
|
|
2014-05-17 01:29:36 +00:00
|
|
|
XMoveResizeWindow(x11->display, x11->window, rc.x0, rc.y0,
|
|
|
|
RC_W(rc), RC_H(rc));
|
2004-12-27 11:09:49 +00:00
|
|
|
|
2014-05-23 18:27:14 +00:00
|
|
|
vo_x11_setlayer(vo, x11->fs || opts->ontop);
|
2014-05-16 23:14:59 +00:00
|
|
|
|
|
|
|
XRaiseWindow(x11->display, x11->window);
|
|
|
|
XFlush(x11->display);
|
|
|
|
}
|
x11_common: fix window mapping, refactor window creation/resize handling
vo_opengl creates an invisible window with VOFLAG_HIDDEN in order to
test whether the OpenGL context is useable. The visible window is
created at a later point. This has been broken forever (in vo_gl,
now called vo_opengl_old, it could be avoided by disabling auto-
detection explicitly using the "yuv" sub-option). Avoiding
VOFLAG_HIDDEN only mitigates the issue, and a related bug can still
happen with some window managers (see below).
As a hack, code was added to vo_gl to destroy the (hidden) window so
that all state was destroyed. Later, more hacks were added to deal with
some caveats that came with recreating the window, such as probing for
the context up to 4 times.
Attempt to fix the problem properly. There were two problems: first,
the window was not resized to video size before mapping. This was the
main cause for the placement issue, e.g. mapping the window as 320x200,
and then resizing it. Second, mpv tried to force the window position
with XSetWMNormalHints and PPosition with values it just read with
XGetGeometry. This messes up the window manager's default placement.
It seems to be a race condition, and behavior is different across
various WMs too: with IceWM, the window manager's placement is usually
preferred, and with Fluxbox, mpv's position is preferred. mpv's default
position is centering the window on the screen, which is much nicer for
video in general than typical WM default placement, so it's possible
that this bug was perceived as a feature. (Users who want this have to
use --geometry="50%:50%", doing this by default is probably not safe
with all WMs.)
Since the old code was hard to follow and full of issues, it's easier
to redo it. Move general window creation stuff out of the
vo_x11_config_vo_window function, and move the resize logic into it.
This has been tested on IcwWM, Fluxbox, awesome, Unity/Compiz.
2013-03-02 16:08:37 +00:00
|
|
|
|
|
|
|
x11->size_changed_during_fs = false;
|
|
|
|
x11->pos_changed_during_fs = false;
|
2016-05-02 19:21:53 +00:00
|
|
|
|
|
|
|
vo_x11_update_composition_hint(vo);
|
2002-03-07 17:50:25 +00:00
|
|
|
}
|
|
|
|
|
2019-11-29 08:51:53 +00:00
|
|
|
static void vo_x11_maximize(struct vo *vo)
|
|
|
|
{
|
|
|
|
struct vo_x11_state *x11 = vo->x11;
|
|
|
|
|
|
|
|
long params[5] = {
|
2019-11-29 12:52:37 +00:00
|
|
|
x11->opts->window_maximized ? NET_WM_STATE_ADD : NET_WM_STATE_REMOVE,
|
2019-11-29 08:51:53 +00:00
|
|
|
XA(x11, _NET_WM_STATE_MAXIMIZED_VERT),
|
|
|
|
XA(x11, _NET_WM_STATE_MAXIMIZED_HORZ),
|
|
|
|
1, // source indication: normal
|
|
|
|
};
|
|
|
|
x11_send_ewmh_msg(x11, "_NET_WM_STATE", params);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void vo_x11_minimize(struct vo *vo)
|
|
|
|
{
|
|
|
|
struct vo_x11_state *x11 = vo->x11;
|
|
|
|
|
2019-11-29 13:27:27 +00:00
|
|
|
if (x11->opts->window_minimized) {
|
2019-11-29 12:52:37 +00:00
|
|
|
XIconifyWindow(x11->display, x11->window, x11->screen);
|
2019-11-29 13:27:27 +00:00
|
|
|
} else {
|
|
|
|
long params[5] = {0};
|
|
|
|
x11_send_ewmh_msg(x11, "_NET_ACTIVE_WINDOW", params);
|
|
|
|
}
|
2019-11-29 08:51:53 +00:00
|
|
|
}
|
|
|
|
|
2020-12-13 23:44:36 +00:00
|
|
|
static void vo_x11_set_geometry(struct vo *vo)
|
|
|
|
{
|
|
|
|
struct vo_x11_state *x11 = vo->x11;
|
|
|
|
|
2021-11-02 23:28:53 +00:00
|
|
|
if (!x11->window)
|
|
|
|
return;
|
|
|
|
|
2021-10-20 02:47:23 +00:00
|
|
|
x11->geometry_change = true;
|
|
|
|
vo_x11_config_vo_window(vo);
|
2020-12-13 23:44:36 +00:00
|
|
|
}
|
|
|
|
|
2022-05-12 21:26:49 +00:00
|
|
|
bool vo_x11_check_visible(struct vo *vo)
|
|
|
|
{
|
x11: avoid wasteful rendering when possible
Because wayland is a special snowflake, mpv wound up incorporating a lot
of logic into its render loop where visibilty checks are performed
before rendering anything (in the name of efficiency of course). Only
wayland actually uses this, but there's no reason why other backends
(x11 in this commit) can't be smarter. It's far easier on xorg since we
can just query _NET_WM_STATE_HIDDEN directly and not have to do silly
callback dances.
The function, vo_x11_check_net_wm_state_change, already tracks net wm
changes, including _NET_WM_STATE_HIDDEN. There is an already existing
window_hidden variable but that is actually just for checking if the
window was mapped and has nothing to do with this particular atom. mpv
also currently assumes that a _NET_WM_STATE_HIDDEN is exactly the same
as being minimized but according to the spec, that's not neccesarily
true (in practice, it's likely that these are the same though). Anyways,
just keep track of this state in a new variable (hidden) and use that
for determing if mpv should render or not.
There is one catch though: this cannot work if a display sync mode is
used. This is why the previous commit is needed. The display sync modes
in mpv require a blocking vsync implementation since its render loop is
directly driven by vsync. In xorg, if nothing is actually rendered, then
there's nothing for eglSwapBuffers (or FIFO for vulkan) to block on so
it returns immediately. This, of course, results in completely broken
video. We just need to check to make sure that we aren't in a display
sync mode before trying to be smart about rendering. Display sync is
power inefficient anyways, so no one is really being hurt here. As an
aside, this happens to work in wayland because there's basically a
custom (and ugly) vsync blocking function + timeout but that's off
topic.
2022-04-07 03:36:30 +00:00
|
|
|
struct vo_x11_state *x11 = vo->x11;
|
|
|
|
struct mp_vo_opts *opts = x11->opts;
|
|
|
|
|
2022-11-11 19:57:53 +00:00
|
|
|
bool render = !x11->hidden || opts->force_render ||
|
|
|
|
VS_IS_DISP(opts->video_sync);
|
x11: avoid wasteful rendering when possible
Because wayland is a special snowflake, mpv wound up incorporating a lot
of logic into its render loop where visibilty checks are performed
before rendering anything (in the name of efficiency of course). Only
wayland actually uses this, but there's no reason why other backends
(x11 in this commit) can't be smarter. It's far easier on xorg since we
can just query _NET_WM_STATE_HIDDEN directly and not have to do silly
callback dances.
The function, vo_x11_check_net_wm_state_change, already tracks net wm
changes, including _NET_WM_STATE_HIDDEN. There is an already existing
window_hidden variable but that is actually just for checking if the
window was mapped and has nothing to do with this particular atom. mpv
also currently assumes that a _NET_WM_STATE_HIDDEN is exactly the same
as being minimized but according to the spec, that's not neccesarily
true (in practice, it's likely that these are the same though). Anyways,
just keep track of this state in a new variable (hidden) and use that
for determing if mpv should render or not.
There is one catch though: this cannot work if a display sync mode is
used. This is why the previous commit is needed. The display sync modes
in mpv require a blocking vsync implementation since its render loop is
directly driven by vsync. In xorg, if nothing is actually rendered, then
there's nothing for eglSwapBuffers (or FIFO for vulkan) to block on so
it returns immediately. This, of course, results in completely broken
video. We just need to check to make sure that we aren't in a display
sync mode before trying to be smart about rendering. Display sync is
power inefficient anyways, so no one is really being hurt here. As an
aside, this happens to work in wayland because there's basically a
custom (and ugly) vsync blocking function + timeout but that's off
topic.
2022-04-07 03:36:30 +00:00
|
|
|
return render;
|
|
|
|
}
|
|
|
|
|
2022-05-12 21:26:49 +00:00
|
|
|
static void vo_x11_set_input_region(struct vo *vo, bool passthrough)
|
|
|
|
{
|
|
|
|
struct vo_x11_state *x11 = vo->x11;
|
|
|
|
|
|
|
|
if (passthrough) {
|
|
|
|
XRectangle rect = {0, 0, 0, 0};
|
|
|
|
Region region = XCreateRegion();
|
|
|
|
XUnionRectWithRegion(&rect, region, region);
|
|
|
|
XShapeCombineRegion(x11->display, x11->window, ShapeInput, 0, 0,
|
|
|
|
region, ShapeSet);
|
|
|
|
XDestroyRegion(region);
|
|
|
|
} else {
|
|
|
|
XShapeCombineMask(x11->display, x11->window, ShapeInput, 0, 0,
|
|
|
|
0, ShapeSet);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-05-15 16:17:18 +00:00
|
|
|
int vo_x11_control(struct vo *vo, int *events, int request, void *arg)
|
|
|
|
{
|
2013-06-13 22:24:41 +00:00
|
|
|
struct vo_x11_state *x11 = vo->x11;
|
2019-11-27 19:30:13 +00:00
|
|
|
struct mp_vo_opts *opts = x11->opts;
|
2013-05-15 16:17:18 +00:00
|
|
|
switch (request) {
|
|
|
|
case VOCTRL_CHECK_EVENTS:
|
2016-07-25 09:28:06 +00:00
|
|
|
vo_x11_check_events(vo);
|
|
|
|
*events |= x11->pending_vo_events;
|
|
|
|
x11->pending_vo_events = 0;
|
2013-05-15 16:17:18 +00:00
|
|
|
return VO_TRUE;
|
2019-11-29 11:50:50 +00:00
|
|
|
case VOCTRL_VO_OPTS_CHANGED: {
|
|
|
|
void *opt;
|
|
|
|
while (m_config_cache_get_next_changed(x11->opts_cache, &opt)) {
|
|
|
|
if (opt == &opts->fullscreen)
|
|
|
|
vo_x11_fullscreen(vo);
|
2019-11-29 12:03:48 +00:00
|
|
|
if (opt == &opts->ontop)
|
|
|
|
vo_x11_setlayer(vo, opts->ontop);
|
2024-04-13 04:00:40 +00:00
|
|
|
if (opt == &opts->show_in_taskbar)
|
|
|
|
vo_x11_set_in_taskbar(vo, opts->show_in_taskbar);
|
2024-02-01 10:32:08 +00:00
|
|
|
if (opt == &opts->border || opt == &opts->title_bar)
|
|
|
|
vo_x11_decoration(vo, opts->border, opts->title_bar);
|
2022-10-09 23:20:32 +00:00
|
|
|
if (opt == &opts->all_workspaces)
|
|
|
|
vo_x11_sticky(vo, opts->all_workspaces);
|
2019-11-29 12:52:37 +00:00
|
|
|
if (opt == &opts->window_minimized)
|
|
|
|
vo_x11_minimize(vo);
|
|
|
|
if (opt == &opts->window_maximized)
|
|
|
|
vo_x11_maximize(vo);
|
2022-05-12 21:26:49 +00:00
|
|
|
if (opt == &opts->cursor_passthrough)
|
|
|
|
vo_x11_set_input_region(vo, opts->cursor_passthrough);
|
2022-06-22 04:13:44 +00:00
|
|
|
if (opt == &opts->x11_present)
|
|
|
|
xpresent_set(x11);
|
2024-07-07 21:04:09 +00:00
|
|
|
if (opt == &opts->keepaspect || opt == &opts->keepaspect_window)
|
|
|
|
vo_x11_sizehint(vo, x11->fs ? x11->nofsrc : x11->winrc, false);
|
2020-12-13 23:44:36 +00:00
|
|
|
if (opt == &opts->geometry || opt == &opts->autofit ||
|
|
|
|
opt == &opts->autofit_smaller || opt == &opts->autofit_larger)
|
|
|
|
{
|
2024-03-04 09:35:45 +00:00
|
|
|
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);
|
|
|
|
}
|
2020-12-13 23:44:36 +00:00
|
|
|
vo_x11_set_geometry(vo);
|
|
|
|
}
|
2019-11-29 11:50:50 +00:00
|
|
|
}
|
2016-09-23 10:09:48 +00:00
|
|
|
return VO_TRUE;
|
2019-11-29 11:50:50 +00:00
|
|
|
}
|
2014-09-04 20:53:50 +00:00
|
|
|
case VOCTRL_GET_UNFS_WINDOW_SIZE: {
|
2013-10-31 19:19:40 +00:00
|
|
|
int *s = arg;
|
2017-08-21 13:14:55 +00:00
|
|
|
if (!x11->window || x11->parent)
|
2013-10-31 19:19:40 +00:00
|
|
|
return VO_FALSE;
|
2024-02-13 23:20:08 +00:00
|
|
|
s[0] = x11->fs ? RC_W(x11->nofsrc) : RC_W(x11->winrc);
|
|
|
|
s[1] = x11->fs ? RC_H(x11->nofsrc) : RC_H(x11->winrc);
|
2013-10-31 19:19:40 +00:00
|
|
|
return VO_TRUE;
|
|
|
|
}
|
2014-09-04 20:53:50 +00:00
|
|
|
case VOCTRL_SET_UNFS_WINDOW_SIZE: {
|
2013-10-31 19:19:40 +00:00
|
|
|
int *s = arg;
|
2017-08-21 13:14:55 +00:00
|
|
|
if (!x11->window || x11->parent)
|
2014-05-18 23:06:43 +00:00
|
|
|
return VO_FALSE;
|
2024-02-13 23:20:08 +00:00
|
|
|
int w = s[0];
|
|
|
|
int h = s[1];
|
2014-05-17 01:29:36 +00:00
|
|
|
struct mp_rect rc = x11->winrc;
|
2019-12-16 01:22:51 +00:00
|
|
|
rc.x1 = rc.x0 + w;
|
|
|
|
rc.y1 = rc.y0 + h;
|
2021-08-08 01:10:56 +00:00
|
|
|
if (x11->opts->window_maximized) {
|
|
|
|
x11->opts->window_maximized = false;
|
|
|
|
m_config_cache_write_opt(x11->opts_cache,
|
|
|
|
&x11->opts->window_maximized);
|
|
|
|
vo_x11_maximize(vo);
|
|
|
|
}
|
2024-03-03 18:27:02 +00:00
|
|
|
vo_x11_highlevel_resize(vo, rc, false);
|
2014-10-13 07:57:31 +00:00
|
|
|
if (!x11->fs) { // guess new window size, instead of waiting for X
|
2019-12-16 01:22:51 +00:00
|
|
|
x11->winrc.x1 = x11->winrc.x0 + w;
|
|
|
|
x11->winrc.y1 = x11->winrc.y0 + h;
|
2014-10-13 07:57:31 +00:00
|
|
|
}
|
2013-10-31 19:19:40 +00:00
|
|
|
return VO_TRUE;
|
|
|
|
}
|
2020-09-07 16:22:25 +00:00
|
|
|
case VOCTRL_GET_FOCUSED: {
|
|
|
|
*(bool *)arg = x11->has_focus;
|
|
|
|
return VO_TRUE;
|
|
|
|
}
|
2014-11-05 23:58:24 +00:00
|
|
|
case VOCTRL_GET_DISPLAY_NAMES: {
|
2015-01-04 02:57:50 +00:00
|
|
|
if (!x11->pseudo_mapped)
|
|
|
|
return VO_FALSE;
|
2014-11-05 23:58:24 +00:00
|
|
|
char **names = NULL;
|
|
|
|
int displays_spanned = 0;
|
|
|
|
for (int n = 0; n < x11->num_displays; n++) {
|
|
|
|
if (rc_overlaps(x11->displays[n].rc, x11->winrc))
|
|
|
|
MP_TARRAY_APPEND(NULL, names, displays_spanned,
|
|
|
|
talloc_strdup(NULL, x11->displays[n].name));
|
|
|
|
}
|
|
|
|
MP_TARRAY_APPEND(NULL, names, displays_spanned, NULL);
|
|
|
|
*(char ***)arg = names;
|
|
|
|
return VO_TRUE;
|
|
|
|
}
|
2015-01-26 01:18:47 +00:00
|
|
|
case VOCTRL_GET_ICC_PROFILE: {
|
2015-03-07 11:32:19 +00:00
|
|
|
if (!x11->pseudo_mapped)
|
|
|
|
return VO_NOTAVAIL;
|
2023-08-13 00:30:11 +00:00
|
|
|
int atom_id = x11->displays[x11->current_screen].atom_id;
|
2015-01-26 01:18:47 +00:00
|
|
|
char prop[80];
|
|
|
|
snprintf(prop, sizeof(prop), "_ICC_PROFILE");
|
2018-11-01 01:00:03 +00:00
|
|
|
if (atom_id > 0)
|
|
|
|
mp_snprintf_cat(prop, sizeof(prop), "_%d", atom_id);
|
2015-01-26 01:18:47 +00:00
|
|
|
x11->icc_profile_property = XAs(x11, prop);
|
|
|
|
int len;
|
2023-08-13 00:30:11 +00:00
|
|
|
MP_VERBOSE(x11, "Retrieving ICC profile for display: %d\n", x11->current_screen);
|
2015-01-26 01:18:47 +00:00
|
|
|
void *icc = x11_get_property(x11, x11->rootwin, x11->icc_profile_property,
|
|
|
|
XA_CARDINAL, 8, &len);
|
|
|
|
if (!icc)
|
|
|
|
return VO_FALSE;
|
|
|
|
*(bstr *)arg = bstrdup(NULL, (bstr){icc, len});
|
|
|
|
XFree(icc);
|
|
|
|
// Watch x11->icc_profile_property
|
|
|
|
XSelectInput(x11->display, x11->rootwin, PropertyChangeMask);
|
|
|
|
return VO_TRUE;
|
|
|
|
}
|
2013-05-16 21:24:56 +00:00
|
|
|
case VOCTRL_SET_CURSOR_VISIBILITY:
|
2018-10-11 04:38:57 +00:00
|
|
|
x11->mouse_cursor_visible = *(bool *)arg;
|
|
|
|
vo_update_cursor(vo);
|
2013-05-16 21:24:56 +00:00
|
|
|
return VO_TRUE;
|
2013-06-13 22:24:41 +00:00
|
|
|
case VOCTRL_KILL_SCREENSAVER:
|
2014-05-17 19:32:11 +00:00
|
|
|
set_screensaver(x11, false);
|
2013-06-13 22:24:41 +00:00
|
|
|
return VO_TRUE;
|
|
|
|
case VOCTRL_RESTORE_SCREENSAVER:
|
2014-05-17 19:32:11 +00:00
|
|
|
set_screensaver(x11, true);
|
2013-06-13 22:24:41 +00:00
|
|
|
return VO_TRUE;
|
2013-06-15 17:04:20 +00:00
|
|
|
case VOCTRL_UPDATE_WINDOW_TITLE:
|
2024-09-05 20:14:28 +00:00
|
|
|
talloc_replace(x11, x11->window_title, (char *)arg);
|
2023-08-10 02:11:53 +00:00
|
|
|
if (!x11->parent || x11->opts->x11_wid_title)
|
2014-10-09 19:24:47 +00:00
|
|
|
vo_x11_update_window_title(vo);
|
2013-06-15 17:04:20 +00:00
|
|
|
return VO_TRUE;
|
video: add VO framedropping mode
This mostly uses the same idea as with vo_vdpau.c, but much simplified.
On X11, it tries to get the display framerate with XF86VM, and limits
the frequency of new video frames against it. Note that this is an old
extension, and is confirmed not to work correctly with multi-monitor
setups. But we're using it because it was already around (it is also
used by vo_vdpau).
This attempts to predict the next vsync event by using the time of the
last frame and the display FPS. Even if that goes completely wrong,
the results are still relatively good.
On other systems, or if the X11 code doesn't return a display FPS, a
framerate of 1000 is assumed. This is infinite for all practical
purposes, and means that only frames which are definitely too late are
dropped. This probably has worse results, but is still useful.
"--framedrop=yes" is basically replaced with "--framedrop=decoder". The
old framedropping mode is kept around, and should perhaps be improved.
Dropping on the decoder level is still useful if decoding itself is too
slow.
2014-08-15 21:33:33 +00:00
|
|
|
case VOCTRL_GET_DISPLAY_FPS: {
|
2014-09-14 13:59:10 +00:00
|
|
|
double fps = x11->current_display_fps;
|
video: add VO framedropping mode
This mostly uses the same idea as with vo_vdpau.c, but much simplified.
On X11, it tries to get the display framerate with XF86VM, and limits
the frequency of new video frames against it. Note that this is an old
extension, and is confirmed not to work correctly with multi-monitor
setups. But we're using it because it was already around (it is also
used by vo_vdpau).
This attempts to predict the next vsync event by using the time of the
last frame and the display FPS. Even if that goes completely wrong,
the results are still relatively good.
On other systems, or if the X11 code doesn't return a display FPS, a
framerate of 1000 is assumed. This is infinite for all practical
purposes, and means that only frames which are definitely too late are
dropped. This probably has worse results, but is still useful.
"--framedrop=yes" is basically replaced with "--framedrop=decoder". The
old framedropping mode is kept around, and should perhaps be improved.
Dropping on the decoder level is still useful if decoding itself is too
slow.
2014-08-15 21:33:33 +00:00
|
|
|
if (fps <= 0)
|
|
|
|
break;
|
|
|
|
*(double *)arg = fps;
|
|
|
|
return VO_TRUE;
|
|
|
|
}
|
2021-04-12 17:51:41 +00:00
|
|
|
case VOCTRL_GET_DISPLAY_RES: {
|
2023-08-13 00:30:11 +00:00
|
|
|
struct xrandr_display *disp = NULL;
|
|
|
|
if (x11->current_screen > -1)
|
|
|
|
disp = &x11->displays[x11->current_screen];
|
|
|
|
if (!x11->window || x11->parent || !disp)
|
2021-04-12 17:51:41 +00:00
|
|
|
return VO_NOTAVAIL;
|
2023-08-13 00:30:11 +00:00
|
|
|
((int *)arg)[0] = mp_rect_w(disp->rc);
|
|
|
|
((int *)arg)[1] = mp_rect_h(disp->rc);
|
2021-04-12 17:51:41 +00:00
|
|
|
return VO_TRUE;
|
|
|
|
}
|
2022-11-22 15:42:30 +00:00
|
|
|
case VOCTRL_GET_WINDOW_ID: {
|
|
|
|
if (!x11->window)
|
|
|
|
return VO_NOTAVAIL;
|
|
|
|
*(int64_t *)arg = x11->window;
|
|
|
|
return VO_TRUE;
|
|
|
|
}
|
2019-12-20 11:48:48 +00:00
|
|
|
case VOCTRL_GET_HIDPI_SCALE:
|
|
|
|
*(double *)arg = x11->dpi_scale;
|
|
|
|
return VO_TRUE;
|
2024-02-26 23:37:12 +00:00
|
|
|
case VOCTRL_BEGIN_DRAGGING:
|
|
|
|
vo_x11_begin_dragging(vo);
|
|
|
|
return VO_TRUE;
|
2013-05-15 16:17:18 +00:00
|
|
|
}
|
|
|
|
return VO_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
x11: support xorg present extension
This builds off of present_sync which was introduced in a previous
commit to support xorg's present extension in all of the X11 backends
(sans vdpau) in mpv. It turns out there is an Xpresent library that
integrates the xorg present extention with Xlib (which barely anyone
seems to use), so this can be added without too much trouble. The
workflow is to first setup the event by telling Xorg we would like to
receive PresentCompleteNotify (there are others in the extension but
this is the only one we really care about). After that, just call
XPresentNotifyMSC after every buffer swap with a target_msc of 0. Xorg
then returns the last presentation through its usual event loop and we
go ahead and use that information to update mpv's values for vsync
timing purposes. One theoretical weakness of this approach is that the
present event is put on the same queue as the rest of the XEvents. It
would be nicer for it be placed somewhere else so we could just wait
on that queue without having to deal with other possible events in
there. In theory, xcb could do that with special events, but it doesn't
really matter in practice.
Unsurprisingly, this doesn't work on NVIDIA. Well NVIDIA does actually
receive presentation events, but for whatever the calculations used make
timings worse which defeats the purpose. This works perfectly fine on
Mesa however. Utilizing the previous commit that detects Xrandr
providers, we can enable this mechanism for users that have both Mesa
and not NVIDIA (to avoid messing up anyone that has a switchable
graphics system or such). Patches welcome if anyone figures out how to
fix this on NVIDIA.
Unlike the EGL/GLX sync extensions, the present extension works with any
graphics API (good for vulkan since its timing extension has been in
development hell). NVIDIA also happens to have zero support for the
EGL/GLX sync extensions, so we can just remove it with no loss. Only
Xorg ever used it and other backends already have their own present
methods. vo_vdpau VO is a special case that has its own fancying timing
code in its flip_page. This presumably works well, and I have no way of
testing it so just leave it as it is.
2022-06-10 16:49:38 +00:00
|
|
|
void vo_x11_present(struct vo *vo)
|
|
|
|
{
|
|
|
|
struct vo_x11_state *x11 = vo->x11;
|
|
|
|
XPresentNotifyMSC(x11->display, x11->window,
|
|
|
|
0, 0, 1, 0);
|
|
|
|
}
|
|
|
|
|
2016-07-20 18:52:08 +00:00
|
|
|
void vo_x11_wakeup(struct vo *vo)
|
|
|
|
{
|
|
|
|
struct vo_x11_state *x11 = vo->x11;
|
|
|
|
|
|
|
|
(void)write(x11->wakeup_pipe[1], &(char){0}, 1);
|
|
|
|
}
|
|
|
|
|
2023-09-18 02:34:32 +00:00
|
|
|
void vo_x11_wait_events(struct vo *vo, int64_t until_time_ns)
|
2016-07-20 18:52:08 +00:00
|
|
|
{
|
|
|
|
struct vo_x11_state *x11 = vo->x11;
|
|
|
|
|
|
|
|
struct pollfd fds[2] = {
|
|
|
|
{ .fd = x11->event_fd, .events = POLLIN },
|
|
|
|
{ .fd = x11->wakeup_pipe[0], .events = POLLIN },
|
|
|
|
};
|
2023-09-18 02:34:32 +00:00
|
|
|
int64_t wait_ns = until_time_ns - mp_time_ns();
|
2023-11-09 04:49:17 +00:00
|
|
|
int64_t timeout_ns = MPCLAMP(wait_ns, 0, MP_TIME_S_TO_NS(10));
|
2016-07-20 18:52:08 +00:00
|
|
|
|
2023-09-18 03:27:12 +00:00
|
|
|
mp_poll(fds, 2, timeout_ns);
|
2016-07-20 18:52:08 +00:00
|
|
|
|
2016-07-29 01:24:52 +00:00
|
|
|
if (fds[1].revents & POLLIN)
|
|
|
|
mp_flush_wakeup_pipe(x11->wakeup_pipe[0]);
|
2016-07-20 18:52:08 +00:00
|
|
|
}
|
|
|
|
|
2017-08-20 07:11:07 +00:00
|
|
|
static void xscreensaver_heartbeat(struct vo_x11_state *x11)
|
|
|
|
{
|
|
|
|
double time = mp_time_sec();
|
|
|
|
|
|
|
|
if (x11->display && !x11->screensaver_enabled &&
|
|
|
|
(time - x11->screensaver_time_last) >= 10)
|
|
|
|
{
|
|
|
|
x11->screensaver_time_last = time;
|
2017-08-20 07:11:26 +00:00
|
|
|
XResetScreenSaver(x11->display);
|
2017-08-20 07:11:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-08-20 07:11:26 +00:00
|
|
|
static int xss_suspend(Display *mDisplay, Bool suspend)
|
|
|
|
{
|
|
|
|
int event, error, major, minor;
|
|
|
|
if (XScreenSaverQueryExtension(mDisplay, &event, &error) != True ||
|
|
|
|
XScreenSaverQueryVersion(mDisplay, &major, &minor) != True)
|
|
|
|
return 0;
|
|
|
|
if (major < 1 || (major == 1 && minor < 1))
|
|
|
|
return 0;
|
|
|
|
XScreenSaverSuspend(mDisplay, suspend);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2014-05-17 19:32:11 +00:00
|
|
|
static void set_screensaver(struct vo_x11_state *x11, bool enabled)
|
2004-06-14 04:53:03 +00:00
|
|
|
{
|
2013-01-26 21:37:47 +00:00
|
|
|
Display *mDisplay = x11->display;
|
2015-04-15 20:39:01 +00:00
|
|
|
if (!mDisplay || x11->screensaver_enabled == enabled)
|
2008-01-07 12:18:17 +00:00
|
|
|
return;
|
2014-05-17 19:32:11 +00:00
|
|
|
MP_VERBOSE(x11, "%s screensaver.\n", enabled ? "Enabling" : "Disabling");
|
|
|
|
x11->screensaver_enabled = enabled;
|
2017-08-20 07:11:26 +00:00
|
|
|
if (xss_suspend(mDisplay, !enabled))
|
|
|
|
return;
|
2001-07-19 18:47:22 +00:00
|
|
|
int nothing;
|
2013-02-13 15:33:01 +00:00
|
|
|
if (DPMSQueryExtension(mDisplay, ¬hing, ¬hing)) {
|
2014-05-17 19:32:11 +00:00
|
|
|
BOOL onoff = 0;
|
2004-06-14 04:53:03 +00:00
|
|
|
CARD16 state;
|
|
|
|
DPMSInfo(mDisplay, &state, &onoff);
|
2014-05-18 20:16:53 +00:00
|
|
|
if (!x11->dpms_touched && enabled)
|
|
|
|
return; // enable DPMS only we we disabled it before
|
2014-05-17 19:32:11 +00:00
|
|
|
if (enabled != !!onoff) {
|
|
|
|
MP_VERBOSE(x11, "Setting DMPS: %s.\n", enabled ? "on" : "off");
|
|
|
|
if (enabled) {
|
|
|
|
DPMSEnable(mDisplay);
|
|
|
|
} else {
|
|
|
|
DPMSDisable(mDisplay);
|
2014-05-18 20:16:53 +00:00
|
|
|
x11->dpms_touched = true;
|
2014-05-17 19:32:11 +00:00
|
|
|
}
|
|
|
|
DPMSInfo(mDisplay, &state, &onoff);
|
|
|
|
if (enabled != !!onoff)
|
|
|
|
MP_WARN(x11, "DPMS state could not be set.\n");
|
2004-06-14 04:53:03 +00:00
|
|
|
}
|
2001-04-10 00:00:04 +00:00
|
|
|
}
|
2001-04-10 02:29:38 +00:00
|
|
|
}
|
2001-04-15 20:31:58 +00:00
|
|
|
|
2013-03-04 16:40:21 +00:00
|
|
|
static void vo_x11_selectinput_witherr(struct vo *vo,
|
|
|
|
Display *display,
|
|
|
|
Window w,
|
2013-01-26 21:37:47 +00:00
|
|
|
long event_mask)
|
2002-08-09 17:43:49 +00:00
|
|
|
{
|
x11_common: fix window mapping, refactor window creation/resize handling
vo_opengl creates an invisible window with VOFLAG_HIDDEN in order to
test whether the OpenGL context is useable. The visible window is
created at a later point. This has been broken forever (in vo_gl,
now called vo_opengl_old, it could be avoided by disabling auto-
detection explicitly using the "yuv" sub-option). Avoiding
VOFLAG_HIDDEN only mitigates the issue, and a related bug can still
happen with some window managers (see below).
As a hack, code was added to vo_gl to destroy the (hidden) window so
that all state was destroyed. Later, more hacks were added to deal with
some caveats that came with recreating the window, such as probing for
the context up to 4 times.
Attempt to fix the problem properly. There were two problems: first,
the window was not resized to video size before mapping. This was the
main cause for the placement issue, e.g. mapping the window as 320x200,
and then resizing it. Second, mpv tried to force the window position
with XSetWMNormalHints and PPosition with values it just read with
XGetGeometry. This messes up the window manager's default placement.
It seems to be a race condition, and behavior is different across
various WMs too: with IceWM, the window manager's placement is usually
preferred, and with Fluxbox, mpv's position is preferred. mpv's default
position is centering the window on the screen, which is much nicer for
video in general than typical WM default placement, so it's possible
that this bug was perceived as a feature. (Users who want this have to
use --geometry="50%:50%", doing this by default is probably not safe
with all WMs.)
Since the old code was hard to follow and full of issues, it's easier
to redo it. Move general window creation stuff out of the
vo_x11_config_vo_window function, and move the resize logic into it.
This has been tested on IcwWM, Fluxbox, awesome, Unity/Compiz.
2013-03-02 16:08:37 +00:00
|
|
|
XSelectInput(display, w, NoEventMask);
|
|
|
|
|
2013-01-27 05:33:58 +00:00
|
|
|
// NOTE: this can raise BadAccess, which should be ignored by the X error
|
|
|
|
// handler; also see below
|
|
|
|
XSelectInput(display, w, event_mask);
|
|
|
|
|
|
|
|
// Test whether setting the event mask failed (with a BadAccess X error,
|
|
|
|
// although we don't know whether this really happened).
|
|
|
|
// This is needed for obscure situations like using --rootwin with a window
|
|
|
|
// manager active.
|
|
|
|
XWindowAttributes a;
|
|
|
|
if (XGetWindowAttributes(display, w, &a)) {
|
|
|
|
long bad = ButtonPressMask | ButtonReleaseMask | PointerMotionMask;
|
|
|
|
if ((event_mask & bad) && (a.all_event_masks & bad) &&
|
|
|
|
((a.your_event_mask & bad) != (event_mask & bad)))
|
|
|
|
{
|
2013-09-11 22:56:41 +00:00
|
|
|
MP_ERR(vo->x11, "X11 error: error during XSelectInput "
|
2013-01-27 05:33:58 +00:00
|
|
|
"call, trying without mouse events\n");
|
|
|
|
XSelectInput(display, w, event_mask & ~bad);
|
|
|
|
}
|
2004-06-14 04:53:03 +00:00
|
|
|
}
|
2002-08-09 17:43:49 +00:00
|
|
|
}
|
2002-01-05 08:49:46 +00:00
|
|
|
|
2012-07-27 00:40:38 +00:00
|
|
|
bool vo_x11_screen_is_composited(struct vo *vo)
|
|
|
|
{
|
|
|
|
struct vo_x11_state *x11 = vo->x11;
|
2014-05-16 21:17:09 +00:00
|
|
|
char buf[50];
|
2023-01-09 20:22:23 +00:00
|
|
|
snprintf(buf, sizeof(buf), "_NET_WM_CM_S%d", x11->screen);
|
2014-05-23 18:22:00 +00:00
|
|
|
Atom NET_WM_CM = XInternAtom(x11->display, buf, False);
|
|
|
|
return XGetSelectionOwner(x11->display, NET_WM_CM) != None;
|
2012-07-27 00:40:38 +00:00
|
|
|
}
|
2016-12-30 18:53:46 +00:00
|
|
|
|
|
|
|
// Return whether the given visual has alpha (when compositing is used).
|
|
|
|
bool vo_x11_is_rgba_visual(XVisualInfo *v)
|
|
|
|
{
|
|
|
|
// This is a heuristic at best. Note that normal 8 bit Visuals use
|
|
|
|
// a depth of 24, even if the pixels are padded to 32 bit. If the
|
|
|
|
// depth is higher than 24, the remaining bits must be alpha.
|
|
|
|
// Note: vinfo->bits_per_rgb appears to be useless (is always 8).
|
|
|
|
unsigned long mask = v->depth == sizeof(unsigned long) * 8 ?
|
|
|
|
(unsigned long)-1 : (1UL << v->depth) - 1;
|
|
|
|
return mask & ~(v->red_mask | v->green_mask | v->blue_mask);
|
|
|
|
}
|