X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Fvideo_output%2Fxcb%2Fxcb.c;h=3c52f776d84e3b3e4377059d8450d644f45cf687;hb=00155d409b3b839f85f7201face93b06a64b56d6;hp=166622e7cadd2923c5482d2bb1307045ece7d13e;hpb=2b9b5c0c6b7a9de32d5a42755f648d5194afaf13;p=vlc diff --git a/modules/video_output/xcb/xcb.c b/modules/video_output/xcb/xcb.c index 166622e7ca..3c52f776d8 100644 --- a/modules/video_output/xcb/xcb.c +++ b/modules/video_output/xcb/xcb.c @@ -77,17 +77,19 @@ struct vout_sys_t xcb_screen_t *screen; vout_window_t *embed; /* VLC window (when windowed) */ - xcb_visualid_t vid; + xcb_visualid_t vid; /* selected visual */ xcb_window_t parent; /* parent X window */ + xcb_colormap_t cmap; /* colormap for selected visual */ xcb_window_t window; /* drawable X window */ xcb_gcontext_t gc; /* context to put images */ bool shm; /* whether to use MIT-SHM */ - bool gray; /* whether display is grayscale */ uint8_t bpp; /* bits per pixel */ + uint8_t pad; /* scanline pad */ }; static int Init (vout_thread_t *); static void Deinit (vout_thread_t *); +static void Render (vout_thread_t *, picture_t *); static void Display (vout_thread_t *, picture_t *); static int Manage (vout_thread_t *); @@ -135,48 +137,108 @@ static int Open (vlc_object_t *obj) } free (display); + const xcb_setup_t *setup = xcb_get_setup (p_sys->conn); + /* Get the preferred screen */ xcb_screen_t *scr = xcb_aux_get_screen (p_sys->conn, snum); p_sys->screen = scr; assert (p_sys->screen); msg_Dbg (vout, "using screen %d (depth %"PRIu8")", snum, scr->root_depth); - if (strchr ("\x08\x0f\x10\x18\x20", scr->root_depth) == NULL) - { - msg_Err (vout, "unsupported %"PRIu8"-bits color depth", - scr->root_depth); - goto error; - } - /* Determine the visual (color depth and palette) */ xcb_visualtype_t *vt = NULL; - p_sys->gray = false; + bool gray = false; + if ((vt = xcb_aux_find_visual_by_attrs (scr, XCB_VISUAL_CLASS_TRUE_COLOR, scr->root_depth)) != NULL) msg_Dbg (vout, "using TrueColor visual ID %d", (int)vt->visual_id); -#if 0 else +#if 0 if ((vt = xcb_aux_find_visual_by_attrs (scr, XCB_VISUAL_CLASS_STATIC_COLOR, scr->root_depth)) != NULL) msg_Dbg (vout, "using static color visual ID %d", (int)vt->visual_id); -#endif else +#endif if ((scr->root_depth == 8) && (vt = xcb_aux_find_visual_by_attrs (scr, XCB_VISUAL_CLASS_STATIC_GRAY, scr->root_depth)) != NULL) { msg_Dbg (vout, "using static gray visual ID %d", (int)vt->visual_id); - p_sys->gray = true; + gray = true; } else { - vt = xcb_aux_find_visual_by_id (scr, scr->root_visual); - assert (vt); - msg_Err (vout, "unsupported visual class %"PRIu8, vt->_class); + msg_Err (vout, "no supported visual class"); goto error; } p_sys->vid = vt->visual_id; + /* Determine our input format (normally, done in Init() but X11 + * never changes its format) */ + vout->output.i_chroma = 0; + for (const xcb_format_t *fmt = xcb_setup_pixmap_formats (setup), + *end = fmt + xcb_setup_pixmap_formats_length (setup); + fmt < end; fmt++) + { + if (fmt->depth != scr->root_depth) + continue; + + switch (fmt->depth) + { + case 24: + if (fmt->bits_per_pixel == 32) + vout->output.i_chroma = VLC_FOURCC ('R', 'V', '3', '2'); + else if (fmt->bits_per_pixel == 24) + vout->output.i_chroma = VLC_FOURCC ('R', 'V', '2', '4'); + else + continue; + break; + case 16: + if (fmt->bits_per_pixel != 16) + continue; + vout->output.i_chroma = VLC_FOURCC ('R', 'V', '1', '6'); + break; + case 15: + if (fmt->bits_per_pixel != 16) + continue; + vout->output.i_chroma = VLC_FOURCC ('R', 'V', '1', '5'); + break; + case 8: + if (fmt->bits_per_pixel != 8) + continue; + vout->output.i_chroma = gray ? VLC_FOURCC ('G', 'R', 'E', 'Y') + : VLC_FOURCC ('R', 'G', 'B', '2'); + break; + } + if ((fmt->bits_per_pixel << 4) % fmt->scanline_pad) + continue; /* VLC pads lines to 16 pixels internally */ + p_sys->bpp = fmt->bits_per_pixel; + p_sys->pad = fmt->scanline_pad; + msg_Dbg (vout, "using %"PRIu8" bits per pixels (line pad: %"PRIu8")", + p_sys->bpp, p_sys->pad); + break; + } + + if (!vout->output.i_chroma) + { + msg_Err (vout, "no supported pixmap formats"); + goto error; + } + + vout->fmt_out.i_chroma = vout->output.i_chroma; + if (!gray) + { + vout->output.i_rmask = vt->red_mask; + vout->output.i_gmask = vt->green_mask; + vout->output.i_bmask = vt->blue_mask; + } + + /* Create colormap (needed to select non-default visual) */ + p_sys->cmap = xcb_generate_id (p_sys->conn); + xcb_create_colormap (p_sys->conn, XCB_COLORMAP_ALLOC_NONE, + p_sys->cmap, scr->root, p_sys->vid); + + /* Check shared memory support */ p_sys->shm = var_CreateGetBool (vout, "x11-shm") > 0; if (p_sys->shm) { @@ -194,8 +256,19 @@ static int Open (vlc_object_t *obj) } } + /* Get window */ + /* FIXME: WTH to put as initial width/height values??? */ + p_sys->embed = vout_RequestXWindow (vout, &(int){ 0 }, &(int){ 0 }, + &(unsigned){ 0 }, &(unsigned){ 0 }); + if (p_sys->embed == NULL) + { + msg_Err (vout, "parent window not available"); + goto error; + } + vout->pf_init = Init; vout->pf_end = Deinit; + vout->pf_render = Render; vout->pf_display = Display; vout->pf_manage = Manage; return VLC_SUCCESS; @@ -214,7 +287,9 @@ static void Close (vlc_object_t *obj) vout_thread_t *vout = (vout_thread_t *)obj; vout_sys_t *p_sys = vout->p_sys; - assert (p_sys->embed == NULL); + if (p_sys->embed) + vout_ReleaseWindow (p_sys->embed); + /* colormap is garbage-ollected by X (?) */ if (p_sys->conn) xcb_disconnect (p_sys->conn); free (p_sys); @@ -222,13 +297,12 @@ static void Close (vlc_object_t *obj) struct picture_sys_t { - xcb_connection_t *conn; - xcb_image_t *image; - xcb_shm_seg_t segment; + xcb_connection_t *conn; /* Shared connection to X server */ + xcb_image_t *image; /* Picture buffer */ + xcb_image_t *native; /* Rendered picture buffer (in X server format) */ + xcb_shm_seg_t segment; /* Shared memory segment X ID */ }; -static void PictureRelease (picture_t *pic); -static void PictureShmRelease (picture_t *pic); #define SHM_ERR ((void *)(intptr_t)(-1)) static int PictureInit (vout_thread_t *vout, picture_t *pic) @@ -280,14 +354,15 @@ static int PictureInit (vout_thread_t *vout, picture_t *pic) shm = SHM_ERR; } } + else + priv->segment = 0; const unsigned real_width = pic->p->i_pitch / (p_sys->bpp >> 3); /* FIXME: anyway to getthing more intuitive than that?? */ - /* NOTE: 32-bits scanline_pad assumed, FIXME? (see xdpyinfo) */ xcb_image_t *img; img = xcb_image_create (real_width, pic->p->i_lines, - XCB_IMAGE_FORMAT_Z_PIXMAP, 32, + XCB_IMAGE_FORMAT_Z_PIXMAP, p_sys->pad, p_sys->screen->root_depth, p_sys->bpp, p_sys->bpp, #ifdef WORDS_BIGENDIAN XCB_IMAGE_ORDER_MSB_FIRST, @@ -306,18 +381,13 @@ static int PictureInit (vout_thread_t *vout, picture_t *pic) goto error; } if (shm != SHM_ERR && xcb_image_native (p_sys->conn, img, 0) == NULL) - { - msg_Err (vout, "incompatible X server image format"); - xcb_image_destroy (img); - goto error; - } + msg_Warn (vout, "incompatible X server image format"); priv->conn = p_sys->conn; priv->image = img; + priv->native = NULL; pic->p_sys = priv; pic->p->p_pixels = img->data; - pic->pf_release = (shm != SHM_ERR) ? PictureShmRelease - : PictureRelease; pic->i_status = DESTROYED_PICTURE; pic->i_type = DIRECT_PICTURE; return VLC_SUCCESS; @@ -325,7 +395,7 @@ static int PictureInit (vout_thread_t *vout, picture_t *pic) error: if (shm != SHM_ERR) shmdt (shm); - free (p_sys); + free (priv); return VLC_EGENERIC; } @@ -333,26 +403,21 @@ error: /** * Release picture private data */ -static void PictureRelease (picture_t *pic) +static void PictureDeinit (picture_t *pic) { struct picture_sys_t *p_sys = pic->p_sys; + if (p_sys->segment != 0) + { + xcb_shm_detach (p_sys->conn, p_sys->segment); + shmdt (p_sys->image->data); + } + if ((p_sys->native != NULL) && (p_sys->native != p_sys->image)) + xcb_image_destroy (p_sys->native); xcb_image_destroy (p_sys->image); free (p_sys); } -/** - * Release shared memory picture private data - */ -static void PictureShmRelease (picture_t *pic) -{ - struct picture_sys_t *p_sys = pic->p_sys; - - xcb_shm_detach (p_sys->conn, p_sys->segment); - shmdt (p_sys->image->data); - PictureRelease (pic); -} - /** * Allocate drawable window and picture buffers. */ @@ -362,58 +427,38 @@ static int Init (vout_thread_t *vout) const xcb_screen_t *screen = p_sys->screen; unsigned x, y, width, height; - /* Determine parent window */ + I_OUTPUTPICTURES = 0; + + /* Determine parent window and size */ if (vout->b_fullscreen) { - p_sys->embed = NULL; p_sys->parent = screen->root; width = screen->width_in_pixels; height = screen->height_in_pixels; } else { - p_sys->embed = vout_RequestXWindow (vout, &(int){ 0 }, &(int){ 0 }, - &width, &height); - if (p_sys->embed == NULL) - { - msg_Err (vout, "cannot get window"); - return VLC_EGENERIC; - } p_sys->parent = p_sys->embed->handle.xid; - } - - /* Determine our input format */ - p_sys->bpp = screen->root_depth; - switch (screen->root_depth) - { - case 24: - p_sys->bpp = 32; - case 32: /* FIXME: untested */ - vout->output.i_chroma = VLC_FOURCC ('R', 'V', '3', '2'); - break; - case 16: - vout->output.i_chroma = VLC_FOURCC ('R', 'V', '1', '6'); - break; - - case 15: - p_sys->bpp = 16; - vout->output.i_chroma = VLC_FOURCC ('R', 'V', '1', '5'); - break; + /* Subscribe to parent window resize events */ + const uint32_t value = XCB_EVENT_MASK_STRUCTURE_NOTIFY; + xcb_change_window_attributes (p_sys->conn, p_sys->parent, + XCB_CW_EVENT_MASK, &value); - case 8: /* FIXME: VLC cannot convert */ - vout->output.i_chroma = - p_sys->gray ? VLC_FOURCC ('G', 'R', 'E', 'Y') - : VLC_FOURCC ('R', 'G', 'B', '2'); - break; + xcb_get_geometry_cookie_t ck; + ck = xcb_get_geometry (p_sys->conn, p_sys->parent); - default: - assert (0); + xcb_get_geometry_reply_t *geo; + xcb_generic_error_t *err; + geo = xcb_get_geometry_reply (p_sys->conn, ck, &err); + width = geo->width; + height = geo->height; + free (geo); } + vout_PlacePicture (vout, width, height, &x, &y, &width, &height); /* FIXME: I don't get the subtlety between output and fmt_out here */ - vout->fmt_out.i_chroma = vout->output.i_chroma; vout->fmt_out.i_visible_width = width; vout->fmt_out.i_visible_height = height; vout->fmt_out.i_sar_num = vout->fmt_out.i_sar_den = 1; @@ -432,13 +477,16 @@ static int Init (vout_thread_t *vout) width * VOUT_ASPECT_FACTOR / height; /* Create window */ - const uint32_t mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK; - uint32_t values[2] = { + const uint32_t mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK + | XCB_CW_COLORMAP; + const uint32_t values[] = { /* XCB_CW_BACK_PIXEL */ screen->black_pixel, /* XCB_CW_EVENT_MASK */ XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE | XCB_EVENT_MASK_POINTER_MOTION, + /* XCB_CW_COLORMAP */ + p_sys->cmap, }; xcb_void_cookie_t c; xcb_window_t window = xcb_generate_id (p_sys->conn); @@ -446,7 +494,7 @@ static int Init (vout_thread_t *vout) c = xcb_create_window_checked (p_sys->conn, screen->root_depth, window, p_sys->parent, x, y, width, height, 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, - screen->root_visual, mask, values); + p_sys->vid, mask, values); if (CheckError (vout, "cannot create X11 window", c)) goto error; p_sys->window = window; @@ -460,15 +508,19 @@ static int Init (vout_thread_t *vout) xcb_flush (p_sys->conn); /* Allocate picture buffers */ - do + I_OUTPUTPICTURES = 0; + for (size_t index = 0; I_OUTPUTPICTURES < 2; index++) { - picture_t *pic = vout->p_picture + I_OUTPUTPICTURES; + picture_t *pic = vout->p_picture + index; + if (index > sizeof (vout->p_picture) / sizeof (pic)) + break; + if (pic->i_status != FREE_PICTURE) + continue; if (PictureInit (vout, pic)) break; PP_OUTPUTPICTURE[I_OUTPUTPICTURES++] = pic; } - while (I_OUTPUTPICTURES < 2); return VLC_SUCCESS; @@ -484,15 +536,27 @@ static void Deinit (vout_thread_t *vout) { vout_sys_t *p_sys = vout->p_sys; - while (I_OUTPUTPICTURES > 0) - picture_Release (PP_OUTPUTPICTURE[--I_OUTPUTPICTURES]); + for (int i = 0; i < I_OUTPUTPICTURES; i++) + PictureDeinit (PP_OUTPUTPICTURE[i]); xcb_unmap_window (p_sys->conn, p_sys->window); xcb_destroy_window (p_sys->conn, p_sys->window); - vout_ReleaseWindow (p_sys->embed); - p_sys->embed = NULL; } +/** + * Prepares an image ahead of display. + */ +static void Render (vout_thread_t *vout, picture_t *pic) +{ + vout_sys_t *p_sys = vout->p_sys; + picture_sys_t *priv = pic->p_sys; + + if ((priv->native != NULL) && (priv->native != priv->image)) + xcb_image_destroy (priv->native); + priv->native = xcb_image_native (p_sys->conn, priv->image, 1); +} + + /** * Sends an image to the X server. */ @@ -500,9 +564,9 @@ static void Display (vout_thread_t *vout, picture_t *pic) { vout_sys_t *p_sys = vout->p_sys; picture_sys_t *priv = pic->p_sys; - xcb_image_t *img = priv->image; + xcb_image_t *img = priv->image, *native = priv->native; - if (img->base == NULL) + if ((native == img) && (img->base == NULL)) { xcb_shm_segment_info_t info = { .shmseg = priv->segment, @@ -514,16 +578,8 @@ static void Display (vout_thread_t *vout, picture_t *pic) 0, 0, 0, 0, img->width, img->height, 0); } else - { - xcb_image_t *native = xcb_image_native (p_sys->conn, img, 1); - - if (native == NULL) - return; - + if (native != NULL) xcb_image_put (p_sys->conn, p_sys->window, p_sys->gc, native, 0, 0, 0); - if (native != img) - xcb_image_destroy (native); - } xcb_flush (p_sys->conn); } @@ -536,7 +592,7 @@ static int Manage (vout_thread_t *vout) xcb_generic_event_t *ev; while ((ev = xcb_poll_for_event (p_sys->conn)) != NULL) - ProcessEvent (vout, ev); + ProcessEvent (vout, p_sys->conn, p_sys->window, ev); if (xcb_connection_has_error (p_sys->conn)) {