summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohns <johns98@gmx.net>2012-07-20 17:57:01 +0200
committerJohns <johns98@gmx.net>2012-07-20 17:57:01 +0200
commit93d0fbcd05c885b4586d140a8506b34450560231 (patch)
tree2ed2df748220de6bbbc5f4971af55a300a53040b
parent4883dc1501c3c9b087e4fd5c724adc4c6e48381b (diff)
downloadvdr-plugin-play-93d0fbcd05c885b4586d140a8506b34450560231.tar.gz
vdr-plugin-play-93d0fbcd05c885b4586d140a8506b34450560231.tar.bz2
Video module.
-rw-r--r--.gitignore2
-rw-r--r--ChangeLog3
-rw-r--r--Makefile6
-rw-r--r--README.txt3
-rw-r--r--misc.h157
-rw-r--r--vdr-play-9999.ebuild5
-rw-r--r--video.c671
-rw-r--r--video.h59
8 files changed, 900 insertions, 6 deletions
diff --git a/.gitignore b/.gitignore
index e9bcf2b..e9d5e10 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,7 +4,7 @@
.*.swp
.gdb_history
# work directory
-chaos
+.chaos
# generated files
.dependencies
libvdr-play.so*
diff --git a/ChangeLog b/ChangeLog
index 2a6a379..f2b9331 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,6 @@
User johns
Date:
+ Add player thread for events and pipe output.
+ Support DVDNAV buttons.
+ Colorkey becomes parameter (f.e. mplayer2).
diff --git a/Makefile b/Makefile
index 953200e..e2260b8 100644
--- a/Makefile
+++ b/Makefile
@@ -60,18 +60,18 @@ DEFINES += $(CONFIG) -D_GNU_SOURCE -DPLUGIN_NAME_I18N='"$(PLUGIN)"' \
$(if $(GIT_REV), -DGIT_REV='"$(GIT_REV)"')
_CFLAGS = $(DEFINES) $(INCLUDES) \
- $(shell pkg-config --cflags xcb xcb-icccm xcb-image)
+ $(shell pkg-config --cflags xcb xcb-event xcb-keysyms xcb-icccm xcb-image)
#override _CFLAGS += -Werror
override CXXFLAGS += $(_CFLAGS)
override CFLAGS += $(_CFLAGS)
LIBS += \
- $(shell pkg-config --libs xcb xcb-icccm xcb-image)
+ $(shell pkg-config --libs xcb xcb-keysyms xcb-event xcb-icccm xcb-image)
### The object files (add further files here):
-OBJS = $(PLUGIN).o
+OBJS = $(PLUGIN).o video.o
SRCS = $(wildcard $(OBJS:.o=.c)) $(PLUGIN).cpp
### The main target:
diff --git a/README.txt b/README.txt
index db884dd..a9b6812 100644
--- a/README.txt
+++ b/README.txt
@@ -99,9 +99,10 @@ Requires:
x11-libs/libxcb,
X C-language Bindings library
http://xcb.freedesktop.org
+ x11-libs/xcb-util,
x11-libs/xcb-util-image,
+ x11-libs/xcb-util-keysyms,
x11-libs/xcb-util-wm,
- not yet: x11-libs/xcb-util-keysyms
X C-language Bindings library
http://xcb.freedesktop.org
Only versions >= 0.3.8 are good supported
diff --git a/misc.h b/misc.h
new file mode 100644
index 0000000..3ced30c
--- /dev/null
+++ b/misc.h
@@ -0,0 +1,157 @@
+///
+/// @file misc.h @brief Misc function header file
+///
+/// Copyright (c) 2009 - 2012 by Lutz Sammer. All Rights Reserved.
+///
+/// Contributor(s):
+/// Copied from vdr-softhddevice.
+///
+/// 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$
+//////////////////////////////////////////////////////////////////////////////
+
+/// @addtogroup misc
+/// @{
+
+#include <syslog.h>
+#include <stdarg.h>
+#include <time.h> // clock_gettime
+
+//////////////////////////////////////////////////////////////////////////////
+// Defines
+//////////////////////////////////////////////////////////////////////////////
+
+//////////////////////////////////////////////////////////////////////////////
+// Declares
+//////////////////////////////////////////////////////////////////////////////
+
+//////////////////////////////////////////////////////////////////////////////
+// Variables
+//////////////////////////////////////////////////////////////////////////////
+
+extern int SysLogLevel; ///< how much information wanted
+
+//////////////////////////////////////////////////////////////////////////////
+// Prototypes
+//////////////////////////////////////////////////////////////////////////////
+
+static inline void Syslog(const int, const char *format, ...)
+ __attribute__ ((format(printf, 2, 3)));
+
+//////////////////////////////////////////////////////////////////////////////
+// Inlines
+//////////////////////////////////////////////////////////////////////////////
+
+#ifdef DEBUG
+#define DebugLevel 4 /// private debug level
+#else
+#define DebugLevel 0 /// private debug level
+#endif
+
+/**
+** Syslog output function.
+**
+** - 0 fatal errors and errors
+** - 1 warnings
+** - 2 info
+** - 3 important debug and fixme's
+*/
+static inline void Syslog(const int level, const char *format, ...)
+{
+ if (SysLogLevel > level || DebugLevel > level) {
+ va_list ap;
+
+ va_start(ap, format);
+ vsyslog(LOG_ERR, format, ap);
+ va_end(ap);
+ }
+}
+
+/**
+** Show error.
+*/
+#define Error(fmt...) Syslog(0, fmt)
+
+/**
+** Show fatal error.
+*/
+#define Fatal(fmt...) do { Error(fmt); abort(); } while (0)
+
+/**
+** Show warning.
+*/
+#define Warning(fmt...) Syslog(1, fmt)
+
+/**
+** Show info.
+*/
+#define Info(fmt...) Syslog(2, fmt)
+
+/**
+** Show debug.
+*/
+#ifdef DEBUG
+#define Debug(level, fmt...) Syslog(level, fmt)
+#else
+#define Debug(level, fmt...) /* disabled */
+#endif
+
+#ifndef AV_NOPTS_VALUE
+#define AV_NOPTS_VALUE INT64_C(0x8000000000000000)
+#endif
+
+/**
+** Nice time-stamp string.
+**
+** @param ts dvb time stamp
+*/
+static inline const char *Timestamp2String(int64_t ts)
+{
+ static char buf[4][16];
+ static int idx;
+
+ if (ts == (int64_t) AV_NOPTS_VALUE) {
+ return "--:--:--.---";
+ }
+ idx = (idx + 1) % 3;
+ snprintf(buf[idx], sizeof(buf[idx]), "%2d:%02d:%02d.%03d",
+ (int)(ts / (90 * 3600000)), (int)((ts / (90 * 60000)) % 60),
+ (int)((ts / (90 * 1000)) % 60), (int)((ts / 90) % 1000));
+
+ return buf[idx];
+}
+
+/**
+** Get ticks in ms.
+**
+** @returns ticks in ms,
+*/
+static inline uint32_t GetMsTicks(void)
+{
+#ifdef CLOCK_MONOTONIC
+ struct timespec tspec;
+
+ clock_gettime(CLOCK_MONOTONIC, &tspec);
+ return (tspec.tv_sec * 1000) + (tspec.tv_nsec / (1000 * 1000));
+#else
+ struct timeval tval;
+
+ if (gettimeofday(&tval, NULL) < 0) {
+ return 0;
+ }
+ return (tval.tv_sec * 1000) + (tval.tv_usec / 1000);
+#endif
+}
+
+/// @}
diff --git a/vdr-play-9999.ebuild b/vdr-play-9999.ebuild
index 5c2005c..0383b21 100644
--- a/vdr-play-9999.ebuild
+++ b/vdr-play-9999.ebuild
@@ -24,10 +24,13 @@ IUSE=""
RDEPEND=">=media-video/vdr-1.7
>=x11-libs/libxcb-1.8
- x11-libs/xcb-icccm
+ x11-libs/xcb-util
x11-libs/xcb-util-image
+ x11-libs/xcb-util-keysyms
+ x11-libs/xcb-util-wm
|| ( media-video/mplayer media-video/mplayer2 )"
DEPEND="${RDEPEND}
+ x11-proto/xproto
sys-devel/gettext
dev-util/pkgconfig
sys-devel/make"
diff --git a/video.c b/video.c
new file mode 100644
index 0000000..878786a
--- /dev/null
+++ b/video.c
@@ -0,0 +1,671 @@
+///
+/// @file video.c @brief Video module
+///
+/// Copyright (c) 2012 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$
+//////////////////////////////////////////////////////////////////////////////
+
+///
+/// @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_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
+
+///
+/// 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[4];
+ xcb_window_t window;
+
+ Debug(3, "video: visual %#0x depth %d\n", visual, depth);
+
+ // Color map
+ VideoColormap = xcb_generate_id(Connection);
+ xcb_create_colormap(Connection, XCB_COLORMAP_ALLOC_NONE, VideoColormap,
+ parent, visual);
+
+ 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;
+ 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, 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);
+
+ xcb_map_window(Connection, window);
+
+ //
+ // hide cursor
+ //
+ if (VideoBlankCursor == XCB_NONE) {
+ xcb_pixmap_t pixmap;
+ xcb_cursor_t cursor;
+
+ pixmap = xcb_generate_id(Connection);
+ xcb_create_pixmap(Connection, 1, pixmap, parent, 1, 1);
+ cursor = xcb_generate_id(Connection);
+ xcb_create_cursor(Connection, cursor, pixmap, pixmap, 0, 0, 0, 0, 0, 0,
+ 1, 1);
+ VideoBlankCursor = cursor;
+ VideoBlankTick = 0;
+ }
+
+ values[0] = VideoBlankCursor;
+ xcb_change_window_attributes(Connection, window, XCB_CW_CURSOR, values);
+ // FIXME: free colormap/cursor/pixmap needed?
+
+ return window;
+}
+
+///
+/// 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;
+
+ if (!Connection) {
+ Debug(3, "play: FIXME: must restore osd provider\n");
+ return;
+ }
+
+ gc = xcb_generate_id(Connection);
+ xcb_create_gc(Connection, gc, VideoOsdWindow, 0, NULL);
+
+ 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;
+ }
+ 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;
+ }
+ xcb_image_put_pixel(xcb_image, sx, sy, pixel);
+ }
+ }
+ }
+
+ // render xcb_image to color data pixmap
+ 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_AudioLowerVolume:
+ FeedKeyPress("XKeySym", "AudioLowerVolume", 0, 0);
+ break;
+ case XF86XK_AudioMute:
+ FeedKeyPress("XKeySym", "AudioMute", 0, 0);
+ break;
+ case XF86XK_AudioRaiseVolume:
+ FeedKeyPress("XKeySym", "AudioRaiseVolume", 0, 0);
+ break;
+ case XF86XK_AudioPlay:
+ FeedKeyPress("XKeySym", "AudioPlay", 0, 0);
+ break;
+ case XF86XK_AudioStop:
+ FeedKeyPress("XKeySym", "AudioStop", 0, 0);
+ break;
+ case XF86XK_AudioPrev:
+ FeedKeyPress("XKeySym", "AudioPrev", 0, 0);
+ break;
+ case XF86XK_AudioNext:
+ FeedKeyPress("XKeySym", "AudioNext", 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;
+
+ fds[0].fd = xcb_get_file_descriptor(Connection);
+ fds[0].events = POLLIN | POLLPRI;
+
+ delay = timeout;
+ for (;;) {
+ xcb_flush(Connection);
+
+ // wait for events or timeout
+ 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);
+ VideoOsdWindow =
+ VideoCreateWindow(VideoPlayWindow, VideoScreen->root_visual,
+ VideoScreen->root_depth);
+ Debug(3, "play: osd %x, play %x\n", VideoOsdWindow, VideoPlayWindow);
+
+ 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 (Connection) {
+ xcb_flush(Connection);
+ xcb_disconnect(Connection);
+ Connection = NULL;
+ }
+}
diff --git a/video.h b/video.h
new file mode 100644
index 0000000..c644959
--- /dev/null
+++ b/video.h
@@ -0,0 +1,59 @@
+///
+/// @file video.h @brief Video module header file
+///
+/// Copyright (c) 2012 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$
+//////////////////////////////////////////////////////////////////////////////
+
+/// @addtogroup Video
+/// @{
+
+ /// C callback feed key press
+extern void FeedKeyPress(const char *, const char *, int, int);
+
+ /// Show window.
+extern void VideoWindowShow(void);
+
+ /// Hide window.
+extern void VideoWindowHide(void);
+
+ /// Clear window.
+extern void VideoWindowClear(void);
+
+ /// Poll video events.
+extern void VideoPollEvents(int);
+
+ /// Get player window id.
+extern int VideoGetPlayWindow(void);
+
+ /// Draw an OSD ARGB image.
+extern void VideoDrawARGB(int, int, int, int, const uint8_t *);
+
+ /// Get OSD size.
+extern void VideoGetOsdSize(int *, int *);
+
+ /// Set video geometry.
+extern void VideoSetGeometry(const char *);
+
+ /// Set video color key.
+extern void VideoSetColorKey(uint32_t);
+
+extern int VideoInit(const char *); ///< Setup video module.
+extern void VideoExit(void); ///< Cleanup and exit video module.
+
+/// @}