gardesk/garlock / 4c3739e

Browse files

Integrate text overlays into render loop

- Add overlay renderer initialization
- Composite time, caps lock, failed attempts, and cooldown overlays
- Position overlays relative to ring center
- Add periodic 1-second timer for time display updates
- Make escape key exit conditional on 'dev' feature
Authored by mfwolffe <wolffemf@dukes.jmu.edu>
SHA
4c3739e7ed18dd0bd2a6318085f179eb37628975
Parents
db39b04
Tree
54ab88b

1 changed file

StatusFile+-
M garlock/src/main.rs 120 3
garlock/src/main.rsmodified
@@ -14,6 +14,7 @@ mod background;
1414
 mod config;
1515
 mod error;
1616
 mod keyboard;
17
+mod overlay;
1718
 mod password;
1819
 mod ring;
1920
 mod screenshot;
@@ -23,6 +24,7 @@ mod x11;
2324
 use auth::{authenticate_async, get_current_username, AuthResult, PendingAuth};
2425
 use background::Background;
2526
 use config::Config;
27
+use overlay::{composite_overlay, OverlayRenderer};
2628
 use keyboard::{KeyResult, Keyboard};
2729
 use password::Password;
2830
 use ring::{composite_ring, RingRenderer};
@@ -153,6 +155,9 @@ fn run_lock(config: Config) -> Result<()> {
153155
     tracing::info!("Initializing ring indicator...");
154156
     let mut ring = RingRenderer::from_config(&config.ring);
155157
 
158
+    // Step 7: Initialize overlay renderer for text elements
159
+    let overlay = OverlayRenderer::new(&config.font);
160
+
156161
     // Get ring center position (center of primary monitor, or screen center)
157162
     let (ring_cx, ring_cy) = match locker.get_monitors() {
158163
         Ok(monitors) => {
@@ -175,13 +180,24 @@ fn run_lock(config: Config) -> Result<()> {
175180
         }
176181
     };
177182
 
178
-    // Helper to composite ring onto background and display
183
+    /// Overlay positioning offset from ring center
184
+    const OVERLAY_TIME_OFFSET_Y: i32 = -150; // Above ring
185
+    const OVERLAY_CAPS_OFFSET_Y: i32 = 130;  // Below ring
186
+    const OVERLAY_ATTEMPTS_OFFSET_Y: i32 = 160; // Below caps lock
187
+    const OVERLAY_COOLDOWN_OFFSET_Y: i32 = 190; // Below attempts
188
+
189
+    // Helper to composite ring and overlays onto background and display
179190
     let render_frame = |background: &mut Background,
180191
                         background_clean: &[u8],
181192
                         ring: &RingRenderer,
193
+                        overlay: &OverlayRenderer,
182194
                         locker: &LockerWindow,
195
+                        config: &Config,
183196
                         ring_cx: i32,
184
-                        ring_cy: i32|
197
+                        ring_cy: i32,
198
+                        caps_lock_active: bool,
199
+                        failed_attempts: u32,
200
+                        cooldown_secs: u64|
185201
      -> Result<()> {
186202
         // Reset background to clean state
187203
         background.data.copy_from_slice(background_clean);
@@ -206,6 +222,78 @@ fn run_lock(config: Config) -> Result<()> {
206222
             dest_y,
207223
         );
208224
 
225
+        // Render and composite time display (above ring)
226
+        if let Some(result) = overlay.render_time(&config.indicator) {
227
+            let mut time_surface = result?;
228
+            let time_data = time_surface.to_bgra()?;
229
+            let time_x = ring_cx - time_surface.width / 2;
230
+            let time_y = ring_cy + OVERLAY_TIME_OFFSET_Y - time_surface.height / 2;
231
+            composite_overlay(
232
+                &mut background.data,
233
+                background.width,
234
+                background.height,
235
+                &time_data,
236
+                time_surface.width,
237
+                time_surface.height,
238
+                time_x,
239
+                time_y,
240
+            );
241
+        }
242
+
243
+        // Render and composite caps lock indicator (below ring)
244
+        if let Some(result) = overlay.render_caps_lock(&config.indicator, caps_lock_active) {
245
+            let mut caps_surface = result?;
246
+            let caps_data = caps_surface.to_bgra()?;
247
+            let caps_x = ring_cx - caps_surface.width / 2;
248
+            let caps_y = ring_cy + OVERLAY_CAPS_OFFSET_Y - caps_surface.height / 2;
249
+            composite_overlay(
250
+                &mut background.data,
251
+                background.width,
252
+                background.height,
253
+                &caps_data,
254
+                caps_surface.width,
255
+                caps_surface.height,
256
+                caps_x,
257
+                caps_y,
258
+            );
259
+        }
260
+
261
+        // Render and composite failed attempts indicator
262
+        if let Some(result) = overlay.render_failed_attempts(&config.indicator, failed_attempts) {
263
+            let mut attempts_surface = result?;
264
+            let attempts_data = attempts_surface.to_bgra()?;
265
+            let attempts_x = ring_cx - attempts_surface.width / 2;
266
+            let attempts_y = ring_cy + OVERLAY_ATTEMPTS_OFFSET_Y - attempts_surface.height / 2;
267
+            composite_overlay(
268
+                &mut background.data,
269
+                background.width,
270
+                background.height,
271
+                &attempts_data,
272
+                attempts_surface.width,
273
+                attempts_surface.height,
274
+                attempts_x,
275
+                attempts_y,
276
+            );
277
+        }
278
+
279
+        // Render and composite cooldown timer
280
+        if let Some(result) = overlay.render_cooldown(cooldown_secs) {
281
+            let mut cooldown_surface = result?;
282
+            let cooldown_data = cooldown_surface.to_bgra()?;
283
+            let cooldown_x = ring_cx - cooldown_surface.width / 2;
284
+            let cooldown_y = ring_cy + OVERLAY_COOLDOWN_OFFSET_Y - cooldown_surface.height / 2;
285
+            composite_overlay(
286
+                &mut background.data,
287
+                background.width,
288
+                background.height,
289
+                &cooldown_data,
290
+                cooldown_surface.width,
291
+                cooldown_surface.height,
292
+                cooldown_x,
293
+                cooldown_y,
294
+            );
295
+        }
296
+
209297
         locker.put_image(&background.data)?;
210298
         Ok(())
211299
     };
@@ -215,9 +303,14 @@ fn run_lock(config: Config) -> Result<()> {
215303
         &mut background,
216304
         &background_clean,
217305
         &ring,
306
+        &overlay,
218307
         &locker,
308
+        &config,
219309
         ring_cx,
220310
         ring_cy,
311
+        keyboard.caps_lock_active(),
312
+        locker_state.failed_attempts,
313
+        locker_state.cooldown_remaining(),
221314
     )?;
222315
 
223316
     // Get current username for PAM authentication
@@ -227,10 +320,17 @@ fn run_lock(config: Config) -> Result<()> {
227320
     // Track pending authentication
228321
     let mut pending_auth: Option<PendingAuth> = None;
229322
 
230
-    tracing::info!("Entering event loop (press Escape to exit - dev mode only)");
323
+    #[cfg(feature = "dev")]
324
+    tracing::info!("Entering event loop (dev mode: press Escape to exit)");
325
+    #[cfg(not(feature = "dev"))]
326
+    tracing::info!("Entering event loop");
231327
 
232328
     let mut needs_redraw = false;
233329
 
330
+    // Timer for periodic time display updates (every second if time is shown)
331
+    let mut last_time_update = std::time::Instant::now();
332
+    let time_update_interval = std::time::Duration::from_secs(1);
333
+
234334
     loop {
235335
         // Process events
236336
         match locker.poll_for_event()? {
@@ -244,11 +344,17 @@ fn run_lock(config: Config) -> Result<()> {
244344
                         let key_result = keyboard.process_key(keycode, true);
245345
 
246346
                         match key_result {
347
+                            #[cfg(feature = "dev")]
247348
                             KeyResult::Escape => {
248349
                                 tracing::info!("Escape pressed, exiting (dev mode)");
249350
                                 break;
250351
                             }
251352
 
353
+                            #[cfg(not(feature = "dev"))]
354
+                            KeyResult::Escape => {
355
+                                // In production, escape does nothing
356
+                            }
357
+
252358
                             KeyResult::Char(c) => {
253359
                                 if password.push(c) {
254360
                                     locker_state.on_input(InputState::Letter);
@@ -375,6 +481,12 @@ fn run_lock(config: Config) -> Result<()> {
375481
                     needs_redraw = true;
376482
                 }
377483
 
484
+                // Periodic time display update (if enabled)
485
+                if config.indicator.show_time && last_time_update.elapsed() >= time_update_interval {
486
+                    last_time_update = std::time::Instant::now();
487
+                    needs_redraw = true;
488
+                }
489
+
378490
                 if needs_redraw {
379491
                     // Update ring state from locker state
380492
                     ring.set_state(locker_state.ring_state());
@@ -383,9 +495,14 @@ fn run_lock(config: Config) -> Result<()> {
383495
                         &mut background,
384496
                         &background_clean,
385497
                         &ring,
498
+                        &overlay,
386499
                         &locker,
500
+                        &config,
387501
                         ring_cx,
388502
                         ring_cy,
503
+                        keyboard.caps_lock_active(),
504
+                        locker_state.failed_attempts,
505
+                        locker_state.cooldown_remaining(),
389506
                     )?;
390507
                     needs_redraw = false;
391508
                 }