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
*);
40 static int vlc_sd_probe_Open (vlc_object_t
*);
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
)
55 VLC_SD_PROBE_SUBMODULE
58 struct services_discovery_sys_t
60 xcb_connection_t
*conn
;
62 xcb_atom_t net_client_list
;
63 xcb_atom_t net_wm_name
;
64 xcb_window_t root_window
;
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
);
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
);
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
));
99 /* Connect to X server */
100 char *display
= var_InheritString (obj
, "x11-display");
102 xcb_connection_t
*conn
= xcb_connect (display
, &snum
);
104 if (xcb_connection_has_error (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
))
126 msg_Err (obj
, "bad X11 screen number");
130 /* Add a permanent item for the entire desktop */
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"),
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;
155 r
= xcb_intern_atom_reply (conn
, nwn
, NULL
);
158 p_sys
->net_wm_name
= r
->atom
;
165 if (vlc_clone (&p_sys
->thread
, Run
, sd
, VLC_THREAD_PRIORITY_LOW
))
170 xcb_disconnect (p_sys
->conn
);
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
);
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
);
200 while (!xcb_connection_has_error (conn
))
202 xcb_generic_event_t
*ev
;
203 struct pollfd ufd
= { .fd
= fd
, .events
= POLLIN
, };
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
)
219 vlc_restorecancel (canc
);
226 xcb_window_t xid
; /* must be first for cmpapp */
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
;
236 if (asprintf (&mrl
, "window://0x%"PRIx8
, xid
) == -1)
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
);
245 name
= strndup (xcb_get_property_value (r
),
246 xcb_get_property_value_length (r
));
248 EnsureUTF8 (name
); /* don't trust third party apps too much ;-) */
251 /* TODO: use WM_NAME (Latin-1) for very old apps */
255 input_item_t
*item
= input_item_NewWithType (VLC_OBJECT (sd
), mrl
,
258 ITEM_TYPE_CARD
/* FIXME */);
264 struct app
*app
= malloc (sizeof (*app
));
267 vlc_gc_decref (item
);
273 services_discovery_AddItem (sd
, item
, _("Applications"));
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
);
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
;
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),
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
++);
320 struct app
**pa
= tfind (&id
, &oldnodes
, cmpapp
);
321 if (pa
!= NULL
) /* existing entry */
324 tdelete (app
, &oldnodes
, cmpapp
);
328 app
= AddItem (sd
, id
);
333 pa
= tsearch (app
, &newnodes
, cmpapp
);
334 if (pa
== NULL
/* OOM */ || *pa
!= app
/* buggy window manager */)
339 /* Remove old nodes */
340 tdestroy (oldnodes
, DelItem
);
341 p_sys
->nodes
= newnodes
;
344 static void AddDesktopItem(services_discovery_t
*sd
)
348 item
= input_item_NewWithType (VLC_OBJECT (sd
), "screen://", _("Desktop"),
349 0, NULL
, 0, -1, ITEM_TYPE_CARD
);
353 services_discovery_AddItem (sd
, item
, NULL
);