Skip to content

Commit 5baafee

Browse files
authored
Merge pull request #49 from devmobasa/zoom-improvements
Zoom improvements
2 parents f50aa94 + 46bd51e commit 5baafee

File tree

23 files changed

+679
-584
lines changed

23 files changed

+679
-584
lines changed

packaging/wayscriber.service

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,18 @@
22
Description=Wayscriber - Screen annotation tool for Wayland
33
Documentation=https://wayscriber.com
44
PartOf=graphical-session.target
5+
After=graphical-session.target
6+
Wants=graphical-session.target
57

68
[Service]
79
Type=simple
10+
ExecStartPre=/bin/sh -c '[ -n "$WAYLAND_DISPLAY" ] && [ -S "$XDG_RUNTIME_DIR/$WAYLAND_DISPLAY" ]'
811
ExecStart=/usr/bin/wayscriber --daemon
912
Restart=on-failure
1013
RestartSec=5
11-
# Ensure Wayland environment is available
14+
RestartPreventExitStatus=75
15+
SuccessExitStatus=75
16+
# Ensure consistent PATH for helper tools
1217
Environment="PATH=/usr/local/bin:/usr/bin:/bin"
1318

1419
[Install]

src/backend/wayland/backend.rs

Lines changed: 61 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,12 @@ use std::{
3535
};
3636
#[cfg(tablet)]
3737
const TABLET_MANAGER_MAX_VERSION: u32 = 2;
38-
use wayland_client::{Connection, globals::registry_queue_init};
38+
use wayland_client::{Connection, backend::WaylandError, globals::registry_queue_init};
3939
#[cfg(tablet)]
4040
use wayland_protocols::wp::tablet::zv2::client::zwp_tablet_manager_v2::ZwpTabletManagerV2;
4141
use wayland_protocols_wlr::screencopy::v1::client::zwlr_screencopy_manager_v1::ZwlrScreencopyManagerV1;
4242

43-
use super::state::WaylandState;
43+
use super::state::{OverlaySuppression, WaylandState};
4444
use crate::{
4545
RESUME_SESSION_ENV,
4646
capture::{CaptureManager, CaptureOutcome},
@@ -641,12 +641,8 @@ impl WaylandBackend {
641641
}
642642

643643
// Apply any completed portal fallback captures without blocking.
644-
state
645-
.frozen
646-
.poll_portal_capture(&mut state.surface, &mut state.input_state);
647-
state
648-
.zoom
649-
.poll_portal_capture(&mut state.surface, &mut state.input_state);
644+
state.frozen.poll_portal_capture(&mut state.input_state);
645+
state.zoom.poll_portal_capture(&mut state.input_state);
650646

651647
if tray_action_flag
652648
.as_ref()
@@ -656,9 +652,46 @@ impl WaylandBackend {
656652
process_tray_action(&mut state);
657653
}
658654

659-
// Dispatch all pending events (blocking) but check should_exit after each batch
660-
match event_queue.blocking_dispatch(&mut state) {
661-
Ok(_) => {
655+
let capture_active = state.capture.is_in_progress()
656+
|| state.frozen.is_in_progress()
657+
|| state.zoom.is_in_progress()
658+
|| state.overlay_suppressed();
659+
660+
let mut dispatch_error: Option<anyhow::Error> = None;
661+
if capture_active {
662+
if let Err(e) = event_queue.dispatch_pending(&mut state) {
663+
dispatch_error = Some(anyhow::anyhow!("Wayland event queue error: {}", e));
664+
}
665+
666+
if dispatch_error.is_none()
667+
&& let Err(e) = event_queue.flush()
668+
{
669+
dispatch_error = Some(anyhow::anyhow!("Wayland flush error: {}", e));
670+
}
671+
672+
if dispatch_error.is_none()
673+
&& let Some(guard) = event_queue.prepare_read()
674+
{
675+
match guard.read() {
676+
Ok(_) => {
677+
if let Err(e) = event_queue.dispatch_pending(&mut state) {
678+
dispatch_error =
679+
Some(anyhow::anyhow!("Wayland event queue error: {}", e));
680+
}
681+
}
682+
Err(WaylandError::Io(err))
683+
if err.kind() == std::io::ErrorKind::WouldBlock => {}
684+
Err(err) => {
685+
dispatch_error = Some(anyhow::anyhow!("Wayland read error: {}", err));
686+
}
687+
}
688+
}
689+
} else if let Err(e) = event_queue.blocking_dispatch(&mut state) {
690+
dispatch_error = Some(anyhow::anyhow!("Wayland event queue error: {}", e));
691+
}
692+
693+
match dispatch_error {
694+
None => {
662695
// Check immediately after dispatch returns
663696
if state.input_state.should_exit {
664697
info!("Exit requested after dispatch, breaking event loop");
@@ -679,13 +712,20 @@ impl WaylandBackend {
679712
state.input_state.needs_redraw = true;
680713
}
681714
}
682-
Err(e) => {
715+
Some(e) => {
683716
warn!("Event queue error: {}", e);
684-
loop_error = Some(anyhow::anyhow!("Wayland event queue error: {}", e));
717+
loop_error = Some(e);
685718
break;
686719
}
687720
}
688721

722+
if capture_active {
723+
let _ = conn.flush();
724+
std::thread::sleep(std::time::Duration::from_millis(10));
725+
}
726+
727+
state.apply_capture_completion();
728+
689729
if state.input_state.take_pending_frozen_toggle() {
690730
if !state.frozen_enabled() {
691731
warn!(
@@ -702,24 +742,20 @@ impl WaylandBackend {
702742
} else {
703743
log::info!("Frozen mode: using screencopy fast path");
704744
}
705-
if let Err(err) = state.frozen.start_capture(
706-
&state.shm,
707-
&mut state.surface,
708-
&qh,
709-
use_fallback,
710-
&mut state.input_state,
711-
&state.tokio_handle,
712-
) {
745+
state.enter_overlay_suppression(OverlaySuppression::Frozen);
746+
if let Err(err) = state
747+
.frozen
748+
.start_capture(use_fallback, &state.tokio_handle)
749+
{
713750
warn!("Frozen capture failed to start: {}", err);
714-
state
715-
.frozen
716-
.cancel(&mut state.surface, &mut state.input_state);
751+
state.exit_overlay_suppression(OverlaySuppression::Frozen);
752+
state.frozen.cancel(&mut state.input_state);
717753
}
718754
}
719755
}
720756

721757
if let Some(action) = state.input_state.take_pending_zoom_action() {
722-
state.handle_zoom_action(action, &qh);
758+
state.handle_zoom_action(action);
723759
}
724760

725761
// Check for completed capture operations

src/backend/wayland/capture.rs

Lines changed: 4 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,13 @@
1-
//! Capture controller for managing overlay visibility and capture state.
1+
//! Capture controller for managing screenshot capture state.
22
//!
3-
//! Keeps the overlay hide/show logic alongside the CaptureManager so the
4-
//! main Wayland loop only coordinates events instead of tracking flags.
3+
//! Keeps the capture manager and in-progress flag together so the main
4+
//! Wayland loop can coordinate capture requests and results.
55
66
use crate::capture::CaptureManager;
7-
use log::{debug, info, warn};
8-
use smithay_client_toolkit::shell::WaylandSurface;
9-
10-
use super::surface::SurfaceState;
11-
12-
/// Tracks capture manager state along with overlay visibility flags.
7+
/// Tracks capture manager state and in-progress flag.
138
pub struct CaptureState {
149
manager: CaptureManager,
1510
in_progress: bool,
16-
overlay_hidden: bool,
1711
}
1812

1913
impl CaptureState {
@@ -22,7 +16,6 @@ impl CaptureState {
2216
Self {
2317
manager,
2418
in_progress: false,
25-
overlay_hidden: false,
2619
}
2720
}
2821

@@ -45,65 +38,4 @@ impl CaptureState {
4538
pub fn clear_in_progress(&mut self) {
4639
self.in_progress = false;
4740
}
48-
49-
/// Returns whether the overlay is currently hidden.
50-
pub fn is_overlay_hidden(&self) -> bool {
51-
self.overlay_hidden
52-
}
53-
54-
/// Hides the overlay before capture.
55-
///
56-
/// Returns `true` if the overlay was hidden, `false` if it was already hidden.
57-
pub fn hide_overlay(&mut self, surface: &mut SurfaceState) -> bool {
58-
if self.overlay_hidden {
59-
warn!("Overlay already hidden for capture");
60-
return false;
61-
}
62-
63-
info!("Hiding overlay for screenshot capture");
64-
let mut hidden = false;
65-
if let Some(layer_surface) = surface.layer_surface_mut() {
66-
layer_surface.set_size(0, 0);
67-
let wl_surface = layer_surface.wl_surface();
68-
wl_surface.commit();
69-
hidden = true;
70-
} else if surface.is_xdg_window() {
71-
if let Some(wl_surface) = surface.wl_surface() {
72-
// Detach the buffer to unmap the xdg window during capture.
73-
wl_surface.attach(None, 0, 0);
74-
wl_surface.commit();
75-
hidden = true;
76-
} else {
77-
warn!("xdg-shell surface missing wl_surface; cannot hide overlay");
78-
}
79-
}
80-
81-
self.overlay_hidden = hidden;
82-
hidden
83-
}
84-
85-
/// Restores the overlay after capture.
86-
///
87-
/// Returns `true` if the overlay was restored, `false` if it wasn't hidden.
88-
pub fn show_overlay(&mut self, surface: &mut SurfaceState) -> bool {
89-
if !self.overlay_hidden {
90-
warn!("Overlay was not hidden, nothing to restore");
91-
return false;
92-
}
93-
94-
info!("Restoring overlay after screenshot capture");
95-
96-
let width = surface.width();
97-
let height = surface.height();
98-
if let Some(layer_surface) = surface.layer_surface_mut() {
99-
layer_surface.set_size(width, height);
100-
let wl_surface = layer_surface.wl_surface();
101-
wl_surface.commit();
102-
} else if surface.is_xdg_window() {
103-
debug!("xdg-shell overlay will be restored on next render");
104-
}
105-
106-
self.overlay_hidden = false;
107-
true
108-
}
10941
}

0 commit comments

Comments
 (0)