3 * @brief List of application windows XCB module for VLC media player
5 /*****************************************************************************
6 * Copyright © 2009 Rémi Denis-Courmont
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public License
10 * as published by the Free Software Foundation; either version 2.1
11 * of the License, or (at your option) any later version.
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.
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 ****************************************************************************/
28 typedef xcb_atom_t Atom;
29 #include <X11/Xatom.h> /* XA_WINDOW */
30 #include <vlc_common.h>
31 #include <vlc_services_discovery.h>
32 #include <vlc_dialog.h>
33 #include <vlc_charset.h>
34 #include <vlc_plugin.h>
38 static int Open (vlc_object_t *);
39 static void Close (vlc_object_t *);
45 set_shortname (N_("Screen capture"))
46 set_description (N_("Screen capture"))
47 set_category (CAT_PLAYLIST)
48 set_subcategory (SUBCAT_PLAYLIST_SD)
49 set_capability ("services_discovery", 0)
50 set_callbacks (Open, Close)
55 struct services_discovery_sys_t
57 xcb_connection_t *conn;
59 xcb_atom_t net_client_list;
60 xcb_atom_t net_wm_name;
61 xcb_window_t root_window;
65 static void *Run (void *);
66 static void Update (services_discovery_t *);
67 static void DelItem (void *);
70 * Probes and initializes.
72 static int Open (vlc_object_t *obj)
74 services_discovery_t *sd = (services_discovery_t *)obj;
75 services_discovery_sys_t *p_sys = malloc (sizeof (*p_sys));
81 /* Connect to X server */
82 char *display = var_CreateGetNonEmptyString (obj, "x11-display");
84 xcb_connection_t *conn = xcb_connect (display, &snum);
86 if (xcb_connection_has_error (conn))
93 /* Find configured screen */
94 const xcb_setup_t *setup = xcb_get_setup (conn);
95 const xcb_screen_t *scr = NULL;
96 for (xcb_screen_iterator_t i = xcb_setup_roots_iterator (setup);
97 i.rem > 0; xcb_screen_next (&i))
108 msg_Err (obj, "bad X11 screen number");
112 p_sys->root_window = scr->root;
113 xcb_change_window_attributes (conn, scr->root, XCB_CW_EVENT_MASK,
114 &(uint32_t) { XCB_EVENT_MASK_PROPERTY_CHANGE });
116 /* TODO: check that _NET_CLIENT_LIST is in _NET_SUPPORTED
117 * (and _NET_SUPPORTING_WM_CHECK) */
118 xcb_intern_atom_reply_t *r;
119 xcb_intern_atom_cookie_t ncl, nwn;
121 ncl = xcb_intern_atom (conn, 1, strlen ("_NET_CLIENT_LIST"),
123 nwn = xcb_intern_atom (conn, 0, strlen ("_NET_WM_NAME"), "_NET_WM_NAME");
125 r = xcb_intern_atom_reply (conn, ncl, NULL);
126 if (r == NULL || r->atom == 0)
128 dialog_Fatal (sd, _("Application list failure"),
129 _("Your window manager does not support application list."));
130 msg_Err (sd, "application list not support (_NET_CLIENT_LIST absent)");
134 p_sys->net_client_list = r->atom;
136 r = xcb_intern_atom_reply (conn, nwn, NULL);
139 p_sys->net_wm_name = r->atom;
146 if (vlc_clone (&p_sys->thread, Run, sd, VLC_THREAD_PRIORITY_LOW))
151 xcb_disconnect (p_sys->conn);
160 static void Close (vlc_object_t *obj)
162 services_discovery_t *sd = (services_discovery_t *)obj;
163 services_discovery_sys_t *p_sys = sd->p_sys;
165 vlc_cancel (p_sys->thread);
166 vlc_join (p_sys->thread, NULL);
167 xcb_disconnect (p_sys->conn);
168 tdestroy (p_sys->nodes, DelItem);
172 static void *Run (void *data)
174 services_discovery_t *sd = data;
175 services_discovery_sys_t *p_sys = sd->p_sys;
176 xcb_connection_t *conn = p_sys->conn;
177 int fd = xcb_get_file_descriptor (conn);
181 while (!xcb_connection_has_error (conn))
183 xcb_generic_event_t *ev;
184 struct pollfd ufd = { .fd = fd, .events = POLLIN, };
188 int canc = vlc_savecancel ();
189 while ((ev = xcb_poll_for_event (conn)) != NULL)
191 if ((ev->response_type & 0x7F) == XCB_PROPERTY_NOTIFY)
193 const xcb_property_notify_event_t *pn =
194 (xcb_property_notify_event_t *)ev;
195 if (pn->atom == p_sys->net_client_list)
200 vlc_restorecancel (canc);
207 xcb_window_t xid; /* must be first for cmpapp */
209 services_discovery_t *owner;
212 static struct app *AddItem (services_discovery_t *sd, xcb_window_t xid)
214 services_discovery_sys_t *p_sys = sd->p_sys;
217 if (asprintf (&mrl, "window://0x%"PRIx8, xid) == -1)
220 xcb_get_property_reply_t *r =
221 xcb_get_property_reply (p_sys->conn,
222 xcb_get_property (p_sys->conn, 0, xid, p_sys->net_wm_name, 0,
223 0, 1023 /* max size */), NULL);
226 name = strndup (xcb_get_property_value (r),
227 xcb_get_property_value_length (r));
229 EnsureUTF8 (name); /* don't trust third party apps too much ;-) */
232 /* TODO: use WM_NAME (Latin-1) for very old apps */
236 input_item_t *item = input_item_NewWithType (VLC_OBJECT (sd), mrl,
239 ITEM_TYPE_CARD /* FIXME */);
245 struct app *app = malloc (sizeof (*app));
248 vlc_gc_decref (item);
254 services_discovery_AddItem (sd, item, _("Applications"));
258 static void DelItem (void *data)
260 struct app *app = data;
262 services_discovery_RemoveItem (app->owner, app->item);
263 vlc_gc_decref (app->item);
267 static int cmpapp (const void *a, const void *b)
269 xcb_window_t wa = *(xcb_window_t *)a;
270 xcb_window_t wb = *(xcb_window_t *)b;
279 static void Update (services_discovery_t *sd)
281 services_discovery_sys_t *p_sys = sd->p_sys;
282 xcb_connection_t *conn = p_sys->conn;
284 xcb_get_property_reply_t *r =
285 xcb_get_property_reply (conn,
286 xcb_get_property (conn, false, p_sys->root_window,
287 p_sys->net_client_list, XA_WINDOW, 0, 1024),
290 return; /* FIXME: remove all entries */
292 xcb_window_t *ent = xcb_get_property_value (r);
293 int n = xcb_get_property_value_length (r) / 4;
294 void *newnodes = NULL, *oldnodes = p_sys->nodes;
296 for (int i = 0; i < n; i++)
298 xcb_window_t id = *(ent++);
301 struct app **pa = tfind (&id, &oldnodes, cmpapp);
302 if (pa != NULL) /* existing entry */
305 tdelete (app, &oldnodes, cmpapp);
309 app = AddItem (sd, id);
314 pa = tsearch (app, &newnodes, cmpapp);
315 if (pa == NULL /* OOM */ || *pa != app /* buggy window manager */)
320 /* Remove old nodes */
321 tdestroy (oldnodes, DelItem);
322 p_sys->nodes = newnodes;