Commit 4ce1f5d9 authored by Rémi Denis-Courmont's avatar Rémi Denis-Courmont

XCB screen capture discovery plugin

TODO:
 - use application names for input items
 - add an item for the whole desktop,
 - add items for XRandR outputs.
parent 3590170d
......@@ -3658,6 +3658,7 @@ AS_IF([test "${enable_xcb}" != "no"], [
PKG_CHECK_MODULES(XCB_SHM, [xcb-shm])
VLC_ADD_PLUGIN([xcb_x11])
VLC_ADD_PLUGIN([xcb_screen])
VLC_ADD_PLUGIN([xcb_apps])
VLC_SET_CFLAGS_WERROR([xcb_screen], [-Wno-error=uninitialized]) # some gcc report a warning which doesn't reveal an error
AS_IF([test "${enable_xvideo}" != "no"], [
......
......@@ -7,3 +7,15 @@ SOURCES_podcast = podcast.c
SOURCES_mtp = mtp.c
SOURCES_mediadirs = mediadirs.c
SOURCES_udev = udev.c
libxcb_apps_plugin_la_SOURCES = xcb_apps.c
libxcb_apps_plugin_la_CFLAGS = $(AM_CFLAGS) \
$(XCB_CFLAGS)
libxcb_apps_plugin_la_LIBADD = $(AM_LIBADD) \
$(XCB_LIBS)
libxcb_apps_plugin_la_DEPENDENCIES =
EXTRA_LTLIBRARIES += \
libxcb_apps_plugin.la
libvlc_LTLIBRARIES += \
$(LTLIBxcb_apps)
/**
* @file xcb_apps.c
* @brief List of application windows XCB module for VLC media player
*/
/*****************************************************************************
* Copyright © 2009 Rémi Denis-Courmont
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.1
* of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
****************************************************************************/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <stdarg.h>
#include <xcb/xcb.h>
typedef xcb_atom_t Atom;
#include <X11/Xatom.h> /* XA_WINDOW */
#include <vlc_common.h>
#include <vlc_services_discovery.h>
#include <vlc_dialog.h>
#include <vlc_plugin.h>
#include <poll.h>
#include <search.h>
static int Open (vlc_object_t *);
static void Close (vlc_object_t *);
/*
* Module descriptor
*/
vlc_module_begin ()
set_shortname (N_("Screen capture"))
set_description (N_("Screen capture"))
set_category (CAT_PLAYLIST)
set_subcategory (SUBCAT_PLAYLIST_SD)
set_capability ("services_discovery", 0)
set_callbacks (Open, Close)
add_shortcut ("apps")
vlc_module_end ()
struct services_discovery_sys_t
{
xcb_connection_t *conn;
vlc_thread_t thread;
xcb_atom_t net_client_list;
xcb_window_t root_window;
void *nodes;
};
static void *Run (void *);
static void Update (services_discovery_t *);
static void DelItem (void *);
/**
* Probes and initializes.
*/
static int Open (vlc_object_t *obj)
{
services_discovery_t *sd = (services_discovery_t *)obj;
services_discovery_sys_t *p_sys = malloc (sizeof (*p_sys));
if (p_sys == NULL)
return VLC_ENOMEM;
sd->p_sys = p_sys;
/* Connect to X server */
char *display = var_CreateGetNonEmptyString (obj, "x11-display");
int snum;
xcb_connection_t *conn = xcb_connect (display, &snum);
free (display);
if (xcb_connection_has_error (conn))
{
free (p_sys);
return VLC_EGENERIC;
}
p_sys->conn = conn;
/* Find configured screen */
const xcb_setup_t *setup = xcb_get_setup (conn);
const xcb_screen_t *scr = NULL;
for (xcb_screen_iterator_t i = xcb_setup_roots_iterator (setup);
i.rem > 0; xcb_screen_next (&i))
{
if (snum == 0)
{
scr = i.data;
break;
}
snum--;
}
if (scr == NULL)
{
msg_Err (obj, "bad X11 screen number");
goto error;
}
p_sys->root_window = scr->root;
xcb_change_window_attributes (conn, scr->root, XCB_CW_EVENT_MASK,
&(uint32_t) { XCB_EVENT_MASK_PROPERTY_CHANGE });
/* TODO: check that _NET_CLIENT_LIST is in _NET_SUPPORTED
* (and _NET_SUPPORTING_WM_CHECK) */
xcb_intern_atom_cookie_t ck;
ck = xcb_intern_atom (conn, 1, strlen ("_NET_CLIENT_LIST"),
"_NET_CLIENT_LIST");
xcb_intern_atom_reply_t *r = xcb_intern_atom_reply (conn, ck, NULL);
if (r == NULL || r->atom == 0)
{
dialog_Fatal (sd, _("Application list failure"),
_("Your window manager does not support application list."));
msg_Err (sd, "application list not support (_NET_CLIENT_LIST absent)");
free (r);
goto error;
}
p_sys->net_client_list = r->atom;
free (r);
p_sys->nodes = NULL;
Update (sd);
if (vlc_clone (&p_sys->thread, Run, sd, VLC_THREAD_PRIORITY_LOW))
goto error;
return VLC_SUCCESS;
error:
xcb_disconnect (p_sys->conn);
free (p_sys);
return VLC_EGENERIC;
}
/**
* Releases resources
*/
static void Close (vlc_object_t *obj)
{
services_discovery_t *sd = (services_discovery_t *)obj;
services_discovery_sys_t *p_sys = sd->p_sys;
vlc_cancel (p_sys->thread);
vlc_join (p_sys->thread, NULL);
xcb_disconnect (p_sys->conn);
tdestroy (p_sys->nodes, DelItem);
free (p_sys);
}
static void *Run (void *data)
{
services_discovery_t *sd = data;
services_discovery_sys_t *p_sys = sd->p_sys;
xcb_connection_t *conn = p_sys->conn;
int fd = xcb_get_file_descriptor (conn);
if (fd == -1)
return NULL;
while (!xcb_connection_has_error (conn))
{
xcb_generic_event_t *ev;
struct pollfd ufd = { .fd = fd, .events = POLLIN, };
poll (&ufd, 1, -1);
int canc = vlc_savecancel ();
while ((ev = xcb_poll_for_event (conn)) != NULL)
{
if ((ev->response_type & 0x7F) == XCB_PROPERTY_NOTIFY)
{
const xcb_property_notify_event_t *pn =
(xcb_property_notify_event_t *)ev;
if (pn->atom == p_sys->net_client_list)
Update (sd);
}
free (ev);
}
vlc_restorecancel (canc);
}
return NULL;
}
struct app
{
xcb_window_t xid; /* must be first for cmpapp */
input_item_t *item;
services_discovery_t *owner;
};
static struct app *AddItem (services_discovery_t *sd, xcb_window_t xid)
{
char *mrl;
if (asprintf (&mrl, "window://0x%"PRIx8, xid) == -1)
return NULL;
input_item_t *item = input_item_NewWithType (VLC_OBJECT (sd), mrl,
mrl,
0, NULL, 0, -1,
ITEM_TYPE_CARD /* FIXME */);
free (mrl);
if (item == NULL)
return NULL;
struct app *app = malloc (sizeof (*app));
if (app == NULL)
{
vlc_gc_decref (item);
return NULL;
}
app->xid = xid;
app->item = item;
app->owner = sd;
services_discovery_AddItem (sd, item, _("Applications"));
return app;
}
static void DelItem (void *data)
{
struct app *app = data;
services_discovery_RemoveItem (app->owner, app->item);
vlc_gc_decref (app->item);
free (app);
}
static int cmpapp (const void *a, const void *b)
{
xcb_window_t wa = *(xcb_window_t *)a;
xcb_window_t wb = *(xcb_window_t *)b;
if (wa > wb)
return 1;
if (wa < wb)
return -1;
return 0;
}
static void Update (services_discovery_t *sd)
{
services_discovery_sys_t *p_sys = sd->p_sys;
xcb_connection_t *conn = p_sys->conn;
xcb_get_property_reply_t *r =
xcb_get_property_reply (conn,
xcb_get_property (conn, false, p_sys->root_window,
p_sys->net_client_list, XA_WINDOW, 0, 1024),
NULL);
if (r == NULL)
return; /* FIXME: remove all entries */
xcb_window_t *ent = xcb_get_property_value (r);
int n = xcb_get_property_value_length (r) / 4;
void *newnodes = NULL, *oldnodes = p_sys->nodes;
for (int i = 0; i < n; i++)
{
xcb_window_t id = *(ent++);
struct app *app;
struct app **pa = tfind (&id, &oldnodes, cmpapp);
if (pa != NULL) /* existing entry */
{
app = *pa;
tdelete (app, &oldnodes, cmpapp);
}
else /* new entry */
{
app = AddItem (sd, id);
if (app == NULL)
continue;
}
pa = tsearch (app, &newnodes, cmpapp);
if (pa == NULL /* OOM */ || *pa != app /* buggy window manager */)
DelItem (app);
}
free (r);
/* Remove old nodes */
tdestroy (oldnodes, DelItem);
p_sys->nodes = newnodes;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment