x11.c
上传用户:kjfoods
上传日期:2020-07-06
资源大小:29949k
文件大小:14k
源码类别:

midi

开发平台:

Unix_Linux

  1. /**
  2.  * @file x11.c
  3.  * @brief X C Bindings video output module for VLC media player
  4.  */
  5. /*****************************************************************************
  6.  * Copyright © 2009 Rémi Denis-Courmont
  7.  *
  8.  * This library is free software; you can redistribute it and/or
  9.  * modify it under the terms of the GNU General Public License
  10.  * as published by the Free Software Foundation; either version 2.0
  11.  * of the License, or (at your option) any later version.
  12.  *
  13.  * This library is distributed in the hope that it will be useful,
  14.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16.  * GNU General Public License for more details.
  17.  *
  18.  * You should have received a copy of the GNU Lesser General Public
  19.  * License along with this library; if not, write to the Free Software
  20.  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  21.  ****************************************************************************/
  22. #ifdef HAVE_CONFIG_H
  23. # include <config.h>
  24. #endif
  25. #include <stdlib.h>
  26. #include <assert.h>
  27. #include <xcb/xcb.h>
  28. #include <xcb/shm.h>
  29. #include <vlc_common.h>
  30. #include <vlc_plugin.h>
  31. #include <vlc_vout.h>
  32. #include <vlc_window.h>
  33. #include "xcb_vlc.h"
  34. #define DISPLAY_TEXT N_("X11 display")
  35. #define DISPLAY_LONGTEXT N_( 
  36.     "X11 hardware display to use. By default VLC will " 
  37.     "use the value of the DISPLAY environment variable.")
  38. #define SHM_TEXT N_("Use shared memory")
  39. #define SHM_LONGTEXT N_( 
  40.     "Use shared memory to communicate between VLC and the X server.")
  41. static int  Open (vlc_object_t *);
  42. static void Close (vlc_object_t *);
  43. /*
  44.  * Module descriptor
  45.  */
  46. vlc_module_begin ()
  47.     set_shortname (N_("XCB"))
  48.     set_description (N_("(Experimental) XCB video output"))
  49.     set_category (CAT_VIDEO)
  50.     set_subcategory (SUBCAT_VIDEO_VOUT)
  51.     set_capability ("video output", 0)
  52.     set_callbacks (Open, Close)
  53.     add_string ("x11-display", NULL, NULL,
  54.                 DISPLAY_TEXT, DISPLAY_LONGTEXT, true)
  55.     add_bool ("x11-shm", true, NULL, SHM_TEXT, SHM_LONGTEXT, true)
  56. vlc_module_end ()
  57. struct vout_sys_t
  58. {
  59.     xcb_connection_t *conn;
  60.     vout_window_t *embed; /* VLC window (when windowed) */
  61.     xcb_window_t window; /* drawable X window */
  62.     xcb_gcontext_t gc; /* context to put images */
  63.     bool shm; /* whether to use MIT-SHM */
  64.     uint8_t bpp; /* bits per pixel */
  65.     uint8_t pad; /* scanline pad */
  66.     uint8_t depth; /* useful bits per pixel */
  67.     uint8_t byte_order; /* server byte order */
  68. };
  69. static int Init (vout_thread_t *);
  70. static void Deinit (vout_thread_t *);
  71. static void Display (vout_thread_t *, picture_t *);
  72. static int Manage (vout_thread_t *);
  73. static int Control (vout_thread_t *, int, va_list);
  74. int CheckError (vout_thread_t *vout, const char *str, xcb_void_cookie_t ck)
  75. {
  76.     xcb_generic_error_t *err;
  77.     err = xcb_request_check (vout->p_sys->conn, ck);
  78.     if (err)
  79.     {
  80.         msg_Err (vout, "%s: X11 error %d", str, err->error_code);
  81.         return VLC_EGENERIC;
  82.     }
  83.     return VLC_SUCCESS;
  84. }
  85. #define p_vout vout
  86. /**
  87.  * Probe the X server.
  88.  */
  89. static int Open (vlc_object_t *obj)
  90. {
  91.     vout_thread_t *vout = (vout_thread_t *)obj;
  92.     vout_sys_t *p_sys = malloc (sizeof (*p_sys));
  93.     if (p_sys == NULL)
  94.         return VLC_ENOMEM;
  95.     vout->p_sys = p_sys;
  96.     /* Connect to X */
  97.     p_sys->conn = Connect (obj);
  98.     if (p_sys->conn == NULL)
  99.     {
  100.         free (p_sys);
  101.         return VLC_EGENERIC;
  102.     }
  103.     /* Get window */
  104.     const xcb_screen_t *scr;
  105.     p_sys->embed = GetWindow (vout, p_sys->conn, &scr, &p_sys->shm);
  106.     if (p_sys->embed == NULL)
  107.     {
  108.         xcb_disconnect (p_sys->conn);
  109.         free (p_sys);
  110.         return VLC_EGENERIC;
  111.     }
  112.     const xcb_setup_t *setup = xcb_get_setup (p_sys->conn);
  113.     p_sys->byte_order = setup->image_byte_order;
  114.     /* Determine our video format. Normally, this is done in pf_init(), but
  115.      * this plugin always uses the same format for a given X11 screen. */
  116.     xcb_visualid_t vid = 0;
  117.     uint8_t depth = 0;
  118.     bool gray = true;
  119.     for (const xcb_format_t *fmt = xcb_setup_pixmap_formats (setup),
  120.              *end = fmt + xcb_setup_pixmap_formats_length (setup);
  121.          fmt < end; fmt++)
  122.     {
  123.         vlc_fourcc_t chroma = 0;
  124.         if (fmt->depth < depth)
  125.             continue; /* We already found a better format! */
  126.         /* Check that the pixmap format is supported by VLC. */
  127.         switch (fmt->depth)
  128.         {
  129.           case 24:
  130.             if (fmt->bits_per_pixel == 32)
  131.                 chroma = VLC_FOURCC ('R', 'V', '3', '2');
  132.             else if (fmt->bits_per_pixel == 24)
  133.                 chroma = VLC_FOURCC ('R', 'V', '2', '4');
  134.             else
  135.                 continue;
  136.             break;
  137.           case 16:
  138.             if (fmt->bits_per_pixel != 16)
  139.                 continue;
  140.             chroma = VLC_FOURCC ('R', 'V', '1', '6');
  141.             break;
  142.           case 15:
  143.             if (fmt->bits_per_pixel != 16)
  144.                 continue;
  145.             chroma = VLC_FOURCC ('R', 'V', '1', '5');
  146.             break;
  147.           case 8:
  148.             if (fmt->bits_per_pixel != 8)
  149.                 continue;
  150.             chroma = VLC_FOURCC ('R', 'G', 'B', '2');
  151.             break;
  152.           default:
  153.             continue;
  154.         }
  155.         if ((fmt->bits_per_pixel << 4) % fmt->scanline_pad)
  156.             continue; /* VLC pads lines to 16 pixels internally */
  157.         /* Byte sex is a non-issue for 8-bits. It can be worked around with
  158.          * RGB masks for 24-bits. Too bad for 15-bits and 16-bits. */
  159.         if (fmt->bits_per_pixel == 16 && setup->image_byte_order != ORDER)
  160.             continue;
  161.         /* Check that the selected screen supports this depth */
  162.         xcb_depth_iterator_t it = xcb_screen_allowed_depths_iterator (scr);
  163.         while (it.rem > 0 && it.data->depth != fmt->depth)
  164.              xcb_depth_next (&it);
  165.         if (!it.rem)
  166.             continue; /* Depth not supported on this screen */
  167.         /* Find a visual type for the selected depth */
  168.         const xcb_visualtype_t *vt = xcb_depth_visuals (it.data);
  169.         for (int i = xcb_depth_visuals_length (it.data); i > 0; i--)
  170.         {
  171.             if (vt->_class == XCB_VISUAL_CLASS_TRUE_COLOR)
  172.             {
  173.                 vid = vt->visual_id;
  174.                 gray = false;
  175.                 break;
  176.             }
  177.             if (fmt->depth == 8 && vt->_class == XCB_VISUAL_CLASS_STATIC_GRAY)
  178.             {
  179.                 if (!gray)
  180.                     continue; /* Prefer color over gray scale */
  181.                 vid = vt->visual_id;
  182.                 chroma = VLC_FOURCC ('G', 'R', 'E', 'Y');
  183.             }
  184.         }
  185.         if (!vid)
  186.             continue; /* The screen does not *really* support this depth */
  187.         vout->fmt_out.i_chroma = vout->output.i_chroma = chroma;
  188.         if (!gray)
  189.         {
  190.             vout->fmt_out.i_rmask = vout->output.i_rmask = vt->red_mask;
  191.             vout->fmt_out.i_gmask = vout->output.i_gmask = vt->green_mask;
  192.             vout->fmt_out.i_bmask = vout->output.i_bmask = vt->blue_mask;
  193.         }
  194.         p_sys->bpp = fmt->bits_per_pixel;
  195.         p_sys->pad = fmt->scanline_pad;
  196.         p_sys->depth = depth = fmt->depth;
  197.     }
  198.     if (depth == 0)
  199.     {
  200.         msg_Err (vout, "no supported pixmap formats or visual types");
  201.         goto error;
  202.     }
  203.     msg_Dbg (vout, "using X11 visual ID 0x%"PRIx32, vid);
  204.     msg_Dbg (vout, " %"PRIu8" bits per pixels, %"PRIu8" bits line pad",
  205.              p_sys->bpp, p_sys->pad);
  206.     /* Create colormap (needed to select non-default visual) */
  207.     xcb_colormap_t cmap;
  208.     if (vid != scr->root_visual)
  209.     {
  210.         cmap = xcb_generate_id (p_sys->conn);
  211.         xcb_create_colormap (p_sys->conn, XCB_COLORMAP_ALLOC_NONE,
  212.                              cmap, scr->root, vid);
  213.     }
  214.     else
  215.         cmap = scr->default_colormap;
  216.     /* Create window */
  217.     {
  218.         const uint32_t mask = XCB_CW_EVENT_MASK | XCB_CW_COLORMAP;
  219.         const uint32_t values[] = {
  220.             /* XCB_CW_EVENT_MASK */
  221.             XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE |
  222.             XCB_EVENT_MASK_POINTER_MOTION,
  223.             /* XCB_CW_COLORMAP */
  224.             cmap,
  225.         };
  226.         xcb_void_cookie_t c;
  227.         xcb_window_t window = xcb_generate_id (p_sys->conn);
  228.         c = xcb_create_window_checked (p_sys->conn, depth, window,
  229.                                        p_sys->embed->handle.xid, 0, 0, 1, 1, 0,
  230.                                        XCB_WINDOW_CLASS_INPUT_OUTPUT,
  231.                                        vid, mask, values);
  232.         if (CheckError (vout, "cannot create X11 window", c))
  233.             goto error;
  234.         p_sys->window = window;
  235.         msg_Dbg (vout, "using X11 window %08"PRIx32, p_sys->window);
  236.         xcb_map_window (p_sys->conn, window);
  237.     }
  238.     /* Create graphic context (I wonder why the heck do we need this) */
  239.     p_sys->gc = xcb_generate_id (p_sys->conn);
  240.     xcb_create_gc (p_sys->conn, p_sys->gc, p_sys->window, 0, NULL);
  241.     msg_Dbg (vout, "using X11 graphic context %08"PRIx32, p_sys->gc);
  242.     vout->pf_init = Init;
  243.     vout->pf_end = Deinit;
  244.     vout->pf_display = Display;
  245.     vout->pf_manage = Manage;
  246.     vout->pf_control = Control;
  247.     return VLC_SUCCESS;
  248. error:
  249.     Close (obj);
  250.     return VLC_EGENERIC;
  251. }
  252. /**
  253.  * Disconnect from the X server.
  254.  */
  255. static void Close (vlc_object_t *obj)
  256. {
  257.     vout_thread_t *vout = (vout_thread_t *)obj;
  258.     vout_sys_t *p_sys = vout->p_sys;
  259.     vout_ReleaseWindow (p_sys->embed);
  260.     /* colormap and window are garbage-collected by X */
  261.     xcb_disconnect (p_sys->conn);
  262.     free (p_sys);
  263. }
  264. /**
  265.  * Allocate drawable window and picture buffers.
  266.  */
  267. static int Init (vout_thread_t *vout)
  268. {
  269.     vout_sys_t *p_sys = vout->p_sys;
  270.     unsigned x, y, width, height;
  271.     if (GetWindowSize (p_sys->embed, p_sys->conn, &width, &height))
  272.         return VLC_EGENERIC;
  273.     vout_PlacePicture (vout, width, height, &x, &y, &width, &height);
  274.     const uint32_t values[] = { x, y, width, height, };
  275.     xcb_configure_window (p_sys->conn, p_sys->window,
  276.                           XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y |
  277.                           XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT,
  278.                           values);
  279.     /* FIXME: I don't get the subtlety between output and fmt_out here */
  280.     vout->fmt_out.i_visible_width = width;
  281.     vout->fmt_out.i_visible_height = height;
  282.     vout->fmt_out.i_sar_num = vout->fmt_out.i_sar_den = 1;
  283.     vout->output.i_width = vout->fmt_out.i_width =
  284.         width * vout->fmt_in.i_width / vout->fmt_in.i_visible_width;
  285.     vout->output.i_height = vout->fmt_out.i_height =
  286.         height * vout->fmt_in.i_height / vout->fmt_in.i_visible_height;
  287.     vout->fmt_out.i_x_offset =
  288.         width * vout->fmt_in.i_x_offset / vout->fmt_in.i_visible_width;
  289.     p_vout->fmt_out.i_y_offset =
  290.         height * vout->fmt_in.i_y_offset / vout->fmt_in.i_visible_height;
  291.     assert (height > 0);
  292.     vout->output.i_aspect = vout->fmt_out.i_aspect =
  293.         width * VOUT_ASPECT_FACTOR / height;
  294.     /* Allocate picture buffers */
  295.     I_OUTPUTPICTURES = 0;
  296.     for (size_t index = 0; I_OUTPUTPICTURES < 2; index++)
  297.     {
  298.         picture_t *pic = vout->p_picture + index;
  299.         if (index > sizeof (vout->p_picture) / sizeof (pic))
  300.             break;
  301.         if (pic->i_status != FREE_PICTURE)
  302.             continue;
  303.         vout_InitPicture (vout, pic, vout->output.i_chroma,
  304.                           vout->output.i_width, vout->output.i_height,
  305.                           vout->output.i_aspect);
  306.         if (PictureAlloc (vout, pic, pic->p->i_pitch * pic->p->i_lines,
  307.                           p_sys->shm ? p_sys->conn : NULL))
  308.             break;
  309.         PP_OUTPUTPICTURE[I_OUTPUTPICTURES++] = pic;
  310.     }
  311.     xcb_flush (p_sys->conn);
  312.     return VLC_SUCCESS;
  313. }
  314. /**
  315.  * Free picture buffers.
  316.  */
  317. static void Deinit (vout_thread_t *vout)
  318. {
  319.     for (int i = 0; i < I_OUTPUTPICTURES; i++)
  320.         PictureFree (PP_OUTPUTPICTURE[i], vout->p_sys->conn);
  321. }
  322. /**
  323.  * Sends an image to the X server.
  324.  */
  325. static void Display (vout_thread_t *vout, picture_t *pic)
  326. {
  327.     vout_sys_t *p_sys = vout->p_sys;
  328.     xcb_shm_seg_t segment = (uintptr_t)pic->p_sys;
  329.     if (segment != 0)
  330.         xcb_shm_put_image (p_sys->conn, p_sys->window, p_sys->gc,
  331.           /* real width */ pic->p->i_pitch / pic->p->i_pixel_pitch,
  332.          /* real height */ pic->p->i_lines,
  333.                    /* x */ vout->fmt_out.i_x_offset,
  334.                    /* y */ vout->fmt_out.i_y_offset,
  335.                /* width */ vout->fmt_out.i_visible_width,
  336.               /* height */ vout->fmt_out.i_visible_height,
  337.                            0, 0, p_sys->depth, XCB_IMAGE_FORMAT_Z_PIXMAP,
  338.                            0, segment, 0);
  339.     else
  340.     {
  341.         const size_t offset = vout->fmt_out.i_y_offset * pic->p->i_pitch;
  342.         const unsigned lines = pic->p->i_lines - vout->fmt_out.i_y_offset;
  343.         xcb_put_image (p_sys->conn, XCB_IMAGE_FORMAT_Z_PIXMAP,
  344.                        p_sys->window, p_sys->gc,
  345.                        pic->p->i_pitch / pic->p->i_pixel_pitch,
  346.                        lines, -vout->fmt_out.i_x_offset, 0, 0, p_sys->depth,
  347.                        pic->p->i_pitch * lines, pic->p->p_pixels + offset);
  348.     }
  349.     xcb_flush (p_sys->conn);
  350. }
  351. /**
  352.  * Process incoming X events.
  353.  */
  354. static int Manage (vout_thread_t *vout)
  355. {
  356.     vout_sys_t *p_sys = vout->p_sys;
  357.     xcb_generic_event_t *ev;
  358.     while ((ev = xcb_poll_for_event (p_sys->conn)) != NULL)
  359.         ProcessEvent (vout, p_sys->conn, p_sys->window, ev);
  360.     if (xcb_connection_has_error (p_sys->conn))
  361.     {
  362.         msg_Err (vout, "X server failure");
  363.         return VLC_EGENERIC;
  364.     }
  365.     CommonManage (vout); /* FIXME: <-- move that to core */
  366.     return VLC_SUCCESS;
  367. }
  368. void
  369. HandleParentStructure (vout_thread_t *vout, xcb_connection_t *conn,
  370.                        xcb_window_t xid, xcb_configure_notify_event_t *ev)
  371. {
  372.     unsigned width, height, x, y;
  373.     vout_PlacePicture (vout, ev->width, ev->height, &x, &y, &width, &height);
  374.     if (width != vout->fmt_out.i_visible_width
  375.      || height != vout->fmt_out.i_visible_height)
  376.     {
  377.         vout->i_changes |= VOUT_SIZE_CHANGE;
  378.         return; /* vout will be reinitialized */
  379.     }
  380.     /* Move the picture within the window */
  381.     const uint32_t values[] = { x, y, };
  382.     xcb_configure_window (conn, xid,
  383.                           XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y,
  384.                           values);
  385. }
  386. static int Control (vout_thread_t *vout, int query, va_list ap)
  387. {
  388.     return vout_ControlWindow (vout->p_sys->embed, query, ap);
  389. }