mirror of
https://repo.dec05eba.com/gpu-screen-recorder-ui
synced 2026-03-31 09:17:04 +09:00
Add webcam support
This commit is contained in:
2
TODO
2
TODO
@@ -253,3 +253,5 @@ Sometimes when opening gpu screen recorder ui gsr-global-hotkeys incorrectly det
|
|||||||
When running replay for a long time and then stopping it it takes a while. Improve this.
|
When running replay for a long time and then stopping it it takes a while. Improve this.
|
||||||
|
|
||||||
When adding webcamera make replay auto start wait for camera to be available (when /dev/video device exists and can be initialized), just like audio device.
|
When adding webcamera make replay auto start wait for camera to be available (when /dev/video device exists and can be initialized), just like audio device.
|
||||||
|
|
||||||
|
Make it possible to resize webcam box from top left, top right and bottom left as well.
|
||||||
Submodule depends/mglpp updated: f69ab7f005...cdd401478d
@@ -61,6 +61,14 @@ namespace gsr {
|
|||||||
bool record_cursor = true;
|
bool record_cursor = true;
|
||||||
bool restore_portal_session = true;
|
bool restore_portal_session = true;
|
||||||
|
|
||||||
|
std::string webcam_source = "";
|
||||||
|
bool webcam_flip_horizontally = false;
|
||||||
|
std::string webcam_video_format = "auto";
|
||||||
|
int32_t webcam_x = 0; // A value between 0 and 100 (percentage)
|
||||||
|
int32_t webcam_y = 0; // A value between 0 and 100 (percentage)
|
||||||
|
int32_t webcam_width = 30; // A value between 0 and 100 (percentage), 0 = Don't scale it
|
||||||
|
int32_t webcam_height = 30; // A value between 0 and 100 (percentage), 0 = Don't scale it
|
||||||
|
|
||||||
bool show_notifications = true;
|
bool show_notifications = true;
|
||||||
bool use_led_indicator = false;
|
bool use_led_indicator = false;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -25,11 +25,22 @@ namespace gsr {
|
|||||||
bool png = false;
|
bool png = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct SupportedCameraPixelFormats {
|
||||||
|
bool yuyv = false;
|
||||||
|
bool mjpeg = false;
|
||||||
|
};
|
||||||
|
|
||||||
struct GsrMonitor {
|
struct GsrMonitor {
|
||||||
std::string name;
|
std::string name;
|
||||||
mgl::vec2i size;
|
mgl::vec2i size;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct GsrCamera {
|
||||||
|
std::string path;
|
||||||
|
mgl::vec2i size;
|
||||||
|
SupportedCameraPixelFormats supported_pixel_formats;
|
||||||
|
};
|
||||||
|
|
||||||
struct GsrVersion {
|
struct GsrVersion {
|
||||||
uint8_t major = 0;
|
uint8_t major = 0;
|
||||||
uint8_t minor = 0;
|
uint8_t minor = 0;
|
||||||
@@ -51,6 +62,7 @@ namespace gsr {
|
|||||||
bool focused = false;
|
bool focused = false;
|
||||||
bool portal = false;
|
bool portal = false;
|
||||||
std::vector<GsrMonitor> monitors;
|
std::vector<GsrMonitor> monitors;
|
||||||
|
std::vector<GsrCamera> cameras;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class DisplayServer {
|
enum class DisplayServer {
|
||||||
@@ -103,4 +115,5 @@ namespace gsr {
|
|||||||
std::vector<AudioDevice> get_audio_devices();
|
std::vector<AudioDevice> get_audio_devices();
|
||||||
std::vector<std::string> get_application_audio();
|
std::vector<std::string> get_application_audio();
|
||||||
SupportedCaptureOptions get_supported_capture_options(const GsrInfo &gsr_info);
|
SupportedCaptureOptions get_supported_capture_options(const GsrInfo &gsr_info);
|
||||||
|
std::vector<GsrCamera> get_v4l2_devices();
|
||||||
}
|
}
|
||||||
@@ -24,6 +24,7 @@ namespace gsr {
|
|||||||
mgl::Font body_font;
|
mgl::Font body_font;
|
||||||
mgl::Font title_font;
|
mgl::Font title_font;
|
||||||
mgl::Font top_bar_font;
|
mgl::Font top_bar_font;
|
||||||
|
mgl::Font camera_setup_font;
|
||||||
|
|
||||||
mgl::Texture combobox_arrow_texture;
|
mgl::Texture combobox_arrow_texture;
|
||||||
mgl::Texture settings_texture;
|
mgl::Texture settings_texture;
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ namespace gsr {
|
|||||||
void draw(mgl::Window &window, mgl::vec2f offset) override;
|
void draw(mgl::Window &window, mgl::vec2f offset) override;
|
||||||
|
|
||||||
void add_item(const std::string &text, const std::string &id);
|
void add_item(const std::string &text, const std::string &id);
|
||||||
|
void clear_items();
|
||||||
|
|
||||||
// The item can only be selected if it's enabled
|
// The item can only be selected if it's enabled
|
||||||
void set_selected_item(const std::string &id, bool trigger_event = true, bool trigger_event_even_if_selection_not_changed = true);
|
void set_selected_item(const std::string &id, bool trigger_event = true, bool trigger_event_even_if_selection_not_changed = true);
|
||||||
void set_item_enabled(const std::string &id, bool enabled);
|
void set_item_enabled(const std::string &id, bool enabled);
|
||||||
|
|||||||
@@ -25,6 +25,14 @@ namespace gsr {
|
|||||||
INPUT
|
INPUT
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class WebcamBoxResizeCorner {
|
||||||
|
NONE,
|
||||||
|
//TOP_LEFT,
|
||||||
|
//TOP_RIGHT,
|
||||||
|
//BOTTOM_LEFT,
|
||||||
|
BOTTOM_RIGHT
|
||||||
|
};
|
||||||
|
|
||||||
class SettingsPage : public StaticPage {
|
class SettingsPage : public StaticPage {
|
||||||
public:
|
public:
|
||||||
enum class Type {
|
enum class Type {
|
||||||
@@ -58,6 +66,9 @@ namespace gsr {
|
|||||||
std::unique_ptr<List> create_restore_portal_session_section();
|
std::unique_ptr<List> create_restore_portal_session_section();
|
||||||
std::unique_ptr<Widget> create_change_video_resolution_section();
|
std::unique_ptr<Widget> create_change_video_resolution_section();
|
||||||
std::unique_ptr<Widget> create_capture_target_section();
|
std::unique_ptr<Widget> create_capture_target_section();
|
||||||
|
std::unique_ptr<List> create_webcam_sources();
|
||||||
|
std::unique_ptr<List> create_webcam_video_format();
|
||||||
|
std::unique_ptr<Widget> create_webcam_section();
|
||||||
std::unique_ptr<ComboBox> create_audio_device_selection_combobox(AudioDeviceType device_type);
|
std::unique_ptr<ComboBox> create_audio_device_selection_combobox(AudioDeviceType device_type);
|
||||||
std::unique_ptr<Button> create_remove_audio_device_button(List *audio_input_list_ptr, List *audio_device_list_ptr);
|
std::unique_ptr<Button> create_remove_audio_device_button(List *audio_input_list_ptr, List *audio_device_list_ptr);
|
||||||
std::unique_ptr<List> create_audio_device(AudioDeviceType device_type, List *audio_input_list_ptr);
|
std::unique_ptr<List> create_audio_device(AudioDeviceType device_type, List *audio_input_list_ptr);
|
||||||
@@ -140,6 +151,8 @@ namespace gsr {
|
|||||||
void save_stream();
|
void save_stream();
|
||||||
|
|
||||||
void view_changed(bool advanced_view);
|
void view_changed(bool advanced_view);
|
||||||
|
|
||||||
|
RecordOptions& get_current_record_options();
|
||||||
private:
|
private:
|
||||||
Type type;
|
Type type;
|
||||||
Config &config;
|
Config &config;
|
||||||
@@ -197,7 +210,29 @@ namespace gsr {
|
|||||||
List *audio_track_section_list_ptr = nullptr;
|
List *audio_track_section_list_ptr = nullptr;
|
||||||
CheckBox *led_indicator_checkbox_ptr = nullptr;
|
CheckBox *led_indicator_checkbox_ptr = nullptr;
|
||||||
CheckBox *show_notification_checkbox_ptr = nullptr;
|
CheckBox *show_notification_checkbox_ptr = nullptr;
|
||||||
|
ComboBox *webcam_sources_box_ptr = nullptr;
|
||||||
|
ComboBox *webcam_video_format_box_ptr = nullptr;
|
||||||
|
List *webcam_body_list_ptr = nullptr;
|
||||||
|
CheckBox *flip_camera_horizontally_checkbox_ptr = nullptr;
|
||||||
|
|
||||||
PageStack *page_stack = nullptr;
|
PageStack *page_stack = nullptr;
|
||||||
|
|
||||||
|
mgl::vec2f webcam_box_pos;
|
||||||
|
mgl::vec2f webcam_box_size;
|
||||||
|
|
||||||
|
mgl::vec2f webcam_box_drawn_pos;
|
||||||
|
mgl::vec2f webcam_box_drawn_size;
|
||||||
|
mgl::vec2f webcam_box_grab_offset;
|
||||||
|
|
||||||
|
mgl::vec2f camera_screen_size;
|
||||||
|
mgl::vec2f screen_inner_size;
|
||||||
|
bool moving_webcam_box = false;
|
||||||
|
|
||||||
|
WebcamBoxResizeCorner webcam_resize_corner = WebcamBoxResizeCorner::NONE;
|
||||||
|
mgl::vec2f webcam_resize_start_pos;
|
||||||
|
mgl::vec2f webcam_box_pos_resize_start;
|
||||||
|
mgl::vec2f webcam_box_size_resize_start;
|
||||||
|
|
||||||
|
std::optional<GsrCamera> selected_camera;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -9,6 +9,10 @@ namespace mgl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
namespace gsr {
|
namespace gsr {
|
||||||
|
mgl::vec2i min_vec2i(mgl::vec2i a, mgl::vec2i b);
|
||||||
|
mgl::vec2i max_vec2i(mgl::vec2i a, mgl::vec2i b);
|
||||||
|
mgl::vec2i clamp_vec2i(mgl::vec2i value, mgl::vec2i min, mgl::vec2i max);
|
||||||
|
|
||||||
// Inner border
|
// Inner border
|
||||||
void draw_rectangle_outline(mgl::Window &window, mgl::vec2f pos, mgl::vec2f size, mgl::Color color, float border_size);
|
void draw_rectangle_outline(mgl::Window &window, mgl::vec2f pos, mgl::vec2f size, mgl::Color color, float border_size);
|
||||||
double get_frame_delta_seconds();
|
double get_frame_delta_seconds();
|
||||||
|
|||||||
@@ -199,6 +199,13 @@ namespace gsr {
|
|||||||
{"streaming.record_options.overclock", &config.streaming_config.record_options.overclock},
|
{"streaming.record_options.overclock", &config.streaming_config.record_options.overclock},
|
||||||
{"streaming.record_options.record_cursor", &config.streaming_config.record_options.record_cursor},
|
{"streaming.record_options.record_cursor", &config.streaming_config.record_options.record_cursor},
|
||||||
{"streaming.record_options.restore_portal_session", &config.streaming_config.record_options.restore_portal_session},
|
{"streaming.record_options.restore_portal_session", &config.streaming_config.record_options.restore_portal_session},
|
||||||
|
{"streaming.record_options.webcam_source", &config.streaming_config.record_options.webcam_source},
|
||||||
|
{"streaming.record_options.webcam_flip_horizontally", &config.streaming_config.record_options.webcam_flip_horizontally},
|
||||||
|
{"streaming.record_options.webcam_video_format", &config.streaming_config.record_options.webcam_video_format},
|
||||||
|
{"streaming.record_options.webcam_x", &config.streaming_config.record_options.webcam_x},
|
||||||
|
{"streaming.record_options.webcam_y", &config.streaming_config.record_options.webcam_y},
|
||||||
|
{"streaming.record_options.webcam_width", &config.streaming_config.record_options.webcam_width},
|
||||||
|
{"streaming.record_options.webcam_height", &config.streaming_config.record_options.webcam_height},
|
||||||
{"streaming.record_options.show_notifications", &config.streaming_config.record_options.show_notifications},
|
{"streaming.record_options.show_notifications", &config.streaming_config.record_options.show_notifications},
|
||||||
{"streaming.record_options.use_led_indicator", &config.streaming_config.record_options.use_led_indicator},
|
{"streaming.record_options.use_led_indicator", &config.streaming_config.record_options.use_led_indicator},
|
||||||
{"streaming.service", &config.streaming_config.streaming_service},
|
{"streaming.service", &config.streaming_config.streaming_service},
|
||||||
@@ -231,6 +238,13 @@ namespace gsr {
|
|||||||
{"record.record_options.overclock", &config.record_config.record_options.overclock},
|
{"record.record_options.overclock", &config.record_config.record_options.overclock},
|
||||||
{"record.record_options.record_cursor", &config.record_config.record_options.record_cursor},
|
{"record.record_options.record_cursor", &config.record_config.record_options.record_cursor},
|
||||||
{"record.record_options.restore_portal_session", &config.record_config.record_options.restore_portal_session},
|
{"record.record_options.restore_portal_session", &config.record_config.record_options.restore_portal_session},
|
||||||
|
{"record.record_options.webcam_source", &config.record_config.record_options.webcam_source},
|
||||||
|
{"record.record_options.webcam_flip_horizontally", &config.record_config.record_options.webcam_flip_horizontally},
|
||||||
|
{"record.record_options.webcam_video_format", &config.record_config.record_options.webcam_video_format},
|
||||||
|
{"record.record_options.webcam_x", &config.record_config.record_options.webcam_x},
|
||||||
|
{"record.record_options.webcam_y", &config.record_config.record_options.webcam_y},
|
||||||
|
{"record.record_options.webcam_width", &config.record_config.record_options.webcam_width},
|
||||||
|
{"record.record_options.webcam_height", &config.record_config.record_options.webcam_height},
|
||||||
{"record.record_options.show_notifications", &config.record_config.record_options.show_notifications},
|
{"record.record_options.show_notifications", &config.record_config.record_options.show_notifications},
|
||||||
{"record.record_options.use_led_indicator", &config.record_config.record_options.use_led_indicator},
|
{"record.record_options.use_led_indicator", &config.record_config.record_options.use_led_indicator},
|
||||||
{"record.save_video_in_game_folder", &config.record_config.save_video_in_game_folder},
|
{"record.save_video_in_game_folder", &config.record_config.save_video_in_game_folder},
|
||||||
@@ -260,6 +274,13 @@ namespace gsr {
|
|||||||
{"replay.record_options.overclock", &config.replay_config.record_options.overclock},
|
{"replay.record_options.overclock", &config.replay_config.record_options.overclock},
|
||||||
{"replay.record_options.record_cursor", &config.replay_config.record_options.record_cursor},
|
{"replay.record_options.record_cursor", &config.replay_config.record_options.record_cursor},
|
||||||
{"replay.record_options.restore_portal_session", &config.replay_config.record_options.restore_portal_session},
|
{"replay.record_options.restore_portal_session", &config.replay_config.record_options.restore_portal_session},
|
||||||
|
{"replay.record_options.webcam_source", &config.replay_config.record_options.webcam_source},
|
||||||
|
{"replay.record_options.webcam_flip_horizontally", &config.replay_config.record_options.webcam_flip_horizontally},
|
||||||
|
{"replay.record_options.webcam_video_format", &config.replay_config.record_options.webcam_video_format},
|
||||||
|
{"replay.record_options.webcam_x", &config.replay_config.record_options.webcam_x},
|
||||||
|
{"replay.record_options.webcam_y", &config.replay_config.record_options.webcam_y},
|
||||||
|
{"replay.record_options.webcam_width", &config.replay_config.record_options.webcam_width},
|
||||||
|
{"replay.record_options.webcam_height", &config.replay_config.record_options.webcam_height},
|
||||||
{"replay.record_options.show_notifications", &config.replay_config.record_options.show_notifications},
|
{"replay.record_options.show_notifications", &config.replay_config.record_options.show_notifications},
|
||||||
{"replay.record_options.use_led_indicator", &config.replay_config.record_options.use_led_indicator},
|
{"replay.record_options.use_led_indicator", &config.replay_config.record_options.use_led_indicator},
|
||||||
{"replay.turn_on_replay_automatically_mode", &config.replay_config.turn_on_replay_automatically_mode},
|
{"replay.turn_on_replay_automatically_mode", &config.replay_config.turn_on_replay_automatically_mode},
|
||||||
|
|||||||
@@ -288,6 +288,56 @@ namespace gsr {
|
|||||||
return application_audio;
|
return application_audio;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct KeyValue3 {
|
||||||
|
std::string_view value1;
|
||||||
|
std::string_view value2;
|
||||||
|
std::string_view value3;
|
||||||
|
};
|
||||||
|
|
||||||
|
static std::optional<KeyValue3> parse_3(std::string_view line) {
|
||||||
|
const size_t space_index1 = line.find('|');
|
||||||
|
if(space_index1 == std::string_view::npos)
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
const size_t space_index2 = line.find('|', space_index1 + 1);
|
||||||
|
if(space_index2 == std::string_view::npos)
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
return KeyValue3{
|
||||||
|
line.substr(0, space_index1),
|
||||||
|
line.substr(space_index1 + 1, space_index2 - (space_index1 + 1)),
|
||||||
|
line.substr(space_index2 + 1),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static SupportedCameraPixelFormats parse_supported_camera_pixel_formats(std::string_view line) {
|
||||||
|
SupportedCameraPixelFormats result;
|
||||||
|
string_split_char(line, ',', [&](std::string_view column) {
|
||||||
|
if(column == "yuyv")
|
||||||
|
result.yuyv = true;
|
||||||
|
else if(column == "mjpeg")
|
||||||
|
result.mjpeg = true;
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::optional<GsrCamera> capture_option_line_to_camera(std::string_view line) {
|
||||||
|
std::optional<GsrCamera> camera;
|
||||||
|
const std::optional<KeyValue3> key_value3 = parse_3(line);
|
||||||
|
if(!key_value3)
|
||||||
|
return camera;
|
||||||
|
|
||||||
|
mgl::vec2i size;
|
||||||
|
char value_buffer[256];
|
||||||
|
snprintf(value_buffer, sizeof(value_buffer), "%.*s", (int)key_value3->value2.size(), key_value3->value2.data());
|
||||||
|
if(sscanf(value_buffer, "%dx%d", &size.x, &size.y) != 2)
|
||||||
|
return camera;
|
||||||
|
|
||||||
|
camera = GsrCamera{std::string(key_value3->value1), size, parse_supported_camera_pixel_formats(key_value3->value3)};
|
||||||
|
return camera;
|
||||||
|
}
|
||||||
|
|
||||||
static std::optional<GsrMonitor> capture_option_line_to_monitor(std::string_view line) {
|
static std::optional<GsrMonitor> capture_option_line_to_monitor(std::string_view line) {
|
||||||
std::optional<GsrMonitor> monitor;
|
std::optional<GsrMonitor> monitor;
|
||||||
const std::optional<KeyValue> key_value = parse_key_value(line);
|
const std::optional<KeyValue> key_value = parse_key_value(line);
|
||||||
@@ -305,15 +355,19 @@ namespace gsr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void parse_capture_options_line(SupportedCaptureOptions &capture_options, std::string_view line) {
|
static void parse_capture_options_line(SupportedCaptureOptions &capture_options, std::string_view line) {
|
||||||
if(line == "window")
|
if(line == "window") {
|
||||||
capture_options.window = true;
|
capture_options.window = true;
|
||||||
else if(line == "region")
|
} else if(line == "region") {
|
||||||
capture_options.region = true;
|
capture_options.region = true;
|
||||||
else if(line == "focused")
|
} else if(line == "focused") {
|
||||||
capture_options.focused = true;
|
capture_options.focused = true;
|
||||||
else if(line == "portal")
|
} else if(line == "portal") {
|
||||||
capture_options.portal = true;
|
capture_options.portal = true;
|
||||||
else {
|
} else if(!line.empty() && line[0] == '/') {
|
||||||
|
std::optional<GsrCamera> camera = capture_option_line_to_camera(line);
|
||||||
|
if(camera)
|
||||||
|
capture_options.cameras.push_back(std::move(camera.value()));
|
||||||
|
} else {
|
||||||
std::optional<GsrMonitor> monitor = capture_option_line_to_monitor(line);
|
std::optional<GsrMonitor> monitor = capture_option_line_to_monitor(line);
|
||||||
if(monitor)
|
if(monitor)
|
||||||
capture_options.monitors.push_back(std::move(monitor.value()));
|
capture_options.monitors.push_back(std::move(monitor.value()));
|
||||||
@@ -348,4 +402,24 @@ namespace gsr {
|
|||||||
|
|
||||||
return capture_options;
|
return capture_options;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<GsrCamera> get_v4l2_devices() {
|
||||||
|
std::vector<GsrCamera> cameras;
|
||||||
|
|
||||||
|
std::string stdout_str;
|
||||||
|
const char *args[] = { "gpu-screen-recorder", "--list-v4l2-devices", nullptr };
|
||||||
|
if(exec_program_get_stdout(args, stdout_str) != 0) {
|
||||||
|
fprintf(stderr, "error: 'gpu-screen-recorder --list-v4l2-devices' failed\n");
|
||||||
|
return cameras;
|
||||||
|
}
|
||||||
|
|
||||||
|
string_split_char(stdout_str, '\n', [&](std::string_view line) {
|
||||||
|
std::optional<GsrCamera> camera = capture_option_line_to_camera(line);
|
||||||
|
if(camera)
|
||||||
|
cameras.push_back(std::move(camera.value()));
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
return cameras;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2220,6 +2220,18 @@ namespace gsr {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool is_webcam_available_to_capture(const RecordOptions &record_options) {
|
||||||
|
if(record_options.webcam_source.empty())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
const auto cameras = get_v4l2_devices();
|
||||||
|
for(const GsrCamera &camera : cameras) {
|
||||||
|
if(camera.path == record_options.webcam_source)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
void Overlay::replay_status_update_status() {
|
void Overlay::replay_status_update_status() {
|
||||||
if(replay_status_update_clock.get_elapsed_time_seconds() < replay_status_update_check_timeout_seconds)
|
if(replay_status_update_clock.get_elapsed_time_seconds() < replay_status_update_check_timeout_seconds)
|
||||||
return;
|
return;
|
||||||
@@ -2245,7 +2257,7 @@ namespace gsr {
|
|||||||
focused_window_is_fullscreen = focused_window != 0 && window_is_fullscreen(display, focused_window);
|
focused_window_is_fullscreen = focused_window != 0 && window_is_fullscreen(display, focused_window);
|
||||||
if(focused_window_is_fullscreen != prev_focused_window_is_fullscreen) {
|
if(focused_window_is_fullscreen != prev_focused_window_is_fullscreen) {
|
||||||
if(recording_status == RecordingStatus::NONE && focused_window_is_fullscreen) {
|
if(recording_status == RecordingStatus::NONE && focused_window_is_fullscreen) {
|
||||||
if(are_all_audio_tracks_available_to_capture(config.replay_config.record_options.audio_tracks_list))
|
if(are_all_audio_tracks_available_to_capture(config.replay_config.record_options.audio_tracks_list) && is_webcam_available_to_capture(config.replay_config.record_options))
|
||||||
on_press_start_replay(false, false);
|
on_press_start_replay(false, false);
|
||||||
} else if(recording_status == RecordingStatus::REPLAY && !focused_window_is_fullscreen) {
|
} else if(recording_status == RecordingStatus::REPLAY && !focused_window_is_fullscreen) {
|
||||||
on_press_start_replay(true, false);
|
on_press_start_replay(true, false);
|
||||||
@@ -2262,7 +2274,7 @@ namespace gsr {
|
|||||||
power_supply_connected = power_supply_online_filepath.empty() || power_supply_is_connected(power_supply_online_filepath.c_str());
|
power_supply_connected = power_supply_online_filepath.empty() || power_supply_is_connected(power_supply_online_filepath.c_str());
|
||||||
if(power_supply_connected != prev_power_supply_status) {
|
if(power_supply_connected != prev_power_supply_status) {
|
||||||
if(recording_status == RecordingStatus::NONE && power_supply_connected) {
|
if(recording_status == RecordingStatus::NONE && power_supply_connected) {
|
||||||
if(are_all_audio_tracks_available_to_capture(config.replay_config.record_options.audio_tracks_list))
|
if(are_all_audio_tracks_available_to_capture(config.replay_config.record_options.audio_tracks_list) && is_webcam_available_to_capture(config.replay_config.record_options))
|
||||||
on_press_start_replay(false, false);
|
on_press_start_replay(false, false);
|
||||||
} else if(recording_status == RecordingStatus::REPLAY && !power_supply_connected) {
|
} else if(recording_status == RecordingStatus::REPLAY && !power_supply_connected) {
|
||||||
on_press_start_replay(false, false);
|
on_press_start_replay(false, false);
|
||||||
@@ -2274,7 +2286,7 @@ namespace gsr {
|
|||||||
if(replay_startup_mode != ReplayStartupMode::TURN_ON_AT_SYSTEM_STARTUP || recording_status != RecordingStatus::NONE || !try_replay_startup)
|
if(replay_startup_mode != ReplayStartupMode::TURN_ON_AT_SYSTEM_STARTUP || recording_status != RecordingStatus::NONE || !try_replay_startup)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if(are_all_audio_tracks_available_to_capture(config.replay_config.record_options.audio_tracks_list))
|
if(are_all_audio_tracks_available_to_capture(config.replay_config.record_options.audio_tracks_list) && is_webcam_available_to_capture(config.replay_config.record_options))
|
||||||
on_press_start_replay(true, false);
|
on_press_start_replay(true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2671,6 +2683,66 @@ namespace gsr {
|
|||||||
return framerate_mode;
|
return framerate_mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct CameraAlignment {
|
||||||
|
std::string halign;
|
||||||
|
std::string valign;
|
||||||
|
mgl::vec2i pos;
|
||||||
|
};
|
||||||
|
|
||||||
|
static CameraAlignment position_to_alignment(mgl::vec2i pos, mgl::vec2i size) {
|
||||||
|
const mgl::vec2i pos_overflow = mgl::vec2i(100, 100) - (pos + size);
|
||||||
|
if(pos_overflow.x < 0)
|
||||||
|
pos.x += pos_overflow.x;
|
||||||
|
if(pos_overflow.y < 0)
|
||||||
|
pos.y += pos_overflow.y;
|
||||||
|
|
||||||
|
if(pos.x < 0)
|
||||||
|
pos.x = 0;
|
||||||
|
if(pos.y < 0)
|
||||||
|
pos.y = 0;
|
||||||
|
|
||||||
|
CameraAlignment camera_alignment;
|
||||||
|
const mgl::vec2i center = pos + size/2;
|
||||||
|
|
||||||
|
if(center.x < 50) {
|
||||||
|
camera_alignment.halign = "start";
|
||||||
|
camera_alignment.pos.x = pos.x;
|
||||||
|
} else {
|
||||||
|
camera_alignment.halign = "end";
|
||||||
|
camera_alignment.pos.x = -(100 - (pos.x + size.x));
|
||||||
|
}
|
||||||
|
|
||||||
|
if(center.y < 50) {
|
||||||
|
camera_alignment.valign = "start";
|
||||||
|
camera_alignment.pos.y = pos.y;
|
||||||
|
} else {
|
||||||
|
camera_alignment.valign = "end";
|
||||||
|
camera_alignment.pos.y = -(100 - (pos.y + size.y));
|
||||||
|
}
|
||||||
|
|
||||||
|
return camera_alignment;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string compose_capture_source_arg(const std::string &capture_target, const RecordOptions &record_options) {
|
||||||
|
std::string capture_source_arg = capture_target;
|
||||||
|
if(!record_options.webcam_source.empty()) {
|
||||||
|
const mgl::vec2i webcam_size(record_options.webcam_width, record_options.webcam_height);
|
||||||
|
const CameraAlignment camera_alignment = position_to_alignment(mgl::vec2i(record_options.webcam_x, record_options.webcam_y), webcam_size);
|
||||||
|
|
||||||
|
capture_source_arg += "|" + record_options.webcam_source;
|
||||||
|
capture_source_arg += ";halign=" + camera_alignment.halign;
|
||||||
|
capture_source_arg += ";valign=" + camera_alignment.valign;
|
||||||
|
capture_source_arg += ";x=" + std::to_string(camera_alignment.pos.x) + "%";
|
||||||
|
capture_source_arg += ";y=" + std::to_string(camera_alignment.pos.y) + "%";
|
||||||
|
capture_source_arg += ";width=" + std::to_string(webcam_size.x) + "%";
|
||||||
|
capture_source_arg += ";height=" + std::to_string(webcam_size.y) + "%";
|
||||||
|
capture_source_arg += ";pixfmt=" + record_options.webcam_video_format;
|
||||||
|
if(record_options.webcam_flip_horizontally)
|
||||||
|
capture_source_arg += ";hflip=true";
|
||||||
|
}
|
||||||
|
return capture_source_arg;
|
||||||
|
}
|
||||||
|
|
||||||
bool Overlay::on_press_start_replay(bool disable_notification, bool finished_selection) {
|
bool Overlay::on_press_start_replay(bool disable_notification, bool finished_selection) {
|
||||||
if(region_selector.is_started() || window_selector.is_started())
|
if(region_selector.is_started() || window_selector.is_started())
|
||||||
return false;
|
return false;
|
||||||
@@ -2760,8 +2832,10 @@ namespace gsr {
|
|||||||
if(config.replay_config.record_options.record_area_option != "focused" && config.replay_config.record_options.change_video_resolution)
|
if(config.replay_config.record_options.record_area_option != "focused" && config.replay_config.record_options.change_video_resolution)
|
||||||
snprintf(size, sizeof(size), "%dx%d", (int)config.replay_config.record_options.video_width, (int)config.replay_config.record_options.video_height);
|
snprintf(size, sizeof(size), "%dx%d", (int)config.replay_config.record_options.video_width, (int)config.replay_config.record_options.video_height);
|
||||||
|
|
||||||
|
const std::string capture_source_arg = compose_capture_source_arg(recording_capture_target, config.replay_config.record_options);
|
||||||
|
|
||||||
std::vector<const char*> args = {
|
std::vector<const char*> args = {
|
||||||
"gpu-screen-recorder", "-w", recording_capture_target.c_str(),
|
"gpu-screen-recorder", "-w", capture_source_arg.c_str(),
|
||||||
"-c", container,
|
"-c", container,
|
||||||
"-ac", config.replay_config.record_options.audio_codec.c_str(),
|
"-ac", config.replay_config.record_options.audio_codec.c_str(),
|
||||||
"-cursor", config.replay_config.record_options.record_cursor ? "yes" : "no",
|
"-cursor", config.replay_config.record_options.record_cursor ? "yes" : "no",
|
||||||
@@ -2982,8 +3056,10 @@ namespace gsr {
|
|||||||
if(config.record_config.record_options.record_area_option != "focused" && config.record_config.record_options.change_video_resolution)
|
if(config.record_config.record_options.record_area_option != "focused" && config.record_config.record_options.change_video_resolution)
|
||||||
snprintf(size, sizeof(size), "%dx%d", (int)config.record_config.record_options.video_width, (int)config.record_config.record_options.video_height);
|
snprintf(size, sizeof(size), "%dx%d", (int)config.record_config.record_options.video_width, (int)config.record_config.record_options.video_height);
|
||||||
|
|
||||||
|
const std::string capture_source_arg = compose_capture_source_arg(recording_capture_target, config.record_config.record_options);
|
||||||
|
|
||||||
std::vector<const char*> args = {
|
std::vector<const char*> args = {
|
||||||
"gpu-screen-recorder", "-w", recording_capture_target.c_str(),
|
"gpu-screen-recorder", "-w", capture_source_arg.c_str(),
|
||||||
"-c", container,
|
"-c", container,
|
||||||
"-ac", config.record_config.record_options.audio_codec.c_str(),
|
"-ac", config.record_config.record_options.audio_codec.c_str(),
|
||||||
"-cursor", config.record_config.record_options.record_cursor ? "yes" : "no",
|
"-cursor", config.record_config.record_options.record_cursor ? "yes" : "no",
|
||||||
@@ -3170,8 +3246,10 @@ namespace gsr {
|
|||||||
if(config.streaming_config.record_options.record_area_option != "focused" && config.streaming_config.record_options.change_video_resolution)
|
if(config.streaming_config.record_options.record_area_option != "focused" && config.streaming_config.record_options.change_video_resolution)
|
||||||
snprintf(size, sizeof(size), "%dx%d", (int)config.streaming_config.record_options.video_width, (int)config.streaming_config.record_options.video_height);
|
snprintf(size, sizeof(size), "%dx%d", (int)config.streaming_config.record_options.video_width, (int)config.streaming_config.record_options.video_height);
|
||||||
|
|
||||||
|
const std::string capture_source_arg = compose_capture_source_arg(recording_capture_target, config.streaming_config.record_options);
|
||||||
|
|
||||||
std::vector<const char*> args = {
|
std::vector<const char*> args = {
|
||||||
"gpu-screen-recorder", "-w", recording_capture_target.c_str(),
|
"gpu-screen-recorder", "-w", capture_source_arg.c_str(),
|
||||||
"-c", container,
|
"-c", container,
|
||||||
"-ac", config.streaming_config.record_options.audio_codec.c_str(),
|
"-ac", config.streaming_config.record_options.audio_codec.c_str(),
|
||||||
"-cursor", config.streaming_config.record_options.record_cursor ? "yes" : "no",
|
"-cursor", config.streaming_config.record_options.record_cursor ? "yes" : "no",
|
||||||
@@ -3301,7 +3379,7 @@ namespace gsr {
|
|||||||
args.push_back("yes");
|
args.push_back("yes");
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string hotkey_window_capture_portal_session_token_filepath = get_config_dir() + "/gpu-screen-recorder/gsr-ui-window-capture-token";
|
const std::string hotkey_window_capture_portal_session_token_filepath = get_config_dir() + "/gsr-ui-window-capture-token";
|
||||||
if(record_area_option == "portal") {
|
if(record_area_option == "portal") {
|
||||||
hide_ui = true;
|
hide_ui = true;
|
||||||
if(force_type == ScreenshotForceType::WINDOW) {
|
if(force_type == ScreenshotForceType::WINDOW) {
|
||||||
|
|||||||
@@ -48,6 +48,9 @@ namespace gsr {
|
|||||||
if(!theme->body_font.load_from_file(theme->body_font_file, std::max(13.0f, window_size.y * 0.015f)))
|
if(!theme->body_font.load_from_file(theme->body_font_file, std::max(13.0f, window_size.y * 0.015f)))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
if(!theme->camera_setup_font.load_from_file(theme->body_font_file, 24))
|
||||||
|
return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -90,6 +90,13 @@ namespace gsr {
|
|||||||
dirty = true;
|
dirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ComboBox::clear_items() {
|
||||||
|
items.clear();
|
||||||
|
selected_item = 0;
|
||||||
|
show_dropdown = false;
|
||||||
|
dirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
void ComboBox::set_selected_item(const std::string &id, bool trigger_event, bool trigger_event_even_if_selection_not_changed) {
|
void ComboBox::set_selected_item(const std::string &id, bool trigger_event, bool trigger_event_even_if_selection_not_changed) {
|
||||||
for(size_t i = 0; i < items.size(); ++i) {
|
for(size_t i = 0; i < items.size(); ++i) {
|
||||||
auto &item = items[i];
|
auto &item = items[i];
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#include "../../include/gui/CustomRendererWidget.hpp"
|
#include "../../include/gui/CustomRendererWidget.hpp"
|
||||||
|
#include "../../include/gui/Utils.hpp"
|
||||||
|
|
||||||
#include <mglpp/window/Window.hpp>
|
#include <mglpp/window/Window.hpp>
|
||||||
|
|
||||||
@@ -17,11 +18,14 @@ namespace gsr {
|
|||||||
|
|
||||||
const mgl::vec2f draw_pos = position + offset;
|
const mgl::vec2f draw_pos = position + offset;
|
||||||
|
|
||||||
const mgl::Scissor prev_scissor = window.get_scissor();
|
const mgl::Scissor parent_scissor = window.get_scissor();
|
||||||
window.set_scissor({draw_pos.to_vec2i(), size.to_vec2i()});
|
const mgl::Scissor scissor = scissor_get_sub_area(parent_scissor, {draw_pos.to_vec2i(), size.to_vec2i()});
|
||||||
|
window.set_scissor(scissor);
|
||||||
|
|
||||||
if(draw_handler)
|
if(draw_handler)
|
||||||
draw_handler(window, draw_pos, size);
|
draw_handler(window, draw_pos, size);
|
||||||
window.set_scissor(prev_scissor);
|
|
||||||
|
window.set_scissor(parent_scissor);
|
||||||
}
|
}
|
||||||
|
|
||||||
mgl::vec2f CustomRendererWidget::get_size() {
|
mgl::vec2f CustomRendererWidget::get_size() {
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ namespace gsr {
|
|||||||
|
|
||||||
std::unique_ptr<Widget> ScreenshotSettingsPage::create_record_area() {
|
std::unique_ptr<Widget> ScreenshotSettingsPage::create_record_area() {
|
||||||
auto record_area_list = std::make_unique<List>(List::Orientation::VERTICAL);
|
auto record_area_list = std::make_unique<List>(List::Orientation::VERTICAL);
|
||||||
record_area_list->add_widget(std::make_unique<Label>(&get_theme().body_font, "Capture target:", get_color_theme().text_color));
|
record_area_list->add_widget(std::make_unique<Label>(&get_theme().body_font, "Capture source:", get_color_theme().text_color));
|
||||||
record_area_list->add_widget(create_record_area_box());
|
record_area_list->add_widget(create_record_area_box());
|
||||||
return record_area_list;
|
return record_area_list;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,7 +50,9 @@ namespace gsr {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(event.type == mgl::Event::MouseButtonPressed || event.type == mgl::Event::MouseButtonReleased) {
|
// Pass release to children even if outside area, because we want to be able to release mouse when moved outside,
|
||||||
|
// for example in Entry when selecting text
|
||||||
|
if(event.type == mgl::Event::MouseButtonPressed/* || event.type == mgl::Event::MouseButtonReleased*/) {
|
||||||
if(!mgl::IntRect(scissor_pos, scissor_size).contains({event.mouse_button.x, event.mouse_button.y}))
|
if(!mgl::IntRect(scissor_pos, scissor_size).contains({event.mouse_button.x, event.mouse_button.y}))
|
||||||
return true;
|
return true;
|
||||||
} else if(event.type == mgl::Event::MouseMoved) {
|
} else if(event.type == mgl::Event::MouseMoved) {
|
||||||
|
|||||||
@@ -4,11 +4,16 @@
|
|||||||
#include "../../include/gui/PageStack.hpp"
|
#include "../../include/gui/PageStack.hpp"
|
||||||
#include "../../include/gui/FileChooser.hpp"
|
#include "../../include/gui/FileChooser.hpp"
|
||||||
#include "../../include/gui/Subsection.hpp"
|
#include "../../include/gui/Subsection.hpp"
|
||||||
#include "../../include/gui/Image.hpp"
|
#include "../../include/gui/CustomRendererWidget.hpp"
|
||||||
|
#include "../../include/gui/Utils.hpp"
|
||||||
#include "../../include/Theme.hpp"
|
#include "../../include/Theme.hpp"
|
||||||
#include "../../include/GsrInfo.hpp"
|
#include "../../include/GsrInfo.hpp"
|
||||||
#include "../../include/Utils.hpp"
|
#include "../../include/Utils.hpp"
|
||||||
|
#include "mglpp/window/Window.hpp"
|
||||||
|
#include "mglpp/window/Event.hpp"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cmath>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
namespace gsr {
|
namespace gsr {
|
||||||
@@ -87,7 +92,7 @@ namespace gsr {
|
|||||||
|
|
||||||
std::unique_ptr<Widget> SettingsPage::create_record_area() {
|
std::unique_ptr<Widget> SettingsPage::create_record_area() {
|
||||||
auto record_area_list = std::make_unique<List>(List::Orientation::VERTICAL);
|
auto record_area_list = std::make_unique<List>(List::Orientation::VERTICAL);
|
||||||
record_area_list->add_widget(std::make_unique<Label>(&get_theme().body_font, "Capture target:", get_color_theme().text_color));
|
record_area_list->add_widget(std::make_unique<Label>(&get_theme().body_font, "Capture source:", get_color_theme().text_color));
|
||||||
record_area_list->add_widget(create_record_area_box());
|
record_area_list->add_widget(create_record_area_box());
|
||||||
return record_area_list;
|
return record_area_list;
|
||||||
}
|
}
|
||||||
@@ -187,6 +192,214 @@ namespace gsr {
|
|||||||
return std::make_unique<Subsection>("Capture", std::move(ll), mgl::vec2f(settings_scrollable_page_ptr->get_inner_size().x, 0.0f));
|
return std::make_unique<Subsection>("Capture", std::move(ll), mgl::vec2f(settings_scrollable_page_ptr->get_inner_size().x, 0.0f));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<List> SettingsPage::create_webcam_sources() {
|
||||||
|
auto ll = std::make_unique<List>(List::Orientation::VERTICAL);
|
||||||
|
ll->add_widget(std::make_unique<Label>(&get_theme().body_font, "Webcam source:", get_color_theme().text_color));
|
||||||
|
|
||||||
|
auto combobox = std::make_unique<ComboBox>(&get_theme().body_font);
|
||||||
|
combobox->add_item("None", "");
|
||||||
|
for(const GsrCamera &camera : capture_options.cameras) {
|
||||||
|
combobox->add_item(camera.path, camera.path);
|
||||||
|
}
|
||||||
|
webcam_sources_box_ptr = combobox.get();
|
||||||
|
|
||||||
|
webcam_sources_box_ptr->on_selection_changed = [this](const std::string&, const std::string &id) {
|
||||||
|
selected_camera = std::nullopt;
|
||||||
|
webcam_video_format_box_ptr->clear_items();
|
||||||
|
if(id == "") {
|
||||||
|
webcam_body_list_ptr->set_visible(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto it = std::find_if(capture_options.cameras.begin(), capture_options.cameras.end(), [&](const GsrCamera &camera) {
|
||||||
|
return camera.path == id;
|
||||||
|
});
|
||||||
|
if(it == capture_options.cameras.end())
|
||||||
|
return;
|
||||||
|
|
||||||
|
webcam_body_list_ptr->set_visible(true);
|
||||||
|
webcam_video_format_box_ptr->add_item("Auto (recommended)", "auto");
|
||||||
|
|
||||||
|
if(it->supported_pixel_formats.yuyv)
|
||||||
|
webcam_video_format_box_ptr->add_item("YUYV", "yuyv");
|
||||||
|
|
||||||
|
if(it->supported_pixel_formats.mjpeg)
|
||||||
|
webcam_video_format_box_ptr->add_item("Motion-JPEG", "mjpeg");
|
||||||
|
|
||||||
|
webcam_video_format_box_ptr->set_selected_item(get_current_record_options().webcam_video_format);
|
||||||
|
selected_camera = *it;
|
||||||
|
};
|
||||||
|
|
||||||
|
ll->add_widget(std::move(combobox));
|
||||||
|
return ll;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<List> SettingsPage::create_webcam_video_format() {
|
||||||
|
auto ll = std::make_unique<List>(List::Orientation::VERTICAL);
|
||||||
|
ll->add_widget(std::make_unique<Label>(&get_theme().body_font, "Video format:", get_color_theme().text_color));
|
||||||
|
|
||||||
|
auto combobox = std::make_unique<ComboBox>(&get_theme().body_font);
|
||||||
|
webcam_video_format_box_ptr = combobox.get();
|
||||||
|
|
||||||
|
webcam_video_format_box_ptr->on_selection_changed = [this](const std::string&, const std::string &id) {
|
||||||
|
get_current_record_options().webcam_video_format = id;
|
||||||
|
};
|
||||||
|
|
||||||
|
ll->add_widget(std::move(combobox));
|
||||||
|
return ll;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<Widget> SettingsPage::create_webcam_section() {
|
||||||
|
auto ll = std::make_unique<List>(List::Orientation::VERTICAL);
|
||||||
|
|
||||||
|
ll->add_widget(create_webcam_sources());
|
||||||
|
|
||||||
|
auto body_list = std::make_unique<List>(List::Orientation::VERTICAL);
|
||||||
|
body_list->set_visible(false);
|
||||||
|
webcam_body_list_ptr = body_list.get();
|
||||||
|
{
|
||||||
|
const float camera_screen_width = std::min(400.0f, (float)settings_scrollable_page_ptr->get_inner_size().x * 0.90f);
|
||||||
|
camera_screen_size = mgl::vec2f(camera_screen_width, camera_screen_width * 0.5625);
|
||||||
|
|
||||||
|
const float screen_border = 2.0f;
|
||||||
|
const mgl::vec2f screen_border_size(screen_border, screen_border);
|
||||||
|
screen_inner_size = mgl::vec2f(camera_screen_size - screen_border_size*2.0f);
|
||||||
|
|
||||||
|
const mgl::vec2f bounding_box_size(30.0f, 30.0f);
|
||||||
|
|
||||||
|
auto camera_location_widget = std::make_unique<CustomRendererWidget>(camera_screen_size);
|
||||||
|
camera_location_widget->draw_handler = [this, screen_border_size, screen_border](mgl::Window &window, mgl::vec2f pos, mgl::vec2f size) {
|
||||||
|
if(!selected_camera.has_value())
|
||||||
|
return;
|
||||||
|
|
||||||
|
pos = pos.floor();
|
||||||
|
size = size.floor();
|
||||||
|
const mgl::vec2i mouse_pos = window.get_mouse_position();
|
||||||
|
const mgl::vec2f webcam_box_min_size = clamp_keep_aspect_ratio(selected_camera->size.to_vec2f(), screen_inner_size * 0.2f);
|
||||||
|
|
||||||
|
if(moving_webcam_box) {
|
||||||
|
webcam_box_pos = mouse_pos.to_vec2f() - screen_border_size - webcam_box_grab_offset - pos;
|
||||||
|
} else if(webcam_resize_corner == WebcamBoxResizeCorner::BOTTOM_RIGHT) {
|
||||||
|
const mgl::vec2f mouse_diff = mouse_pos.to_vec2f() - webcam_resize_start_pos;
|
||||||
|
webcam_box_size = webcam_box_size_resize_start + mouse_diff;
|
||||||
|
}
|
||||||
|
|
||||||
|
webcam_box_size = clamp_keep_aspect_ratio(selected_camera->size.to_vec2f(), webcam_box_size);
|
||||||
|
|
||||||
|
if(webcam_box_pos.x < 0.0f)
|
||||||
|
webcam_box_pos.x = 0.0f;
|
||||||
|
else if(webcam_box_pos.x + webcam_box_size.x > screen_inner_size.x)
|
||||||
|
webcam_box_pos.x = screen_inner_size.x - webcam_box_size.x;
|
||||||
|
|
||||||
|
if(webcam_box_pos.y < 0.0f)
|
||||||
|
webcam_box_pos.y = 0.0f;
|
||||||
|
else if(webcam_box_pos.y + webcam_box_size.y > screen_inner_size.y)
|
||||||
|
webcam_box_pos.y = screen_inner_size.y - webcam_box_size.y;
|
||||||
|
|
||||||
|
if(webcam_box_size.x < webcam_box_min_size.x)
|
||||||
|
webcam_box_size.x = webcam_box_min_size.x;
|
||||||
|
else if(webcam_box_pos.x + webcam_box_size.x > screen_inner_size.x)
|
||||||
|
webcam_box_size.x = screen_inner_size.x - webcam_box_pos.x;
|
||||||
|
|
||||||
|
//webcam_box_size = clamp_keep_aspect_ratio(selected_camera->size.to_vec2f(), webcam_box_size);
|
||||||
|
|
||||||
|
if(webcam_box_size.y < webcam_box_min_size.y)
|
||||||
|
webcam_box_size.y = webcam_box_min_size.y;
|
||||||
|
else if(webcam_box_pos.y + webcam_box_size.y > screen_inner_size.y)
|
||||||
|
webcam_box_size.y = screen_inner_size.y - webcam_box_pos.y;
|
||||||
|
|
||||||
|
webcam_box_size = clamp_keep_aspect_ratio(selected_camera->size.to_vec2f(), webcam_box_size);
|
||||||
|
|
||||||
|
{
|
||||||
|
draw_rectangle_outline(window, pos, size, mgl::Color(255, 0, 0, 255), screen_border);
|
||||||
|
mgl::Text screen_text("Screen", get_theme().camera_setup_font);
|
||||||
|
screen_text.set_position((pos + size * 0.5f - screen_text.get_bounds().size * 0.5f).floor());
|
||||||
|
window.draw(screen_text);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
webcam_box_drawn_size = clamp_keep_aspect_ratio(selected_camera->size.to_vec2f(), webcam_box_size);
|
||||||
|
webcam_box_drawn_pos = (pos + screen_border_size + webcam_box_pos).floor();
|
||||||
|
|
||||||
|
draw_rectangle_outline(window, webcam_box_drawn_pos, webcam_box_drawn_size, mgl::Color(0, 255, 0, 255), screen_border);
|
||||||
|
|
||||||
|
// mgl::Rectangle resize_area(webcam_box_drawn_pos + webcam_box_drawn_size - bounding_box_size*0.5f - screen_border_size*0.5f, bounding_box_size);
|
||||||
|
// resize_area.set_color(mgl::Color(0, 0, 255, 255));
|
||||||
|
// window.draw(resize_area);
|
||||||
|
|
||||||
|
mgl::Text webcam_text("Webcam", get_theme().camera_setup_font);
|
||||||
|
webcam_text.set_position((webcam_box_drawn_pos + webcam_box_drawn_size * 0.5f - webcam_text.get_bounds().size * 0.5f).floor());
|
||||||
|
window.draw(webcam_text);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
camera_location_widget->event_handler = [this, screen_border_size, bounding_box_size](mgl::Event &event, mgl::Window&, mgl::vec2f, mgl::vec2f) {
|
||||||
|
switch(event.type) {
|
||||||
|
case mgl::Event::MouseButtonPressed: {
|
||||||
|
if(event.mouse_button.button == mgl::Mouse::Left && webcam_resize_corner == WebcamBoxResizeCorner::NONE) {
|
||||||
|
const mgl::vec2f mouse_button_pos(event.mouse_button.x, event.mouse_button.y);
|
||||||
|
if(mgl::FloatRect(webcam_box_drawn_pos, webcam_box_drawn_size).contains(mouse_button_pos)) {
|
||||||
|
moving_webcam_box = true;
|
||||||
|
webcam_box_grab_offset = mouse_button_pos - webcam_box_drawn_pos;
|
||||||
|
} else {
|
||||||
|
moving_webcam_box = false;
|
||||||
|
}
|
||||||
|
} else if(event.mouse_button.button == mgl::Mouse::Right && !moving_webcam_box) {
|
||||||
|
const mgl::vec2f mouse_button_pos(event.mouse_button.x, event.mouse_button.y);
|
||||||
|
webcam_resize_start_pos = mouse_button_pos;
|
||||||
|
webcam_box_pos_resize_start = webcam_box_pos;
|
||||||
|
webcam_box_size_resize_start = webcam_box_size;
|
||||||
|
webcam_box_grab_offset = mouse_button_pos - webcam_box_drawn_pos;
|
||||||
|
|
||||||
|
/*if(mgl::FloatRect(webcam_box_drawn_pos - bounding_box_size*0.5f, bounding_box_size).contains(mouse_button_pos)) {
|
||||||
|
webcam_resize_corner = WebcamBoxResizeCorner::TOP_LEFT;
|
||||||
|
fprintf(stderr, "top left\n");
|
||||||
|
} else if(mgl::FloatRect(webcam_box_drawn_pos + mgl::vec2f(webcam_box_drawn_size.x, 0.0f) - bounding_box_size*0.5f, bounding_box_size).contains(mouse_button_pos)) {
|
||||||
|
webcam_resize_corner = WebcamBoxResizeCorner::TOP_RIGHT;
|
||||||
|
fprintf(stderr, "top right\n");
|
||||||
|
} else if(mgl::FloatRect(webcam_box_drawn_pos + mgl::vec2f(0.0f, webcam_box_drawn_size.y) - bounding_box_size*0.5f, bounding_box_size).contains(mouse_button_pos)) {
|
||||||
|
webcam_resize_corner = WebcamBoxResizeCorner::BOTTOM_LEFT;
|
||||||
|
fprintf(stderr, "bottom left\n");
|
||||||
|
} else */if(mgl::FloatRect(webcam_box_drawn_pos + webcam_box_drawn_size - bounding_box_size*0.5f - screen_border_size*0.5f, bounding_box_size).contains(mouse_button_pos)) {
|
||||||
|
webcam_resize_corner = WebcamBoxResizeCorner::BOTTOM_RIGHT;
|
||||||
|
} else {
|
||||||
|
webcam_resize_corner = WebcamBoxResizeCorner::NONE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case mgl::Event::MouseButtonReleased: {
|
||||||
|
if(event.mouse_button.button == mgl::Mouse::Left && webcam_resize_corner == WebcamBoxResizeCorner::NONE) {
|
||||||
|
moving_webcam_box = false;
|
||||||
|
} else if(event.mouse_button.button == mgl::Mouse::Right && !moving_webcam_box) {
|
||||||
|
webcam_resize_corner = WebcamBoxResizeCorner::NONE;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
body_list->add_widget(std::move(camera_location_widget));
|
||||||
|
}
|
||||||
|
|
||||||
|
body_list->add_widget(std::make_unique<Label>(&get_theme().body_font, "* Right click in the bottom right corner to resize the webcam", get_color_theme().text_color));
|
||||||
|
|
||||||
|
{
|
||||||
|
auto flip_camera_horizontally_checkbox = std::make_unique<CheckBox>(&get_theme().body_font, "Flip camera horizontally");
|
||||||
|
flip_camera_horizontally_checkbox_ptr = flip_camera_horizontally_checkbox.get();
|
||||||
|
body_list->add_widget(std::move(flip_camera_horizontally_checkbox));
|
||||||
|
}
|
||||||
|
|
||||||
|
body_list->add_widget(create_webcam_video_format());
|
||||||
|
ll->add_widget(std::move(body_list));
|
||||||
|
|
||||||
|
return std::make_unique<Subsection>("Webcam", std::move(ll), mgl::vec2f(settings_scrollable_page_ptr->get_inner_size().x, 0.0f));
|
||||||
|
}
|
||||||
|
|
||||||
static bool audio_device_is_output(const std::string &audio_device_id) {
|
static bool audio_device_is_output(const std::string &audio_device_id) {
|
||||||
return audio_device_id == "default_output" || ends_with(audio_device_id, ".monitor");
|
return audio_device_id == "default_output" || ends_with(audio_device_id, ".monitor");
|
||||||
}
|
}
|
||||||
@@ -618,6 +831,7 @@ namespace gsr {
|
|||||||
auto settings_list = std::make_unique<List>(List::Orientation::VERTICAL);
|
auto settings_list = std::make_unique<List>(List::Orientation::VERTICAL);
|
||||||
settings_list->set_spacing(0.018f);
|
settings_list->set_spacing(0.018f);
|
||||||
settings_list->add_widget(create_capture_target_section());
|
settings_list->add_widget(create_capture_target_section());
|
||||||
|
settings_list->add_widget(create_webcam_section());
|
||||||
settings_list->add_widget(create_audio_section());
|
settings_list->add_widget(create_audio_section());
|
||||||
settings_list->add_widget(create_video_section());
|
settings_list->add_widget(create_video_section());
|
||||||
settings_list_ptr = settings_list.get();
|
settings_list_ptr = settings_list.get();
|
||||||
@@ -830,6 +1044,16 @@ namespace gsr {
|
|||||||
settings_scrollable_page_ptr->reset_scroll();
|
settings_scrollable_page_ptr->reset_scroll();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RecordOptions& SettingsPage::get_current_record_options() {
|
||||||
|
switch(type) {
|
||||||
|
default:
|
||||||
|
assert(false);
|
||||||
|
case Type::REPLAY: return config.replay_config.record_options;
|
||||||
|
case Type::RECORD: return config.record_config.record_options;
|
||||||
|
case Type::STREAM: return config.streaming_config.record_options;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
std::unique_ptr<CheckBox> SettingsPage::create_led_indicator(const char *type) {
|
std::unique_ptr<CheckBox> SettingsPage::create_led_indicator(const char *type) {
|
||||||
char label_str[256];
|
char label_str[256];
|
||||||
snprintf(label_str, sizeof(label_str), "Show %s status with scroll lock led", type);
|
snprintf(label_str, sizeof(label_str), "Show %s status with scroll lock led", type);
|
||||||
@@ -1200,6 +1424,17 @@ namespace gsr {
|
|||||||
show_notification_checkbox_ptr->set_checked(record_options.show_notifications);
|
show_notification_checkbox_ptr->set_checked(record_options.show_notifications);
|
||||||
led_indicator_checkbox_ptr->set_checked(record_options.use_led_indicator);
|
led_indicator_checkbox_ptr->set_checked(record_options.use_led_indicator);
|
||||||
|
|
||||||
|
webcam_sources_box_ptr->set_selected_item(record_options.webcam_source);
|
||||||
|
flip_camera_horizontally_checkbox_ptr->set_checked(record_options.webcam_flip_horizontally);
|
||||||
|
webcam_video_format_box_ptr->set_selected_item(record_options.webcam_video_format);
|
||||||
|
webcam_box_pos.x = ((float)record_options.webcam_x / 100.0f) * screen_inner_size.x;
|
||||||
|
webcam_box_pos.y = ((float)record_options.webcam_y / 100.0f) * screen_inner_size.y;
|
||||||
|
webcam_box_size.x = ((float)record_options.webcam_width / 100.0f * screen_inner_size.x);
|
||||||
|
webcam_box_size.y = ((float)record_options.webcam_height / 100.0f * screen_inner_size.y);
|
||||||
|
|
||||||
|
if(selected_camera.has_value())
|
||||||
|
webcam_box_size = clamp_keep_aspect_ratio(selected_camera->size.to_vec2f(), webcam_box_size);
|
||||||
|
|
||||||
if(record_options.record_area_width == 0)
|
if(record_options.record_area_width == 0)
|
||||||
record_options.record_area_width = 1920;
|
record_options.record_area_width = 1920;
|
||||||
|
|
||||||
@@ -1332,6 +1567,17 @@ namespace gsr {
|
|||||||
record_options.show_notifications = show_notification_checkbox_ptr->is_checked();
|
record_options.show_notifications = show_notification_checkbox_ptr->is_checked();
|
||||||
record_options.use_led_indicator = led_indicator_checkbox_ptr->is_checked();
|
record_options.use_led_indicator = led_indicator_checkbox_ptr->is_checked();
|
||||||
|
|
||||||
|
if(selected_camera.has_value())
|
||||||
|
webcam_box_size = clamp_keep_aspect_ratio(selected_camera->size.to_vec2f(), webcam_box_size);
|
||||||
|
|
||||||
|
record_options.webcam_source = webcam_sources_box_ptr->get_selected_id();
|
||||||
|
record_options.webcam_flip_horizontally = flip_camera_horizontally_checkbox_ptr->is_checked();
|
||||||
|
record_options.webcam_video_format = webcam_video_format_box_ptr->get_selected_id();
|
||||||
|
record_options.webcam_x = std::round((webcam_box_pos.x / screen_inner_size.x) * 100.0f);
|
||||||
|
record_options.webcam_y = std::round((webcam_box_pos.y / screen_inner_size.y) * 100.0f);
|
||||||
|
record_options.webcam_width = std::round((webcam_box_size.x / screen_inner_size.x) * 100.0f);
|
||||||
|
record_options.webcam_height = std::round((webcam_box_size.y / screen_inner_size.y) * 100.0f);
|
||||||
|
|
||||||
if(record_options.record_area_width == 0)
|
if(record_options.record_area_width == 0)
|
||||||
record_options.record_area_width = 1920;
|
record_options.record_area_width = 1920;
|
||||||
|
|
||||||
|
|||||||
@@ -5,15 +5,15 @@
|
|||||||
namespace gsr {
|
namespace gsr {
|
||||||
static double frame_delta_seconds = 1.0;
|
static double frame_delta_seconds = 1.0;
|
||||||
|
|
||||||
static mgl::vec2i min_vec2i(mgl::vec2i a, mgl::vec2i b) {
|
mgl::vec2i min_vec2i(mgl::vec2i a, mgl::vec2i b) {
|
||||||
return { std::min(a.x, b.x), std::min(a.y, b.y) };
|
return { std::min(a.x, b.x), std::min(a.y, b.y) };
|
||||||
}
|
}
|
||||||
|
|
||||||
static mgl::vec2i max_vec2i(mgl::vec2i a, mgl::vec2i b) {
|
mgl::vec2i max_vec2i(mgl::vec2i a, mgl::vec2i b) {
|
||||||
return { std::max(a.x, b.x), std::max(a.y, b.y) };
|
return { std::max(a.x, b.x), std::max(a.y, b.y) };
|
||||||
}
|
}
|
||||||
|
|
||||||
static mgl::vec2i clamp_vec2i(mgl::vec2i value, mgl::vec2i min, mgl::vec2i max) {
|
mgl::vec2i clamp_vec2i(mgl::vec2i value, mgl::vec2i min, mgl::vec2i max) {
|
||||||
return min_vec2i(max, max_vec2i(value, min));
|
return min_vec2i(max, max_vec2i(value, min));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,7 +91,7 @@ namespace gsr {
|
|||||||
const mgl::vec2i pos = clamp_vec2i(child.position, parent.position, parent.position + parent.size);
|
const mgl::vec2i pos = clamp_vec2i(child.position, parent.position, parent.position + parent.size);
|
||||||
return mgl::Scissor{
|
return mgl::Scissor{
|
||||||
pos,
|
pos,
|
||||||
min_vec2i(child.size, parent.position + parent.size - pos)
|
max_vec2i(mgl::vec2i(0, 0), min_vec2i(child.position + child.size - pos, parent.position + parent.size - pos))
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -195,11 +195,14 @@ static void keyboard_event_process_input_event_data(keyboard_event *self, event_
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(extra_data->gsr_ui_virtual_keyboard) {
|
if(extra_data->gsr_ui_virtual_keyboard) {
|
||||||
if(event.type == EV_KEY || event.type == EV_MSC)
|
if(event.type == EV_KEY)
|
||||||
self->check_grab_lock = false;
|
self->check_grab_lock = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(extra_data->is_non_keyboard_device)
|
||||||
|
return;
|
||||||
|
|
||||||
if(event.type == EV_SYN && event.code == SYN_DROPPED) {
|
if(event.type == EV_SYN && event.code == SYN_DROPPED) {
|
||||||
/* TODO: Don't do this on every SYN_DROPPED to prevent spamming this, instead wait until the next event or wait for timeout */
|
/* TODO: Don't do this on every SYN_DROPPED to prevent spamming this, instead wait until the next event or wait for timeout */
|
||||||
keyboard_event_fetch_update_key_states(self, extra_data, fd);
|
keyboard_event_fetch_update_key_states(self, extra_data, fd);
|
||||||
@@ -210,6 +213,8 @@ static void keyboard_event_process_input_event_data(keyboard_event *self, event_
|
|||||||
//fprintf(stderr, "fd: %d, type: %d, pressed %d, value: %d\n", fd, event.type, event.code, event.value);
|
//fprintf(stderr, "fd: %d, type: %d, pressed %d, value: %d\n", fd, event.type, event.code, event.value);
|
||||||
//}
|
//}
|
||||||
|
|
||||||
|
const bool prev_grabbed = extra_data->grabbed;
|
||||||
|
|
||||||
const bool keyboard_key = is_keyboard_key(event.code);
|
const bool keyboard_key = is_keyboard_key(event.code);
|
||||||
if(event.type == EV_KEY && keyboard_key) {
|
if(event.type == EV_KEY && keyboard_key) {
|
||||||
keyboard_event_process_key_state_change(self, &event, extra_data, fd);
|
keyboard_event_process_key_state_change(self, &event, extra_data, fd);
|
||||||
@@ -228,7 +233,7 @@ static void keyboard_event_process_input_event_data(keyboard_event *self, event_
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(extra_data->grabbed) {
|
if(extra_data->grabbed) {
|
||||||
if(!self->check_grab_lock && (event.type == EV_KEY || event.type == EV_MSC)) {
|
if(prev_grabbed && !self->check_grab_lock && event.type == EV_KEY) {
|
||||||
self->uinput_written_time_seconds = clock_get_monotonic_seconds();
|
self->uinput_written_time_seconds = clock_get_monotonic_seconds();
|
||||||
self->check_grab_lock = true;
|
self->check_grab_lock = true;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user