From 8f7608e7eec14edeb31e2538d217e35802d1ad10 Mon Sep 17 00:00:00 2001 From: dec05eba Date: Mon, 9 Feb 2026 15:03:10 +0100 Subject: [PATCH] Add --list-monitors option to list only monitors, refactor --- gpu-screen-recorder.1 | 15 +++-- include/args_parser.h | 1 + src/args_parser.c | 8 ++- src/main.cpp | 153 ++++++++++++++++++++++-------------------- 4 files changed, 96 insertions(+), 81 deletions(-) diff --git a/gpu-screen-recorder.1 b/gpu-screen-recorder.1 index 6ea1719..e8637be 100644 --- a/gpu-screen-recorder.1 +++ b/gpu-screen-recorder.1 @@ -14,13 +14,17 @@ gpu-screen-recorder \- The fastest screen recording tool for Linux | .B \-\-version | +.B \-\-info +| .B \-\-list\-capture\-options | +.B \-\-list\-monitors +| +.B \-\-list\-v4l2\-devices +| .B \-\-list\-audio\-devices | .B \-\-list\-application\-audio -| -.B \-\-info .SH DESCRIPTION .B gpu-screen-recorder is the fastest screen recording tool for Linux. It uses the GPU @@ -92,10 +96,6 @@ Run .B \-\-list\-capture\-options to list available capture sources. .PP -Run -.B \-\-list\-v4l2\-devices -to list available camera devices (V4L2). -.PP Additional options can be passed to each capture source by splitting capture source with .B ; for example @@ -403,6 +403,9 @@ Show system info (codecs, capture options). .B \-\-list\-capture\-options List available capture sources (window, monitors, portal, v4l2 device path). .TP +.B \-\-list\-monitors +List available monitors. +.TP .B \-\-list\-v4l2\-devices List available cameras devices (V4L2). .TP diff --git a/include/args_parser.h b/include/args_parser.h index 7f57530..98fb30c 100644 --- a/include/args_parser.h +++ b/include/args_parser.h @@ -63,6 +63,7 @@ typedef struct { void (*list_application_audio)(void *userdata); void (*list_v4l2_devices)(void *userdata); void (*list_capture_options)(const char *card_path, void *userdata); + void (*list_monitors)(void *userdata); } args_handlers; typedef struct { diff --git a/src/args_parser.c b/src/args_parser.c index fc3c904..aaa8edd 100644 --- a/src/args_parser.c +++ b/src/args_parser.c @@ -196,7 +196,8 @@ static void usage_header(void) { "[-bm auto|qp|vbr|cbr] [-cr limited|full] [-tune performance|quality] [-df yes|no] [-sc ] [-p ] " "[-cursor yes|no] [-keyint ] [-restore-portal-session yes|no] [-portal-session-token-filepath filepath] [-encoder gpu|cpu] " "[-fallback-cpu-encoding yes|no] [-o ] [-ro ] [-ffmpeg-opts ] [--list-capture-options [card_path]] " - "[--list-audio-devices] [--list-application-audio] [--list-v4l2-devices] [-write-first-frame-ts yes|no] [-low-power yes|no] [-v yes|no] [-gl-debug yes|no] [--version] [-h|--help]\n", program_name); + "[--list-monitors] [--list-audio-devices] [--list-application-audio] [--list-v4l2-devices] [-write-first-frame-ts yes|no] [-low-power yes|no] " + "[-v yes|no] [-gl-debug yes|no] [--version] [-h|--help]\n", program_name); fflush(stdout); } @@ -500,6 +501,11 @@ bool args_parser_parse(args_parser *self, int argc, char **argv, const args_hand } } + if(strcmp(argv[1], "--list-monitors") == 0) { + arg_handlers->list_monitors(userdata); + return true; + } + if(argc == 2 && strcmp(argv[1], "--version") == 0) { arg_handlers->version(userdata); return true; diff --git a/src/main.cpp b/src/main.cpp index 507861f..2c3acbe 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1888,23 +1888,31 @@ static void camera_query_callback(const char *path, const gsr_capture_v4l2_suppo printf("%s|%ux%u@%uhz|%s\n", path, setup->resolution.width, setup->resolution.height, gsr_capture_v4l2_framerate_to_number(setup->framerate), gsr_capture_v4l2_pixfmt_to_string(setup->pixfmt)); } -static void list_supported_capture_options(const gsr_window *window, const char *card_path, bool list_monitors) { +// Returns the number of monitors found +static int list_monitors(const gsr_window *window, const char *card_path) { + capture_options_callback options; + options.window = window; + options.num_monitors = 0; + + const bool is_x11 = gsr_window_get_display_server(window) == GSR_DISPLAY_SERVER_X11; + const gsr_connection_type connection_type = is_x11 ? GSR_CONNECTION_X11 : GSR_CONNECTION_DRM; + for_each_active_monitor_output(window, card_path, connection_type, output_monitor_info, &options); + + return options.num_monitors; +} + +static void list_supported_capture_options(const gsr_window *window, const char *card_path, bool do_list_monitors) { const bool wayland = gsr_window_get_display_server(window) == GSR_DISPLAY_SERVER_WAYLAND; if(!wayland) { puts("window"); puts("focused"); } - capture_options_callback options; - options.window = window; - options.num_monitors = 0; - if(list_monitors) { - const bool is_x11 = gsr_window_get_display_server(window) == GSR_DISPLAY_SERVER_X11; - const gsr_connection_type connection_type = is_x11 ? GSR_CONNECTION_X11 : GSR_CONNECTION_DRM; - for_each_active_monitor_output(window, card_path, connection_type, output_monitor_info, &options); - } + int num_monitors = 0; + if(do_list_monitors) + num_monitors = list_monitors(window, card_path); - if(options.num_monitors > 0) + if(num_monitors > 0) puts("region"); gsr_capture_v4l2_list_devices(camera_query_callback, NULL); @@ -1933,11 +1941,20 @@ static void version_command(void *userdata) { _exit(0); } -static void info_command(void *userdata) { - (void)userdata; +struct WindowingSetup { + Display *dpy; + gsr_window *window; + gsr_egl egl; + bool list_monitors; +}; + +static WindowingSetup setup_windowing(bool setup_egl) { + WindowingSetup setup; + memset(&setup, 0, sizeof(setup)); + bool wayland = false; - Display *dpy = XOpenDisplay(nullptr); - if (!dpy) { + setup.dpy = XOpenDisplay(nullptr); + if (!setup.dpy) { wayland = true; fprintf(stderr, "gsr warning: failed to connect to the X server. Assuming wayland is running without Xwayland\n"); } @@ -1946,7 +1963,7 @@ static void info_command(void *userdata) { XSetIOErrorHandler(x11_io_error_handler); if(!wayland) - wayland = is_xwayland(dpy); + wayland = is_xwayland(setup.dpy); if(!wayland && is_using_prime_run()) { // Disable prime-run and similar options as it doesn't work, the monitor to capture has to be run on the same device. @@ -1956,46 +1973,56 @@ static void info_command(void *userdata) { disable_prime_run(); } - gsr_window *window = gsr_window_create(dpy, wayland); - if(!window) { + setup.window = gsr_window_create(setup.dpy, wayland); + if(!setup.window) { fprintf(stderr, "gsr error: failed to create window\n"); _exit(1); } - gsr_egl egl; - if(!gsr_egl_load(&egl, window, false, false)) { - fprintf(stderr, "gsr error: failed to load opengl\n"); - _exit(22); - } + setup.list_monitors = true; - bool list_monitors = true; - egl.card_path[0] = '\0'; - if(monitor_capture_use_drm(window, egl.gpu_info.vendor)) { - // TODO: Allow specifying another card, and in other places - if(!gsr_get_valid_card_path(&egl, egl.card_path, true)) { - fprintf(stderr, "gsr error: no /dev/dri/cardX device found. Make sure that you have at least one monitor connected\n"); - list_monitors = false; + if(setup_egl) { + if(!gsr_egl_load(&setup.egl, setup.window, false, false)) { + fprintf(stderr, "gsr error: failed to load opengl\n"); + _exit(22); + } + + setup.egl.card_path[0] = '\0'; + if(monitor_capture_use_drm(setup.window, setup.egl.gpu_info.vendor)) { + // TODO: Allow specifying another card, and in other places + if(!gsr_get_valid_card_path(&setup.egl, setup.egl.card_path, true)) { + fprintf(stderr, "gsr error: no /dev/dri/cardX device found. Make sure that you have at least one monitor connected\n"); + setup.list_monitors = false; + } } } + return setup; +} + +static void info_command(void *userdata) { + (void)userdata; + WindowingSetup windowing_setup = setup_windowing(true); + const bool wayland = gsr_window_get_display_server(windowing_setup.window) == GSR_DISPLAY_SERVER_WAYLAND; + av_log_set_level(AV_LOG_FATAL); puts("section=system_info"); list_system_info(wayland); - if(egl.gpu_info.is_steam_deck) + if(windowing_setup.egl.gpu_info.is_steam_deck) puts("is_steam_deck|yes"); else puts("is_steam_deck|no"); printf("gsr_version|%s\n", GSR_VERSION); puts("section=gpu_info"); - list_gpu_info(&egl); + list_gpu_info(&windowing_setup.egl); puts("section=video_codecs"); - list_supported_video_codecs(&egl, wayland); + list_supported_video_codecs(&windowing_setup.egl, wayland); puts("section=image_formats"); puts("jpeg"); puts("png"); puts("section=capture_options"); - list_supported_capture_options(window, egl.card_path, list_monitors); + list_supported_capture_options(windowing_setup.window, windowing_setup.egl.card_path, windowing_setup.list_monitors); fflush(stdout); @@ -2058,53 +2085,30 @@ static void list_v4l2_devices(void *userdata) { // |card_path| can be NULL. If not NULL then |vendor| has to be valid static void list_capture_options_command(const char *card_path, void *userdata) { (void)userdata; - bool wayland = false; - Display *dpy = XOpenDisplay(nullptr); - if (!dpy) { - wayland = true; - fprintf(stderr, "gsr warning: failed to connect to the X server. Assuming wayland is running without Xwayland\n"); - } + WindowingSetup windowing_setup = setup_windowing(card_path != nullptr); - XSetErrorHandler(x11_error_handler); - XSetIOErrorHandler(x11_io_error_handler); + if(card_path) + list_supported_capture_options(windowing_setup.window, card_path, true); + else + list_supported_capture_options(windowing_setup.window, windowing_setup.egl.card_path, windowing_setup.list_monitors); - if(!wayland) - wayland = is_xwayland(dpy); + fflush(stdout); - if(!wayland && is_using_prime_run()) { - // Disable prime-run and similar options as it doesn't work, the monitor to capture has to be run on the same device. - // This is fine on wayland since nvidia uses drm interface there and the monitor query checks the monitors connected - // to the drm device. - fprintf(stderr, "gsr warning: use of prime-run on X11 is not supported. Disabling prime-run\n"); - disable_prime_run(); - } + // Not needed as this will just slow down shutdown + //gsr_egl_unload(&egl); + //gsr_window_destroy(&window); + //if(dpy) + // XCloseDisplay(dpy); - gsr_window *window = gsr_window_create(dpy, wayland); - if(!window) { - fprintf(stderr, "gsr error: failed to create window\n"); - _exit(1); - } + _exit(0); +} - if(card_path) { - list_supported_capture_options(window, card_path, true); - } else { - gsr_egl egl; - if(!gsr_egl_load(&egl, window, false, false)) { - fprintf(stderr, "gsr error: failed to load opengl\n"); - _exit(1); - } +static void list_monitors_command(void *userdata) { + (void)userdata; + WindowingSetup windowing_setup = setup_windowing(true); - bool list_monitors = true; - egl.card_path[0] = '\0'; - if(monitor_capture_use_drm(window, egl.gpu_info.vendor)) { - // TODO: Allow specifying another card, and in other places - if(!gsr_get_valid_card_path(&egl, egl.card_path, true)) { - fprintf(stderr, "gsr error: no /dev/dri/cardX device found. Make sure that you have at least one monitor connected\n"); - list_monitors = false; - } - } - list_supported_capture_options(window, egl.card_path, list_monitors); - } + if(windowing_setup.list_monitors) + list_monitors(windowing_setup.window, windowing_setup.egl.card_path); fflush(stdout); @@ -3682,6 +3686,7 @@ int main(int argc, char **argv) { arg_handlers.list_application_audio = list_application_audio_command; arg_handlers.list_v4l2_devices = list_v4l2_devices; arg_handlers.list_capture_options = list_capture_options_command; + arg_handlers.list_monitors = list_monitors_command; args_parser arg_parser; if(!args_parser_parse(&arg_parser, argc, argv, &arg_handlers, NULL))