Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 40 additions & 16 deletions src/state/actor.zig
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,13 @@ pub const Actor = struct {

const thread_2 = try std.Thread.spawn(.{}, struct {
fn run(_self: *Self) void {
if (_self.state.user_settings.settings.restore_capture_source_on_startup) {
const restore_capture_source_on_startup = blk: {
const settings_locked = _self.state.user_settings.settings.lock();
defer settings_locked.unlock();
const settings = settings_locked.unwrap_ptr();
break :blk settings.restore_capture_source_on_startup;
};
if (restore_capture_source_on_startup) {
_self.handle_action(.restore_capture_session) catch |err| {
log.err("[capture_startup] restore_capture_session error: {}\n", .{err});
};
Expand All @@ -151,9 +157,13 @@ pub const Actor = struct {
fn run(_self: *Self, t1: std.Thread, t2: std.Thread) void {
t1.join();
t2.join();
if (_self.state.user_settings.settings.restore_capture_source_on_startup and
_self.state.user_settings.settings.start_replay_buffer_on_startup)
{
const should_handle_action: bool = blk: {
const settings_locked = _self.state.user_settings.settings.lock();
defer settings_locked.unlock();
const settings = settings_locked.unwrap_ptr();
break :blk settings.restore_capture_source_on_startup and settings.start_replay_buffer_on_startup;
};
if (should_handle_action) {
_self.handle_action(.start_record) catch |err| {
log.err("[capture_startup] start_record error: {}", .{err});
};
Expand Down Expand Up @@ -226,8 +236,14 @@ pub const Actor = struct {
log.debug("[handle_action] save_replay - not recording, skipping capture", .{});
return;
}
fps = self.state.user_settings.settings.capture_fps;
replay_seconds = self.state.user_settings.settings.replay_seconds;
}

{
const settings_locked = self.state.user_settings.settings.lock();
defer settings_locked.unlock();
const settings = settings_locked.unwrap_ptr();
fps = settings.capture_fps;
replay_seconds = settings.replay_seconds;
}

// We should always have a size if the state is recording.
Expand Down Expand Up @@ -320,9 +336,9 @@ pub const Actor = struct {
try self.stop_capture();

const fps = blk: {
self.ui_mutex.lock();
defer self.ui_mutex.unlock();
break :blk self.state.user_settings.settings.capture_fps;
const settings_locked = self.state.user_settings.settings.lock();
defer settings_locked.unlock();
break :blk settings_locked.unwrap_ptr().capture_fps;
};

self.video_capture.select_source(selection, fps) catch |err| {
Expand Down Expand Up @@ -355,9 +371,9 @@ pub const Actor = struct {

while (true) {
const fps = blk: {
self.ui_mutex.lock();
defer self.ui_mutex.unlock();
break :blk self.state.user_settings.settings.capture_fps;
const settings_locked = self.state.user_settings.settings.lock();
defer settings_locked.unlock();
break :blk settings_locked.unwrap_ptr().capture_fps;
};

// Here we wait until the next projected frame time. This will happen if we are
Expand Down Expand Up @@ -515,10 +531,18 @@ pub const Actor = struct {
{
self.ui_mutex.lock();
defer self.ui_mutex.unlock();
if (!self.state.is_capturing_video or self.state.is_recording_video) return;
fps = self.state.user_settings.settings.capture_fps;
capture_bit_rate = self.state.user_settings.settings.capture_bit_rate;
replay_seconds = self.state.user_settings.settings.replay_seconds;
if (!self.state.is_capturing_video or self.state.is_recording_video) {
return;
}
}

{
const settings_locked = self.state.user_settings.settings.lock();
defer settings_locked.unlock();
const settings = settings_locked.unwrap_ptr();
fps = settings.capture_fps;
capture_bit_rate = settings.capture_bit_rate;
replay_seconds = settings.replay_seconds;
}

const size = self.video_capture.size() orelse {
Expand Down
14 changes: 8 additions & 6 deletions src/state/audio_state.zig
Original file line number Diff line number Diff line change
Expand Up @@ -91,9 +91,10 @@ pub const AudioState = struct {
defer available_devices.deinit();

var user_settings = blk: {
actor.ui_mutex.lock();
defer actor.ui_mutex.unlock();
break :blk try actor.state.user_settings.settings.clone(self.allocator);
const settings_locked = actor.state.user_settings.settings.lock();
defer settings_locked.unlock();
const settings = settings_locked.unwrap_ptr();
break :blk try settings.clone(self.allocator);
};
defer user_settings.deinit(self.allocator);

Expand Down Expand Up @@ -185,9 +186,10 @@ pub const AudioState = struct {
},
.start_record => {
const replay_seconds = blk: {
actor.ui_mutex.lock();
defer actor.ui_mutex.unlock();
break :blk actor.state.user_settings.settings.replay_seconds;
const settings_locked = actor.state.user_settings.settings.lock();
defer settings_locked.unlock();
const settings = settings_locked.unwrap_ptr();
break :blk settings.replay_seconds;
};
var replay_buffer_locked = self.audio_replay_buffer.lock();
defer replay_buffer_locked.unlock();
Expand Down
3 changes: 0 additions & 3 deletions src/state/user_settings.zig
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,6 @@ pub const UserSettings = struct {
gain: f32 = 1.0,
};

// NOTE: Default values here are default user settings.
gui_foreground_fps: u32 = 120,
gui_background_fps: u32 = 30,
capture_fps: u32 = 60,
/// In bits per second (bps).
capture_bit_rate: u64 = 10_000_000,
Expand Down
42 changes: 21 additions & 21 deletions src/state/user_settings_state.zig
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,14 @@ const ActionPayload = @import("./action_payload.zig").ActionPayload;
const util = @import("../util.zig");
const Actions = @import("./actor.zig").Actions;
const UserSettings = @import("./user_settings.zig").UserSettings;
const Mutex = @import("../mutex.zig").Mutex;

const log = std.log.scoped(.user_settings_state);

pub const UserSettingsActions = union(enum) {
set_capture_fps: u32,
set_capture_bit_rate: u64,
set_replay_seconds: u32,
set_gui_foreground_fps: u32,
set_gui_background_fps: u32,
set_restore_capture_source_on_startup: bool,
set_start_replay_buffer_on_startup: bool,
set_audio_device_settings: *ActionPayload(struct {
Expand All @@ -38,8 +37,7 @@ pub const UserSettingsState = struct {
const Self = @This();

allocator: Allocator,
// TODO: Use mutex here instead of ui_mutex.
settings: UserSettings = .{},
settings: Mutex(UserSettings) = .init(.{}),

pub fn init(allocator: Allocator) !Self {
var self: Self = .{
Expand All @@ -52,7 +50,10 @@ pub const UserSettingsState = struct {
}

pub fn deinit(self: *Self) void {
self.settings.deinit(self.allocator);
var settings_locked = self.settings.lock();
defer settings_locked.unlock();
const settings = settings_locked.unwrap_ptr();
settings.deinit(self.allocator);
}

pub fn handle_action(self: *Self, actor: *Actor, action: Actions) !void {
Expand All @@ -69,12 +70,6 @@ pub const UserSettingsState = struct {
.set_replay_seconds => |replay_seconds| {
try self.set_state(actor, "replay_seconds", replay_seconds);
},
.set_gui_foreground_fps => |gui_foreground_fps| {
try self.set_state(actor, "gui_foreground_fps", gui_foreground_fps);
},
.set_gui_background_fps => |gui_background_fps| {
try self.set_state(actor, "gui_background_fps", gui_background_fps);
},
.set_start_replay_buffer_on_startup => |start_replay_buffer_on_startup| {
try self.set_state(actor, "start_replay_buffer_on_startup", start_replay_buffer_on_startup);
},
Expand All @@ -86,15 +81,16 @@ pub const UserSettingsState = struct {
const payload = _action.payload;
var settings_snapshot: UserSettings = undefined;
{
actor.ui_mutex.lock();
defer actor.ui_mutex.unlock();
try self.settings.update_audio_device_settings(
const settings_locked = actor.state.user_settings.settings.lock();
defer settings_locked.unlock();
const settings = settings_locked.unwrap_ptr();
try settings.update_audio_device_settings(
self.allocator,
payload.device_id,
payload.selected,
payload.gain,
);
settings_snapshot = try self.settings.clone(self.allocator);
settings_snapshot = try settings.clone(self.allocator);
}
defer settings_snapshot.deinit(self.allocator);
try self.save(&settings_snapshot);
Expand All @@ -120,10 +116,11 @@ pub const UserSettingsState = struct {
value: anytype,
) !void {
var settings_snapshot: UserSettings = blk: {
actor.ui_mutex.lock();
defer actor.ui_mutex.unlock();
@field(self.settings, field_name) = value;
break :blk try self.settings.clone(self.allocator);
const settings_locked = actor.state.user_settings.settings.lock();
defer settings_locked.unlock();
const settings = settings_locked.unwrap_ptr();
@field(settings, field_name) = value;
break :blk try settings.clone(self.allocator);
};
defer settings_snapshot.deinit(self.allocator);
try self.save(&settings_snapshot);
Expand Down Expand Up @@ -171,8 +168,11 @@ pub const UserSettingsState = struct {
);
}

self.settings.deinit(self.allocator);
self.settings = loaded;
var settings_locked = self.settings.lock();
defer settings_locked.unlock();
const settings = settings_locked.unwrap_ptr();
settings.deinit(self.allocator);
settings_locked.set(loaded);
}

/// Save a copy of settings to disk.
Expand Down
83 changes: 17 additions & 66 deletions src/ui/draw_left_column.zig
Original file line number Diff line number Diff line change
Expand Up @@ -188,8 +188,6 @@ pub fn draw_left_column(allocator: std.mem.Allocator, actor: *Actor) !void {
c.ImGuiWindowFlags_NoCollapse);
defer c.ImGui_End();

// c.ImGui_SeparatorText("Spacecap");

if (c.ImGui_BeginTabBar("MainTabBar", 0)) {
defer c.ImGui_EndTabBar();

Expand Down Expand Up @@ -291,64 +289,6 @@ pub fn draw_left_column(allocator: std.mem.Allocator, actor: *Actor) !void {

try draw_capture_settings(allocator, actor);

c.ImGui_SeparatorText("GUI Settings");

c.ImGui_Text("FG FPS");
c.ImGui_SameLine();
imgui_util.help_marker("Forground FPS. This is the max frame rate Spacecap will render while focused. Drag or double click to change.");
c.ImGui_SetNextItemWidth(-std.math.floatMin(f32));
const current_fg_fps: i32 = @intCast(actor.state.user_settings.settings.gui_foreground_fps);
var fg_fps = fg_fps_local orelse current_fg_fps;
if (c.ImGui_InputIntEx(
"##fg_fps",
&fg_fps,
5,
10,
c.ImGuiInputTextFlags_None,
)) {
fg_fps = std.math.clamp(fg_fps, GUI_FPS_MIN, GUI_FPS_MAX);
fg_fps_local = fg_fps;
}
if (c.ImGui_IsItemDeactivatedAfterEdit() and fg_fps != current_fg_fps) {
try actor.dispatch(.{ .user_settings = .{
.set_gui_foreground_fps = @intCast(fg_fps),
} });
fg_fps_local = null;
} else if (!c.ImGui_IsItemActive()) {
// Keep the UI synced with state when not actively editing.
fg_fps_local = null;
}

c.ImGui_Text("BG FPS");
c.ImGui_SameLine();
imgui_util.help_marker("Background FPS. This is the max frame rate Spacecap will render while NOT focused. Drag or double click to change.");
c.ImGui_SetNextItemWidth(-std.math.floatMin(f32));
const current_bg_fps: i32 = @intCast(actor.state.user_settings.settings.gui_background_fps);
var bg_fps = bg_fps_local orelse current_bg_fps;
if (c.ImGui_InputIntEx(
"##bg_fps",
&bg_fps,
5,
10,
c.ImGuiInputTextFlags_None,
)) {
bg_fps = std.math.clamp(bg_fps, GUI_FPS_MIN, GUI_FPS_MAX);
bg_fps_local = bg_fps;
}
if (c.ImGui_IsItemDeactivatedAfterEdit() and bg_fps != current_bg_fps) {
try actor.dispatch(.{ .user_settings = .{
.set_gui_background_fps = @intCast(bg_fps),
} });
bg_fps_local = null;
} else if (!c.ImGui_IsItemActive()) {
// Keep the UI synced with state when not actively editing.
bg_fps_local = null;
}

const io = c.ImGui_GetIO();
c.ImGui_TextDisabled("%.3f ms/frame", 1000.0 / io.*.Framerate);
c.ImGui_TextDisabled("%.1f fps", io.*.Framerate);

// NOTE: Hiding this for now. Linux shortcuts can be configured at the
// desktop environment level. See comments regarding `Method.configure_shortcuts`
// in `xdg_desktop_portal_global_shortcuts.zig` for more info.
Expand All @@ -369,6 +309,11 @@ pub fn draw_left_column(allocator: std.mem.Allocator, actor: *Actor) !void {
if (c.ImGui_ButtonEx("Show Demo", .{ .x = c.ImGui_GetContentRegionAvail().x, .y = 0 })) {
try actor.dispatch(.show_demo);
}

c.ImGui_Spacing();
const io = c.ImGui_GetIO();
c.ImGui_TextDisabled("%.3f ms/frame", 1000.0 / io.*.Framerate);
c.ImGui_TextDisabled("%.1f fps", io.*.Framerate);
}
}
}
Expand All @@ -377,9 +322,19 @@ pub fn draw_left_column(allocator: std.mem.Allocator, actor: *Actor) !void {
fn draw_capture_settings(allocator: std.mem.Allocator, actor: *Actor) !void {
c.ImGui_SeparatorText("Capture Settings");

const settings_locked = actor.state.user_settings.settings.lock();
const settings = settings_locked.unwrap_ptr();

const current_capture_fps: i32 = @intCast(settings.capture_fps);
const current_capture_bit_rate: i32 = @intCast(settings.capture_bit_rate / CAPTURE_BIT_RATE_BPS_PER_KBPS);
const current_replay_seconds: i32 = @intCast(settings.replay_seconds);
var restore_capture_source_on_startup = settings.restore_capture_source_on_startup;
var start_replay_buffer_on_startup = settings.start_replay_buffer_on_startup;

settings_locked.unlock();

// FPS
{
const current_capture_fps: i32 = @intCast(actor.state.user_settings.settings.capture_fps);
var fps = capture_fps_local orelse current_capture_fps;
c.ImGui_Text("Max FPS");
c.ImGui_SameLine();
Expand Down Expand Up @@ -412,7 +367,6 @@ fn draw_capture_settings(allocator: std.mem.Allocator, actor: *Actor) !void {
c.ImGui_SameLine();
imgui_util.help_marker("Capture bitrate in Kbps");
c.ImGui_SetNextItemWidth(-std.math.floatMin(f32));
const current_capture_bit_rate: i32 = @intCast(actor.state.user_settings.settings.capture_bit_rate / CAPTURE_BIT_RATE_BPS_PER_KBPS);
var capture_bit_rate = capture_bit_rate_local orelse current_capture_bit_rate;
if (c.ImGui_InputIntEx(
"##bitrate",
Expand Down Expand Up @@ -447,7 +401,6 @@ fn draw_capture_settings(allocator: std.mem.Allocator, actor: *Actor) !void {
c.ImGui_SameLine();
imgui_util.help_marker("Length of video and audio stored in memory (seconds)");
c.ImGui_SetNextItemWidth(-std.math.floatMin(f32));
const current_replay_seconds: i32 = @intCast(actor.state.user_settings.settings.replay_seconds);
var replay_seconds = replay_seconds_local orelse current_replay_seconds;
if (c.ImGui_InputIntEx(
"##replay_buffer_length",
Expand Down Expand Up @@ -478,7 +431,6 @@ fn draw_capture_settings(allocator: std.mem.Allocator, actor: *Actor) !void {
c.ImGui_Text("Restore capture source on startup");
c.ImGui_SameLine();
imgui_util.help_marker("Try to restore the last capture source when Spacecap starts.");
var restore_capture_source_on_startup = actor.state.user_settings.settings.restore_capture_source_on_startup;
if (c.ImGui_Checkbox("##restore_capture_source_on_startup", &restore_capture_source_on_startup)) {
try actor.dispatch(.{ .user_settings = .{
.set_restore_capture_source_on_startup = restore_capture_source_on_startup,
Expand All @@ -489,9 +441,8 @@ fn draw_capture_settings(allocator: std.mem.Allocator, actor: *Actor) !void {
c.ImGui_Text("Start replay buffer on startup");
c.ImGui_SameLine();
imgui_util.help_marker("Start the replay buffer when Spacecap starts. Requires 'Restore capture source on startup'.");
c.ImGui_BeginDisabled(!actor.state.user_settings.settings.restore_capture_source_on_startup);
c.ImGui_BeginDisabled(!restore_capture_source_on_startup);
defer c.ImGui_EndDisabled();
var start_replay_buffer_on_startup = actor.state.user_settings.settings.start_replay_buffer_on_startup;
if (c.ImGui_Checkbox("##start_replay_buffer_on_startup", &start_replay_buffer_on_startup)) {
try actor.dispatch(.{ .user_settings = .{
.set_start_replay_buffer_on_startup = start_replay_buffer_on_startup,
Expand Down
Loading
Loading