summaryrefslogtreecommitdiffstats
path: root/video/out/x11_common.c
diff options
context:
space:
mode:
Diffstat (limited to 'video/out/x11_common.c')
-rw-r--r--video/out/x11_common.c2404
1 files changed, 2404 insertions, 0 deletions
diff --git a/video/out/x11_common.c b/video/out/x11_common.c
new file mode 100644
index 0000000000..04d5c6880b
--- /dev/null
+++ b/video/out/x11_common.c
@@ -0,0 +1,2404 @@
+/*
+ * This file is part of MPlayer.
+ *
+ * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
+ *
+ * MPlayer is distributed in the hope that it will be useful,
+ * 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
+ * with MPlayer; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <inttypes.h>
+#include <limits.h>
+
+#include "config.h"
+#include "bstr.h"
+#include "options.h"
+#include "mp_msg.h"
+#include "mp_fifo.h"
+#include "libavutil/common.h"
+#include "x11_common.h"
+#include "talloc.h"
+
+#include <string.h>
+#include <unistd.h>
+#include <assert.h>
+
+#include "video_out.h"
+#include "aspect.h"
+#include "geometry.h"
+#include "osdep/timer.h"
+
+#include <X11/Xmd.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/Xatom.h>
+#include <X11/keysym.h>
+
+#ifdef CONFIG_XSS
+#include <X11/extensions/scrnsaver.h>
+#endif
+
+#ifdef CONFIG_XDPMS
+#include <X11/extensions/dpms.h>
+#endif
+
+#ifdef CONFIG_XINERAMA
+#include <X11/extensions/Xinerama.h>
+#endif
+
+#ifdef CONFIG_XF86VM
+#include <X11/extensions/xf86vmode.h>
+#endif
+
+#ifdef CONFIG_XF86XK
+#include <X11/XF86keysym.h>
+#endif
+
+#ifdef CONFIG_XV
+#include <X11/extensions/Xv.h>
+#include <X11/extensions/Xvlib.h>
+
+#include "subopt-helper.h"
+#endif
+
+#include "input/input.h"
+#include "input/keycodes.h"
+
+#define WIN_LAYER_ONBOTTOM 2
+#define WIN_LAYER_NORMAL 4
+#define WIN_LAYER_ONTOP 6
+#define WIN_LAYER_ABOVE_DOCK 10
+
+int fs_layer = WIN_LAYER_ABOVE_DOCK;
+
+int stop_xscreensaver = 1;
+
+static int dpms_disabled = 0;
+
+char *mDisplayName = NULL;
+
+char **vo_fstype_list;
+
+/* 1 means that the WM is metacity (broken as hell) */
+int metacity_hack = 0;
+
+#ifdef CONFIG_XF86VM
+static int modecount;
+static XF86VidModeModeInfo **vidmodes;
+static XF86VidModeModeLine modeline;
+#endif
+
+static int vo_x11_get_fs_type(int supported);
+static void saver_off(Display *);
+static void saver_on(Display *);
+
+/*
+ * Sends the EWMH fullscreen state event.
+ *
+ * action: could be one of _NET_WM_STATE_REMOVE -- remove state
+ * _NET_WM_STATE_ADD -- add state
+ * _NET_WM_STATE_TOGGLE -- toggle
+ */
+void vo_x11_ewmh_fullscreen(struct vo_x11_state *x11, int action)
+{
+ assert(action == _NET_WM_STATE_REMOVE ||
+ action == _NET_WM_STATE_ADD || action == _NET_WM_STATE_TOGGLE);
+
+ if (x11->fs_type & vo_wm_FULLSCREEN)
+ {
+ XEvent xev;
+
+ /* init X event structure for _NET_WM_FULLSCREEN client message */
+ xev.xclient.type = ClientMessage;
+ xev.xclient.serial = 0;
+ xev.xclient.send_event = True;
+ xev.xclient.message_type = x11->XA_NET_WM_STATE;
+ xev.xclient.window = x11->window;
+ xev.xclient.format = 32;
+ xev.xclient.data.l[0] = action;
+ xev.xclient.data.l[1] = x11->XA_NET_WM_STATE_FULLSCREEN;
+ xev.xclient.data.l[2] = 0;
+ xev.xclient.data.l[3] = 0;
+ xev.xclient.data.l[4] = 0;
+
+ /* finally send that damn thing */
+ if (!XSendEvent(x11->display, DefaultRootWindow(x11->display), False,
+ SubstructureRedirectMask | SubstructureNotifyMask,
+ &xev))
+ {
+ mp_tmsg(MSGT_VO, MSGL_ERR, "\nX11: Couldn't send EWMH fullscreen event!\n");
+ }
+ }
+}
+
+static void vo_hidecursor(Display * disp, Window win)
+{
+ Cursor no_ptr;
+ Pixmap bm_no;
+ XColor black, dummy;
+ Colormap colormap;
+ const char bm_no_data[] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+
+ if (WinID == 0)
+ return; // do not hide if playing on the root window
+
+ 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);
+}
+
+static void vo_showcursor(Display * disp, Window win)
+{
+ if (WinID == 0)
+ return;
+ XDefineCursor(disp, win, 0);
+}
+
+static int x11_errorhandler(Display * display, XErrorEvent * event)
+{
+#define MSGLEN 60
+ char msg[MSGLEN];
+
+ XGetErrorText(display, event->error_code, (char *) &msg, MSGLEN);
+
+ mp_msg(MSGT_VO, MSGL_ERR, "X11 error: %s\n", msg);
+
+ mp_msg(MSGT_VO, MSGL_V,
+ "Type: %x, display: %p, resourceid: %lx, serial: %lx\n",
+ event->type, event->display, event->resourceid, event->serial);
+ mp_msg(MSGT_VO, MSGL_V,
+ "Error code: %x, request code: %x, minor code: %x\n",
+ event->error_code, event->request_code, event->minor_code);
+
+// abort();
+ return 0;
+#undef MSGLEN
+}
+
+void fstype_help(void)
+{
+ mp_tmsg(MSGT_VO, MSGL_INFO, "Available fullscreen layer change modes:\n");
+ mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_FULL_SCREEN_TYPES\n");
+
+ mp_msg(MSGT_VO, MSGL_INFO, " %-15s %s\n", "none",
+ "don't set fullscreen window layer");
+ mp_msg(MSGT_VO, MSGL_INFO, " %-15s %s\n", "layer",
+ "use _WIN_LAYER hint with default layer");
+ mp_msg(MSGT_VO, MSGL_INFO, " %-15s %s\n", "layer=<0..15>",
+ "use _WIN_LAYER hint with a given layer number");
+ mp_msg(MSGT_VO, MSGL_INFO, " %-15s %s\n", "netwm",
+ "force NETWM style");
+ mp_msg(MSGT_VO, MSGL_INFO, " %-15s %s\n", "above",
+ "use _NETWM_STATE_ABOVE hint if available");
+ mp_msg(MSGT_VO, MSGL_INFO, " %-15s %s\n", "below",
+ "use _NETWM_STATE_BELOW hint if available");
+ mp_msg(MSGT_VO, MSGL_INFO, " %-15s %s\n", "fullscreen",
+ "use _NETWM_STATE_FULLSCREEN hint if available");
+ mp_msg(MSGT_VO, MSGL_INFO, " %-15s %s\n", "stays_on_top",
+ "use _NETWM_STATE_STAYS_ON_TOP hint if available");
+ mp_msg(MSGT_VO, MSGL_INFO,
+ "You can also negate the settings with simply putting '-' in the beginning");
+ mp_msg(MSGT_VO, MSGL_INFO, "\n");
+}
+
+static void fstype_dump(int fstype)
+{
+ if (fstype)
+ {
+ mp_msg(MSGT_VO, MSGL_V, "[x11] Current fstype setting honours");
+ if (fstype & vo_wm_LAYER)
+ mp_msg(MSGT_VO, MSGL_V, " LAYER");
+ if (fstype & vo_wm_FULLSCREEN)
+ mp_msg(MSGT_VO, MSGL_V, " FULLSCREEN");
+ if (fstype & vo_wm_STAYS_ON_TOP)
+ mp_msg(MSGT_VO, MSGL_V, " STAYS_ON_TOP");
+ if (fstype & vo_wm_ABOVE)
+ mp_msg(MSGT_VO, MSGL_V, " ABOVE");
+ if (fstype & vo_wm_BELOW)
+ mp_msg(MSGT_VO, MSGL_V, " BELOW");
+ mp_msg(MSGT_VO, MSGL_V, " X atoms\n");
+ } else
+ mp_msg(MSGT_VO, MSGL_V,
+ "[x11] Current fstype setting doesn't honour any X atoms\n");
+}
+
+static int net_wm_support_state_test(struct vo_x11_state *x11, Atom atom)
+{
+#define NET_WM_STATE_TEST(x) { if (atom == x11->XA_NET_WM_STATE_##x) { mp_msg( MSGT_VO,MSGL_V, "[x11] Detected wm supports " #x " state.\n" ); return vo_wm_##x; } }
+
+ NET_WM_STATE_TEST(FULLSCREEN);
+ NET_WM_STATE_TEST(ABOVE);
+ NET_WM_STATE_TEST(STAYS_ON_TOP);
+ NET_WM_STATE_TEST(BELOW);
+ return 0;
+}
+
+static int x11_get_property(struct vo_x11_state *x11, Atom type, Atom ** args,
+ unsigned long *nitems)
+{
+ int format;
+ unsigned long bytesafter;
+
+ return Success ==
+ XGetWindowProperty(x11->display, x11->rootwin, type, 0, 16384, False,
+ AnyPropertyType, &type, &format, nitems,
+ &bytesafter, (unsigned char **) args)
+ && *nitems > 0;
+}
+
+static int vo_wm_detect(struct vo *vo)
+{
+ struct vo_x11_state *x11 = vo->x11;
+ int i;
+ int wm = 0;
+ unsigned long nitems;
+ Atom *args = NULL;
+
+ if (WinID >= 0)
+ return 0;
+
+// -- supports layers
+ if (x11_get_property(x11, x11->XA_WIN_PROTOCOLS, &args, &nitems))
+ {
+ mp_msg(MSGT_VO, MSGL_V, "[x11] Detected wm supports layers.\n");
+ for (i = 0; i < nitems; i++)
+ {
+ if (args[i] == x11->XA_WIN_LAYER)
+ {
+ wm |= vo_wm_LAYER;
+ metacity_hack |= 1;
+ } else
+ /* metacity is the only window manager I know which reports
+ * supporting only the _WIN_LAYER hint in _WIN_PROTOCOLS.
+ * (what's more support for it is broken) */
+ metacity_hack |= 2;
+ }
+ XFree(args);
+ if (wm && (metacity_hack == 1))
+ {
+ // metacity claims to support layers, but it is not the truth :-)
+ wm ^= vo_wm_LAYER;
+ mp_msg(MSGT_VO, MSGL_V,
+ "[x11] Using workaround for Metacity bugs.\n");
+ }
+ }
+// --- netwm
+ if (x11_get_property(x11, x11->XA_NET_SUPPORTED, &args, &nitems))
+ {
+ mp_msg(MSGT_VO, MSGL_V, "[x11] Detected wm supports NetWM.\n");
+ for (i = 0; i < nitems; i++)
+ wm |= net_wm_support_state_test(vo->x11, args[i]);
+ XFree(args);
+ }
+
+ if (wm == 0)
+ mp_msg(MSGT_VO, MSGL_V, "[x11] Unknown wm type...\n");
+ return wm;
+}
+
+#define XA_INIT(x) x11->XA##x = XInternAtom(x11->display, #x, False)
+static void init_atoms(struct vo_x11_state *x11)
+{
+ XA_INIT(_NET_SUPPORTED);
+ XA_INIT(_NET_WM_STATE);
+ XA_INIT(_NET_WM_STATE_FULLSCREEN);
+ XA_INIT(_NET_WM_STATE_ABOVE);
+ XA_INIT(_NET_WM_STATE_STAYS_ON_TOP);
+ XA_INIT(_NET_WM_STATE_BELOW);
+ XA_INIT(_NET_WM_PID);
+ XA_INIT(_NET_WM_NAME);
+ XA_INIT(_NET_WM_ICON_NAME);
+ XA_INIT(_WIN_PROTOCOLS);
+ XA_INIT(_WIN_LAYER);
+ XA_INIT(_WIN_HINTS);
+ XA_INIT(WM_PROTOCOLS);
+ XA_INIT(WM_DELETE_WINDOW);
+ XA_INIT(UTF8_STRING);
+ char buf[50];
+ sprintf(buf, "_NET_WM_CM_S%d", x11->screen);
+ x11->XA_NET_WM_CM = XInternAtom(x11->display, buf, False);
+}
+
+void update_xinerama_info(struct vo *vo) {
+ struct MPOpts *opts = vo->opts;
+ xinerama_x = xinerama_y = 0;
+#ifdef CONFIG_XINERAMA
+ if (xinerama_screen >= -1 && XineramaIsActive(vo->x11->display))
+ {
+ int screen = xinerama_screen;
+ XineramaScreenInfo *screens;
+ int num_screens;
+
+ screens = XineramaQueryScreens(vo->x11->display, &num_screens);
+ if (screen >= num_screens)
+ screen = num_screens - 1;
+ if (screen == -1) {
+ int x = vo->dx + vo->dwidth / 2;
+ int y = vo->dy + vo->dheight / 2;
+ for (screen = num_screens - 1; screen > 0; screen--) {
+ int left = screens[screen].x_org;
+ int right = left + screens[screen].width;
+ int top = screens[screen].y_org;
+ int bottom = top + screens[screen].height;
+ if (left <= x && x <= right && top <= y && y <= bottom)
+ break;
+ }
+ }
+ if (screen < 0)
+ screen = 0;
+ opts->vo_screenwidth = screens[screen].width;
+ opts->vo_screenheight = screens[screen].height;
+ xinerama_x = screens[screen].x_org;
+ xinerama_y = screens[screen].y_org;
+
+ XFree(screens);
+ }
+#endif
+ aspect_save_screenres(vo, opts->vo_screenwidth, opts->vo_screenheight);
+}
+
+int vo_init(struct vo *vo)
+{
+ struct MPOpts *opts = vo->opts;
+// int mScreen;
+ int depth, bpp;
+ unsigned int mask;
+
+// char * DisplayName = ":0.0";
+// Display * mDisplay;
+ XImage *mXImage = NULL;
+
+// Window mRootWin;
+ XWindowAttributes attribs;
+ char *dispName;
+
+ if (vo->x11)
+ return 1;
+
+ vo->x11 = vo_x11_init_state();
+ struct vo_x11_state *x11 = vo->x11;
+
+ if (vo_rootwin)
+ WinID = 0; // use root window
+
+ if (x11->depthonscreen)
+ {
+ saver_off(x11->display);
+ return 1; // already called
+ }
+
+ XSetErrorHandler(x11_errorhandler);
+
+#if 0
+ if (!mDisplayName)
+ if (!(mDisplayName = getenv("DISPLAY")))
+ mDisplayName = strdup(":0.0");
+#else
+ dispName = XDisplayName(mDisplayName);
+#endif
+
+ mp_msg(MSGT_VO, MSGL_V, "X11 opening display: %s\n", dispName);
+
+ x11->display = XOpenDisplay(dispName);
+ if (!x11->display)
+ {
+ mp_msg(MSGT_VO, MSGL_ERR,
+ "vo: couldn't open the X11 display (%s)!\n", dispName);
+ talloc_free(x11);
+ vo->x11 = NULL;
+ return 0;
+ }
+ x11->screen = DefaultScreen(x11->display); // screen ID
+ x11->rootwin = RootWindow(x11->display, x11->screen); // root window ID
+
+ x11->xim = XOpenIM(x11->display, NULL, NULL, NULL);
+
+ init_atoms(vo->x11);
+
+#ifdef CONFIG_XF86VM
+ {
+ int clock;
+
+ XF86VidModeGetModeLine(x11->display, x11->screen, &clock, &modeline);
+ if (!opts->vo_screenwidth)
+ opts->vo_screenwidth = modeline.hdisplay;
+ if (!opts->vo_screenheight)
+ opts->vo_screenheight = modeline.vdisplay;
+ }
+#endif
+ {
+ if (!opts->vo_screenwidth)
+ opts->vo_screenwidth = DisplayWidth(x11->display, x11->screen);
+ if (!opts->vo_screenheight)
+ opts->vo_screenheight = DisplayHeight(x11->display, x11->screen);
+ }
+ // get color depth (from root window, or the best visual):
+ XGetWindowAttributes(x11->display, x11->rootwin, &attribs);
+ depth = attribs.depth;
+
+ if (depth != 15 && depth != 16 && depth != 24 && depth != 32)
+ {
+ Visual *visual;
+
+ depth = vo_find_depth_from_visuals(x11->display, x11->screen, &visual);
+ if (depth != -1)
+ mXImage = XCreateImage(x11->display, visual, depth, ZPixmap,
+ 0, NULL, 1, 1, 8, 1);
+ } else
+ mXImage =
+ XGetImage(x11->display, x11->rootwin, 0, 0, 1, 1, AllPlanes, ZPixmap);
+
+ x11->depthonscreen = depth; // display depth on screen
+
+ // get bits/pixel from XImage structure:
+ if (mXImage == NULL)
+ {
+ mask = 0;
+ } else
+ {
+ /*
+ * for the depth==24 case, the XImage structures might use
+ * 24 or 32 bits of data per pixel. The x11->depthonscreen
+ * field stores the amount of data per pixel in the
+ * XImage structure!
+ *
+ * Maybe we should rename vo_depthonscreen to (or add) vo_bpp?
+ */
+ bpp = mXImage->bits_per_pixel;
+ if ((x11->depthonscreen + 7) / 8 != (bpp + 7) / 8)
+ x11->depthonscreen = bpp; // by A'rpi
+ mask =
+ mXImage->red_mask | mXImage->green_mask | mXImage->blue_mask;
+ mp_msg(MSGT_VO, MSGL_V,
+ "vo: X11 color mask: %X (R:%lX G:%lX B:%lX)\n", mask,
+ mXImage->red_mask, mXImage->green_mask, mXImage->blue_mask);
+ XDestroyImage(mXImage);
+ }
+ if (((x11->depthonscreen + 7) / 8) == 2)
+ {
+ if (mask == 0x7FFF)
+ x11->depthonscreen = 15;
+ else if (mask == 0xFFFF)
+ x11->depthonscreen = 16;
+ }
+// XCloseDisplay( mDisplay );
+/* slightly improved local display detection AST */
+ if (strncmp(dispName, "unix:", 5) == 0)
+ dispName += 4;
+ else if (strncmp(dispName, "localhost:", 10) == 0)
+ dispName += 9;
+ if (*dispName == ':' && atoi(dispName + 1) < 10)
+ x11->display_is_local = 1;
+ else
+ x11->display_is_local = 0;
+ mp_msg(MSGT_VO, MSGL_V,
+ "vo: X11 running at %dx%d with depth %d and %d bpp (\"%s\" => %s display)\n",
+ opts->vo_screenwidth, opts->vo_screenheight, depth, x11->depthonscreen,
+ dispName, x11->display_is_local ? "local" : "remote");
+
+ x11->wm_type = vo_wm_detect(vo);
+
+ x11->fs_type = vo_x11_get_fs_type(x11->wm_type);
+
+ fstype_dump(x11->fs_type);
+
+ saver_off(x11->display);
+ return 1;
+}
+
+void vo_uninit(struct vo_x11_state *x11)
+{
+ if (!x11)
+ return;
+ if (!x11->display)
+ {
+ mp_msg(MSGT_VO, MSGL_V,
+ "vo: x11 uninit called but X11 not initialized..\n");
+ } else {
+ mp_msg(MSGT_VO, MSGL_V, "vo: uninit ...\n");
+ if (x11->xim)
+ XCloseIM(x11->xim);
+ XSetErrorHandler(NULL);
+ XCloseDisplay(x11->display);
+ x11->depthonscreen = 0;
+ x11->display = NULL;
+ }
+ talloc_free(x11);
+}
+
+static const struct mp_keymap keymap[] = {
+ // special keys
+ {XK_Pause, KEY_PAUSE}, {XK_Escape, KEY_ESC}, {XK_BackSpace, KEY_BS},
+ {XK_Tab, KEY_TAB}, {XK_Return, KEY_ENTER},
+ {XK_Menu, KEY_MENU}, {XK_Print, KEY_PRINT},
+
+ // cursor keys
+ {XK_Left, KEY_LEFT}, {XK_Right, KEY_RIGHT}, {XK_Up, KEY_UP}, {XK_Down, KEY_DOWN},
+
+ // navigation block
+ {XK_Insert, KEY_INSERT}, {XK_Delete, KEY_DELETE}, {XK_Home, KEY_HOME}, {XK_End, KEY_END},
+ {XK_Page_Up, KEY_PAGE_UP}, {XK_Page_Down, KEY_PAGE_DOWN},
+
+ // F-keys
+ {XK_F1, KEY_F+1}, {XK_F2, KEY_F+2}, {XK_F3, KEY_F+3}, {XK_F4, KEY_F+4},
+ {XK_F5, KEY_F+5}, {XK_F6, KEY_F+6}, {XK_F7, KEY_F+7}, {XK_F8, KEY_F+8},
+ {XK_F9, KEY_F+9}, {XK_F10, KEY_F+10}, {XK_F11, KEY_F+11}, {XK_F12, KEY_F+12},
+
+ // numpad independent of numlock
+ {XK_KP_Subtract, '-'}, {XK_KP_Add, '+'}, {XK_KP_Multiply, '*'}, {XK_KP_Divide, '/'},
+ {XK_KP_Enter, KEY_KPENTER},
+
+ // numpad with numlock
+ {XK_KP_0, KEY_KP0}, {XK_KP_1, KEY_KP1}, {XK_KP_2, KEY_KP2},
+ {XK_KP_3, KEY_KP3}, {XK_KP_4, KEY_KP4}, {XK_KP_5, KEY_KP5},
+ {XK_KP_6, KEY_KP6}, {XK_KP_7, KEY_KP7}, {XK_KP_8, KEY_KP8},
+ {XK_KP_9, KEY_KP9}, {XK_KP_Decimal, KEY_KPDEC},
+ {XK_KP_Separator, KEY_KPDEC},
+
+ // numpad without numlock
+ {XK_KP_Insert, KEY_KPINS}, {XK_KP_End, KEY_KP1}, {XK_KP_Down, KEY_KP2},
+ {XK_KP_Page_Down, KEY_KP3}, {XK_KP_Left, KEY_KP4}, {XK_KP_Begin, KEY_KP5},
+ {XK_KP_Right, KEY_KP6}, {XK_KP_Home, KEY_KP7}, {XK_KP_Up, KEY_KP8},
+ {XK_KP_Page_Up, KEY_KP9}, {XK_KP_Delete, KEY_KPDEL},
+
+#ifdef XF86XK_AudioPause
+ {XF86XK_MenuKB, KEY_MENU},
+ {XF86XK_AudioPlay, KEY_PLAY}, {XF86XK_AudioPause, KEY_PAUSE}, {XF86XK_AudioStop, KEY_STOP},
+ {XF86XK_AudioPrev, KEY_PREV}, {XF86XK_AudioNext, KEY_NEXT},
+ {XF86XK_AudioMute, KEY_MUTE}, {XF86XK_AudioLowerVolume, KEY_VOLUME_DOWN}, {XF86XK_AudioRaiseVolume, KEY_VOLUME_UP},
+#endif
+
+ {0, 0}
+};
+
+static int vo_x11_lookupkey(int key)
+{
+ static const char *passthrough_keys = " -+*/<>`~!@#$%^&()_{}:;\"\',.?\\|=[]";
+ int mpkey = 0;
+ if ((key >= 'a' && key <= 'z') ||
+ (key >= 'A' && key <= 'Z') ||
+ (key >= '0' && key <= '9') ||
+ (key > 0 && key < 256 && strchr(passthrough_keys, key)))
+ mpkey = key;
+
+ if (!mpkey)
+ mpkey = lookup_keymap_table(keymap, key);
+
+ return mpkey;
+}
+
+
+// ----- Motif header: -------
+
+#define MWM_HINTS_FUNCTIONS (1L << 0)
+#define MWM_HINTS_DECORATIONS (1L << 1)
+#define MWM_HINTS_INPUT_MODE (1L << 2)
+#define MWM_HINTS_STATUS (1L << 3)
+
+#define MWM_FUNC_ALL (1L << 0)
+#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)
+
+#define MWM_DECOR_ALL (1L << 0)
+#define MWM_DECOR_BORDER (1L << 1)
+#define MWM_DECOR_RESIZEH (1L << 2)
+#define MWM_DECOR_TITLE (1L << 3)
+#define MWM_DECOR_MENU (1L << 4)
+#define MWM_DECOR_MINIMIZE (1L << 5)
+#define MWM_DECOR_MAXIMIZE (1L << 6)
+
+#define MWM_INPUT_MODELESS 0
+#define MWM_INPUT_PRIMARY_APPLICATION_MODAL 1
+#define MWM_INPUT_SYSTEM_MODAL 2
+#define MWM_INPUT_FULL_APPLICATION_MODAL 3
+#define MWM_INPUT_APPLICATION_MODAL MWM_INPUT_PRIMARY_APPLICATION_MODAL
+
+#define MWM_TEAROFF_WINDOW (1L<<0)
+
+typedef struct
+{
+ long flags;
+ long functions;
+ long decorations;
+ long input_mode;
+ long state;
+} MotifWmHints;
+
+static MotifWmHints vo_MotifWmHints;
+static Atom vo_MotifHints = None;
+
+void vo_x11_decoration(struct vo *vo, int d)
+{
+ struct vo_x11_state *x11 = vo->x11;
+ Atom mtype;
+ int mformat;
+ unsigned long mn, mb;
+
+ if (!WinID)
+ return;
+
+ if (vo_fsmode & 8)
+ {
+ XSetTransientForHint(x11->display, x11->window,
+ RootWindow(x11->display, x11->screen));
+ }
+
+ vo_MotifHints = XInternAtom(x11->display, "_MOTIF_WM_HINTS", 0);
+ if (vo_MotifHints != None)
+ {
+ if (!d)
+ {
+ MotifWmHints *mhints = NULL;
+
+ XGetWindowProperty(x11->display, x11->window,
+ vo_MotifHints, 0, 20, False,
+ vo_MotifHints, &mtype, &mformat, &mn,
+ &mb, (unsigned char **) &mhints);
+ if (mhints)
+ {
+ if (mhints->flags & MWM_HINTS_DECORATIONS)
+ x11->olddecor = mhints->decorations;
+ if (mhints->flags & MWM_HINTS_FUNCTIONS)
+ x11->oldfuncs = mhints->functions;
+ XFree(mhints);
+ }
+ }
+
+ memset(&vo_MotifWmHints, 0, sizeof(MotifWmHints));
+ vo_MotifWmHints.flags =
+ MWM_HINTS_FUNCTIONS | MWM_HINTS_DECORATIONS;
+ if (d)
+ {
+ vo_MotifWmHints.functions = x11->oldfuncs;
+ d = x11->olddecor;
+ }
+#if 0
+ vo_MotifWmHints.decorations =
+ d | ((vo_fsmode & 2) ? 0 : MWM_DECOR_MENU);
+#else
+ vo_MotifWmHints.decorations =
+ d | ((vo_fsmode & 2) ? MWM_DECOR_MENU : 0);
+#endif
+ XChangeProperty(x11->display, x11->window, vo_MotifHints,
+ vo_MotifHints, 32,
+ PropModeReplace,
+ (unsigned char *) &vo_MotifWmHints,
+ (vo_fsmode & 4) ? 4 : 5);
+ }
+}
+
+void vo_x11_classhint(struct vo *vo, Window window, const char *name)
+{
+ struct MPOpts *opts = vo->opts;
+ struct vo_x11_state *x11 = vo->x11;
+ XClassHint wmClass;
+ pid_t pid = getpid();
+
+ wmClass.res_name = opts->vo_winname ? opts->vo_winname : (char *)name;
+ wmClass.res_class = "mpv";
+ XSetClassHint(x11->display, window, &wmClass);
+ XChangeProperty(x11->display, window, x11->XA_NET_WM_PID, XA_CARDINAL,
+ 32, PropModeReplace, (unsigned char *) &pid, 1);
+}
+
+void vo_x11_uninit(struct vo *vo)
+{
+ struct vo_x11_state *x11 = vo->x11;
+ saver_on(x11->display);
+ if (x11->window != None)
+ vo_showcursor(x11->display, x11->window);
+
+ if (x11->f_gc != None)
+ {
+ XFreeGC(vo->x11->display, x11->f_gc);
+ x11->f_gc = None;
+ }
+ {
+ if (x11->vo_gc != None)
+ {
+ XFreeGC(vo->x11->display, x11->vo_gc);
+ x11->vo_gc = None;
+ }
+ if (x11->window != None)
+ {
+ XClearWindow(x11->display, x11->window);
+ if (WinID < 0)
+ {
+ XEvent xev;
+
+ if (x11->xic)
+ XDestroyIC(x11->xic);
+ x11->xic = NULL;
+
+ XUnmapWindow(x11->display, x11->window);
+ XSelectInput(x11->display, x11->window, StructureNotifyMask);
+ XDestroyWindow(x11->display, x11->window);
+ do
+ {
+ XNextEvent(x11->display, &xev);
+ }
+ while (xev.type != DestroyNotify
+ || xev.xdestroywindow.event != x11->window);
+ }
+ x11->window = None;
+ }
+ vo_fs = 0;
+ x11->vo_old_width = x11->vo_old_height = 0;
+ x11->last_video_width = 0;
+ x11->last_video_height = 0;
+ x11->size_changed_during_fs = false;
+ }
+ vo_uninit(x11);
+ vo->x11 = NULL;
+}
+
+static int check_resize(struct vo *vo)
+{
+ int old_w = vo->dwidth, old_h = vo->dheight;
+ int old_x = vo->dx, old_y = vo->dy;
+ int rc = 0;
+ vo_x11_update_geometry(vo, true);
+ if (vo->dwidth != old_w || vo->dheight != old_h)
+ rc |= VO_EVENT_RESIZE;
+ if (vo->dx != old_x || vo->dy != old_y)
+ rc |= VO_EVENT_MOVE;
+ return rc;
+}
+
+int vo_x11_check_events(struct vo *vo)
+{
+ struct vo_x11_state *x11 = vo->x11;
+ struct MPOpts *opts = vo->opts;
+ Display *display = vo->x11->display;
+ int ret = 0;
+ XEvent Event;
+
+ if (x11->mouse_waiting_hide && opts->cursor_autohide_delay != -1 &&
+ (GetTimerMS() - x11->mouse_timer >= opts->cursor_autohide_delay)) {
+ vo_hidecursor(display, x11->window);
+ x11->mouse_waiting_hide = 0;
+ }
+
+ if (WinID > 0)
+ ret |= check_resize(vo);
+ while (XPending(display))
+ {
+ XNextEvent(display, &Event);
+// printf("\rEvent.type=%X \n",Event.type);
+ switch (Event.type)
+ {
+ case Expose:
+ ret |= VO_EVENT_EXPOSE;
+ break;
+ case ConfigureNotify:
+ if (x11->window == None)
+ break;
+ ret |= check_resize(vo);
+ break;
+ case KeyPress:
+ {
+ char buf[100];
+ KeySym keySym = 0;
+ int modifiers = 0;
+ if (Event.xkey.state & ShiftMask)
+ modifiers |= KEY_MODIFIER_SHIFT;
+ if (Event.xkey.state & ControlMask)
+ modifiers |= KEY_MODIFIER_CTRL;
+ if (Event.xkey.state & Mod1Mask)
+ modifiers |= KEY_MODIFIER_ALT;
+ if (Event.xkey.state & Mod4Mask)
+ modifiers |= KEY_MODIFIER_META;
+ 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) {
+ mplayer_put_key(vo->key_fifo, mpkey | modifiers);
+ } else if (status == XLookupChars
+ || status == XLookupBoth)
+ {
+ struct bstr t = { buf, len };
+ mplayer_put_key_utf8(vo->key_fifo, modifiers, t);
+ }
+ } else {
+ XLookupString(&Event.xkey, buf, sizeof(buf), &keySym,
+ &x11->compose_status);
+ int mpkey = vo_x11_lookupkey(keySym);
+ if (mpkey)
+ mplayer_put_key(vo->key_fifo, mpkey | modifiers);
+ }
+ ret |= VO_EVENT_KEYPRESS;
+ }
+ break;
+ case MotionNotify:
+ vo_mouse_movement(vo, Event.xmotion.x, Event.xmotion.y);
+
+ if (opts->cursor_autohide_delay > -2) {
+ vo_showcursor(display, x11->window);
+ x11->mouse_waiting_hide = 1;
+ x11->mouse_timer = GetTimerMS();
+ }
+ break;
+ case ButtonPress:
+ if (opts->cursor_autohide_delay > -2) {
+ vo_showcursor(display, x11->window);
+ x11->mouse_waiting_hide = 1;
+ x11->mouse_timer = GetTimerMS();
+ }
+ mplayer_put_key(vo->key_fifo,
+ (MOUSE_BTN0 + Event.xbutton.button - 1)
+ | MP_KEY_DOWN);
+ break;
+ case ButtonRelease:
+ if (opts->cursor_autohide_delay > -2) {
+ vo_showcursor(display, x11->window);
+ x11->mouse_waiting_hide = 1;
+ x11->mouse_timer = GetTimerMS();
+ }
+ mplayer_put_key(vo->key_fifo,
+ MOUSE_BTN0 + Event.xbutton.button - 1);
+ break;
+ case PropertyNotify:
+ {
+ char *name =
+ XGetAtomName(display, Event.xproperty.atom);
+
+ if (!name)
+ break;
+
+// fprintf(stderr,"[ws] PropertyNotify ( 0x%x ) %s ( 0x%x )\n",vo_window,name,Event.xproperty.atom );
+
+ XFree(name);
+ }
+ break;
+ case MapNotify:
+ x11->vo_hint.win_gravity = x11->old_gravity;
+ XSetWMNormalHints(display, x11->window, &x11->vo_hint);
+ x11->fs_flip = 0;
+ break;
+ case DestroyNotify:
+ mp_msg(MSGT_VO, MSGL_WARN, "Our window was destroyed, exiting\n");
+ mplayer_put_key(vo->key_fifo, KEY_CLOSE_WIN);
+ break;
+ case ClientMessage:
+ if (Event.xclient.message_type == x11->XAWM_PROTOCOLS &&
+ Event.xclient.data.l[0] == x11->XAWM_DELETE_WINDOW)
+ mplayer_put_key(vo->key_fifo, KEY_CLOSE_WIN);
+ break;
+ }
+ }
+ return ret;
+}
+
+/**
+ * \brief sets the size and position of the non-fullscreen window.
+ */
+static void vo_x11_nofs_sizepos(struct vo *vo, int x, int y,
+ int width, int height)
+{
+ struct vo_x11_state *x11 = vo->x11;
+ if (width == x11->last_video_width && height == x11->last_video_height) {
+ if (!vo->opts->force_window_position && !x11->size_changed_during_fs)
+ return;
+ } else if (vo_fs)
+ x11->size_changed_during_fs = true;
+ x11->last_video_height = height;
+ x11->last_video_width = width;
+ vo_x11_sizehint(vo, x, y, width, height, 0);
+ if (vo_fs) {
+ x11->vo_old_x = x;
+ x11->vo_old_y = y;
+ x11->vo_old_width = width;
+ x11->vo_old_height = height;
+ }
+ else
+ {
+ vo->dwidth = width;
+ vo->dheight = height;
+ if (vo->opts->force_window_position)
+ XMoveResizeWindow(vo->x11->display, vo->x11->window, x, y, width,
+ height);
+ else
+ XResizeWindow(vo->x11->display, vo->x11->window, width, height);
+ }
+}
+
+void vo_x11_sizehint(struct vo *vo, int x, int y, int width, int height, int max)
+{
+ struct vo_x11_state *x11 = vo->x11;
+ x11->vo_hint.flags = 0;
+ if (vo_keepaspect)
+ {
+ x11->vo_hint.flags |= PAspect;
+ x11->vo_hint.min_aspect.x = width;
+ x11->vo_hint.min_aspect.y = height;
+ x11->vo_hint.max_aspect.x = width;
+ x11->vo_hint.max_aspect.y = height;
+ }
+
+ x11->vo_hint.flags |= PPosition | PSize;
+ x11->vo_hint.x = x;
+ x11->vo_hint.y = y;
+ x11->vo_hint.width = width;
+ x11->vo_hint.height = height;
+ if (max)
+ {
+ x11->vo_hint.flags |= PMaxSize;
+ x11->vo_hint.max_width = width;
+ x11->vo_hint.max_height = height;
+ } else
+ {
+ x11->vo_hint.max_width = 0;
+ x11->vo_hint.max_height = 0;
+ }
+
+ // Set minimum height/width to 4 to avoid off-by-one errors.
+ x11->vo_hint.flags |= PMinSize;
+ x11->vo_hint.min_width = x11->vo_hint.min_height = 4;
+
+ // Set the base size. A window manager might display the window
+ // size to the user relative to this.
+ // Setting these to width/height might be nice, but e.g. fluxbox can't handle it.
+ x11->vo_hint.flags |= PBaseSize;
+ x11->vo_hint.base_width = 0 /*width*/;
+ x11->vo_hint.base_height = 0 /*height*/;
+
+ x11->vo_hint.flags |= PWinGravity;
+ x11->vo_hint.win_gravity = StaticGravity;
+ XSetWMNormalHints(x11->display, x11->window, &x11->vo_hint);
+}
+
+static int vo_x11_get_gnome_layer(struct vo_x11_state *x11, Window win)
+{
+ Atom type;
+ int format;
+ unsigned long nitems;
+ unsigned long bytesafter;
+ unsigned short *args = NULL;
+
+ if (XGetWindowProperty(x11->display, win, x11->XA_WIN_LAYER, 0, 16384,
+ False, AnyPropertyType, &type, &format, &nitems,
+ &bytesafter,
+ (unsigned char **) &args) == Success
+ && nitems > 0 && args)
+ {
+ mp_msg(MSGT_VO, MSGL_V, "[x11] original window layer is %d.\n",
+ *args);
+ return *args;
+ }
+ return WIN_LAYER_NORMAL;
+}
+
+// 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;
+
+ XChangeProperty(x11->display, x11->window, name, x11->XAUTF8_STRING, 8,
+ PropModeReplace, t, strlen(t));
+}
+
+// set a X text property that expects a STRING or COMPOUND_TEXT type
+static void vo_x11_set_property_string(struct vo *vo, Atom name, const char *t)
+{
+ struct vo_x11_state *x11 = vo->x11;
+