diff options
Diffstat (limited to 'src/video_out/xcbosd.c')
-rw-r--r-- | src/video_out/xcbosd.c | 546 |
1 files changed, 546 insertions, 0 deletions
diff --git a/src/video_out/xcbosd.c b/src/video_out/xcbosd.c new file mode 100644 index 000000000..9013bab10 --- /dev/null +++ b/src/video_out/xcbosd.c @@ -0,0 +1,546 @@ +/* + * Copyright (C) 2003, 2007 the xine project + * + * This file is part of xine, a free video player. + * + * xine 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. + * + * xine 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * + * $Id: xcbosd.c,v 1.1 2007/02/15 15:19:33 dgp85 Exp $ + * + * xcbosd.c, use X11 Nonrectangular Window Shape Extension to draw xine OSD + * + * Nov 2003 - Miguel Freitas + * Feb 2007 - ported to xcb by Christoph Pfister + * + * based on ideas and code of + * xosd Copyright (c) 2000 Andre Renaud (andre@ignavus.net) + * + * colorkey support by Yann Vernier + */ + +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <signal.h> +#include <time.h> + +#include <assert.h> + +#include <netinet/in.h> + +#include <xcb/shape.h> + +#define LOG_MODULE "xcbosd" +#define LOG_VERBOSE + +/* +#define LOG +*/ + +#include "xine_internal.h" +#include "xcbosd.h" + +struct xcbosd +{ + xcb_connection_t *connection; + xcb_screen_t *screen; + enum xcbosd_mode mode; + + union { + struct { + xcb_window_t window; + xcb_pixmap_t mask_bitmap; + xcb_gc_t mask_gc; + xcb_gc_t mask_gc_back; + int mapped; + } shaped; + struct { + uint32_t colorkey; + vo_scale_t *sc; + } colorkey; + } u; + xcb_window_t window; + unsigned int depth; + xcb_pixmap_t bitmap; + xcb_visualid_t visual; + xcb_colormap_t cmap; + + xcb_gc_t gc; + + int width; + int height; + int x; + int y; + enum {DRAWN, WIPED, UNDEFINED} clean; + xine_t *xine; +}; + + +void xcbosd_expose(xcbosd *osd) +{ + assert (osd); + + lprintf("expose (state:%d)\n", osd->clean ); + + switch (osd->mode) { + case XCBOSD_SHAPED: + xcb_shape_mask(osd->connection, XCB_SHAPE_SO_SET, XCB_SHAPE_SK_BOUNDING, + osd->u.shaped.window, 0, 0, osd->u.shaped.mask_bitmap); + if( osd->clean==DRAWN ) { + + if( !osd->u.shaped.mapped ) { + unsigned int stack_mode = XCB_STACK_MODE_ABOVE; + xcb_configure_window(osd->connection, osd->u.shaped.window, XCB_CONFIG_WINDOW_STACK_MODE, &stack_mode); + xcb_map_window(osd->connection, osd->u.shaped.window); + } + osd->u.shaped.mapped = 1; + + xcb_copy_area(osd->connection, osd->bitmap, osd->u.shaped.window, + osd->gc, 0, 0, 0, 0, osd->width, osd->height); + } else { + if( osd->u.shaped.mapped ) + xcb_unmap_window(osd->connection, osd->u.shaped.window); + osd->u.shaped.mapped = 0; + } + break; + case XCBOSD_COLORKEY: + if( osd->clean!=UNDEFINED ) + xcb_copy_area(osd->connection, osd->bitmap, osd->window, osd->gc, 0, 0, + 0, 0, osd->width, osd->height); + } +} + + +void xcbosd_resize(xcbosd *osd, int width, int height) +{ + assert (osd); + assert (width); + assert (height); + + lprintf("resize old:%dx%d new:%dx%d\n", osd->width, osd->height, width, height ); + + osd->width = width; + osd->height = height; + + xcb_free_pixmap(osd->connection, osd->bitmap); + switch(osd->mode) { + case XCBOSD_SHAPED: { + unsigned int window_config[] = { osd->width, osd->height }; + xcb_configure_window(osd->connection, osd->u.shaped.window, XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT, window_config); + xcb_free_pixmap(osd->connection, osd->u.shaped.mask_bitmap); + osd->u.shaped.mask_bitmap = xcb_generate_id(osd->connection); + xcb_create_pixmap(osd->connection, 1, osd->u.shaped.mask_bitmap, osd->u.shaped.window, osd->width, osd->height); + osd->bitmap = xcb_generate_id(osd->connection); + xcb_create_pixmap(osd->connection, osd->depth, osd->bitmap, osd->u.shaped.window, osd->width, osd->height); + break; + } + case XCBOSD_COLORKEY: + osd->bitmap = xcb_generate_id(osd->connection); + xcb_create_pixmap(osd->connection, osd->depth, osd->bitmap, osd->window, osd->width, osd->height); + break; + } + + osd->clean = UNDEFINED; + xcbosd_clear(osd); +} + +void xcbosd_drawable_changed(xcbosd *osd, xcb_window_t window) +{ + xcb_get_geometry_cookie_t get_geometry_cookie; + xcb_get_geometry_reply_t *get_geometry_reply; + + assert (osd); + + lprintf("drawable changed\n"); + +/* + Do I need to recreate the GC's?? + + XFreeGC (osd->display, osd->gc); + XFreeGC (osd->display, osd->mask_gc); + XFreeGC (osd->display, osd->mask_gc_back); +*/ + xcb_free_pixmap(osd->connection, osd->bitmap); + xcb_free_colormap(osd->connection, osd->cmap); + + /* we need to call XSync(), because otherwise, calling XDestroyWindow() + on the parent window could destroy our OSD window twice !! */ + /* XSync (osd->display, False); FIXME don't think that we need that --pfister */ + + osd->window = window; + + get_geometry_cookie = xcb_get_geometry(osd->connection, osd->window); + get_geometry_reply = xcb_get_geometry_reply(osd->connection, get_geometry_cookie, NULL); + osd->depth = get_geometry_reply->depth; + osd->width = get_geometry_reply->width; + osd->height = get_geometry_reply->height; + free(get_geometry_reply); + + assert(osd->width); + assert(osd->height); + + switch(osd->mode) { + case XCBOSD_SHAPED: { + xcb_free_pixmap(osd->connection, osd->u.shaped.mask_bitmap); + xcb_destroy_window(osd->connection, osd->u.shaped.window); + + unsigned int window_params[] = { osd->screen->black_pixel, 1, XCB_EVENT_MASK_EXPOSURE }; + osd->u.shaped.window = xcb_generate_id(osd->connection); + xcb_create_window(osd->connection, XCB_COPY_FROM_PARENT, osd->u.shaped.window, + osd->window, 0, 0, osd->width, osd->height, 0, XCB_COPY_FROM_PARENT, + XCB_COPY_FROM_PARENT, XCB_CW_BACK_PIXEL | XCB_CW_OVERRIDE_REDIRECT | XCB_CW_EVENT_MASK, + window_params); + + osd->u.shaped.mapped = 0; + + osd->u.shaped.mask_bitmap = xcb_generate_id(osd->connection); + xcb_create_pixmap(osd->connection, 1, osd->u.shaped.mask_bitmap, osd->u.shaped.window, osd->width, osd->height); + + osd->bitmap = xcb_generate_id(osd->connection); + xcb_create_pixmap(osd->connection, osd->depth, osd->bitmap, osd->u.shaped.window, osd->width, osd->height); + + osd->cmap = xcb_generate_id(osd->connection); + xcb_create_colormap(osd->connection, XCB_COLORMAP_ALLOC_NONE, osd->cmap, osd->u.shaped.window, osd->visual); + break; + } + case XCBOSD_COLORKEY: + osd->bitmap = xcb_generate_id(osd->connection); + xcb_create_pixmap(osd->connection, osd->depth, osd->bitmap, osd->window, osd->width, osd->height); + osd->cmap = xcb_generate_id(osd->connection); + xcb_create_colormap(osd->connection, XCB_COLORMAP_ALLOC_NONE, osd->cmap, osd->window, osd->visual); + + break; + } + + osd->clean = UNDEFINED; + /* do not xcbosd_clear() here: osd->u.colorkey.sc has not being updated yet */ +} + +xcbosd *xcbosd_create(xine_t *xine, xcb_connection_t *connection, xcb_screen_t *screen, xcb_window_t window, enum xcbosd_mode mode) +{ + xcbosd *osd; + + xcb_get_geometry_cookie_t get_geometry_cookie; + xcb_get_geometry_reply_t *get_geometry_reply; + + xcb_void_cookie_t generic_cookie; + xcb_generic_error_t *generic_error; + + osd = xine_xmalloc (sizeof (xcbosd)); + if (!osd) + return NULL; + + osd->mode = mode; + osd->xine = xine; + osd->connection = connection; + osd->screen = screen; + osd->window = window; + + osd->visual = osd->screen->root_visual; + + get_geometry_cookie = xcb_get_geometry(osd->connection, osd->window); + get_geometry_reply = xcb_get_geometry_reply(osd->connection, get_geometry_cookie, NULL); + osd->depth = get_geometry_reply->depth; + osd->width = get_geometry_reply->width; + osd->height = get_geometry_reply->height; + free(get_geometry_reply); + + assert(osd->width); + assert(osd->height); + + switch (mode) { + case XCBOSD_SHAPED: { + const xcb_query_extension_reply_t *query_extension_reply = xcb_get_extension_data(osd->connection, &xcb_shape_id); + + if (!query_extension_reply || !query_extension_reply->present) { + xprintf(osd->xine, XINE_VERBOSITY_LOG, _("x11osd: XShape extension not available. unscaled overlay disabled.\n")); + goto error2; + } + + unsigned int window_params[] = { osd->screen->black_pixel, 1, XCB_EVENT_MASK_EXPOSURE }; + osd->u.shaped.window = xcb_generate_id(osd->connection); + generic_cookie = xcb_create_window_checked(osd->connection, XCB_COPY_FROM_PARENT, osd->u.shaped.window, + osd->window, 0, 0, osd->width, osd->height, 0, XCB_COPY_FROM_PARENT, + XCB_COPY_FROM_PARENT, XCB_CW_BACK_PIXEL | XCB_CW_OVERRIDE_REDIRECT | XCB_CW_EVENT_MASK, + window_params); + generic_error = xcb_request_check(osd->connection, generic_cookie); + + if (generic_error != NULL) { + xprintf(osd->xine, XINE_VERBOSITY_LOG, _("x11osd: error creating window. unscaled overlay disabled.\n")); + free(generic_error); + goto error_window; + } + + osd->u.shaped.mask_bitmap = xcb_generate_id(osd->connection); + generic_cookie = xcb_create_pixmap_checked(osd->connection, 1, osd->u.shaped.mask_bitmap, osd->u.shaped.window, osd->width, osd->height); + generic_error = xcb_request_check(osd->connection, generic_cookie); + + if (generic_error != NULL) { + xprintf(osd->xine, XINE_VERBOSITY_LOG, _("x11osd: error creating pixmap. unscaled overlay disabled.\n")); + free(generic_error); + goto error_aftermaskbitmap; + } + + osd->bitmap = xcb_generate_id(osd->connection); + xcb_create_pixmap(osd->connection, osd->depth, osd->bitmap, osd->u.shaped.window, osd->width, osd->height); + osd->gc = xcb_generate_id(osd->connection); + xcb_create_gc(osd->connection, osd->gc, osd->u.shaped.window, 0, NULL); + + osd->u.shaped.mask_gc = xcb_generate_id(osd->connection); + xcb_create_gc(osd->connection, osd->u.shaped.mask_gc, osd->u.shaped.mask_bitmap, XCB_GC_FOREGROUND, &osd->screen->white_pixel); + + osd->u.shaped.mask_gc_back = xcb_generate_id(osd->connection); + xcb_create_gc(osd->connection, osd->u.shaped.mask_gc_back, osd->u.shaped.mask_bitmap, XCB_GC_FOREGROUND, &osd->screen->black_pixel); + + osd->u.shaped.mapped = 0; + osd->cmap = xcb_generate_id(osd->connection); + xcb_create_colormap(osd->connection, XCB_COLORMAP_ALLOC_NONE, osd->cmap, osd->u.shaped.window, osd->visual); + break; + } + case XCBOSD_COLORKEY: + osd->bitmap = xcb_generate_id(osd->connection); + xcb_create_pixmap(osd->connection, osd->depth, osd->bitmap, osd->window, osd->width, osd->height); + osd->gc = xcb_generate_id(osd->connection); + xcb_create_gc(osd->connection, osd->gc, osd->window, 0, NULL); + osd->cmap = xcb_generate_id(osd->connection); + xcb_create_colormap(osd->connection, XCB_COLORMAP_ALLOC_NONE, osd->cmap, osd->window, osd->visual); + /* FIXME: the expose event doesn't seem to happen? */ + /*XSelectInput (osd->display, osd->window, ExposureMask);*/ + break; + default: + goto error2; + } + + osd->clean = UNDEFINED; + xcbosd_expose(osd); + + xprintf(osd->xine, XINE_VERBOSITY_DEBUG, + _("x11osd: unscaled overlay created (%s mode).\n"), + (mode==XCBOSD_SHAPED) ? "XShape" : "Colorkey" ); + + return osd; + +/* + XFreeGC (osd->display, osd->gc); + XFreeGC (osd->display, osd->mask_gc); + XFreeGC (osd->display, osd->mask_gc_back); +*/ + +error_aftermaskbitmap: + if(mode==XCBOSD_SHAPED) + xcb_free_pixmap(osd->connection, osd->u.shaped.mask_bitmap); +error_window: + if(mode==XCBOSD_SHAPED) + xcb_destroy_window(osd->connection, osd->u.shaped.window); +error2: + free (osd); + return NULL; +} + +void xcbosd_colorkey(xcbosd *osd, uint32_t colorkey, vo_scale_t *scaling) +{ + assert (osd); + assert (osd->mode==XCBOSD_COLORKEY); + + osd->u.colorkey.colorkey=colorkey; + osd->u.colorkey.sc=scaling; + osd->clean = UNDEFINED; + xcbosd_clear(osd); + xcbosd_expose(osd); +} + +void xcbosd_destroy(xcbosd *osd) +{ + + assert (osd); + + xcb_free_gc(osd->connection, osd->gc); + xcb_free_pixmap(osd->connection, osd->bitmap); + xcb_free_colormap(osd->connection, osd->cmap); + if(osd->mode==XCBOSD_SHAPED) { + xcb_free_gc(osd->connection, osd->u.shaped.mask_gc); + xcb_free_gc(osd->connection, osd->u.shaped.mask_gc_back); + xcb_free_pixmap(osd->connection, osd->u.shaped.mask_bitmap); + xcb_destroy_window(osd->connection, osd->u.shaped.window); + } + + free (osd); +} + +void xcbosd_clear(xcbosd *osd) +{ + int i; + + lprintf("clear (state:%d)\n", osd->clean ); + + if( osd->clean != WIPED ) + switch (osd->mode) { + case XCBOSD_SHAPED: { + xcb_rectangle_t rectangle = { 0, 0, osd->width, osd->height }; + xcb_poly_fill_rectangle(osd->connection, osd->u.shaped.mask_bitmap, osd->u.shaped.mask_gc_back, 1, &rectangle); + break; + } + case XCBOSD_COLORKEY: + xcb_change_gc(osd->connection, osd->gc, XCB_GC_FOREGROUND, &osd->u.colorkey.colorkey); + if(osd->u.colorkey.sc) { + xcb_rectangle_t rectangle = { osd->u.colorkey.sc->output_xoffset, osd->u.colorkey.sc->output_yoffset, + osd->u.colorkey.sc->output_width, osd->u.colorkey.sc->output_height }; + xcb_poly_fill_rectangle(osd->connection, osd->bitmap, osd->gc, 1, &rectangle); + xcb_change_gc(osd->connection, osd->gc, XCB_GC_FOREGROUND, &osd->screen->black_pixel); + + xcb_rectangle_t rects[4]; + int rects_count = 0; + + for( i = 0; i < 4; i++ ) { + if( osd->u.colorkey.sc->border[i].w && osd->u.colorkey.sc->border[i].h ) { + rects[rects_count].x = osd->u.colorkey.sc->border[i].x; + rects[rects_count].y = osd->u.colorkey.sc->border[i].y; + rects[rects_count].width = osd->u.colorkey.sc->border[i].w; + rects[rects_count].height = osd->u.colorkey.sc->border[i].h; + rects_count++; + } + + if (rects_count > 0) + xcb_poly_fill_rectangle(osd->connection, osd->bitmap, osd->gc, rects_count, rects); + } + } else { + xcb_rectangle_t rectangle = { 0, 0, osd->width, osd->height }; + xcb_poly_fill_rectangle(osd->connection, osd->bitmap, osd->gc, 1, &rectangle); + } + break; + } + osd->clean = WIPED; +} + +#define TRANSPARENT 0xffffffff + +#define saturate(n, l, u) ((n) < (l) ? (l) : ((n) > (u) ? (u) : (n))) + +void xcbosd_blend(xcbosd *osd, vo_overlay_t *overlay) +{ + xcb_alloc_color_cookie_t alloc_color_cookie; + xcb_alloc_color_reply_t *alloc_color_reply; + + if (osd->clean==UNDEFINED) + xcbosd_clear(osd); /* Workaround. Colorkey mode needs sc data before the clear. */ + + if (overlay->rle) { + int i, x, y, len, width; + int use_clip_palette, max_palette_colour[2]; + uint32_t palette[2][OVL_PALETTE_SIZE]; + + max_palette_colour[0] = -1; + max_palette_colour[1] = -1; + + for (i=0, x=0, y=0; i<overlay->num_rle; i++) { + len = overlay->rle[i].len; + + while (len > 0) { + use_clip_palette = 0; + if (len > overlay->width) { + width = overlay->width; + len -= overlay->width; + } + else { + width = len; + len = 0; + } + if ((y >= overlay->hili_top) && (y <= overlay->hili_bottom) && (x <= overlay->hili_right)) { + if ((x < overlay->hili_left) && (x + width - 1 >= overlay->hili_left)) { + width -= overlay->hili_left - x; + len += overlay->hili_left - x; + } + else if (x > overlay->hili_left) { + use_clip_palette = 1; + if (x + width - 1 > overlay->hili_right) { + width -= overlay->hili_right - x; + len += overlay->hili_right - x; + } + } + } + + if (overlay->rle[i].color > max_palette_colour[use_clip_palette]) { + int j; + clut_t *src_clut; + uint8_t *src_trans; + + if (use_clip_palette) { + src_clut = (clut_t *)&overlay->hili_color; + src_trans = (uint8_t *)&overlay->hili_trans; + } + else { + src_clut = (clut_t *)&overlay->color; + src_trans = (uint8_t *)&overlay->trans; + } + for (j=max_palette_colour[use_clip_palette]+1; j<=overlay->rle[i].color; j++) { + if (src_trans[j]) { + if (1) { + int red, green, blue; + int y, u, v, r, g, b; + + y = saturate(src_clut[j].y, 16, 235); + u = saturate(src_clut[j].cb, 16, 240); + v = saturate(src_clut[j].cr, 16, 240); + y = (9 * y) / 8; + r = y + (25 * v) / 16 - 218; + red = (65536 * saturate(r, 0, 255)) / 256; + g = y + (-13 * v) / 16 + (-25 * u) / 64 + 136; + green = (65536 * saturate(g, 0, 255)) / 256; + b = y + 2 * u - 274; + blue = (65536 * saturate(b, 0, 255)) / 256; + + alloc_color_cookie = xcb_alloc_color(osd->connection, osd->cmap, red, green, blue); + alloc_color_reply = xcb_alloc_color_reply(osd->connection, alloc_color_cookie, NULL); + + palette[use_clip_palette][j] = alloc_color_reply->pixel; + free(alloc_color_reply); + } + else { + if (src_clut[j].y > 127) { + palette[use_clip_palette][j] = osd->screen->white_pixel; + } + else { + palette[use_clip_palette][j] = osd->screen->black_pixel; + } + } + } + else { + palette[use_clip_palette][j] = TRANSPARENT; + } + } + max_palette_colour[use_clip_palette] = overlay->rle[i].color; + } + + if(palette[use_clip_palette][overlay->rle[i].color] != TRANSPARENT) { + xcb_change_gc(osd->connection, osd->gc, XCB_GC_FOREGROUND, &palette[use_clip_palette][overlay->rle[i].color]); + xcb_rectangle_t rectangle = { overlay->x + x, overlay->y + y, width, 1 }; + xcb_poly_fill_rectangle(osd->connection, osd->bitmap, osd->gc, 1, &rectangle); + if(osd->mode==XCBOSD_SHAPED) + xcb_poly_fill_rectangle(osd->connection, osd->u.shaped.mask_bitmap, osd->u.shaped.mask_gc, 1, &rectangle); + } + + x += width; + if (x == overlay->width) { + x = 0; + y++; + } + } + } + osd->clean = DRAWN; + } +} + |