diff options
| author | dec05eba <dec05eba@protonmail.com> | 2026-08-01 23:08:48 +0200 |
|---|---|---|
| committer | dec05eba <dec05eba@protonmail.com> | 2026-08-01 23:08:48 +0200 |
| commit | 9dda64c39d0fdd6409305faff33c217dcf161e60 (patch) | |
| tree | b66b522a0af8d0730bef7852cbe9e3b78835fa86 /src/cli | |
| parent | 40b6a13d14680cfacb3cdb59f04224540b80d9e4 (diff) | |
Move screenshot capture and CLI commands from main.cpp into separate modules
The list commands return exit codes through args_parser instead of exiting.
Diffstat (limited to 'src/cli')
| -rw-r--r-- | src/cli/commands.c | 372 |
1 files changed, 372 insertions, 0 deletions
diff --git a/src/cli/commands.c b/src/cli/commands.c new file mode 100644 index 0000000..4eaaf65 --- /dev/null +++ b/src/cli/commands.c @@ -0,0 +1,372 @@ +#include "../../include/cli/commands.h" +#include "../../include/recorder/windowing.h" +#include "../../include/recorder/codec_select.h" +#include "../../include/recorder/error.h" +#include "../../include/capture/v4l2.h" +#include "../../include/window/window.h" +#include "../../include/sound.h" +#include "../../include/utils.h" +#include "../../include/log.h" +#ifdef GSR_APP_AUDIO +#include "../../include/pipewire_audio.h" +#endif +#ifdef GSR_PORTAL +#include "../../include/dbus.h" +#endif + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/wait.h> + +#ifndef GSR_VERSION +#define GSR_VERSION "unknown" +#endif + +static void list_system_info(bool wayland) { + printf("display_server|%s\n", wayland ? "wayland" : "x11"); + bool supports_app_audio = false; +#ifdef GSR_APP_AUDIO + supports_app_audio = pulseaudio_server_is_pipewire(); + if(supports_app_audio) { + gsr_pipewire_audio audio; + if(gsr_pipewire_audio_init(&audio)) + gsr_pipewire_audio_deinit(&audio); + else + supports_app_audio = false; + } +#endif + printf("supports_app_audio|%s\n", supports_app_audio ? "yes" : "no"); +} + +static void list_gpu_info(gsr_egl *egl) { + switch(egl->gpu_info.vendor) { + case GSR_GPU_VENDOR_AMD: + printf("vendor|amd\n"); + break; + case GSR_GPU_VENDOR_INTEL: + printf("vendor|intel\n"); + break; + case GSR_GPU_VENDOR_NVIDIA: + printf("vendor|nvidia\n"); + break; + case GSR_GPU_VENDOR_BROADCOM: + printf("vendor|broadcom\n"); + break; + } + printf("card_path|%s\n", egl->card_path); +} + +static void list_supported_video_codecs(gsr_egl *egl, bool wayland) { + // Dont clean it up on purpose to increase shutdown speed + gsr_supported_video_codecs supported_video_codecs; + get_supported_video_codecs(egl, GSR_VIDEO_CODEC_H264, false, false, &supported_video_codecs); + + gsr_supported_video_codecs supported_video_codecs_vulkan; + get_supported_video_codecs(egl, GSR_VIDEO_CODEC_H264_VULKAN, false, false, &supported_video_codecs_vulkan); + + set_supported_video_codecs_ffmpeg(&supported_video_codecs, &supported_video_codecs_vulkan, egl->gpu_info.vendor); + + if(supported_video_codecs.h264.supported) + puts("h264"); + if(avcodec_find_encoder_by_name("libx264")) + puts("h264_software"); + if(supported_video_codecs.hevc.supported) + puts("hevc"); + if(supported_video_codecs.hevc_hdr.supported && wayland) + puts("hevc_hdr"); + if(supported_video_codecs.hevc_10bit.supported) + puts("hevc_10bit"); + if(supported_video_codecs.av1.supported) + puts("av1"); + if(supported_video_codecs.av1_hdr.supported && wayland) + puts("av1_hdr"); + if(supported_video_codecs.av1_10bit.supported) + puts("av1_10bit"); + if(supported_video_codecs.vp8.supported) + puts("vp8"); + if(supported_video_codecs.vp9.supported) + puts("vp9"); + if(supported_video_codecs_vulkan.h264.supported) + puts("h264_vulkan"); + if(supported_video_codecs_vulkan.hevc.supported) + puts("hevc_vulkan"); + if(supported_video_codecs_vulkan.hevc_hdr.supported && wayland) + puts("hevc_hdr_vulkan"); + if(supported_video_codecs_vulkan.hevc_10bit.supported) + puts("hevc_10bit_vulkan"); + if(supported_video_codecs_vulkan.av1.supported) + puts("av1_vulkan"); + if(supported_video_codecs_vulkan.av1_hdr.supported && wayland) + puts("av1_hdr_vulkan"); + if(supported_video_codecs_vulkan.av1_10bit.supported) + puts("av1_10bit_vulkan"); +} + +void run_recording_saved_script_async(const char *script_file, const char *video_file, const char *type) { + char script_file_full[PATH_MAX]; + script_file_full[0] = '\0'; + if(!realpath(script_file, script_file_full)) { + gsr_log(GSR_LOG_LEVEL_ERROR, "script file not found: %s", script_file); + return; + } + + const char *args[7]; + const bool inside_flatpak = getenv("FLATPAK_ID") != NULL; + + if(inside_flatpak) { + args[0] = "flatpak-spawn"; + args[1] = "--host"; + args[2] = "--"; + args[3] = script_file_full; + args[4] = video_file; + args[5] = type; + args[6] = NULL; + } else { + args[0] = script_file_full; + args[1] = video_file; + args[2] = type; + args[3] = NULL; + } + + pid_t pid = fork(); + if(pid == -1) { + perror(script_file_full); + return; + } else if(pid == 0) { // child + setsid(); + signal(SIGHUP, SIG_IGN); + + pid_t second_child = fork(); + if(second_child == 0) { // child + execvp(args[0], (char* const*)args); + perror(script_file_full); + _exit(127); + } else if(second_child != -1) { // parent + _exit(0); + } + } else { // parent + waitpid(pid, NULL, 0); + } +} + +typedef struct { + const gsr_window *window; + int num_monitors; +} capture_options_callback; + +static void output_monitor_info(const gsr_monitor *monitor, void *userdata) { + capture_options_callback *options = (capture_options_callback*)userdata; + if(gsr_window_get_display_server(options->window) == GSR_DISPLAY_SERVER_WAYLAND) { + vec2i monitor_size = monitor->size; + gsr_monitor_rotation monitor_rotation = GSR_MONITOR_ROT_0; + vec2i monitor_position = {0, 0}; + drm_monitor_get_display_server_data(options->window, monitor, &monitor_rotation, &monitor_position); + if(monitor_rotation == GSR_MONITOR_ROT_90 || monitor_rotation == GSR_MONITOR_ROT_270) + { + const int tmp = monitor_size.x; + monitor_size.x = monitor_size.y; + monitor_size.y = tmp; + } + printf("%.*s|%dx%d\n", monitor->name_len, monitor->name, monitor_size.x, monitor_size.y); + } else { + printf("%.*s|%dx%d\n", monitor->name_len, monitor->name, monitor->size.x, monitor->size.y); + } + ++options->num_monitors; +} + +static void camera_query_callback(const char *path, const gsr_capture_v4l2_supported_setup *setup, void *userdata) { + (void)userdata; + 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 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"); + } + + int num_monitors = 0; + if(do_list_monitors) + num_monitors = list_monitors(window, card_path); + + if(num_monitors > 0) + puts("region"); + + gsr_capture_v4l2_list_devices(camera_query_callback, NULL); + +#ifdef GSR_PORTAL + // Desktop portal capture on x11 doesn't seem to be hardware accelerated + if(!wayland) + return; + + gsr_dbus dbus; + if(!gsr_dbus_init(&dbus, NULL)) + return; + + char *session_handle = NULL; + if(gsr_dbus_screencast_create_session(&dbus, &session_handle) == 0) + puts("portal"); + + gsr_dbus_deinit(&dbus); +#endif +} + +int version_command(void *userdata) { + (void)userdata; + puts(GSR_VERSION); + fflush(stdout); + return 0; +} + +int info_command(void *userdata) { + (void)userdata; + gsr_windowing windowing; + const gsr_windowing_params windowing_params = {false, false, false}; + if(gsr_windowing_init(&windowing, &windowing_params) != GSR_ERROR_OK) + return 1; + if(gsr_windowing_load_egl(&windowing, &windowing_params) != GSR_ERROR_OK) + return 22; + const bool wayland = gsr_windowing_is_wayland(&windowing); + + av_log_set_level(AV_LOG_FATAL); + + puts("section=system_info"); + list_system_info(wayland); + if(windowing.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(&windowing.egl); + puts("section=video_codecs"); + list_supported_video_codecs(&windowing.egl, wayland); + puts("section=image_formats"); + puts("jpeg"); + puts("png"); + puts("section=capture_options"); + list_supported_capture_options(windowing.window, windowing.egl.card_path, windowing.card_path_found); + + fflush(stdout); + + // Not needed as this will just slow down shutdown + //gsr_egl_unload(&egl); + //gsr_window_destroy(&window); + //if(dpy) + // XCloseDisplay(dpy); + + return 0; +} + +int list_audio_devices_command(void *userdata) { + (void)userdata; + gsr_audio_devices audio_devices; + get_pulseaudio_inputs(&audio_devices); + + if(audio_devices.default_output[0] != '\0') + puts("default_output|Default output"); + + if(audio_devices.default_input[0] != '\0') + puts("default_input|Default input"); + + for(size_t i = 0; i < audio_devices.num_items; ++i) { + printf("%s|%s\n", audio_devices.items[i].name, audio_devices.items[i].description); + } + + gsr_audio_devices_deinit(&audio_devices); + fflush(stdout); + return 0; +} + +static bool app_audio_query_callback(const char *app_name, void *userdata) { + (void)userdata; + puts(app_name); + return true; +} + +int list_application_audio_command(void *userdata) { + (void)userdata; +#ifdef GSR_APP_AUDIO + if(pulseaudio_server_is_pipewire()) { + gsr_pipewire_audio audio; + if(gsr_pipewire_audio_init(&audio)) { + gsr_pipewire_audio_for_each_app(&audio, app_audio_query_callback, NULL); + gsr_pipewire_audio_deinit(&audio); + } + } +#endif + + fflush(stdout); + return 0; +} + +int list_v4l2_devices(void *userdata) { + (void)userdata; + gsr_capture_v4l2_list_devices(camera_query_callback, NULL); + + fflush(stdout); + return 0; +} + +int list_capture_options_command(const char *card_path, void *userdata) { + (void)userdata; + gsr_windowing windowing; + const gsr_windowing_params windowing_params = {false, false, false}; + if(gsr_windowing_init(&windowing, &windowing_params) != GSR_ERROR_OK) + return 1; + if(!card_path && gsr_windowing_load_egl(&windowing, &windowing_params) != GSR_ERROR_OK) + return 22; + + if(card_path) + list_supported_capture_options(windowing.window, card_path, true); + else + list_supported_capture_options(windowing.window, windowing.egl.card_path, windowing.card_path_found); + + fflush(stdout); + + // Not needed as this will just slow down shutdown + //gsr_egl_unload(&egl); + //gsr_window_destroy(&window); + //if(dpy) + // XCloseDisplay(dpy); + + return 0; +} + +int list_monitors_command(void *userdata) { + (void)userdata; + gsr_windowing windowing; + const gsr_windowing_params windowing_params = {false, false, false}; + if(gsr_windowing_init(&windowing, &windowing_params) != GSR_ERROR_OK) + return 1; + if(gsr_windowing_load_egl(&windowing, &windowing_params) != GSR_ERROR_OK) + return 22; + + if(windowing.card_path_found) + list_monitors(windowing.window, windowing.egl.card_path); + + fflush(stdout); + + // Not needed as this will just slow down shutdown + //gsr_egl_unload(&egl); + //gsr_window_destroy(&window); + //if(dpy) + // XCloseDisplay(dpy); + + return 0; +} |
