Remove legacy parameter from add_string()
[vlc/asuraparaju-public.git] / modules / services_discovery / xcb_apps.c
blob88927c3fe9289958f287b84f757352a3aa74efde
1 /**
2 * @file xcb_apps.c
3 * @brief List of application windows XCB module for VLC media player
4 */
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 ****************************************************************************/
23 #ifdef HAVE_CONFIG_H
24 # include <config.h>
25 #endif
26 #include <stdarg.h>
27 #include <xcb/xcb.h>
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>
35 #include <poll.h>
36 #include <search.h>
38 static int Open (vlc_object_t *);
39 static void Close (vlc_object_t *);
40 static int vlc_sd_probe_Open (vlc_object_t *);
43 * Module descriptor
45 vlc_module_begin ()
46 set_shortname (N_("Screen capture"))
47 set_description (N_("Screen capture"))
48 set_category (CAT_PLAYLIST)
49 set_subcategory (SUBCAT_PLAYLIST_SD)
50 set_capability ("services_discovery", 0)
51 set_callbacks (Open, Close)
53 add_shortcut ("apps")
55 VLC_SD_PROBE_SUBMODULE
56 vlc_module_end ()
58 struct services_discovery_sys_t
60 xcb_connection_t *conn;
61 vlc_thread_t thread;
62 xcb_atom_t net_client_list;
63 xcb_atom_t net_wm_name;
64 xcb_window_t root_window;
65 void *nodes;
68 static void *Run (void *);
69 static void Update (services_discovery_t *);
70 static void DelItem (void *);
71 static void AddDesktopItem(services_discovery_t *);
73 static int vlc_sd_probe_Open (vlc_object_t *obj)
75 vlc_probe_t *probe = (vlc_probe_t *)obj;
77 char *display = var_InheritString (obj, "x11-display");
78 xcb_connection_t *conn = xcb_connect (display, NULL);
79 free (display);
80 if (xcb_connection_has_error (conn))
81 return VLC_PROBE_CONTINUE;
82 xcb_disconnect (conn);
83 return vlc_sd_probe_Add (probe, "xcb_apps{longname=\"Screen capture\"}",
84 N_("Screen capture"), SD_CAT_MYCOMPUTER);
87 /**
88 * Probes and initializes.
90 static int Open (vlc_object_t *obj)
92 services_discovery_t *sd = (services_discovery_t *)obj;
93 services_discovery_sys_t *p_sys = malloc (sizeof (*p_sys));
95 if (p_sys == NULL)
96 return VLC_ENOMEM;
97 sd->p_sys = p_sys;
99 /* Connect to X server */
100 char *display = var_InheritString (obj, "x11-display");
101 int snum;
102 xcb_connection_t *conn = xcb_connect (display, &snum);
103 free (display);
104 if (xcb_connection_has_error (conn))
106 free (p_sys);
107 return VLC_EGENERIC;
109 p_sys->conn = conn;
111 /* Find configured screen */
112 const xcb_setup_t *setup = xcb_get_setup (conn);
113 const xcb_screen_t *scr = NULL;
114 for (xcb_screen_iterator_t i = xcb_setup_roots_iterator (setup);
115 i.rem > 0; xcb_screen_next (&i))
117 if (snum == 0)
119 scr = i.data;
120 break;
122 snum--;
124 if (scr == NULL)
126 msg_Err (obj, "bad X11 screen number");
127 goto error;
130 /* Add a permanent item for the entire desktop */
131 AddDesktopItem (sd);
133 p_sys->root_window = scr->root;
134 xcb_change_window_attributes (conn, scr->root, XCB_CW_EVENT_MASK,
135 &(uint32_t) { XCB_EVENT_MASK_PROPERTY_CHANGE });
137 /* TODO: check that _NET_CLIENT_LIST is in _NET_SUPPORTED
138 * (and _NET_SUPPORTING_WM_CHECK) */
139 xcb_intern_atom_reply_t *r;
140 xcb_intern_atom_cookie_t ncl, nwn;
142 ncl = xcb_intern_atom (conn, 1, strlen ("_NET_CLIENT_LIST"),
143 "_NET_CLIENT_LIST");
144 nwn = xcb_intern_atom (conn, 0, strlen ("_NET_WM_NAME"), "_NET_WM_NAME");
146 r = xcb_intern_atom_reply (conn, ncl, NULL);
147 if (r == NULL || r->atom == 0)
149 dialog_Fatal (sd, _("Screen capture"),
150 _("Your window manager does not provide a list of applications."));
151 msg_Err (sd, "client list not supported (_NET_CLIENT_LIST absent)");
153 p_sys->net_client_list = r ? r->atom : 0;
154 free (r);
155 r = xcb_intern_atom_reply (conn, nwn, NULL);
156 if (r != NULL)
158 p_sys->net_wm_name = r->atom;
159 free (r);
162 p_sys->nodes = NULL;
163 Update (sd);
165 if (vlc_clone (&p_sys->thread, Run, sd, VLC_THREAD_PRIORITY_LOW))
166 goto error;
167 return VLC_SUCCESS;
169 error:
170 xcb_disconnect (p_sys->conn);
171 free (p_sys);
172 return VLC_EGENERIC;
177 * Releases resources
179 static void Close (vlc_object_t *obj)
181 services_discovery_t *sd = (services_discovery_t *)obj;
182 services_discovery_sys_t *p_sys = sd->p_sys;
184 vlc_cancel (p_sys->thread);
185 vlc_join (p_sys->thread, NULL);
186 xcb_disconnect (p_sys->conn);
187 tdestroy (p_sys->nodes, DelItem);
188 free (p_sys);
191 static void *Run (void *data)
193 services_discovery_t *sd = data;
194 services_discovery_sys_t *p_sys = sd->p_sys;
195 xcb_connection_t *conn = p_sys->conn;
196 int fd = xcb_get_file_descriptor (conn);
197 if (fd == -1)
198 return NULL;
200 while (!xcb_connection_has_error (conn))
202 xcb_generic_event_t *ev;
203 struct pollfd ufd = { .fd = fd, .events = POLLIN, };
205 poll (&ufd, 1, -1);
207 int canc = vlc_savecancel ();
208 while ((ev = xcb_poll_for_event (conn)) != NULL)
210 if ((ev->response_type & 0x7F) == XCB_PROPERTY_NOTIFY)
212 const xcb_property_notify_event_t *pn =
213 (xcb_property_notify_event_t *)ev;
214 if (pn->atom == p_sys->net_client_list)
215 Update (sd);
217 free (ev);
219 vlc_restorecancel (canc);
221 return NULL;
224 struct app
226 xcb_window_t xid; /* must be first for cmpapp */
227 input_item_t *item;
228 services_discovery_t *owner;
231 static struct app *AddItem (services_discovery_t *sd, xcb_window_t xid)
233 services_discovery_sys_t *p_sys = sd->p_sys;
234 char *mrl, *name;
236 if (asprintf (&mrl, "window://0x%"PRIx8, xid) == -1)
237 return NULL;
239 xcb_get_property_reply_t *r =
240 xcb_get_property_reply (p_sys->conn,
241 xcb_get_property (p_sys->conn, 0, xid, p_sys->net_wm_name, 0,
242 0, 1023 /* max size */), NULL);
243 if (r != NULL)
245 name = strndup (xcb_get_property_value (r),
246 xcb_get_property_value_length (r));
247 if (name != NULL)
248 EnsureUTF8 (name); /* don't trust third party apps too much ;-) */
249 free (r);
251 /* TODO: use WM_NAME (Latin-1) for very old apps */
252 else
253 name = NULL;
255 input_item_t *item = input_item_NewWithType (VLC_OBJECT (sd), mrl,
256 name ? name : mrl,
257 0, NULL, 0, -1,
258 ITEM_TYPE_CARD /* FIXME */);
259 free (mrl);
260 free (name);
261 if (item == NULL)
262 return NULL;
264 struct app *app = malloc (sizeof (*app));
265 if (app == NULL)
267 vlc_gc_decref (item);
268 return NULL;
270 app->xid = xid;
271 app->item = item;
272 app->owner = sd;
273 services_discovery_AddItem (sd, item, _("Applications"));
274 return app;
277 static void DelItem (void *data)
279 struct app *app = data;
281 services_discovery_RemoveItem (app->owner, app->item);
282 vlc_gc_decref (app->item);
283 free (app);
286 static int cmpapp (const void *a, const void *b)
288 xcb_window_t wa = *(xcb_window_t *)a;
289 xcb_window_t wb = *(xcb_window_t *)b;
291 if (wa > wb)
292 return 1;
293 if (wa < wb)
294 return -1;
295 return 0;
298 static void Update (services_discovery_t *sd)
300 services_discovery_sys_t *p_sys = sd->p_sys;
301 xcb_connection_t *conn = p_sys->conn;
303 xcb_get_property_reply_t *r =
304 xcb_get_property_reply (conn,
305 xcb_get_property (conn, false, p_sys->root_window,
306 p_sys->net_client_list, XA_WINDOW, 0, 1024),
307 NULL);
308 if (r == NULL)
309 return; /* FIXME: remove all entries */
311 xcb_window_t *ent = xcb_get_property_value (r);
312 int n = xcb_get_property_value_length (r) / 4;
313 void *newnodes = NULL, *oldnodes = p_sys->nodes;
315 for (int i = 0; i < n; i++)
317 xcb_window_t id = *(ent++);
318 struct app *app;
320 struct app **pa = tfind (&id, &oldnodes, cmpapp);
321 if (pa != NULL) /* existing entry */
323 app = *pa;
324 tdelete (app, &oldnodes, cmpapp);
326 else /* new entry */
328 app = AddItem (sd, id);
329 if (app == NULL)
330 continue;
333 pa = tsearch (app, &newnodes, cmpapp);
334 if (pa == NULL /* OOM */ || *pa != app /* buggy window manager */)
335 DelItem (app);
337 free (r);
339 /* Remove old nodes */
340 tdestroy (oldnodes, DelItem);
341 p_sys->nodes = newnodes;
344 static void AddDesktopItem(services_discovery_t *sd)
346 input_item_t *item;
348 item = input_item_NewWithType (VLC_OBJECT (sd), "screen://", _("Desktop"),
349 0, NULL, 0, -1, ITEM_TYPE_CARD);
350 if (item == NULL)
351 return;
353 services_discovery_AddItem (sd, item, NULL);