From 1a51d5fab79ace07e079071dab89c442c198fc9e Mon Sep 17 00:00:00 2001 From: fryshorts Date: Tue, 18 Nov 2014 20:15:25 +0100 Subject: [PATCH 1/7] linux-xshm: Improve source struct. This adds documentation to the source struct and moves one bool to the end to avoid a hole due to alignment. --- plugins/linux-capture/xshm-input.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/plugins/linux-capture/xshm-input.c b/plugins/linux-capture/xshm-input.c index 795c27c4d..fff1fe001 100644 --- a/plugins/linux-capture/xshm-input.c +++ b/plugins/linux-capture/xshm-input.c @@ -28,18 +28,23 @@ along with this program. If not, see . #define XSHM_DATA(voidptr) struct xshm_data *data = voidptr; struct xshm_data { + /** Xlib display object */ Display *dpy; + /** Xlib screen object */ Screen *screen; - + /** root coordinates for the capture */ int_fast32_t x_org, y_org; + /** size for the capture */ int_fast32_t width, height; - + /** shared memory management object */ xshm_t *xshm; + /** the texture used to display the capture */ gs_texture_t *texture; - - bool show_cursor; + /** cursor object for displaying the server */ xcursor_t *cursor; - + /** user setting - if cursor should be displayed */ + bool show_cursor; + /** set if xinerama is available and active on the screen */ bool use_xinerama; }; From 08c9f39220c75345431924e38a7aa728cb269a04 Mon Sep 17 00:00:00 2001 From: fryshorts Date: Tue, 18 Nov 2014 20:18:14 +0100 Subject: [PATCH 2/7] linux-xshm: Use macro for logging. This adds a macro to automatically prepend the plugin name to debug statements like it is done in other plugins. --- plugins/linux-capture/xshm-input.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/plugins/linux-capture/xshm-input.c b/plugins/linux-capture/xshm-input.c index fff1fe001..1f7f3337a 100644 --- a/plugins/linux-capture/xshm-input.c +++ b/plugins/linux-capture/xshm-input.c @@ -27,6 +27,8 @@ along with this program. If not, see . #define XSHM_DATA(voidptr) struct xshm_data *data = voidptr; +#define blog(level, msg, ...) blog(level, "xshm-input: " msg, ##__VA_ARGS__) + struct xshm_data { /** Xlib display object */ Display *dpy; @@ -96,11 +98,11 @@ static int_fast32_t xshm_update_geometry(struct xshm_data *data, } if (!data->width || !data->height) { - blog(LOG_ERROR, "xshm-input: Failed to get geometry"); + blog(LOG_ERROR, "Failed to get geometry"); return -1; } - blog(LOG_INFO, "xshm-input: Geometry %"PRIdFAST32"x%"PRIdFAST32 + blog(LOG_INFO, "Geometry %"PRIdFAST32"x%"PRIdFAST32 " @ %"PRIdFAST32",%"PRIdFAST32, data->width, data->height, data->x_org, data->y_org); @@ -131,7 +133,7 @@ static void xshm_update(void *vptr, obs_data_t *settings) xshm_detach(data->xshm); if (xshm_update_geometry(data, settings) < 0) { - blog(LOG_ERROR, "xshm-input: failed to update geometry !"); + blog(LOG_ERROR, "failed to update geometry !"); return; } @@ -141,7 +143,7 @@ static void xshm_update(void *vptr, obs_data_t *settings) data->xshm = xshm_attach(data->dpy, data->screen, data->width, data->height); if (!data->xshm) { - blog(LOG_ERROR, "xshm-input: failed to attach shm !"); + blog(LOG_ERROR, "failed to attach shm !"); return; } } @@ -218,12 +220,12 @@ static void *xshm_create(obs_data_t *settings, obs_source_t *source) data->dpy = XOpenDisplay(NULL); if (!data->dpy) { - blog(LOG_ERROR, "xshm-input: Unable to open X display !"); + blog(LOG_ERROR, "Unable to open X display !"); goto fail; } if (!XShmQueryExtension(data->dpy)) { - blog(LOG_ERROR, "xshm-input: XShm extension not found !"); + blog(LOG_ERROR, "XShm extension not found !"); goto fail; } From cc52b432e778bd08f795855203adb43e50c0cb14 Mon Sep 17 00:00:00 2001 From: fryshorts Date: Tue, 18 Nov 2014 20:23:13 +0100 Subject: [PATCH 3/7] linux-xshm: Improve geometry update function. This adds the screen id from the source properties to the source struct and changes the geometry function to use that value instead of requiring the settings object of the source. --- plugins/linux-capture/xshm-input.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/plugins/linux-capture/xshm-input.c b/plugins/linux-capture/xshm-input.c index 1f7f3337a..5a226d437 100644 --- a/plugins/linux-capture/xshm-input.c +++ b/plugins/linux-capture/xshm-input.c @@ -34,6 +34,8 @@ struct xshm_data { Display *dpy; /** Xlib screen object */ Screen *screen; + /** user setting - the id of the screen that should be captured */ + uint_fast32_t screen_id; /** root coordinates for the capture */ int_fast32_t x_org, y_org; /** size for the capture */ @@ -72,15 +74,13 @@ static void xshm_resize_texture(struct xshm_data *data) * * @return < 0 on error, 0 when size is unchanged, > 1 on size change */ -static int_fast32_t xshm_update_geometry(struct xshm_data *data, - obs_data_t *settings) +static int_fast32_t xshm_update_geometry(struct xshm_data *data) { int_fast32_t old_width = data->width; int_fast32_t old_height = data->height; - int_fast32_t screen = obs_data_get_int(settings, "screen"); if (data->use_xinerama) { - if (xinerama_screen_geo(data->dpy, screen, + if (xinerama_screen_geo(data->dpy, data->screen_id, &data->x_org, &data->y_org, &data->width, &data->height) < 0) { return -1; @@ -90,11 +90,11 @@ static int_fast32_t xshm_update_geometry(struct xshm_data *data, else { data->x_org = 0; data->y_org = 0; - if (x11_screen_geo(data->dpy, screen, + if (x11_screen_geo(data->dpy, data->screen_id, &data->width, &data->height) < 0) { return -1; } - data->screen = XScreenOfDisplay(data->dpy, screen); + data->screen = XScreenOfDisplay(data->dpy, data->screen_id); } if (!data->width || !data->height) { @@ -127,12 +127,13 @@ static void xshm_update(void *vptr, obs_data_t *settings) { XSHM_DATA(vptr); + data->screen_id = obs_data_get_int(settings, "screen"); data->show_cursor = obs_data_get_bool(settings, "show_cursor"); if (data->xshm) xshm_detach(data->xshm); - if (xshm_update_geometry(data, settings) < 0) { + if (xshm_update_geometry(data) < 0) { blog(LOG_ERROR, "failed to update geometry !"); return; } From a4d09fe74201a6c8572691b8dad6791067aa4fca Mon Sep 17 00:00:00 2001 From: fryshorts Date: Tue, 18 Nov 2014 20:32:47 +0100 Subject: [PATCH 4/7] linux-xshm: Small change for texture update function. This removes the entering/leaving of the graphics context from this function and requires the calling code to make sure the context is present. --- plugins/linux-capture/xshm-input.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/plugins/linux-capture/xshm-input.c b/plugins/linux-capture/xshm-input.c index 5a226d437..a969a05ae 100644 --- a/plugins/linux-capture/xshm-input.c +++ b/plugins/linux-capture/xshm-input.c @@ -56,17 +56,15 @@ struct xshm_data { * Resize the texture * * This will automatically create the texture if it does not exist + * + * @note requires to be called within the obs graphics context */ -static void xshm_resize_texture(struct xshm_data *data) +static inline void xshm_resize_texture(struct xshm_data *data) { - obs_enter_graphics(); - if (data->texture) gs_texture_destroy(data->texture); data->texture = gs_texture_create(data->width, data->height, GS_BGRA, 1, NULL, GS_DYNAMIC); - - obs_leave_graphics(); } /** @@ -138,7 +136,9 @@ static void xshm_update(void *vptr, obs_data_t *settings) return; } + obs_enter_graphics(); xshm_resize_texture(data); + obs_leave_graphics(); xcursor_offset(data->cursor, data->x_org, data->y_org); data->xshm = xshm_attach(data->dpy, data->screen, From 04a7db867f9a9b024aab7cbe88f9e619cc665b24 Mon Sep 17 00:00:00 2001 From: fryshorts Date: Tue, 18 Nov 2014 21:01:35 +0100 Subject: [PATCH 5/7] linux-xshm: Refactoring of setup code. This moves the code to start/stop the capture to respectively named function in order to clean up the update function. This means that the capture is stopped/started whenever the settings are changed. While this increases overhead for some settings, this will also enable future settings that require a full restart of capture process. --- plugins/linux-capture/xshm-input.c | 133 +++++++++++++++++------------ 1 file changed, 79 insertions(+), 54 deletions(-) diff --git a/plugins/linux-capture/xshm-input.c b/plugins/linux-capture/xshm-input.c index a969a05ae..00aba343d 100644 --- a/plugins/linux-capture/xshm-input.c +++ b/plugins/linux-capture/xshm-input.c @@ -118,6 +118,79 @@ static const char* xshm_getname(void) return obs_module_text("X11SharedMemoryScreenInput"); } +/** + * Stop the capture + */ +static void xshm_capture_stop(struct xshm_data *data) +{ + obs_enter_graphics(); + + if (data->texture) { + gs_texture_destroy(data->texture); + data->texture = NULL; + } + if (data->cursor) { + xcursor_destroy(data->cursor); + data->cursor = NULL; + } + + obs_leave_graphics(); + + if (data->xshm) { + xshm_detach(data->xshm); + data->xshm = NULL; + } + + if (data->dpy) { + XSync(data->dpy, true); + XCloseDisplay(data->dpy); + data->dpy = NULL; + } +} + +/** + * Start the capture + */ +static void xshm_capture_start(struct xshm_data *data) +{ + data->dpy = XOpenDisplay(NULL); + if (!data->dpy) { + blog(LOG_ERROR, "Unable to open X display !"); + goto fail; + } + + if (!XShmQueryExtension(data->dpy)) { + blog(LOG_ERROR, "XShm extension not found !"); + goto fail; + } + + data->use_xinerama = xinerama_is_active(data->dpy) ? true : false; + + if (xshm_update_geometry(data) < 0) { + blog(LOG_ERROR, "failed to update geometry !"); + goto fail; + } + + data->xshm = xshm_attach(data->dpy, data->screen, + data->width, data->height); + if (!data->xshm) { + blog(LOG_ERROR, "failed to attach shm !"); + goto fail; + } + + obs_enter_graphics(); + + data->cursor = xcursor_init(data->dpy); + xcursor_offset(data->cursor, data->x_org, data->y_org); + xshm_resize_texture(data); + + obs_leave_graphics(); + + return; +fail: + xshm_capture_stop(data); +} + /** * Update the capture with changed settings */ @@ -125,28 +198,12 @@ static void xshm_update(void *vptr, obs_data_t *settings) { XSHM_DATA(vptr); + xshm_capture_stop(data); + data->screen_id = obs_data_get_int(settings, "screen"); data->show_cursor = obs_data_get_bool(settings, "show_cursor"); - if (data->xshm) - xshm_detach(data->xshm); - - if (xshm_update_geometry(data) < 0) { - blog(LOG_ERROR, "failed to update geometry !"); - return; - } - - obs_enter_graphics(); - xshm_resize_texture(data); - obs_leave_graphics(); - xcursor_offset(data->cursor, data->x_org, data->y_org); - - data->xshm = xshm_attach(data->dpy, data->screen, - data->width, data->height); - if (!data->xshm) { - blog(LOG_ERROR, "failed to attach shm !"); - return; - } + xshm_capture_start(data); } /** @@ -193,19 +250,7 @@ static void xshm_destroy(void *vptr) if (!data) return; - obs_enter_graphics(); - - if (data->texture) - gs_texture_destroy(data->texture); - if (data->cursor) - xcursor_destroy(data->cursor); - - obs_leave_graphics(); - - if (data->xshm) - xshm_detach(data->xshm); - if (data->dpy) - XCloseDisplay(data->dpy); + xshm_capture_stop(data); bfree(data); } @@ -219,29 +264,9 @@ static void *xshm_create(obs_data_t *settings, obs_source_t *source) struct xshm_data *data = bzalloc(sizeof(struct xshm_data)); - data->dpy = XOpenDisplay(NULL); - if (!data->dpy) { - blog(LOG_ERROR, "Unable to open X display !"); - goto fail; - } - - if (!XShmQueryExtension(data->dpy)) { - blog(LOG_ERROR, "XShm extension not found !"); - goto fail; - } - - data->use_xinerama = xinerama_is_active(data->dpy) ? true : false; - - obs_enter_graphics(); - data->cursor = xcursor_init(data->dpy); - obs_leave_graphics(); - xshm_update(data, settings); return data; -fail: - xshm_destroy(data); - return NULL; } /** @@ -252,7 +277,7 @@ static void xshm_video_tick(void *vptr, float seconds) UNUSED_PARAMETER(seconds); XSHM_DATA(vptr); - if (!data->xshm) + if (!data->texture) return; obs_enter_graphics(); @@ -274,7 +299,7 @@ static void xshm_video_render(void *vptr, gs_effect_t *effect) { XSHM_DATA(vptr); - if (!data->xshm) + if (!data->texture) return; gs_eparam_t *image = gs_effect_get_param_by_name(effect, "image"); From 67ca94f7e407404e2bf200f0d4fa8930f13848c3 Mon Sep 17 00:00:00 2001 From: fryshorts Date: Tue, 18 Nov 2014 22:18:43 +0100 Subject: [PATCH 6/7] linux-xshm: Add server property. This adds a property to the source properties which lets the user specify the X server to capture from. Since this is probably something thats only useful under certain circumstances it is implemented as an advanced setting which is only shown when the corresponding option for advanced settings is checked. --- plugins/linux-capture/data/locale/en-US.ini | 2 + plugins/linux-capture/xshm-input.c | 122 +++++++++++++++++--- 2 files changed, 109 insertions(+), 15 deletions(-) diff --git a/plugins/linux-capture/data/locale/en-US.ini b/plugins/linux-capture/data/locale/en-US.ini index e7ada900e..4f20c37cc 100644 --- a/plugins/linux-capture/data/locale/en-US.ini +++ b/plugins/linux-capture/data/locale/en-US.ini @@ -1,6 +1,8 @@ X11SharedMemoryScreenInput="Screen Capture (XSHM)" Screen="Screen" CaptureCursor="Capture Cursor" +AdvancedSettings="Advanced Settings" +XServer="X Server" XCCapture="Window Capture (Xcomposite)" Window="Window" CropTop="Crop Top (pixels)" diff --git a/plugins/linux-capture/xshm-input.c b/plugins/linux-capture/xshm-input.c index 00aba343d..3de08f0b3 100644 --- a/plugins/linux-capture/xshm-input.c +++ b/plugins/linux-capture/xshm-input.c @@ -22,6 +22,7 @@ along with this program. If not, see . #include #include +#include #include "xcursor.h" #include "xhelpers.h" @@ -30,10 +31,14 @@ along with this program. If not, see . #define blog(level, msg, ...) blog(level, "xshm-input: " msg, ##__VA_ARGS__) struct xshm_data { + /** The source object */ + obs_source_t *source; /** Xlib display object */ Display *dpy; /** Xlib screen object */ Screen *screen; + /** user setting - the server name to capture from */ + char *server; /** user setting - the id of the screen that should be captured */ uint_fast32_t screen_id; /** root coordinates for the capture */ @@ -50,6 +55,8 @@ struct xshm_data { bool show_cursor; /** set if xinerama is available and active on the screen */ bool use_xinerama; + /** user setting - if advanced settings should be displayed */ + bool advanced; }; /** @@ -146,6 +153,11 @@ static void xshm_capture_stop(struct xshm_data *data) XCloseDisplay(data->dpy); data->dpy = NULL; } + + if (data->server) { + bfree(data->server); + data->server = NULL; + } } /** @@ -153,7 +165,10 @@ static void xshm_capture_stop(struct xshm_data *data) */ static void xshm_capture_start(struct xshm_data *data) { - data->dpy = XOpenDisplay(NULL); + const char *server = (data->advanced && *data->server) + ? data->server : NULL; + + data->dpy = XOpenDisplay(server); if (!data->dpy) { blog(LOG_ERROR, "Unable to open X display !"); goto fail; @@ -202,6 +217,8 @@ static void xshm_update(void *vptr, obs_data_t *settings) data->screen_id = obs_data_get_int(settings, "screen"); data->show_cursor = obs_data_get_bool(settings, "show_cursor"); + data->advanced = obs_data_get_bool(settings, "advanced"); + data->server = bstrdup(obs_data_get_string(settings, "server")); xshm_capture_start(data); } @@ -213,29 +230,105 @@ static void xshm_defaults(obs_data_t *defaults) { obs_data_set_default_int(defaults, "screen", 0); obs_data_set_default_bool(defaults, "show_cursor", true); + obs_data_set_default_bool(defaults, "advanced", false); +} + +/** + * Toggle visibility of advanced settings + */ +static bool xshm_toggle_advanced(obs_properties_t *props, + obs_property_t *p, obs_data_t *settings) +{ + UNUSED_PARAMETER(p); + const bool visible = obs_data_get_bool(settings, "advanced"); + obs_property_t *server = obs_properties_get(props, "server"); + + obs_property_set_visible(server, visible); + + /* trigger server changed callback so the screen list is refreshed */ + obs_property_modified(server, settings); + + return true; +} + +/** + * The x server was changed + */ +static bool xshm_server_changed(obs_properties_t *props, + obs_property_t *p, obs_data_t *settings) +{ + UNUSED_PARAMETER(p); + + bool advanced = obs_data_get_bool(settings, "advanced"); + const char *server = obs_data_get_string(settings, "server"); + obs_property_t *screens = obs_properties_get(props, "screen"); + + /* we want a real NULL here in case there is no string here */ + server = (advanced && *server) ? server : NULL; + + obs_property_list_clear(screens); + + Display *dpy = XOpenDisplay(server); + if (!dpy) { + obs_property_set_enabled(screens, false); + return true; + } + + struct dstr screen_info; + dstr_init(&screen_info); + bool xinerama = xinerama_is_active(dpy); + int_fast32_t count = (xinerama) ? + xinerama_screen_count(dpy) : XScreenCount(dpy); + + for (int_fast32_t i = 0; i < count; ++i) { + int_fast32_t x, y, w, h; + x = y = w = h = 0; + + if (xinerama) + xinerama_screen_geo(dpy, i, &x, &y, &w, &h); + else + x11_screen_geo(dpy, i, &w, &h); + + dstr_printf(&screen_info, "Screen %"PRIuFAST32" (%"PRIuFAST32 + "x%"PRIuFAST32" @ %"PRIuFAST32 + ",%"PRIuFAST32")", i, w, h, x, y); + + obs_property_list_add_int(screens, screen_info.array, i); + } + + dstr_free(&screen_info); + + XCloseDisplay(dpy); + obs_property_set_enabled(screens, true); + + return true; } /** * Get the properties for the capture */ -static obs_properties_t *xshm_properties(void *unused) +static obs_properties_t *xshm_properties(void *vptr) { - UNUSED_PARAMETER(unused); + XSHM_DATA(vptr); obs_properties_t *props = obs_properties_create(); - int_fast32_t screen_max; - Display *dpy = XOpenDisplay(NULL); - screen_max = xinerama_is_active(dpy) - ? xinerama_screen_count(dpy) - : XScreenCount(dpy); - screen_max = (screen_max) ? screen_max - 1 : 0; - XCloseDisplay(dpy); - - obs_properties_add_int(props, "screen", - obs_module_text("Screen"), 0, screen_max, 1); + obs_properties_add_list(props, "screen", obs_module_text("Screen"), + OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); obs_properties_add_bool(props, "show_cursor", obs_module_text("CaptureCursor")); + obs_property_t *advanced = obs_properties_add_bool(props, "advanced", + obs_module_text("AdvancedSettings")); + obs_property_t *server = obs_properties_add_text(props, "server", + obs_module_text("XServer"), OBS_TEXT_DEFAULT); + + obs_property_set_modified_callback(advanced, xshm_toggle_advanced); + obs_property_set_modified_callback(server, xshm_server_changed); + + /* trigger server callback to get screen count ... */ + obs_data_t *settings = obs_source_get_settings(data->source); + obs_property_modified(server, settings); + obs_data_release(settings); return props; } @@ -260,9 +353,8 @@ static void xshm_destroy(void *vptr) */ static void *xshm_create(obs_data_t *settings, obs_source_t *source) { - UNUSED_PARAMETER(source); - struct xshm_data *data = bzalloc(sizeof(struct xshm_data)); + data->source = source; xshm_update(data, settings); From cc56634e910ccfb3090ca48a5155c94600f50bdc Mon Sep 17 00:00:00 2001 From: fryshorts Date: Tue, 18 Nov 2014 22:41:11 +0100 Subject: [PATCH 7/7] linux-xshm: Handle missing screens. This adds a check to the screen enumeration in order to handle missing screens by adding them to the list but disabling them. --- plugins/linux-capture/xshm-input.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/plugins/linux-capture/xshm-input.c b/plugins/linux-capture/xshm-input.c index 3de08f0b3..6cd305278 100644 --- a/plugins/linux-capture/xshm-input.c +++ b/plugins/linux-capture/xshm-input.c @@ -260,6 +260,7 @@ static bool xshm_server_changed(obs_properties_t *props, UNUSED_PARAMETER(p); bool advanced = obs_data_get_bool(settings, "advanced"); + int_fast32_t old_screen = obs_data_get_int(settings, "screen"); const char *server = obs_data_get_string(settings, "server"); obs_property_t *screens = obs_properties_get(props, "screen"); @@ -296,6 +297,16 @@ static bool xshm_server_changed(obs_properties_t *props, obs_property_list_add_int(screens, screen_info.array, i); } + /* handle missing screen */ + if (old_screen + 1 > count) { + dstr_printf(&screen_info, "Screen %"PRIuFAST32" (not found)", + old_screen); + size_t index = obs_property_list_add_int(screens, + screen_info.array, old_screen); + obs_property_list_item_disable(screens, index, true); + + } + dstr_free(&screen_info); XCloseDisplay(dpy);