]> git.sesse.net Git - vlc/blobdiff - modules/video_output/xcb/xcb.c
Factor SHM and non SHM pictures a little
[vlc] / modules / video_output / xcb / xcb.c
index 34b83d09f6b90f966984ea219c7d3be2640335c6..99520d99b531e7abf5f92fdbbb8555ae196e95d5 100644 (file)
@@ -77,17 +77,18 @@ 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 */
 };
 
 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 *);
 
@@ -141,30 +142,26 @@ static int Open (vlc_object_t *obj)
     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);
     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);
     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
     {
@@ -175,6 +172,50 @@ static int Open (vlc_object_t *obj)
     }
     p_sys->vid = vt->visual_id;
 
+    /* Determine our input format (normally, done in Init() but X11
+     * never changes its format) */
+    p_sys->bpp = scr->root_depth;
+    switch (scr->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;
+
+        case 8: /* FIXME: VLC cannot convert */
+            vout->output.i_chroma = gray ? VLC_FOURCC ('G', 'R', 'E', 'Y')
+                                         : VLC_FOURCC ('R', 'G', 'B', '2');
+            break;
+
+        default:
+            msg_Err (vout, "unsupported %"PRIu8"-bits screen depth",
+                     scr->root_depth);
+            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)
     {
@@ -192,8 +233,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;
@@ -212,7 +264,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);
@@ -220,13 +274,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)
@@ -278,6 +331,8 @@ 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?? */
@@ -304,18 +359,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;
@@ -323,7 +373,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;
 }
 
@@ -331,26 +381,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.
  */
@@ -360,58 +405,36 @@ static int Init (vout_thread_t *vout)
     const xcb_screen_t *screen = p_sys->screen;
     unsigned x, y, width, height;
 
-    /* Determine parent window */
+    /* 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_RequestWindow (vout, &(int){ 0 }, &(int){ 0 },
-                                            &width, &height);
-        if (p_sys->embed == NULL)
-        {
-            msg_Err (vout, "cannot get window");
-            return VLC_EGENERIC;
-        }
-        p_sys->parent = (intptr_t)p_sys->embed->handle;
-    }
+        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;
+        /* 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 16:
-            vout->output.i_chroma = VLC_FOURCC ('R', 'V', '1', '6');
-            break;
+        xcb_get_geometry_cookie_t ck;
+        ck = xcb_get_geometry (p_sys->conn, p_sys->parent);
 
-        case 15:
-            p_sys->bpp = 16;
-            vout->output.i_chroma = VLC_FOURCC ('R', 'V', '1', '5');
-            break;
-
-        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;
-
-        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;
@@ -430,13 +453,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);
@@ -444,7 +470,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;
@@ -483,14 +509,26 @@ 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]);
+        PictureDeinit (PP_OUTPUTPICTURE[--I_OUTPUTPICTURES]);
 
     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.
  */
@@ -498,9 +536,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,
@@ -512,16 +550,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);
 }
 
@@ -534,7 +564,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))
     {