]> git.sesse.net Git - vlc/blob - modules/video_output/xcb/window.c
XCB: set WM_CLIENT_MACHINE properly
[vlc] / modules / video_output / xcb / window.c
1 /**
2  * @file window.c
3  * @brief X C Bindings window provider 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
23 #ifdef HAVE_CONFIG_H
24 # include <config.h>
25 #endif
26
27 #include <stdarg.h>
28 #include <assert.h>
29 #include <poll.h>
30 #include <unistd.h> /* gethostname() */
31 #include <limits.h> /* HOST_NAME_MAX */
32
33 #include <xcb/xcb.h>
34 #include <xcb/xcb_aux.h>
35 typedef xcb_atom_t Atom;
36 #include <X11/Xatom.h> /* XA_WM_NAME */
37
38 #include <vlc_common.h>
39 #include <vlc_plugin.h>
40 #include <vlc_window.h>
41
42 #include "xcb_vlc.h"
43
44 #define DISPLAY_TEXT N_("X11 display")
45 #define DISPLAY_LONGTEXT N_( \
46     "X11 hardware display to use. By default VLC will " \
47     "use the value of the DISPLAY environment variable.")
48
49 static int  Open (vlc_object_t *);
50 static void Close (vlc_object_t *);
51
52 /*
53  * Module descriptor
54  */
55 vlc_module_begin ()
56     set_shortname (N_("XCB window"))
57     set_description (N_("(Experimental) XCB video window"))
58     set_category (CAT_VIDEO)
59     set_subcategory (SUBCAT_VIDEO_VOUT)
60     set_capability ("xwindow", 10)
61     set_callbacks (Open, Close)
62
63     add_string ("x11-display", NULL, NULL,
64                 DISPLAY_TEXT, DISPLAY_LONGTEXT, true)
65 vlc_module_end ()
66
67 static int Control (vout_window_t *, int, va_list ap);
68 static void *Thread (void *);
69
70 struct vout_window_sys_t
71 {
72     xcb_connection_t *conn;
73     key_handler_t *keys;
74     vlc_thread_t thread;
75 };
76
77 static inline
78 void set_ascii_prop (xcb_connection_t *conn, xcb_window_t window,
79                      xcb_atom_t atom, const char *value)
80 {
81     xcb_change_property (conn, XCB_PROP_MODE_REPLACE, window, atom,
82                          XA_STRING, 8, strlen (value), value);
83 }
84
85 static inline
86 void set_hostname_prop (xcb_connection_t *conn, xcb_window_t window)
87 {
88     char hostname[HOST_NAME_MAX];
89
90     if (gethostname (hostname, sizeof (hostname)) == 0)
91     {
92         hostname[sizeof (hostname) - 1] = '\0';
93         set_ascii_prop (conn, window, XA_WM_CLIENT_MACHINE, hostname);
94     }
95 }
96
97
98 /**
99  * Create an X11 window.
100  */
101 static int Open (vlc_object_t *obj)
102 {
103     vout_window_t *wnd = (vout_window_t *)obj;
104     vout_window_sys_t *p_sys = malloc (sizeof (*p_sys));
105     xcb_generic_error_t *err;
106     xcb_void_cookie_t ck;
107
108     if (p_sys == NULL)
109         return VLC_ENOMEM;
110
111     /* Connect to X */
112     char *display = var_CreateGetNonEmptyString (wnd, "x11-display");
113     int snum;
114
115     xcb_connection_t *conn = xcb_connect (display, &snum);
116     free (display);
117     if (xcb_connection_has_error (conn) /*== NULL*/)
118         goto error;
119
120     /* Create window */
121     xcb_screen_t *scr = xcb_aux_get_screen (conn, snum);
122     const uint32_t mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK;
123     uint32_t values[2] = {
124         /* XCB_CW_BACK_PIXEL */
125         scr->black_pixel,
126         /* XCB_CW_EVENT_MASK */
127         XCB_EVENT_MASK_KEY_PRESS,
128     };
129
130     xcb_window_t window = xcb_generate_id (conn);
131     ck = xcb_create_window_checked (conn, scr->root_depth, window, scr->root,
132                                     0, 0, wnd->width, wnd->height, 0,
133                                     XCB_WINDOW_CLASS_INPUT_OUTPUT,
134                                     scr->root_visual, mask, values);
135     err = xcb_request_check (conn, ck);
136     if (err)
137     {
138         msg_Err (wnd, "creating window: X11 error %d", err->error_code);
139         goto error;
140     }
141
142     /* ICCCM
143      * No cut&paste nor drag&drop, only Window Manager communication. */
144     /* Plain ASCII localization of VLC for ICCCM window name */
145     set_ascii_prop (conn, window, XA_WM_NAME,
146                     pgettext ("ASCII VLC media player", "VLC media player"));
147     set_ascii_prop (conn, window, XA_WM_ICON_NAME,
148                     pgettext ("ASCII VLC", "VLC"));
149     xcb_change_property (conn, XCB_PROP_MODE_REPLACE, window, XA_WM_CLASS,
150                          XA_STRING, 8, 8, "vlc\0VLC");
151     set_hostname_prop (conn, window);
152
153     wnd->handle.xid = window;
154     wnd->p_sys = p_sys;
155     wnd->control = Control;
156
157     p_sys->conn = conn;
158     p_sys->keys = CreateKeyHandler (obj, conn);
159
160     if ((p_sys->keys != NULL)
161      && vlc_clone (&p_sys->thread, Thread, wnd, VLC_THREAD_PRIORITY_LOW))
162         DestroyKeyHandler (p_sys->keys);
163
164     /* Make sure the window is ready */
165     xcb_map_window (conn, window);
166     xcb_flush (conn);
167
168     return VLC_SUCCESS;
169
170 error:
171     xcb_disconnect (conn);
172     free (p_sys);
173     return VLC_EGENERIC;
174 }
175
176
177 /**
178  * Destroys the X11 window.
179  */
180 static void Close (vlc_object_t *obj)
181 {
182     vout_window_t *wnd = (vout_window_t *)obj;
183     vout_window_sys_t *p_sys = wnd->p_sys;
184     xcb_connection_t *conn = p_sys->conn;
185     xcb_window_t window = wnd->handle.xid;
186
187     if (p_sys->keys)
188     {
189         vlc_cancel (p_sys->thread);
190         vlc_join (p_sys->thread, NULL);
191         DestroyKeyHandler (p_sys->keys);
192     }
193     xcb_unmap_window (conn, window);
194     xcb_destroy_window (conn, window);
195     xcb_disconnect (conn);
196     free (p_sys);
197 }
198
199
200 static void *Thread (void *data)
201 {
202     vout_window_t *wnd = data;
203     vout_window_sys_t *p_sys = wnd->p_sys;
204     xcb_connection_t *conn = p_sys->conn;
205
206     int fd = xcb_get_file_descriptor (conn);
207     if (fd == -1)
208         return NULL;
209
210     for (;;)
211     {
212         xcb_generic_event_t *ev;
213         struct pollfd ufd = { .fd = fd, .events = POLLIN, };
214
215         poll (&ufd, 1, -1);
216
217         int canc = vlc_savecancel ();
218         while ((ev = xcb_poll_for_event (conn)) != NULL)
219         {
220             if (ProcessKeyEvent (p_sys->keys, ev) == 0)
221                 continue;
222             msg_Dbg (wnd, "unhandled event: %"PRIu8, ev->response_type);
223             free (ev);
224         }
225         vlc_restorecancel (canc);
226
227         if (xcb_connection_has_error (conn))
228         {
229             msg_Err (wnd, "X server failure");
230             break;
231         }
232     }
233     return NULL;
234 }
235
236 #include <vlc_vout.h>
237
238 static int Control (vout_window_t *wnd, int cmd, va_list ap)
239 {
240     vout_window_sys_t *p_sys = wnd->p_sys;
241     xcb_connection_t *conn = p_sys->conn;
242
243     switch (cmd)
244     {
245         case VOUT_SET_SIZE:
246         {
247             unsigned width = va_arg (ap, unsigned);
248             unsigned height = va_arg (ap, unsigned);
249             const uint32_t values[] = { width, height, };
250
251             xcb_configure_window (conn, wnd->handle.xid,
252                                   XCB_CONFIG_WINDOW_WIDTH |
253                                   XCB_CONFIG_WINDOW_HEIGHT, values);
254             xcb_flush (conn);
255             break;
256         }
257
258         default:
259             msg_Err (wnd, "request %d not implemented", cmd);
260             return VLC_EGENERIC;
261     }
262     return VLC_SUCCESS;
263 }
264