summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorphintuka <phintuka>2008-03-31 22:20:30 +0000
committerphintuka <phintuka>2008-03-31 22:20:30 +0000
commit330cf88ffd3688abad5bbfe83b202251ef3ecfd7 (patch)
treeb138e8789c6e7b03a8dde49b79a770ae992ae1cd
parent224e30034054603bc6b7f1be04a9117809b03155 (diff)
downloadxineliboutput-330cf88ffd3688abad5bbfe83b202251ef3ecfd7.tar.gz
xineliboutput-330cf88ffd3688abad5bbfe83b202251ef3ecfd7.tar.bz2
HUD OSD
(Patch from Antti Seppälä and Rolf Ahrenberg)
-rw-r--r--xine_sxfe_frontend.c460
1 files changed, 453 insertions, 7 deletions
diff --git a/xine_sxfe_frontend.c b/xine_sxfe_frontend.c
index 7256f77b..ee056b15 100644
--- a/xine_sxfe_frontend.c
+++ b/xine_sxfe_frontend.c
@@ -4,7 +4,7 @@
* See the main source file 'xineliboutput.c' for copyright information and
* how to reach the author.
*
- * $Id: xine_sxfe_frontend.c,v 1.33 2008-03-31 21:36:37 phintuka Exp $
+ * $Id: xine_sxfe_frontend.c,v 1.34 2008-03-31 22:20:30 phintuka Exp $
*
*/
@@ -25,11 +25,14 @@
#include <pthread.h>
#include <sched.h>
#include <poll.h>
+#include <sys/ipc.h>
+#include <sys/shm.h>
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <X11/keysym.h>
#include <X11/extensions/XShm.h>
+#include <X11/extensions/Xrender.h>
#include <X11/Xutil.h>
#ifdef HAVE_XF86VIDMODE
# include <X11/extensions/xf86vmode.h>
@@ -63,6 +66,7 @@
#include <xine/plugin_catalog.h>
#include "xine_input_vdr.h"
+#include "xine_osd_command.h"
#include "xine_frontend.h"
#include "xine/post.h"
@@ -84,6 +88,16 @@ typedef struct _mwmhints {
uint32_t status;
} MWMHints;
+/* HUD Scaling */
+typedef struct _xrender_surf
+{
+ int w, h;
+ int depth;
+ Visual *vis;
+ Drawable draw;
+ Picture pic;
+ int allocated : 1;
+} Xrender_Surf;
/*
* data
@@ -161,6 +175,21 @@ typedef struct sxfe_s {
char configfile[256];
char modeline[256];
+ /* HUD stuff */
+ uint8_t hud;
+ GC gc;
+ Window hud_window;
+ XImage *hud_img;
+ Visual *hud_vis;
+ XShmSegmentInfo hud_shminfo;
+ Xrender_Surf *surf_win;
+ Xrender_Surf *surf_img;
+ int osd_width;
+ int osd_height;
+ int osd_pad_x;
+ int osd_pad_y;
+ uint32_t* hud_img_mem;
+
} fe_t, sxfe_t;
@@ -171,6 +200,10 @@ typedef struct sxfe_s {
#define DOUBLECLICK_TIME 500 // ms
+#define OSD_DEF_WIDTH 720
+#define OSD_DEF_HEIGHT 576
+#define HUD_MAX_WIDTH 1920
+#define HUD_MAX_HEIGHT 1080
static void fe_dest_size_cb (void *data,
int video_width, int video_height, double video_pixel_aspect,
@@ -389,6 +422,346 @@ static void set_cursor(Display *dpy, Window win, const int enable)
}
}
+Xrender_Surf * xrender_surf_new(Display *dpy, Drawable draw, Visual *vis, int w, int h, int alpha)
+{
+ Xrender_Surf *rs;
+ XRenderPictFormat *fmt;
+ XRenderPictureAttributes att;
+
+ rs = calloc(1, sizeof (Xrender_Surf));
+
+ if(alpha)
+ fmt = XRenderFindStandardFormat (dpy, PictStandardARGB32);
+ else
+ fmt = XRenderFindStandardFormat (dpy, PictStandardRGB24);
+ rs->w = w;
+ rs->h = h;
+ rs->depth = fmt->depth;
+ rs->vis = vis;
+ rs->draw = XCreatePixmap(dpy, draw, w, h, fmt->depth);
+ att.dither = 1;
+ att.component_alpha = 1;
+ att.repeat = 0;
+ rs->pic = XRenderCreatePicture(dpy, rs->draw, fmt, CPRepeat | CPDither | CPComponentAlpha, &att);
+ rs->allocated = 1;
+ return rs;
+}
+
+void xrender_surf_blend(Display *dpy, Xrender_Surf *src, Xrender_Surf *dst,
+ int x, int y, int w, int h, XDouble scale_x, XDouble scale_y, int smooth)
+{
+ XTransform xf;
+
+ if(!scale_x)
+ scale_x = 1;
+ if(!scale_y)
+ scale_y = 1;
+
+ xf.matrix[0][0] = XDoubleToFixed(1 / scale_x); xf.matrix[0][1] = 0; xf.matrix[0][2] = 0;
+ xf.matrix[1][0] = 0; xf.matrix[1][1] = XDoubleToFixed(1 / scale_y); xf.matrix[1][2] = 0;
+ xf.matrix[2][0] = 0; xf.matrix[2][1] = 0; xf.matrix[2][2] = 65536;
+ if(smooth)
+ XRenderSetPictureFilter(dpy, src->pic, "bilinear", NULL, 0);
+ else
+ XRenderSetPictureFilter(dpy, src->pic, "nearest", NULL, 0);
+ XRenderSetPictureTransform(dpy, src->pic, &xf);
+ XRenderComposite(dpy, PictOpSrc, src->pic, None, dst->pic,
+ x * scale_x + 1,
+ y * scale_y + 1,
+ 0, 0,
+ x * scale_x,
+ y * scale_y,
+ w * scale_x + 1,
+ h * scale_y + 1);
+}
+
+Xrender_Surf * xrender_surf_adopt(Display *dpy, Drawable draw, Visual *vis, int w, int h)
+{
+ Xrender_Surf *rs;
+ XRenderPictFormat *fmt;
+ XRenderPictureAttributes att;
+
+ rs = calloc(1, sizeof(Xrender_Surf));
+
+ fmt = XRenderFindVisualFormat(dpy, vis);
+ rs->w = w;
+ rs->h = h;
+ rs->depth = fmt->depth;
+ rs->vis = vis;
+ rs->draw = draw;
+ att.dither = 1;
+ att.component_alpha = 1;
+ att.repeat = 0;
+ rs->pic = XRenderCreatePicture(dpy, rs->draw, fmt, CPRepeat | CPDither | CPComponentAlpha, &att);
+ rs->allocated = 0;
+ return rs;
+}
+
+void xrender_surf_free(Display *dpy, Xrender_Surf *rs)
+{
+ if(rs->allocated)
+ XFreePixmap(dpy, rs->draw);
+ XRenderFreePicture(dpy, rs->pic);
+ free(rs);
+}
+
+static Visual *find_argb_visual(Display *dpy, int scr)
+{
+ XVisualInfo *xvi, template;
+ int nvi, i;
+ XRenderPictFormat *format;
+ Visual *visual;
+
+ template.screen = scr;
+ template.depth = 32;
+ template.class = TrueColor;
+ xvi = XGetVisualInfo(dpy, VisualScreenMask | VisualDepthMask |
+ VisualClassMask, &template, &nvi);
+
+ if(!xvi) {
+ LOGERR("No xvi\n");
+ return 0;
+ }
+
+ visual = 0;
+ for(i = 0; i < nvi; i++) {
+ LOGDBG("iteration %d of %d\n", i, nvi);
+ format = XRenderFindVisualFormat(dpy, xvi[i].visual);
+ if((format->type == PictTypeDirect) && format->direct.alphaMask) {
+ visual = xvi[i].visual;
+ break;
+ }
+ }
+
+ XFree(xvi);
+
+ if(!visual)
+ LOGERR("No visual found\n");
+
+ return visual;
+}
+
+static int hud_osd_open(frontend_t *this_gen)
+{
+ sxfe_t *this = (sxfe_t*)this_gen;
+ if(this && this->hud) {
+ XLockDisplay(this->display);
+
+ LOGDBG("opening HUD window...\n");
+
+ this->hud_vis = find_argb_visual(this->display, DefaultScreen(this->display));
+
+ Colormap hud_colormap = XCreateColormap(this->display,
+ RootWindow(this->display, DefaultScreen(this->display)),
+ this->hud_vis, AllocNone);
+
+ XSetWindowAttributes attributes;
+ attributes.override_redirect = True;
+ attributes.background_pixel = 0x00000000;
+ attributes.border_pixel = 0;
+ attributes.colormap = hud_colormap;
+ attributes.backing_store = Always;
+
+ this->hud_window = XCreateWindow(this->display, DefaultRootWindow(this->display),
+ this->xpos, this->ypos,
+ this->width, this->height,
+ 0, 32, InputOutput, this->hud_vis,
+ CWBackPixel | CWBorderPixel |
+ CWOverrideRedirect | CWColormap,
+ &attributes);
+
+ XSelectInput(this->display, this->hud_window,
+ StructureNotifyMask |
+ ExposureMask |
+ KeyPressMask |
+ ButtonPressMask |
+ FocusChangeMask);
+
+ XStoreName(this->display, this->hud_window, "HUD");
+ this->gc = XCreateGC(this->display, this->hud_window, 0, NULL);
+
+ if(this->completion_event != -1) {
+ this->hud_img = XShmCreateImage(this->display, this->hud_vis, 32, ZPixmap, NULL, &(this->hud_shminfo),
+ HUD_MAX_WIDTH, HUD_MAX_HEIGHT);
+
+ this->hud_shminfo.shmid = shmget(IPC_PRIVATE, this->hud_img->bytes_per_line * this->hud_img->height,
+ IPC_CREAT | 0777);
+
+ this->hud_shminfo.shmaddr = this->hud_img->data = shmat(this->hud_shminfo.shmid, 0, 0);
+ this->hud_shminfo.readOnly = True;
+
+ XShmAttach(this->display, &(this->hud_shminfo));
+ } else {
+ /* Fall-back to traditional memory */
+ LOGMSG("HUD falling back to normal (slow) memory\n");
+ this->hud_img_mem = malloc(4 * HUD_MAX_WIDTH * HUD_MAX_HEIGHT);
+ this->hud_img = XCreateImage(this->display, this->hud_vis, 32, ZPixmap, 0, (char*)this->hud_img_mem,
+ HUD_MAX_WIDTH, HUD_MAX_HEIGHT, 32, 0);
+ }
+
+ this->surf_win = xrender_surf_adopt(this->display, this->hud_window, this->hud_vis, HUD_MAX_WIDTH, HUD_MAX_HEIGHT);
+ this->surf_img = xrender_surf_new(this->display, this->hud_window, this->hud_vis, HUD_MAX_WIDTH, HUD_MAX_HEIGHT, 1);
+
+ XUnlockDisplay(this->display);
+ }
+ return 1;
+}
+
+static void hud_osd_close(frontend_t *this_gen)
+{
+ sxfe_t *this = (sxfe_t*)this_gen;
+ if(this && this->hud) {
+ XLockDisplay(this->display);
+ LOGDBG("closing hud window...\n");
+
+ if(this->completion_event != -1) {
+ XShmDetach(this->display, &(this->hud_shminfo));
+ XDestroyImage(this->hud_img);
+ shmdt(this->hud_shminfo.shmaddr);
+ shmctl(this->hud_shminfo.shmid, IPC_RMID, 0);
+ } else
+ XDestroyImage(this->hud_img);
+
+ if(this->surf_img)
+ xrender_surf_free(this->display, this->surf_img);
+ if(this->surf_win)
+ xrender_surf_free(this->display, this->surf_win);
+
+ XDestroyWindow(this->display, this->hud_window);
+ XUnlockDisplay(this->display);
+ }
+}
+
+static void hud_fill_img_memory(uint32_t* dst, const struct osd_command_s *cmd)
+{
+ int i, pixelcounter = 0;
+ int idx = cmd->y * HUD_MAX_WIDTH + cmd->x;
+
+ for(i = 0; i < cmd->num_rle; ++i) {
+ const uint8_t alpha = (cmd->palette + (cmd->data + i)->color)->alpha;
+ const uint8_t r = (cmd->palette + (cmd->data + i)->color)->r;
+ const uint8_t g = (cmd->palette + (cmd->data + i)->color)->g;
+ const uint8_t b = (cmd->palette + (cmd->data + i)->color)->b;
+ int j, finalcolor = 0;
+ finalcolor |= ((alpha << 24) & 0xFF000000);
+ finalcolor |= ((r << 16) & 0x00FF0000);
+ finalcolor |= ((g << 8) & 0x0000FF00);
+ finalcolor |= (b & 0x000000FF);
+
+ for(j = 0; j < (cmd->data + i)->len; ++j) {
+ if(pixelcounter >= cmd->w) {
+ idx += HUD_MAX_WIDTH - pixelcounter;
+ pixelcounter = 0;
+ }
+ dst[idx++] = finalcolor;
+ ++pixelcounter;
+ }
+ }
+}
+
+static int hud_osd_command(frontend_t *this_gen, struct osd_command_s *cmd)
+{
+ sxfe_t *this = (sxfe_t*)this_gen;
+ if(this && this->hud && cmd) {
+ XLockDisplay(this->display);
+ switch(cmd->cmd) {
+ case OSD_Nop: /* Do nothing ; used to initialize delay_ms counter */
+ LOGDBG("HUD osd NOP\n");
+ break;
+
+ case OSD_Size: /* Set size of VDR OSD area */
+ LOGDBG("HUD Set Size\n");
+ this->osd_width = (cmd->w > 0) ? cmd->w : OSD_DEF_WIDTH;
+ this->osd_height = (cmd->h > 0) ? cmd->h : OSD_DEF_HEIGHT;
+ this->osd_pad_x = (this->osd_width != OSD_DEF_WIDTH) ? 96 : 0;
+ this->osd_pad_y = (this->osd_height != OSD_DEF_HEIGHT) ? 90 : 0;
+ break;
+
+ case OSD_Set_RLE: /* Create/update OSD window. Data is rle-compressed. */
+ LOGDBG("HUD Set RLE\n");
+ if(this->completion_event != -1) {
+ hud_fill_img_memory((uint32_t*)(this->hud_img->data), cmd);
+ if(!cmd->scaling) {
+ /* Place image directly onto hud window */
+ XShmPutImage(this->display, this->hud_window, this->gc, this->hud_img,
+ cmd->x + cmd->dirty_area.x1, cmd->y + cmd->dirty_area.y1,
+ cmd->x + cmd->dirty_area.x1, cmd->y + cmd->dirty_area.y1,
+ cmd->dirty_area.x2 - cmd->dirty_area.x1,
+ cmd->dirty_area.y2 - cmd->dirty_area.y1,
+ False);
+ } else {
+ /* Place image onto Xrender surface which will be blended onto hud window */
+ XShmPutImage(this->display, this->surf_img->draw, this->gc, this->hud_img,
+ cmd->x + cmd->dirty_area.x1 - 1, cmd->y + cmd->dirty_area.y1 - 1,
+ cmd->x + cmd->dirty_area.x1 - 1, cmd->y + cmd->dirty_area.y1 - 1,
+ cmd->dirty_area.x2 - cmd->dirty_area.x1 + 2,
+ cmd->dirty_area.y2 - cmd->dirty_area.y1 + 2,
+ False);
+ xrender_surf_blend(this->display, this->surf_img, this->surf_win,
+ cmd->x + cmd->dirty_area.x1 - 1, cmd->y + cmd->dirty_area.y1 - 1,
+ cmd->dirty_area.x2 - cmd->dirty_area.x1 + 2,
+ cmd->dirty_area.y2 - cmd->dirty_area.y1 + 2,
+ (XDouble)(this->width) / (XDouble)(this->osd_width + this->osd_pad_x),
+ (XDouble)(this->height) / (XDouble)(this->osd_height + this->osd_pad_y),
+ (cmd->scaling & 2)); // HUD_SCALING_BILINEAR=2
+ }
+ } else {
+ hud_fill_img_memory(this->hud_img_mem, cmd);
+ if(!cmd->scaling) {
+ /* Place image directly onto hud window (always unscaled) */
+ XPutImage(this->display, this->hud_window, this->gc, this->hud_img,
+ cmd->x + cmd->dirty_area.x1, cmd->y + cmd->dirty_area.y1,
+ cmd->x + cmd->dirty_area.x1, cmd->y + cmd->dirty_area.y1,
+ cmd->dirty_area.x2 - cmd->dirty_area.x1,
+ cmd->dirty_area.y2 - cmd->dirty_area.y1);
+ } else {
+ /* Place image onto Xrender surface which will be blended onto hud window */
+ XPutImage(this->display, this->surf_img->draw, this->gc, this->hud_img,
+ cmd->x + cmd->dirty_area.x1 - 1, cmd->y + cmd->dirty_area.y1 - 1,
+ cmd->x + cmd->dirty_area.x1 - 1, cmd->y + cmd->dirty_area.y1 - 1,
+ cmd->dirty_area.x2 - cmd->dirty_area.x1 + 2,
+ cmd->dirty_area.y2 - cmd->dirty_area.y1 + 2);
+ xrender_surf_blend(this->display, this->surf_img, this->surf_win,
+ cmd->x + cmd->dirty_area.x1 - 1, cmd->y + cmd->dirty_area.y1 - 1,
+ cmd->dirty_area.x2 - cmd->dirty_area.x1 + 2,
+ cmd->dirty_area.y2 - cmd->dirty_area.y1 + 2,
+ (XDouble)(this->width) / (XDouble)(this->osd_width + this->osd_pad_x),
+ (XDouble)(this->height) / (XDouble)(this->osd_height + this->osd_pad_y),
+ (cmd->scaling & 2)); // HUD_SCALING_BILINEAR=2
+ }
+ }
+ break;
+
+ case OSD_SetPalette: /* Modify palette of already created OSD window */
+ LOGDBG("HUD osd SetPalette\n");
+ break;
+
+ case OSD_Move: /* Change x/y position of already created OSD window */
+ LOGDBG("HUD osd Move\n");
+ break;
+
+ case OSD_Set_YUV: /* Create/update OSD window. Data is in YUV420 format. */
+ LOGDBG("HUD osd set YUV\n");
+ break;
+
+ case OSD_Close: /* Close OSD window */
+ LOGDBG("HUD osd Close\n");
+ XSetForeground(this->display, this->gc, 0x00000000);
+ XFillRectangle(this->display, this->hud_window, this->gc,
+ 0, 0, this->width, this->height);
+ XFlush(this->display);
+ break;
+
+ default:
+ LOGDBG("unknown osd command\n");
+ break;
+ }
+ XUnlockDisplay(this->display);
+ }
+ return 1;
+}
+
+
/*
* sxfe_display_open
*
@@ -406,6 +779,18 @@ static int sxfe_display_open(frontend_t *this_gen, int width, int height, int fu
XSizeHints hint;
double res_h, res_v, aspect_diff;
+ if(hud) {
+ LOGDBG("Enabling HUD\n");
+ this->hud = hud;
+ this->osd_width = OSD_DEF_WIDTH;
+ this->osd_height = OSD_DEF_HEIGHT;
+ this->osd_pad_x = 0;
+ this->osd_pad_y = 0;
+#ifndef FE_STANDALONE
+ this->fe.xine_osd_command = hud_osd_command;
+#endif
+ }
+
if(this->display)
this->fe.fe_display_close(this_gen);
@@ -423,8 +808,8 @@ static int sxfe_display_open(frontend_t *this_gen, int width, int height, int fu
this->origypos = 0;
this->width = width;
this->height = height;
- this->origwidth = width>0 ? width : 720;
- this->origheight = height>0 ? height : 576;
+ this->origwidth = width>0 ? width : OSD_DEF_WIDTH;
+ this->origheight = height>0 ? height : OSD_DEF_HEIGHT;
this->check_move = 0;
this->fullscreen = fullscreen;
@@ -533,12 +918,14 @@ static int sxfe_display_open(frontend_t *this_gen, int width, int height, int fu
StructureNotifyMask |
ExposureMask |
KeyPressMask |
- ButtonPressMask | ButtonReleaseMask | ButtonMotionMask);
+ ButtonPressMask | ButtonReleaseMask | ButtonMotionMask |
+ FocusChangeMask);
XSelectInput (this->display, this->window[1],
StructureNotifyMask |
ExposureMask |
KeyPressMask |
- ButtonPressMask);
+ ButtonPressMask |
+ FocusChangeMask);
if(this->window_id <= 0) {
@@ -616,7 +1003,7 @@ static int sxfe_display_open(frontend_t *this_gen, int width, int height, int fu
set_fullscreen_props(this);
- return 1;
+ return hud_osd_open(this_gen);
}
/*
@@ -782,6 +1169,23 @@ static int sxfe_run(frontend_t *this_gen)
XConfigureEvent *cev = (XConfigureEvent *) &event;
Window tmp_win;
+ /* Move and resize HUD along with main or fullscreen window */
+ if(this->hud) {
+ if(cev->window == this->window[0]) {
+ int hud_x, hud_y;
+ XTranslateCoordinates(this->display, this->window[0],
+ DefaultRootWindow(this->display),
+ 0, 0, &hud_x, &hud_y, &tmp_win);
+ XResizeWindow(this->display, this->hud_window, cev->width, cev->height);
+ XMoveWindow(this->display, this->hud_window, hud_x, hud_y);
+ set_cursor(this->display, this->hud_window, 1);
+ } else if(cev->window == this->window[1]) {
+ XResizeWindow(this->display, this->hud_window, cev->width, cev->height);
+ XMoveWindow(this->display, this->hud_window, 0, 0);
+ set_cursor(this->display, this->hud_window, 0);
+ }
+ }
+
this->width = cev->width;
this->height = cev->height;
@@ -809,6 +1213,28 @@ static int sxfe_run(frontend_t *this_gen)
break;
}
+ case FocusIn:
+ {
+ if(this->hud) {
+ XFocusChangeEvent *fev = (XFocusChangeEvent *) &event;
+ /* Show HUD again if sxfe window receives focus */
+ if(fev->window == this->window[0] || fev->window == this->window[1]) {
+ XMapWindow(this->display, this->hud_window);
+ }
+ }
+ break;
+ }
+ case FocusOut:
+ {
+ if(this->hud) {
+ XFocusChangeEvent *fev = (XFocusChangeEvent *) &event;
+ /* Dismiss HUD window if focusing away from frontend window */
+ if(fev->window == this->window[0] || fev->window == this->window[1]) {
+ XUnmapWindow(this->display, this->hud_window);
+ }
+ }
+ break;
+ }
case ButtonRelease:
{
dragging = 0;
@@ -934,6 +1360,8 @@ static void sxfe_display_close(frontend_t *this_gen)
{
sxfe_t *this = (sxfe_t*)this_gen;
+ hud_osd_close(this_gen);
+
if(this && this->display) {
if(this->xine)
@@ -951,6 +1379,24 @@ static void sxfe_display_close(frontend_t *this_gen)
}
}
+static int sxfe_xine_play(frontend_t *this_gen)
+{
+ int r = fe_xine_play(this_gen);
+
+#ifdef FE_STANDALONE
+ sxfe_t *this = (sxfe_t*)this_gen;
+
+ if(r && this->input && this->hud) {
+ vdr_input_plugin_t *input_vdr = (vdr_input_plugin_t *)this->input;
+ LOGDBG("Enabling HUD");
+ input_vdr->f.fe_handle = this_gen;
+ input_vdr->f.intercept_osd = hud_osd_command;
+ }
+#endif
+
+ return r;
+}
+
static frontend_t *sxfe_get_frontend(void)
{
sxfe_t *this = malloc(sizeof(sxfe_t));
@@ -964,7 +1410,7 @@ static frontend_t *sxfe_get_frontend(void)
this->fe.xine_init = fe_xine_init;
this->fe.xine_open = fe_xine_open;
- this->fe.xine_play = fe_xine_play;
+ this->fe.xine_play = sxfe_xine_play;
this->fe.xine_stop = fe_xine_stop;
this->fe.xine_close = fe_xine_close;
this->fe.xine_exit = fe_xine_exit;