Skip to content

Commit cfd2eb3

Browse files
committed
feat: add FPS limit support to GUI and related components
1 parent f2d48b1 commit cfd2eb3

6 files changed

Lines changed: 23 additions & 10 deletions

File tree

crates/gui-tester/src/main.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ fn main() {
4848
Some(gui::window::types::AppState::Test),
4949
messages_rx,
5050
stop_trigger.clone(),
51+
None,
5152
)
5253
.unwrap();
5354

crates/gui/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@ pub fn run_gui(
148148
initial_state: Option<window::types::AppState>,
149149
messages_rx: Receiver<window::types::GuiMessage>,
150150
stop: Trigger,
151+
fps_limit: Option<u32>,
151152
) -> Result<()> {
152153
let (keys_tx, keys_rx) = bounded::<RawKey>(1024);
153154
let processing_events = Arc::new(AtomicBool::new(false));
@@ -181,6 +182,7 @@ pub fn run_gui(
181182
catalog,
182183
initial_state,
183184
cc,
185+
fps_limit,
184186
)))
185187
}),
186188
&event_loop,

crates/gui/src/window/mod.rs

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,8 @@ use std::sync::{
3434
atomic::{AtomicBool, Ordering},
3535
};
3636

37-
use flume::Receiver;
3837
use eframe::egui;
38+
use flume::Receiver;
3939

4040
use shared::{log, system::trigger::Trigger};
4141

@@ -63,10 +63,12 @@ pub(super) struct AppWindow {
6363
pub gui_messages_rx: Receiver<types::GuiMessage>,
6464
pub stop: Trigger, // For stopping any ongoing operations
6565
pub screen_size: Option<(u32, u32)>, // Cached screen size
66+
pub fps_limit: Option<u32>, // FPS limit for RDP connection, if any
6667
pub catalog: gettext::Catalog, // For translations
6768
}
6869

6970
impl AppWindow {
71+
#[allow(clippy::too_many_arguments)]
7072
pub fn new(
7173
processing_events: Arc<AtomicBool>,
7274
events: Receiver<crate::RawKey>,
@@ -75,6 +77,7 @@ impl AppWindow {
7577
catalog: gettext::Catalog,
7678
initial_state: Option<types::AppState>,
7779
cc: &eframe::CreationContext<'_>,
80+
fps_limit: Option<u32>,
7881
) -> Self {
7982
processing_events.store(false, Ordering::Relaxed); // Initially not processing events
8083
let texture = cc.egui_ctx.load_texture(
@@ -91,6 +94,7 @@ impl AppWindow {
9194
processing_events,
9295
stop,
9396
screen_size: None,
97+
fps_limit,
9498
catalog,
9599
}
96100
}
@@ -139,11 +143,7 @@ impl AppWindow {
139143
self.app_state = new_state;
140144
}
141145

142-
pub fn restore_previous_state(
143-
&mut self,
144-
ui: &mut egui::Ui,
145-
frame: &mut eframe::Frame,
146-
) {
146+
pub fn restore_previous_state(&mut self, ui: &mut egui::Ui, frame: &mut eframe::Frame) {
147147
self.processing_events.store(false, Ordering::Relaxed); // Stop processing rdp raw events on event loop
148148
self.app_state = self.prev_app_state.clone();
149149
self.prev_app_state = types::AppState::default();
@@ -160,7 +160,8 @@ impl AppWindow {
160160

161161
pub fn set_visible(&mut self, ui: &mut egui::Ui, visible: bool) {
162162
log::debug!("Setting window visibility to {}", visible);
163-
ui.ctx().send_viewport_cmd(egui::ViewportCommand::Minimized(!visible));
163+
ui.ctx()
164+
.send_viewport_cmd(egui::ViewportCommand::Minimized(!visible));
164165
}
165166
}
166167

@@ -249,7 +250,9 @@ impl eframe::App for AppWindow {
249250
}
250251
let frame_duration = frame_start.elapsed();
251252
// ctx.request_repaint(); // Repaint asap
252-
let remaining = std::time::Duration::from_millis(16).saturating_sub(frame_duration);
253-
ui.ctx().request_repaint_after(remaining); // Aim for ~60 FPS
253+
let fps_limit = self.fps_limit.unwrap_or(60);
254+
let frame_time = std::time::Duration::from_secs_f32(1.0 / fps_limit as f32);
255+
let remaining = frame_time.saturating_sub(frame_duration);
256+
ui.ctx().request_repaint_after(remaining); // Aim for consistent FPS, but allow sleeping if we're ahead of schedule
254257
}
255258
}

crates/launcher/src/main.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,8 +107,11 @@ fn main() {
107107
// Launch async thread with tokio runtime
108108
asyncthread::run(messages_tx, stop.clone(), host, ticket, scrambler);
109109

110+
// Read app data, which may contain overrides for proxy and ssl settings, and fps limit
111+
let app_data = shared::appdata::AppData::load();
112+
110113
// Run the GUI, this will block until the GUI is closed
111-
gui::run_gui(intl::get_catalog().clone(), None, messages_rx, stop.clone()).unwrap();
114+
gui::run_gui(intl::get_catalog().clone(), None, messages_rx, stop.clone(), app_data.fps_limit).unwrap();
112115

113116
// Gui closed, wait for app to finish also
114117
stop.wait();

crates/script-tester/src/main.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,11 +143,14 @@ fn main() -> Result<()> {
143143
// Run the script on a thread
144144
std::thread::spawn(run_script);
145145

146+
let app_data = shared::appdata::AppData::load();
147+
146148
gui::run_gui(
147149
fake_catalog,
148150
Some(gui::window::types::AppState::Test),
149151
messages_rx,
150152
stop_trigger.clone(),
153+
app_data.fps_limit,
151154
)
152155
.unwrap();
153156

crates/shared/src/appdata.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ pub struct AppData {
4545
// So we can override proxy and ssl settings if needed
4646
pub disable_proxy: Option<bool>,
4747
pub verify_ssl: Option<bool>,
48+
pub fps_limit: Option<u32>,
4849
// On mac, also allow override launcher path
4950
#[cfg(target_os = "macos")]
5051
pub launcher_path: Option<String>,

0 commit comments

Comments
 (0)