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>
|
|
|
|
#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>
|
|
|
|
#include <X11/extensions/Xinerama.h>
|
|
|
|
#include <X11/extensions/Xrandr.h>
|
|
|
|
|
2001-04-15 20:31:58 +00:00
|
|
|
#include "config.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"
|
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"
|
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
|
|
|
|
|
|
|
|
/* 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
|
|
|
|
2013-01-26 21:37:47 +00:00
|
|
|
#define MWM_DECOR_ALL (1L << 0)
|
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[] =
|
|
|
|
#include "video/out/x11_icon_16.inc"
|
|
|
|
;
|
|
|
|
|
|
|
|
static const char x11_icon_32[] =
|
|
|
|
#include "video/out/x11_icon_32.inc"
|
|
|
|
;
|
|
|
|
|
|
|
|
static const char x11_icon_64[] =
|
|
|
|
#include "video/out/x11_icon_64.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[] =
|
|
|
|
#include "video/out/x11_icon_128.inc"
|
|
|
|
;
|
|
|
|
|
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
|
|
|
|
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);
|
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);
|
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);
|
|
|
|
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
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
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]);
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2013-05-16 12:02:28 +00:00
|
|
|
static void vo_x11_update_screeninfo(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;
|
2013-07-18 12:05:39 +00:00
|
|
|
bool all_screens = opts->fullscreen && opts->fsscreen_id == -2;
|
2014-05-06 18:24:35 +00:00
|
|
|
x11->screenrc = (struct mp_rect){.x1 = x11->ws_width, .y1 = x11->ws_height};
|
2013-10-30 20:58:55 +00:00
|
|
|
if (opts->screen_id >= -1 && XineramaIsActive(x11->display) && !all_screens)
|
2013-02-17 15:35:44 +00:00
|
|
|
{
|
2013-07-18 12:05:39 +00:00
|
|
|
int screen = opts->fullscreen ? opts->fsscreen_id : opts->screen_id;
|
2006-04-16 13:38:28 +00:00
|
|
|
XineramaScreenInfo *screens;
|
|
|
|
int num_screens;
|
|
|
|
|
2019-12-22 00:19:53 +00:00
|
|
|
if (opts->fullscreen && opts->fsscreen_id == -1)
|
|
|
|
screen = opts->screen_id;
|
|
|
|
|
2013-02-15 18:47:34 +00:00
|
|
|
screens = XineramaQueryScreens(x11->display, &num_screens);
|
2008-04-19 09:58:01 +00:00
|
|
|
if (screen >= num_screens)
|
|
|
|
screen = num_screens - 1;
|
|
|
|
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;
|
2008-04-19 09:58:01 +00:00
|
|
|
for (screen = num_screens - 1; screen > 0; screen--) {
|
2013-02-13 15:33:01 +00:00
|
|
|
int left = screens[screen].x_org;
|
|
|
|
int right = left + screens[screen].width;
|
|
|
|
int top = screens[screen].y_org;
|
|
|
|
int bottom = top + screens[screen].height;
|
|
|
|
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
|
|
|
}
|
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){
|
|
|
|
.x0 = screens[screen].x_org,
|
|
|
|
.y0 = screens[screen].y_org,
|
|
|
|
.x1 = screens[screen].x_org + screens[screen].width,
|
|
|
|
.y1 = screens[screen].y_org + screens[screen].height,
|
|
|
|
};
|
2006-04-16 13:38:28 +00:00
|
|
|
|
|
|
|
XFree(screens);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
int num_screens = 0;
|
|
|
|
XineramaScreenInfo *screens = XineramaQueryScreens(x11->display, &num_screens);
|
|
|
|
if (!screens)
|
|
|
|
return;
|
|
|
|
for (int n = 0; n < num_screens; n++) {
|
|
|
|
XineramaScreenInfo *s = &screens[n];
|
|
|
|
if (s->y_org < screens[b[0]].y_org)
|
|
|
|
b[0] = n;
|
|
|
|
if (s->y_org + s->height > screens[b[1]].y_org + screens[b[1]].height)
|
|
|
|
b[1] = n;
|
|
|
|
if (s->x_org < screens[b[2]].x_org)
|
|
|
|
b[2] = n;
|
|
|
|
if (s->x_org + s->width > screens[b[3]].x_org + screens[b[3]].width)
|
|
|
|
b[3] = n;
|
|
|
|
}
|
|
|
|
XFree(screens);
|
|
|
|
}
|
|
|
|
|
2015-04-15 20:39:01 +00:00
|
|
|
static void *screensaver_thread(void *arg)
|
|
|
|
{
|
|
|
|
struct vo_x11_state *x11 = arg;
|
|
|
|
|
2017-08-20 07:11:07 +00:00
|
|
|
for (;;) {
|
|
|
|
sem_wait(&x11->screensaver_sem);
|
|
|
|
// don't queue multiple wakeups
|
|
|
|
while (!sem_trywait(&x11->screensaver_sem)) {}
|
|
|
|
|
|
|
|
if (atomic_load(&x11->screensaver_terminate))
|
|
|
|
break;
|
2015-04-15 20:39:01 +00:00
|
|
|
|
2017-08-20 07:11:07 +00:00
|
|
|
char *args[] = {"xdg-screensaver", "reset", NULL};
|
|
|
|
int status = mp_subprocess(args, NULL, NULL, mp_devnull, mp_devnull, &(char*){0});
|
2015-06-20 19:55:15 +00:00
|
|
|
if (status) {
|
2017-08-20 07:11:07 +00:00
|
|
|
MP_VERBOSE(x11, "Disabling screensaver failed (%d). Make sure the "
|
2016-01-03 14:50:00 +00:00
|
|
|
"xdg-screensaver script is installed.\n", status);
|
2015-04-15 20:39:01 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2013-01-26 21:37:47 +00:00
|
|
|
int 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
|
|
|
|
2017-08-20 07:11:07 +00:00
|
|
|
sem_init(&x11->screensaver_sem, 0, 0);
|
|
|
|
if (pthread_create(&x11->screensaver_thread, NULL, screensaver_thread, x11)) {
|
|
|
|
sem_destroy(&x11->screensaver_sem);
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
x11->screensaver_thread_running = true;
|
2015-04-15 20:39:01 +00:00
|
|
|
|
2013-12-27 22:07:57 +00:00
|
|
|
x11_error_output = x11->log;
|
|
|
|
XSetErrorHandler(x11_errorhandler);
|
|
|
|
|
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
|
|
|
|
2017-01-19 15:30:38 +00:00
|
|
|
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;
|
2019-12-16 20:45:46 +00:00
|
|
|
if (isfinite(dpi_x) && isfinite(dpi_y) && x11->opts->hidpi_window_scale) {
|
2017-01-19 15:30:38 +00:00
|
|
|
int s_x = lrint(MPCLAMP(dpi_x / base_dpi, 0, 10));
|
|
|
|
int s_y = lrint(MPCLAMP(dpi_y / base_dpi, 0, 10));
|
|
|
|
if (s_x == s_y && s_x > 1 && s_x < 10) {
|
|
|
|
x11->dpi_scale = s_x;
|
|
|
|
MP_VERBOSE(x11, "Assuming DPI scale %d for prescaling. This can "
|
|
|
|
"be disabled with --hidpi-window-scale=no.\n",
|
|
|
|
x11->dpi_scale);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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);
|
|
|
|
|
2013-01-26 21:37:47 +00:00
|
|
|
return 1;
|
2015-04-15 20:39:01 +00:00
|
|
|
|
|
|
|
error:
|
|
|
|
vo_x11_uninit(vo);
|
|
|
|
return 0;
|
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},
|
2008-12-20 11:12:45 +00:00
|
|
|
|
|
|
|
// numpad independent of numlock
|
2013-02-12 00:34:04 +00:00
|
|
|
{XK_KP_Subtract, '-'}, {XK_KP_Add, '+'}, {XK_KP_Multiply, '*'},
|
|
|
|
{XK_KP_Divide, '/'}, {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
|
2013-02-12 00:34:04 +00:00
|
|
|
{XK_KP_Insert, MP_KEY_KPINS}, {XK_KP_End, MP_KEY_KP1},
|
|
|
|
{XK_KP_Down, MP_KEY_KP2}, {XK_KP_Page_Down, MP_KEY_KP3},
|
|
|
|
{XK_KP_Left, MP_KEY_KP4}, {XK_KP_Begin, MP_KEY_KP5},
|
|
|
|
{XK_KP_Right, MP_KEY_KP6}, {XK_KP_Home, MP_KEY_KP7}, {XK_KP_Up, MP_KEY_KP8},
|
|
|
|
{XK_KP_Page_Up, MP_KEY_KP9}, {XK_KP_Delete, MP_KEY_KPDEL},
|
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},
|
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
|
|
|
}
|
|
|
|
|
2014-06-06 16:44:57 +00:00
|
|
|
static void vo_x11_decoration(struct vo *vo, bool d)
|
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));
|
|
|
|
// hints weren't set, and decorations requested -> assume WM displays them
|
|
|
|
if (!got && d)
|
|
|
|
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;
|
|
|
|
mhints.decorations = d ? MWM_DECOR_ALL : 0;
|
|
|
|
XChangeProperty(x11->display, x11->window, motif_hints, motif_hints, 32,
|
|
|
|
PropModeReplace, (unsigned char *) &mhints, 5);
|
2001-03-03 21:47:37 +00:00
|
|
|
}
|
|
|
|
|
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);
|
|
|
|
|
2013-10-30 21:09:40 +00:00
|
|
|
if (x11->window != None && x11->window != x11->rootwin) {
|
2013-02-24 22:35:13 +00:00
|
|
|
XUnmapWindow(x11->display, x11->window);
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2015-05-18 20:25:59 +00:00
|
|
|
if (x11->screensaver_thread_running) {
|
2017-08-20 07:11:07 +00:00
|
|
|
atomic_store(&x11->screensaver_terminate, true);
|
|
|
|
sem_post(&x11->screensaver_sem);
|
2015-04-15 20:39:01 +00:00
|
|
|
pthread_join(x11->screensaver_thread, NULL);
|
2017-08-20 07:11:07 +00:00
|
|
|
sem_destroy(&x11->screensaver_sem);
|
2015-04-15 20:39:01 +00:00
|
|
|
}
|
2012-01-14 13:09:44 +00:00
|
|
|
|
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) &&
|
2014-01-04 00:16:44 +00:00
|
|
|
se->target == x11->dnd_requested_format)
|
|
|
|
{
|
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) {
|
2015-08-29 09:13:22 +00:00
|
|
|
enum mp_dnd_action action =
|
|
|
|
x11->dnd_requested_action == XA(x11, XdndActionCopy) ?
|
|
|
|
DND_REPLACE : DND_APPEND;
|
|
|
|
|
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
|
|
|
}
|
|
|
|
|
|
|
|
XChangeProperty(x11->display, x11->window, XA(x11,_NET_WM_BYPASS_COMPOSITOR),
|
|
|
|
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;
|
|
|
|
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);
|
|
|
|
x11->win_drag_button1_down = false;
|
|
|
|
}
|
|
|
|
|
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);
|
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);
|
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);
|
|
|
|
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);
|
|
|
|
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:
|
2014-03-18 19:38:23 +00:00
|
|
|
if (x11->win_drag_button1_down && !x11->fs &&
|
2016-03-15 21:11:31 +00:00
|
|
|
!mp_input_test_dragging(x11->input_ctx, Event.xmotion.x,
|
|
|
|
Event.xmotion.y))
|
2014-03-18 19:38:23 +00:00
|
|
|
{
|
2016-03-15 21:11:31 +00:00
|
|
|
mp_input_put_key(x11->input_ctx, MP_INPUT_RELEASE_ALL);
|
2014-03-18 19:38:23 +00:00
|
|
|
XUngrabPointer(x11->display, CurrentTime);
|
|
|
|
|
2014-05-17 16:03:28 +00:00
|
|
|
long params[5] = {
|
|
|
|
Event.xmotion.x_root, Event.xmotion.y_root,
|
|
|
|
8, // _NET_WM_MOVERESIZE_MOVE
|
|
|
|
1, // button 1
|
|
|
|
1, // source indication: normal
|
|
|
|
};
|
|
|
|
x11_send_ewmh_msg(x11, "_NET_WM_MOVERESIZE", params);
|
2014-03-18 19:38:23 +00:00
|
|
|
} else {
|
2016-03-15 21:11:31 +00:00
|
|
|
mp_input_set_mouse_pos(x11->input_ctx, Event.xmotion.x,
|
|
|
|
Event.xmotion.y);
|
2014-03-18 19:38:23 +00:00
|
|
|
}
|
2014-03-22 01:12:20 +00:00
|
|
|
x11->win_drag_button1_down = false;
|
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;
|
2014-03-18 19:38:23 +00:00
|
|
|
x11->win_drag_button1_down = false;
|
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;
|
2014-03-18 19:38:23 +00:00
|
|
|
if (Event.xbutton.button == 1)
|
|
|
|
x11->win_drag_button1_down = true;
|
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);
|
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;
|
2014-03-18 19:38:23 +00:00
|
|
|
if (Event.xbutton.button == 1)
|
|
|
|
x11->win_drag_button1_down = false;
|
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);
|
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;
|
2016-07-18 11:06:38 +00:00
|
|
|
x11->current_icc_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;
|
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);
|
|
|
|
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;
|
|
|
|
|
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
|
|
|
|
opts->screen_id >= 0 || // 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();
|
|
|
|
if (!hint)
|
|
|
|
return; // OOM
|
|
|
|
|
|
|
|
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
|
|
|
}
|
2011-12-06 17:48:31 +00:00
|
|
|
|
2012-01-25 01:01:22 +00:00
|
|
|
if (prop.value)
|
|
|
|
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);
|
|
|
|
vo_x11_set_property_utf8(vo, XA(x11, _NET_WM_NAME), x11->window_title);
|
|
|
|
vo_x11_set_property_utf8(vo, XA(x11, _NET_WM_ICON_NAME), x11->window_title);
|
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
|
|
|
|
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,
|
|
|
|
XNInputStyle, XIMPreeditNone | XIMStatusNone,
|
|
|
|
XNClientWindow, x11->window,
|
|
|
|
XNFocusWindow, x11->window,
|
|
|
|
NULL);
|
|
|
|
}
|
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_update_window_title(vo);
|
|
|
|
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
|
|
|
}
|
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);
|
2019-11-27 19:30:13 +00:00
|
|
|
vo_x11_decoration(vo, x11->opts->border);
|
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;
|
|
|
|
// 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
|
|
|
}
|
|
|
|
|
2019-11-27 19:30:13 +00:00
|
|
|
if (x11->opts->all_workspaces) {
|
2015-01-17 18:26:39 +00:00
|
|
|
long v = 0xFFFFFFFF;
|
|
|
|
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
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2014-05-17 01:29:36 +00:00
|
|
|
static void vo_x11_highlevel_resize(struct vo *vo, struct mp_rect rc)
|
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
|
|
|
|
|
|
|
bool reset_pos = opts->force_window_position;
|
|
|
|
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);
|
|
|
|
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;
|
2017-01-19 15:30:38 +00:00
|
|
|
vo_calc_window_geometry2(vo, &x11->screenrc, x11->dpi_scale, &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
|
|
|
|
2014-05-17 01:29:36 +00:00
|
|
|
bool reset_size = x11->old_dw != RC_W(rc) || x11->old_dh != RC_H(rc);
|
|
|
|
x11->old_dw = RC_W(rc);
|
|
|
|
x11->old_dh = RC_H(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
|
|
|
|
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) {
|
2014-05-17 01:29:36 +00:00
|
|
|
vo_x11_highlevel_resize(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
|
|
|
}
|
|
|
|
|
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);
|
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
|
|
|
|
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
|
|
|
}
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2016-07-18 11:06:38 +00:00
|
|
|
// which screen's ICC profile we're going to use
|
|
|
|
static int get_icc_screen(struct vo *vo)
|
|
|
|
{
|
|
|
|
struct vo_x11_state *x11 = vo->x11;
|
|
|
|
int cx = x11->winrc.x0 + (x11->winrc.x1 - x11->winrc.x0)/2,
|
|
|
|
cy = x11->winrc.y0 + (x11->winrc.y1 - x11->winrc.y0)/2;
|
2018-10-14 00:37:55 +00:00
|
|
|
int screen = x11->current_icc_screen; // xinerama screen number
|
2016-07-18 11:06:38 +00:00
|
|
|
for (int n = 0; n < x11->num_displays; n++) {
|
|
|
|
struct xrandr_display *disp = &x11->displays[n];
|
|
|
|
if (mp_rect_contains(&disp->rc, cx, cy)) {
|
|
|
|
screen = n;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return screen;
|
|
|
|
}
|
|
|
|
|
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
|
|
|
}
|
2014-08-16 19:43:13 +00:00
|
|
|
double fps = 1000.0;
|
|
|
|
for (int n = 0; n < x11->num_displays; n++) {
|
2015-01-26 01:18:47 +00:00
|
|
|
struct xrandr_display *disp = &x11->displays[n];
|
|
|
|
disp->overlaps = rc_overlaps(disp->rc, x11->winrc);
|
|
|
|
if (disp->overlaps)
|
|
|
|
fps = MPMIN(fps, disp->fps);
|
2014-08-16 19:43:13 +00:00
|
|
|
}
|
|
|
|
double fallback = x11->num_displays > 0 ? x11->displays[0].fps : 0;
|
|
|
|
fps = fps < 1000.0 ? fps : fallback;
|
|
|
|
if (fps != x11->current_display_fps)
|
|
|
|
MP_VERBOSE(x11, "Current display FPS: %f\n", fps);
|
|
|
|
x11->current_display_fps = fps;
|
2015-01-03 02:06:26 +00:00
|
|
|
// might have changed displays
|
2016-07-18 11:06:38 +00:00
|
|
|
x11->pending_vo_events |= VO_EVENT_WIN_STATE;
|
|
|
|
int icc_screen = get_icc_screen(vo);
|
|
|
|
if (x11->current_icc_screen != icc_screen) {
|
|
|
|
x11->current_icc_screen = icc_screen;
|
|
|
|
x11->pending_vo_events |= VO_EVENT_ICC_PROFILE_CHANGED;
|
|
|
|
}
|
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;
|
|
|
|
}
|
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
|
|
|
}
|
|
|
|
|
2013-07-18 12:05:39 +00:00
|
|
|
vo_x11_decoration(vo, opts->border && !x11->fs);
|
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
|
|
|
}
|
|
|
|
|
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);
|
|
|
|
if (opt == &opts->border)
|
|
|
|
vo_x11_decoration(vo, opts->border);
|
|
|
|
if (opt == &opts->all_workspaces) {
|
|
|
|
long params[5] = {0xFFFFFFFF, 1};
|
|
|
|
if (!opts->all_workspaces) {
|
|
|
|
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);
|
|
|
|
}
|
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);
|
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;
|
2019-12-16 01:22:51 +00:00
|
|
|
s[0] = (x11->fs ? RC_W(x11->nofsrc) : RC_W(x11->winrc)) / x11->dpi_scale;
|
|
|
|
s[1] = (x11->fs ? RC_H(x11->nofsrc) : RC_H(x11->winrc)) / x11->dpi_scale;
|
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;
|
2019-12-16 01:22:51 +00:00
|
|
|
int w = s[0] * x11->dpi_scale;
|
|
|
|
int h = s[1] * x11->dpi_scale;
|
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;
|
2014-05-17 01:29:36 +00:00
|
|
|
vo_x11_highlevel_resize(vo, rc);
|
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;
|
|
|
|
}
|
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;
|
2016-07-18 11:06:38 +00:00
|
|
|
int screen = get_icc_screen(vo);
|
2018-11-01 01:00:03 +00:00
|
|
|
int atom_id = x11->displays[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;
|
2015-03-07 11:32:19 +00:00
|
|
|
MP_VERBOSE(x11, "Retrieving ICC profile for display: %d\n", 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:
|
2015-12-06 17:39:09 +00:00
|
|
|
talloc_free(x11->window_title);
|
|
|
|
x11->window_title = talloc_strdup(x11, (char *)arg);
|
2014-12-09 20:28:07 +00:00
|
|
|
if (!x11->parent)
|
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;
|
|
|
|
}
|
2019-12-20 11:48:48 +00:00
|
|
|
case VOCTRL_GET_HIDPI_SCALE:
|
|
|
|
*(double *)arg = x11->dpi_scale;
|
|
|
|
return VO_TRUE;
|
2013-05-15 16:17:18 +00:00
|
|
|
}
|
|
|
|
return VO_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
void vo_x11_wait_events(struct vo *vo, int64_t until_time_us)
|
|
|
|
{
|
|
|
|
struct vo_x11_state *x11 = vo->x11;
|
|
|
|
|
|
|
|
struct pollfd fds[2] = {
|
|
|
|
{ .fd = x11->event_fd, .events = POLLIN },
|
|
|
|
{ .fd = x11->wakeup_pipe[0], .events = POLLIN },
|
|
|
|
};
|
|
|
|
int64_t wait_us = until_time_us - mp_time_us();
|
2016-08-26 18:22:33 +00:00
|
|
|
int timeout_ms = MPCLAMP((wait_us + 999) / 1000, 0, 10000);
|
2016-07-20 18:52:08 +00:00
|
|
|
|
|
|
|
poll(fds, 2, timeout_ms);
|
|
|
|
|
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;
|
|
|
|
sem_post(&x11->screensaver_sem);
|
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];
|
|
|
|
sprintf(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);
|
|
|
|
}
|