summaryrefslogtreecommitdiff
path: root/video.c
diff options
context:
space:
mode:
Diffstat (limited to 'video.c')
-rw-r--r--video.c775
1 files changed, 775 insertions, 0 deletions
diff --git a/video.c b/video.c
new file mode 100644
index 0000000..7fe2fff
--- /dev/null
+++ b/video.c
@@ -0,0 +1,775 @@
+///
+/// @file video.c @brief Video module
+///
+/// Copyright (c) 2012, 2013 by Johns. All Rights Reserved.
+///
+/// Contributor(s):
+///
+/// License: AGPLv3
+///
+/// This program is free software: you can redistribute it and/or modify
+/// it under the terms of the GNU Affero General Public License as
+/// published by the Free Software Foundation, either version 3 of the
+/// License.
+///
+/// This program 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 Affero General Public License for more details.
+///
+/// $Id: 54d117902655b23d2eff9ecaf59ce4eab5f46d42 $
+//////////////////////////////////////////////////////////////////////////////
+
+///
+/// @defgroup Video The video module.
+///
+/// This module contains all video rendering functions.
+///
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <libintl.h>
+#define _(str) gettext(str) ///< gettext shortcut
+#define _N(str) str ///< gettext_noop shortcut
+
+#include "misc.h"
+#include "video.h"
+
+//////////////////////////////////////////////////////////////////////////////
+// X11 / XCB
+//////////////////////////////////////////////////////////////////////////////
+
+#include <xcb/xcb.h>
+#include <xcb/xcb_icccm.h>
+#include <xcb/xcb_image.h>
+#include <xcb/xcb_pixel.h>
+#include <xcb/xcb_event.h>
+#include <xcb/xcb_keysyms.h>
+#include <X11/keysym.h> // keysym XK_
+#include <X11/XF86keysym.h> // XF86XK_
+#include <poll.h>
+
+static xcb_connection_t *Connection; ///< xcb connection
+static xcb_colormap_t VideoColormap; ///< video colormap
+static xcb_window_t VideoOsdWindow; ///< video osd window
+static xcb_window_t VideoPlayWindow; ///< video player window
+static xcb_screen_t const *VideoScreen; ///< video screen
+static uint32_t VideoBlankTick; ///< blank cursor timer
+static xcb_pixmap_t VideoPixmap; ///< blank cursor pixmap
+static xcb_cursor_t VideoBlankCursor; ///< empty invisible cursor
+
+static uint32_t VideoColorKey; ///< color key pixel value
+
+static int VideoWindowX; ///< video output window x coordinate
+static int VideoWindowY; ///< video outout window y coordinate
+static unsigned VideoWindowWidth; ///< video output window width
+static unsigned VideoWindowHeight; ///< video output window height
+
+static char Osd3DMode; ///< 3D OSD mode
+
+///
+/// Create X11 window.
+///
+/// @param parent parent of new window
+/// @param visual visual of parent
+/// @param depth depth of parent
+///
+/// @returns created X11 window id
+///
+static xcb_window_t VideoCreateWindow(xcb_window_t parent,
+ xcb_visualid_t visual, uint8_t depth)
+{
+ uint32_t values[5];
+ xcb_window_t window;
+
+ Debug(3, "video: visual %#0x depth %d\n", visual, depth);
+
+ //
+ // create color map
+ //
+ if (VideoColormap == XCB_NONE) {
+ VideoColormap = xcb_generate_id(Connection);
+ xcb_create_colormap(Connection, XCB_COLORMAP_ALLOC_NONE, VideoColormap,
+ parent, visual);
+ }
+ //
+ // create blank cursor
+ //
+ if (VideoBlankCursor == XCB_NONE) {
+ VideoPixmap = xcb_generate_id(Connection);
+ xcb_create_pixmap(Connection, 1, VideoPixmap, parent, 1, 1);
+ VideoBlankCursor = xcb_generate_id(Connection);
+ xcb_create_cursor(Connection, VideoBlankCursor, VideoPixmap,
+ VideoPixmap, 0, 0, 0, 0, 0, 0, 1, 1);
+ VideoBlankTick = 0;
+ }
+
+ values[0] = VideoColorKey; // ARGB
+ values[1] = VideoColorKey;
+ values[2] =
+ XCB_EVENT_MASK_KEY_PRESS | XCB_EVENT_MASK_KEY_RELEASE |
+ XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE |
+ XCB_EVENT_MASK_POINTER_MOTION | XCB_EVENT_MASK_EXPOSURE |
+ XCB_EVENT_MASK_STRUCTURE_NOTIFY;
+ values[3] = VideoColormap;
+ values[4] = VideoBlankCursor;
+ window = xcb_generate_id(Connection);
+ xcb_create_window(Connection, depth, window, parent, VideoWindowX,
+ VideoWindowY, VideoWindowWidth, VideoWindowHeight, 0,
+ XCB_WINDOW_CLASS_INPUT_OUTPUT, visual,
+ XCB_CW_BACK_PIXEL | XCB_CW_BORDER_PIXEL | XCB_CW_EVENT_MASK |
+ XCB_CW_COLORMAP | XCB_CW_CURSOR, values);
+
+ // define only available with xcb-utils-0.3.8
+#ifdef XCB_ICCCM_NUM_WM_SIZE_HINTS_ELEMENTS
+ // FIXME: utf _NET_WM_NAME
+ xcb_icccm_set_wm_name(Connection, window, XCB_ATOM_STRING, 8,
+ sizeof("play control") - 1, "play control");
+ xcb_icccm_set_wm_icon_name(Connection, window, XCB_ATOM_STRING, 8,
+ sizeof("play control") - 1, "play control");
+#endif
+ // define only available with xcb-utils-0.3.6
+#ifdef XCB_NUM_WM_HINTS_ELEMENTS
+ // FIXME: utf _NET_WM_NAME
+ xcb_set_wm_name(Connection, window, XCB_ATOM_STRING,
+ sizeof("play control") - 1, "play control");
+ xcb_set_wm_icon_name(Connection, window, XCB_ATOM_STRING,
+ sizeof("play control") - 1, "play control");
+#endif
+
+ // FIXME: size hints
+
+ // window above parent
+ values[0] = parent;
+ values[1] = XCB_STACK_MODE_ABOVE;
+ xcb_configure_window(Connection, window,
+ XCB_CONFIG_WINDOW_SIBLING | XCB_CONFIG_WINDOW_STACK_MODE, values);
+
+ return window;
+}
+
+///
+/// Enable OSD 3d mode.
+///
+/// @param mode turn 3d mode on/off
+///
+void VideoSetOsd3DMode(int mode)
+{
+ Osd3DMode = mode;
+}
+
+///
+/// Draw a ARGB image.
+///
+/// @param x x position of image in osd
+/// @param y y position of image in osd
+/// @param width width of image
+/// @param height height of image
+/// @param argb argb image
+///
+void VideoDrawARGB(int x, int y, int width, int height, const uint8_t * argb)
+{
+ xcb_image_t *xcb_image;
+ xcb_gcontext_t gc;
+ int sx;
+ int sy;
+ int fs;
+
+ if (!Connection) {
+ Debug(3, "play: FIXME: must restore osd provider\n");
+ return;
+ }
+
+ if (x + y < 1 && (unsigned)height == VideoWindowHeight
+ && (unsigned)width == VideoWindowWidth) {
+ fs = 1;
+ } else {
+ fs = 0;
+ }
+
+ gc = xcb_generate_id(Connection);
+ xcb_create_gc(Connection, gc, VideoOsdWindow, 0, NULL);
+
+ switch (Osd3DMode) {
+ case 1: // SBS
+ xcb_image =
+ xcb_image_create_native(Connection, width / 2, height,
+ XCB_IMAGE_FORMAT_Z_PIXMAP, VideoScreen->root_depth, NULL, 0L,
+ NULL);
+ break;
+ case 2: // TB
+ xcb_image =
+ xcb_image_create_native(Connection, width, height / 2,
+ XCB_IMAGE_FORMAT_Z_PIXMAP, VideoScreen->root_depth, NULL, 0L,
+ NULL);
+ break;
+ default:
+ xcb_image =
+ xcb_image_create_native(Connection, width, height,
+ XCB_IMAGE_FORMAT_Z_PIXMAP, VideoScreen->root_depth, NULL, 0L,
+ NULL);
+ }
+
+ // fast 32it versions
+ if (xcb_image->bpp == 32) {
+ if (xcb_image->byte_order == XCB_IMAGE_ORDER_LSB_FIRST) {
+ for (sy = 0; sy < height; ++sy) {
+ for (sx = 0; sx < width; ++sx) {
+ uint32_t pixel;
+
+ if (argb[(width * sy + sx) * 4 + 3] < 200) {
+ pixel = VideoColorKey;
+ } else {
+ pixel = argb[(width * sy + sx) * 4 + 0] << 0;
+ pixel |= argb[(width * sy + sx) * 4 + 1] << 8;
+ pixel |= argb[(width * sy + sx) * 4 + 2] << 16;
+ }
+ switch (Osd3DMode) {
+ case 1: // SBS
+ xcb_image_put_pixel_Z32L(xcb_image, sx / 2, sy,
+ pixel);
+ break;
+ case 2: // TB
+ xcb_image_put_pixel_Z32L(xcb_image, sx, sy / 2,
+ pixel);
+ break;
+ default:
+ xcb_image_put_pixel_Z32L(xcb_image, sx, sy, pixel);
+ }
+ }
+ }
+ } else {
+ Error(_("play: unsupported put_image\n"));
+ }
+ } else {
+ for (sy = 0; sy < height; ++sy) {
+ for (sx = 0; sx < width; ++sx) {
+ uint32_t pixel;
+
+ if (argb[(width * sy + sx) * 4 + 3] < 200) {
+ pixel = argb[(width * sy + sx) * 4 + 3] << 0;
+ pixel |= argb[(width * sy + sx) * 4 + 3] << 8;
+ pixel |= argb[(width * sy + sx) * 4 + 3] << 16;
+ } else {
+ pixel = argb[(width * sy + sx) * 4 + 0] << 0;
+ pixel |= argb[(width * sy + sx) * 4 + 1] << 8;
+ pixel |= argb[(width * sy + sx) * 4 + 2] << 16;
+ }
+ switch (Osd3DMode) {
+ case 1: // SBS
+ xcb_image_put_pixel(xcb_image, sx / 2, sy, pixel);
+ break;
+ case 2: // TB
+ xcb_image_put_pixel(xcb_image, sx, sy / 2, pixel);
+ break;
+ default:
+ xcb_image_put_pixel(xcb_image, sx, sy, pixel);
+ }
+ }
+ }
+ }
+
+ // render xcb_image to color data pixmap
+ switch (Osd3DMode) {
+ case 1: // SBS
+ if (fs) {
+ xcb_image_put(Connection, VideoOsdWindow, gc, xcb_image, x, y,
+ 0);
+ xcb_image_put(Connection, VideoOsdWindow, gc, xcb_image,
+ x + VideoWindowWidth / 2, y, 0);
+ } else {
+ xcb_image_put(Connection, VideoOsdWindow, gc, xcb_image, x / 2,
+ y, 0);
+ xcb_image_put(Connection, VideoOsdWindow, gc, xcb_image,
+ x / 2 + VideoWindowWidth / 2, y, 0);
+ }
+ break;
+ case 2: // TB
+ if (fs) {
+ xcb_image_put(Connection, VideoOsdWindow, gc, xcb_image, x, y,
+ 0);
+ xcb_image_put(Connection, VideoOsdWindow, gc, xcb_image, x,
+ y + VideoWindowHeight / 2, 0);
+ } else {
+ xcb_image_put(Connection, VideoOsdWindow, gc, xcb_image, x,
+ y / 2, 0);
+ xcb_image_put(Connection, VideoOsdWindow, gc, xcb_image, x,
+ y / 2 + VideoWindowHeight / 2, 0);
+ }
+ break;
+ default:
+ xcb_image_put(Connection, VideoOsdWindow, gc, xcb_image, x, y, 0);
+ }
+ // release xcb_image
+ xcb_image_destroy(xcb_image);
+ xcb_free_gc(Connection, gc);
+ xcb_flush(Connection);
+}
+
+///
+/// Show window.
+///
+void VideoWindowShow(void)
+{
+ if (!Connection) {
+ Debug(3, "play: FIXME: must restore osd provider\n");
+ return;
+ }
+ xcb_map_window(Connection, VideoOsdWindow);
+}
+
+///
+/// Hide window.
+///
+void VideoWindowHide(void)
+{
+ if (!Connection) {
+ Debug(3, "play: FIXME: must restore osd provider\n");
+ return;
+ }
+ xcb_unmap_window(Connection, VideoOsdWindow);
+}
+
+///
+/// Clear window.
+///
+void VideoWindowClear(void)
+{
+ if (!Connection) {
+ Debug(3, "play: FIXME: must restore osd provider\n");
+ return;
+ }
+ xcb_clear_area(Connection, 0, VideoOsdWindow, 0, 0, VideoWindowWidth,
+ VideoWindowHeight);
+ xcb_flush(Connection);
+}
+
+static xcb_key_symbols_t *XcbKeySymbols; ///< Keyboard symbols
+static uint16_t NumLockMask; ///< mod mask for num-lock
+static uint16_t ShiftLockMask; ///< mod mask for shift-lock
+static uint16_t CapsLockMask; ///< mod mask for caps-lock
+static uint16_t ModeSwitchMask; ///< mod mask for mode-switch
+
+///
+/// Handle key press event.
+///
+static void VideoKeyPress(const xcb_key_press_event_t * event)
+{
+ char buf[2];
+ xcb_keysym_t ks0;
+ xcb_keysym_t ks1;
+ xcb_keysym_t keysym;
+ xcb_keycode_t keycode;
+ unsigned modifier;
+
+ if (!XcbKeySymbols) {
+ XcbKeySymbols = xcb_key_symbols_alloc(Connection);
+ if (!XcbKeySymbols) {
+ Error(_("play/event: can't read key symbols\n"));
+ return;
+ }
+ NumLockMask = ShiftLockMask = CapsLockMask = ModeSwitchMask = 0;
+
+ // FIXME: lock and mode keys are not prepared!
+ }
+
+ keycode = event->detail;
+ modifier = event->state;
+ // handle mode-switch
+ if (modifier & ModeSwitchMask) {
+ ks0 = xcb_key_symbols_get_keysym(XcbKeySymbols, keycode, 2);
+ ks1 = xcb_key_symbols_get_keysym(XcbKeySymbols, keycode, 3);
+ } else {
+ ks0 = xcb_key_symbols_get_keysym(XcbKeySymbols, keycode, 0);
+ ks1 = xcb_key_symbols_get_keysym(XcbKeySymbols, keycode, 1);
+ }
+ // use first keysym, if second keysym didn't exists
+ if (ks1 == XCB_NO_SYMBOL) {
+ ks1 = ks0;
+ }
+ // see xcb-util-0.3.6/keysyms/keysyms.c:
+ if (!(modifier & XCB_MOD_MASK_SHIFT) && !(modifier & XCB_MOD_MASK_LOCK)) {
+ keysym = ks0;
+ } else {
+ // FIXME: more cases
+
+ keysym = ks0;
+ }
+
+ // FIXME: use xcb_lookup_string
+ switch (keysym) {
+ case XK_space:
+ FeedKeyPress("XKeySym", "space", 0, 0);
+ break;
+ case XK_exclam ... XK_slash:
+ case XK_0 ... XK_9:
+ case XK_A ... XK_Z:
+ case XK_a ... XK_z:
+ buf[0] = keysym;
+ buf[1] = '\0';
+ FeedKeyPress("XKeySym", buf, 0, 0);
+ break;
+
+ case XK_BackSpace:
+ FeedKeyPress("XKeySym", "BackSpace", 0, 0);
+ break;
+ case XK_Tab:
+ FeedKeyPress("XKeySym", "Tab", 0, 0);
+ break;
+ case XK_Return:
+ FeedKeyPress("XKeySym", "Return", 0, 0);
+ break;
+ case XK_Escape:
+ FeedKeyPress("XKeySym", "Escape", 0, 0);
+ break;
+ case XK_Delete:
+ FeedKeyPress("XKeySym", "Delete", 0, 0);
+ break;
+
+ case XK_Home:
+ FeedKeyPress("XKeySym", "Home", 0, 0);
+ break;
+ case XK_Left:
+ FeedKeyPress("XKeySym", "Left", 0, 0);
+ break;
+ case XK_Up:
+ FeedKeyPress("XKeySym", "Up", 0, 0);
+ break;
+ case XK_Right:
+ FeedKeyPress("XKeySym", "Right", 0, 0);
+ break;
+ case XK_Down:
+ FeedKeyPress("XKeySym", "Down", 0, 0);
+ break;
+ case XK_Page_Up:
+ FeedKeyPress("XKeySym", "Page_Up", 0, 0);
+ break;
+ case XK_Page_Down:
+ FeedKeyPress("XKeySym", "Page_Down", 0, 0);
+ break;
+ case XK_End:
+ FeedKeyPress("XKeySym", "End", 0, 0);
+ break;
+ case XK_Begin:
+ FeedKeyPress("XKeySym", "Begin", 0, 0);
+ break;
+
+ case XK_F1:
+ FeedKeyPress("XKeySym", "F1", 0, 0);
+ break;
+ case XK_F2:
+ FeedKeyPress("XKeySym", "F2", 0, 0);
+ break;
+ case XK_F3:
+ FeedKeyPress("XKeySym", "F3", 0, 0);
+ break;
+ case XK_F4:
+ FeedKeyPress("XKeySym", "F4", 0, 0);
+ break;
+ case XK_F5:
+ FeedKeyPress("XKeySym", "F5", 0, 0);
+ break;
+ case XK_F6:
+ FeedKeyPress("XKeySym", "F6", 0, 0);
+ break;
+ case XK_F7:
+ FeedKeyPress("XKeySym", "F7", 0, 0);
+ break;
+ case XK_F8:
+ FeedKeyPress("XKeySym", "F8", 0, 0);
+ break;
+ case XK_F9:
+ FeedKeyPress("XKeySym", "F9", 0, 0);
+ break;
+ case XK_F10:
+ FeedKeyPress("XKeySym", "F10", 0, 0);
+ break;
+ case XK_F11:
+ FeedKeyPress("XKeySym", "F11", 0, 0);
+ break;
+ case XK_F12:
+ FeedKeyPress("XKeySym", "F12", 0, 0);
+ break;
+
+ case XF86XK_Red:
+ FeedKeyPress("XKeySym", "Red", 0, 0);
+ break;
+ case XF86XK_Green:
+ FeedKeyPress("XKeySym", "Green", 0, 0);
+ break;
+ case XF86XK_Yellow:
+ FeedKeyPress("XKeySym", "Yellow", 0, 0);
+ break;
+ case XF86XK_Blue:
+ FeedKeyPress("XKeySym", "Blue", 0, 0);
+ break;
+
+ case XF86XK_HomePage:
+ FeedKeyPress("XKeySym", "XF86HomePage", 0, 0);
+ break;
+ case XF86XK_AudioLowerVolume:
+ FeedKeyPress("XKeySym", "XF86AudioLowerVolume", 0, 0);
+ break;
+ case XF86XK_AudioMute:
+ FeedKeyPress("XKeySym", "XF86AudioMute", 0, 0);
+ break;
+ case XF86XK_AudioRaiseVolume:
+ FeedKeyPress("XKeySym", "XF86AudioRaiseVolume", 0, 0);
+ break;
+ case XF86XK_AudioPlay:
+ FeedKeyPress("XKeySym", "XF86AudioPlay", 0, 0);
+ break;
+ case XF86XK_AudioStop:
+ FeedKeyPress("XKeySym", "XF86AudioStop", 0, 0);
+ break;
+ case XF86XK_AudioPrev:
+ FeedKeyPress("XKeySym", "XF86AudioPrev", 0, 0);
+ break;
+ case XF86XK_AudioNext:
+ FeedKeyPress("XKeySym", "XF86AudioNext", 0, 0);
+ break;
+
+ default:
+ Debug(3, "play/event: keycode %d\n", event->detail);
+ break;
+
+ }
+}
+
+///
+/// Poll video events.
+///
+/// @param timeout timeout in milliseconds
+///
+void VideoPollEvents(int timeout)
+{
+ struct pollfd fds[1];
+ xcb_generic_event_t *event;
+ int n;
+ int delay;
+
+ if (!Connection) {
+ Debug(3, "play: poll without connection\n");
+ return;
+ }
+
+ fds[0].fd = xcb_get_file_descriptor(Connection);
+ fds[0].events = POLLIN | POLLPRI;
+
+ delay = timeout;
+ for (;;) {
+ xcb_flush(Connection);
+
+ // wait for events or timeout
+ // FIXME: this can poll forever
+ if ((n = poll(fds, 1, delay)) <= 0) {
+ // error or timeout
+ if (n) { // error
+ Error(_("play/event: poll failed: %s\n"), strerror(errno));
+ }
+ return;
+ }
+ if (fds[0].revents & (POLLIN | POLLPRI)) {
+ if ((event = xcb_poll_for_event(Connection))) {
+
+ switch (XCB_EVENT_RESPONSE_TYPE(event)) {
+#if 0
+ // background pixmap no need to redraw
+ case XCB_EXPOSE:
+ // collapse multi expose
+ if (!((xcb_expose_event_t *) event)->count) {
+ xcb_clear_area(Connection, 0, Window, 0, 0, 64,
+ 64);
+ // flush the request
+ xcb_flush(Connection);
+ }
+ break;
+#endif
+ case XCB_MAP_NOTIFY:
+ Debug(3, "video/event: MapNotify\n");
+ // hide cursor after mapping
+ xcb_change_window_attributes(Connection,
+ VideoOsdWindow, XCB_CW_CURSOR, &VideoBlankCursor);
+ xcb_change_window_attributes(Connection,
+ VideoPlayWindow, XCB_CW_CURSOR, &VideoBlankCursor);
+ break;
+ case XCB_DESTROY_NOTIFY:
+ return;
+ case XCB_KEY_PRESS:
+ VideoKeyPress((xcb_key_press_event_t *) event);
+ break;
+ case XCB_KEY_RELEASE:
+ case XCB_BUTTON_PRESS:
+ case XCB_BUTTON_RELEASE:
+ break;
+ case XCB_MOTION_NOTIFY:
+ break;
+
+ case 0:
+ // error_code
+ Debug(3, "play/event: error %x\n",
+ event->response_type);
+ break;
+ default:
+ // unknown event type, ignore it
+ Debug(3, "play/event: unknown %x\n",
+ event->response_type);
+ break;
+ }
+
+ free(event);
+ } else {
+ // no event, can happen, but we must check for close
+ if (xcb_connection_has_error(Connection)) {
+ return;
+ }
+ }
+ }
+ }
+}
+
+///
+/// Get OSD size.
+///
+/// @param[out] width OSD width
+/// @param[out] height OSD height
+///
+void VideoGetOsdSize(int *width, int *height)
+{
+ *width = 1920;
+ *height = 1080; // unknown default
+ if (VideoWindowWidth && VideoWindowHeight) {
+ *width = VideoWindowWidth;
+ *height = VideoWindowHeight;
+ }
+}
+
+///
+/// Get player video window id.
+///
+int VideoGetPlayWindow(void)
+{
+ return VideoPlayWindow;
+}
+
+///
+/// Set video geometry.
+///
+/// @todo write/search the good version
+///
+void VideoSetGeometry(const char *geometry)
+{
+ sscanf(geometry, "%dx%d%d%d", &VideoWindowWidth, &VideoWindowHeight,
+ &VideoWindowX, &VideoWindowY);
+}
+
+///
+/// Set video color key.
+///
+/// Should be called before VideoInit().
+///
+void VideoSetColorKey(uint32_t color_key)
+{
+ VideoColorKey = color_key;
+}
+
+///
+/// Initialize video.
+///
+int VideoInit(const char *display)
+{
+ const char *display_name;
+ xcb_connection_t *connection;
+ xcb_screen_iterator_t iter;
+ int screen_nr;
+ int i;
+
+ display_name = display ? display : getenv("DISPLAY");
+
+ // Open the connection to the X server.
+ connection = xcb_connect(display_name, &screen_nr);
+ if (!connection || xcb_connection_has_error(connection)) {
+ fprintf(stderr, "play: can't connect to X11 server on %s\n",
+ display_name);
+ return -1;
+ }
+ Connection = connection;
+
+ // Get the requested screen number
+ iter = xcb_setup_roots_iterator(xcb_get_setup(connection));
+ for (i = 0; i < screen_nr; ++i) {
+ xcb_screen_next(&iter);
+ }
+ VideoScreen = iter.data;
+
+ //
+ // Default window size
+ //
+ if (!VideoWindowHeight) {
+ if (VideoWindowWidth) {
+ VideoWindowHeight = (VideoWindowWidth * 9) / 16;
+ } else { // default to fullscreen
+ VideoWindowHeight = VideoScreen->height_in_pixels;
+ VideoWindowWidth = VideoScreen->width_in_pixels;
+ }
+ }
+ if (!VideoWindowWidth) {
+ VideoWindowWidth = (VideoWindowHeight * 16) / 9;
+ }
+
+ VideoPlayWindow =
+ VideoCreateWindow(VideoScreen->root, VideoScreen->root_visual,
+ VideoScreen->root_depth);
+ xcb_map_window(Connection, VideoPlayWindow);
+ VideoOsdWindow =
+ VideoCreateWindow(VideoPlayWindow, VideoScreen->root_visual,
+ VideoScreen->root_depth);
+ Debug(3, "play: osd %x, play %x\n", VideoOsdWindow, VideoPlayWindow);
+
+ VideoWindowClear();
+ // done by clear: xcb_flush(Connection);
+
+ return 0;
+}
+
+///
+/// Cleanup video.
+///
+void VideoExit(void)
+{
+ if (VideoOsdWindow != XCB_NONE) {
+ xcb_destroy_window(Connection, VideoOsdWindow);
+ VideoOsdWindow = XCB_NONE;
+ }
+ if (VideoPlayWindow != XCB_NONE) {
+ xcb_destroy_window(Connection, VideoPlayWindow);
+ VideoPlayWindow = XCB_NONE;
+ }
+ if (VideoColormap != XCB_NONE) {
+ xcb_free_colormap(Connection, VideoColormap);
+ VideoColormap = XCB_NONE;
+ }
+ if (VideoBlankCursor != XCB_NONE) {
+ xcb_free_cursor(Connection, VideoBlankCursor);
+ VideoBlankCursor = XCB_NONE;
+ }
+ if (VideoPixmap != XCB_NONE) {
+ xcb_free_pixmap(Connection, VideoPixmap);
+ VideoPixmap = XCB_NONE;
+ }
+ if (XcbKeySymbols != XCB_NONE) {
+ xcb_key_symbols_free(XcbKeySymbols);
+ XcbKeySymbols = XCB_NONE;
+ }
+
+ if (Connection) {
+ xcb_flush(Connection);
+ xcb_disconnect(Connection);
+ Connection = NULL;
+ }
+}