Add --list-monitors option to list only monitors, refactor

This commit is contained in:
dec05eba
2026-02-09 15:03:10 +01:00
parent f3235ed1bf
commit 8f7608e7ee
4 changed files with 96 additions and 81 deletions

View File

@@ -14,13 +14,17 @@ gpu-screen-recorder \- The fastest screen recording tool for Linux
| |
.B \-\-version .B \-\-version
| |
.B \-\-info
|
.B \-\-list\-capture\-options .B \-\-list\-capture\-options
| |
.B \-\-list\-monitors
|
.B \-\-list\-v4l2\-devices
|
.B \-\-list\-audio\-devices .B \-\-list\-audio\-devices
| |
.B \-\-list\-application\-audio .B \-\-list\-application\-audio
|
.B \-\-info
.SH DESCRIPTION .SH DESCRIPTION
.B gpu-screen-recorder .B gpu-screen-recorder
is the fastest screen recording tool for Linux. It uses the GPU is the fastest screen recording tool for Linux. It uses the GPU
@@ -92,10 +96,6 @@ Run
.B \-\-list\-capture\-options .B \-\-list\-capture\-options
to list available capture sources. to list available capture sources.
.PP .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 Additional options can be passed to each capture source by splitting capture source with
.B ; .B ;
for example for example
@@ -403,6 +403,9 @@ Show system info (codecs, capture options).
.B \-\-list\-capture\-options .B \-\-list\-capture\-options
List available capture sources (window, monitors, portal, v4l2 device path). List available capture sources (window, monitors, portal, v4l2 device path).
.TP .TP
.B \-\-list\-monitors
List available monitors.
.TP
.B \-\-list\-v4l2\-devices .B \-\-list\-v4l2\-devices
List available cameras devices (V4L2). List available cameras devices (V4L2).
.TP .TP

View File

@@ -63,6 +63,7 @@ typedef struct {
void (*list_application_audio)(void *userdata); void (*list_application_audio)(void *userdata);
void (*list_v4l2_devices)(void *userdata); void (*list_v4l2_devices)(void *userdata);
void (*list_capture_options)(const char *card_path, void *userdata); void (*list_capture_options)(const char *card_path, void *userdata);
void (*list_monitors)(void *userdata);
} args_handlers; } args_handlers;
typedef struct { typedef struct {

View File

@@ -196,7 +196,8 @@ static void usage_header(void) {
"[-bm auto|qp|vbr|cbr] [-cr limited|full] [-tune performance|quality] [-df yes|no] [-sc <script_path>] [-p <plugin_path>] " "[-bm auto|qp|vbr|cbr] [-cr limited|full] [-tune performance|quality] [-df yes|no] [-sc <script_path>] [-p <plugin_path>] "
"[-cursor yes|no] [-keyint <value>] [-restore-portal-session yes|no] [-portal-session-token-filepath filepath] [-encoder gpu|cpu] " "[-cursor yes|no] [-keyint <value>] [-restore-portal-session yes|no] [-portal-session-token-filepath filepath] [-encoder gpu|cpu] "
"[-fallback-cpu-encoding yes|no] [-o <output_file>] [-ro <output_directory>] [-ffmpeg-opts <options>] [--list-capture-options [card_path]] " "[-fallback-cpu-encoding yes|no] [-o <output_file>] [-ro <output_directory>] [-ffmpeg-opts <options>] [--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); 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) { if(argc == 2 && strcmp(argv[1], "--version") == 0) {
arg_handlers->version(userdata); arg_handlers->version(userdata);
return true; return true;

View File

@@ -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)); 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; const bool wayland = gsr_window_get_display_server(window) == GSR_DISPLAY_SERVER_WAYLAND;
if(!wayland) { if(!wayland) {
puts("window"); puts("window");
puts("focused"); puts("focused");
} }
capture_options_callback options; int num_monitors = 0;
options.window = window; if(do_list_monitors)
options.num_monitors = 0; num_monitors = list_monitors(window, card_path);
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);
}
if(options.num_monitors > 0) if(num_monitors > 0)
puts("region"); puts("region");
gsr_capture_v4l2_list_devices(camera_query_callback, NULL); gsr_capture_v4l2_list_devices(camera_query_callback, NULL);
@@ -1933,11 +1941,20 @@ static void version_command(void *userdata) {
_exit(0); _exit(0);
} }
static void info_command(void *userdata) { struct WindowingSetup {
(void)userdata; 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; bool wayland = false;
Display *dpy = XOpenDisplay(nullptr); setup.dpy = XOpenDisplay(nullptr);
if (!dpy) { if (!setup.dpy) {
wayland = true; wayland = true;
fprintf(stderr, "gsr warning: failed to connect to the X server. Assuming wayland is running without Xwayland\n"); 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); XSetIOErrorHandler(x11_io_error_handler);
if(!wayland) if(!wayland)
wayland = is_xwayland(dpy); wayland = is_xwayland(setup.dpy);
if(!wayland && is_using_prime_run()) { 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. // 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(); disable_prime_run();
} }
gsr_window *window = gsr_window_create(dpy, wayland); setup.window = gsr_window_create(setup.dpy, wayland);
if(!window) { if(!setup.window) {
fprintf(stderr, "gsr error: failed to create window\n"); fprintf(stderr, "gsr error: failed to create window\n");
_exit(1); _exit(1);
} }
gsr_egl egl; setup.list_monitors = true;
if(!gsr_egl_load(&egl, window, false, false)) {
fprintf(stderr, "gsr error: failed to load opengl\n");
_exit(22);
}
bool list_monitors = true; if(setup_egl) {
egl.card_path[0] = '\0'; if(!gsr_egl_load(&setup.egl, setup.window, false, false)) {
if(monitor_capture_use_drm(window, egl.gpu_info.vendor)) { fprintf(stderr, "gsr error: failed to load opengl\n");
// TODO: Allow specifying another card, and in other places _exit(22);
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; 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); av_log_set_level(AV_LOG_FATAL);
puts("section=system_info"); puts("section=system_info");
list_system_info(wayland); 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"); puts("is_steam_deck|yes");
else else
puts("is_steam_deck|no"); puts("is_steam_deck|no");
printf("gsr_version|%s\n", GSR_VERSION); printf("gsr_version|%s\n", GSR_VERSION);
puts("section=gpu_info"); puts("section=gpu_info");
list_gpu_info(&egl); list_gpu_info(&windowing_setup.egl);
puts("section=video_codecs"); puts("section=video_codecs");
list_supported_video_codecs(&egl, wayland); list_supported_video_codecs(&windowing_setup.egl, wayland);
puts("section=image_formats"); puts("section=image_formats");
puts("jpeg"); puts("jpeg");
puts("png"); puts("png");
puts("section=capture_options"); 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); 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 // |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) { static void list_capture_options_command(const char *card_path, void *userdata) {
(void)userdata; (void)userdata;
bool wayland = false; WindowingSetup windowing_setup = setup_windowing(card_path != nullptr);
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");
}
XSetErrorHandler(x11_error_handler); if(card_path)
XSetIOErrorHandler(x11_io_error_handler); 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) fflush(stdout);
wayland = is_xwayland(dpy);
if(!wayland && is_using_prime_run()) { // Not needed as this will just slow down shutdown
// Disable prime-run and similar options as it doesn't work, the monitor to capture has to be run on the same device. //gsr_egl_unload(&egl);
// This is fine on wayland since nvidia uses drm interface there and the monitor query checks the monitors connected //gsr_window_destroy(&window);
// to the drm device. //if(dpy)
fprintf(stderr, "gsr warning: use of prime-run on X11 is not supported. Disabling prime-run\n"); // XCloseDisplay(dpy);
disable_prime_run();
}
gsr_window *window = gsr_window_create(dpy, wayland); _exit(0);
if(!window) { }
fprintf(stderr, "gsr error: failed to create window\n");
_exit(1);
}
if(card_path) { static void list_monitors_command(void *userdata) {
list_supported_capture_options(window, card_path, true); (void)userdata;
} else { WindowingSetup windowing_setup = setup_windowing(true);
gsr_egl egl;
if(!gsr_egl_load(&egl, window, false, false)) {
fprintf(stderr, "gsr error: failed to load opengl\n");
_exit(1);
}
bool list_monitors = true; if(windowing_setup.list_monitors)
egl.card_path[0] = '\0'; list_monitors(windowing_setup.window, windowing_setup.egl.card_path);
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);
}
fflush(stdout); fflush(stdout);
@@ -3682,6 +3686,7 @@ int main(int argc, char **argv) {
arg_handlers.list_application_audio = list_application_audio_command; arg_handlers.list_application_audio = list_application_audio_command;
arg_handlers.list_v4l2_devices = list_v4l2_devices; arg_handlers.list_v4l2_devices = list_v4l2_devices;
arg_handlers.list_capture_options = list_capture_options_command; arg_handlers.list_capture_options = list_capture_options_command;
arg_handlers.list_monitors = list_monitors_command;
args_parser arg_parser; args_parser arg_parser;
if(!args_parser_parse(&arg_parser, argc, argv, &arg_handlers, NULL)) if(!args_parser_parse(&arg_parser, argc, argv, &arg_handlers, NULL))