Skip to content

Commit c49a0c6

Browse files
authored
Merge pull request #27 from devmobasa/feat-add-toolbars
Add icon-first toolbar UI
2 parents 704adc1 + 597f69b commit c49a0c6

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+4519
-833
lines changed

.gitignore

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ packaging/**
2424
!packaging/wayscriber.service
2525
!packaging/PKGBUILD
2626
!packaging/package.yaml
27-
!packaging/PKGBUILD.hyprmarker-meta
2827

2928
docs/board
3029

README.md

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -464,23 +464,10 @@ wayscriber/
464464
└── config.example.toml # Example configuration
465465
```
466466

467-
### Project History
468-
469-
Wayscriber shipped under the name **hyprmarker** through the v0.4 release line. The rename in v0.5.0 reflects the broader compositor support that has been built since the original Hyprland-only prototype. Use `wayscriber --migrate-config` to copy existing settings, and see **[docs/MIGRATION.md](docs/MIGRATION.md)** for the full compatibility checklist.
470-
471-
**Coming from hyprmarker?** Uninstall the old package (`paru -R hyprmarker`, etc.) and disable the legacy user service before installing Wayscriber:
472-
473-
```bash
474-
systemctl --user disable --now hyprmarker.service 2>/dev/null || true
475-
```
476-
477-
Then install Wayscriber and enable `wayscriber.service` if you want the daemon on login.
478-
479467
### Documentation
480468

481469
- **[docs/SETUP.md](docs/SETUP.md)** – system setup and installation details
482470
- **[docs/CONFIG.md](docs/CONFIG.md)** – configuration reference
483-
- **[docs/MIGRATION.md](docs/MIGRATION.md)** – guidance for migrating from hyprmarker
484471

485472
### Comparison with ZoomIt
486473

docs/codebase-overview.md

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,7 @@ This document explains how the application boots, how user input travels through
77
## 1. Execution Flow From `main.rs`
88

99
1. **CLI parsing (`src/main.rs`)**
10-
- Uses `clap` to parse `--daemon`, `--active`, `--mode`, and migration flags.
11-
- Immediately handles `--migrate-config` via `config::migrate_config` and exits.
10+
- Uses `clap` to parse `--daemon`, `--active`, `--mode`, and session management flags.
1211
- Verifies `WAYLAND_DISPLAY` when a Wayland session is required.
1312

1413
2. **Mode selection**
@@ -18,7 +17,6 @@ This document explains how the application boots, how user input travels through
1817

1918
3. **Shared subsystems automatically pulled in**
2019
- `config`: loads user settings, key bindings, and drawing defaults.
21-
- `legacy`: prints notices for old binary names and handles hyprmarker compatibility.
2220

2321
---
2422

@@ -126,10 +124,9 @@ Notifications are sent via `notification::send_notification_async`, keeping all
126124

127125
---
128126

129-
## 6. Configuration & Legacy Support
127+
## 6. Configuration
130128

131-
- **`src/config/`** handles loading `config.toml`, validating fields, and building the keybinding map. It also houses migration helpers shared with `main.rs`.
132-
- **`src/legacy.rs`** contains helpers to detect old binary names, environment overrides, and configurator paths. The daemon tray relies on it to launch the correct configurator binary.
129+
- **`src/config/`** handles loading `config.toml`, validating fields, and building the keybinding map.
133130

134131
---
135132

@@ -147,15 +144,14 @@ Notifications are sent via `notification::send_notification_async`, keeping all
147144

148145
| Path | Role |
149146
|------|------|
150-
| `src/main.rs` | CLI entry point, mode selection, migration trigger. |
147+
| `src/main.rs` | CLI entry point, mode selection. |
151148
| `src/daemon.rs` | Background daemon, tray menu, signal handling, overlay toggling. |
152149
| `src/backend/` | Wayland backend implementation split into bootstrap (`mod.rs`), runtime (`state.rs`), and input/render handlers. |
153150
| `src/input/` | Event/state machine for drawing tools, board modes, and capture triggers. |
154151
| `src/draw/` | Vector drawing primitives, canvases, fonts. |
155152
| `src/ui.rs` | Status/help overlays. |
156153
| `src/capture/` | Screenshot pipeline (manager, dependencies, sources, clipboard/file helpers). |
157-
| `src/config/` | Config parsing, defaults, migration helpers. |
158-
| `src/legacy.rs` | Compatibility notices and configurator path helpers. |
154+
| `src/config/` | Config parsing, defaults, keybinding map. |
159155
| `src/notification.rs` | Desktop notifications for capture results. |
160156
| `src/util.rs` | Shared math/color utilities. |
161157
| `tests/` | CLI + rendering integration tests. |
@@ -169,6 +165,6 @@ Notifications are sent via `notification::send_notification_async`, keeping all
169165
3. **Backend** sets up Wayland surfaces and loops, forwarding input to `InputState`.
170166
4. **InputState + draw/ui** update the overlay contents and request renders.
171167
5. **Capture** subsystem handles screenshot actions asynchronously and notifies the user.
172-
6. **Config/legacy** modules ensure user preferences and backward compatibility are honored everywhere.
168+
6. **Config** module ensures user preferences are honored everywhere.
173169

174170
Use this document to trace any feature: locate the entry point (CLI, tray, keybinding), follow it through the backend/input/capture stacks, and consult the relevant modules listed above for details.

packaging/PKGBUILD

Lines changed: 3 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,67 +1,8 @@
1-
# Maintainer: hyprarcher <[email protected]>
2-
pkgname=hyprmarker
3-
pkgver=0.4.0
4-
pkgrel=2
5-
pkgdesc='ZoomIt-like screen annotation tool for Wayland compositors with wlr-layer-shell support'
6-
arch=('x86_64' 'aarch64')
7-
url='https://github.com/devmobasa/hyprmarker'
8-
license=('MIT')
9-
depends=(
10-
'cairo'
11-
'wayland'
12-
'pango'
13-
'gcc-libs'
14-
'glibc'
15-
'wl-clipboard'
16-
'grim'
17-
'slurp'
18-
)
19-
makedepends=(
20-
'cargo'
21-
'git'
22-
)
23-
# Use GitHub as source
24-
source=("git+$url.git#tag=v$pkgver")
25-
sha256sums=('SKIP')
26-
27-
prepare() {
28-
cd "$pkgname"
29-
export RUSTUP_TOOLCHAIN=stable
30-
cargo fetch --locked --target "$CARCH-unknown-linux-gnu"
31-
}
32-
33-
build() {
34-
cd "$pkgname"
35-
export RUSTUP_TOOLCHAIN=stable
36-
export CARGO_TARGET_DIR=target
37-
cargo build --frozen --release --all-features
38-
cargo build --frozen --release --manifest-path configurator/Cargo.toml
39-
}
40-
41-
package() {
42-
cd "$pkgname"
43-
44-
# Install binary
45-
install -Dm755 "target/release/hyprmarker" "$pkgdir/usr/bin/hyprmarker"
46-
install -Dm755 "target/release/hyprmarker-configurator" "$pkgdir/usr/bin/hyprmarker-configurator"
47-
48-
# Install systemd user service
49-
install -Dm644 packaging/hyprmarker.service "$pkgdir/usr/lib/systemd/user/hyprmarker.service"
50-
51-
# Install example config
52-
install -Dm644 config.example.toml "$pkgdir/usr/share/doc/$pkgname/config.example.toml"
53-
54-
# Install documentation
55-
install -Dm644 README.md "$pkgdir/usr/share/doc/$pkgname/README.md"
56-
57-
# Install license if available
58-
[ -f LICENSE ] && install -Dm644 LICENSE "$pkgdir/usr/share/licenses/$pkgname/LICENSE" || true
59-
}
601
# Maintainer: wayscriber maintainers <[email protected]>
612
pkgname=wayscriber
62-
pkgver=0.6.0
63-
pkgrel=2
64-
pkgdesc='Screen annotation tool for Wayland compositors (formerly hyprmarker)'
3+
pkgver=0.8.0
4+
pkgrel=1
5+
pkgdesc='Screen annotation tool for Wayland compositors'
656
arch=('x86_64' 'aarch64')
667
url='https://wayscriber.com'
678
license=('MIT')
@@ -79,9 +20,6 @@ makedepends=(
7920
'cargo'
8021
'git'
8122
)
82-
provides=('hyprmarker')
83-
conflicts=('hyprmarker<0.6.0' 'hyprmarker-debug<0.6.0' 'wayscriber-debug<0.6.0')
84-
replaces=('hyprmarker' 'hyprmarker-debug' 'wayscriber-debug')
8523
source=("git+https://github.com/devmobasa/wayscriber.git#tag=v$pkgver")
8624
sha256sums=('SKIP')
8725

src/backend/wayland/backend.rs

Lines changed: 42 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use smithay_client_toolkit::{
1515
seat::SeatState,
1616
shell::{
1717
WaylandSurface,
18-
wlr_layer::{Anchor, KeyboardInteractivity, Layer, LayerShell},
18+
wlr_layer::{Anchor, Layer, LayerShell},
1919
xdg::{XdgShell, window::WindowDecorations},
2020
},
2121
shm::Shm,
@@ -37,7 +37,7 @@ use crate::{
3737
capture::{CaptureManager, CaptureOutcome},
3838
config::{Config, ConfigSource},
3939
input::{BoardMode, ClickHighlightSettings, InputState},
40-
legacy, notification, session,
40+
notification, session,
4141
};
4242

4343
fn friendly_capture_error(error: &str) -> String {
@@ -165,11 +165,6 @@ impl WaylandBackend {
165165
}
166166
};
167167

168-
if matches!(config_source, ConfigSource::Legacy(_)) && !legacy::warnings_suppressed() {
169-
warn!(
170-
"Continuing with settings from legacy hyprmarker config. Run `wayscriber --migrate-config` when convenient."
171-
);
172-
}
173168
info!("Configuration loaded");
174169
debug!(" Color: {:?}", config.drawing.default_color);
175170
debug!(" Thickness: {:.1}px", config.drawing.default_thickness);
@@ -245,6 +240,7 @@ impl WaylandBackend {
245240
let mut input_state = InputState::with_defaults(
246241
config.drawing.default_color.to_color(),
247242
config.drawing.default_thickness,
243+
config.drawing.default_fill_enabled,
248244
config.drawing.default_font_size,
249245
font_descriptor,
250246
config.drawing.text_background_enabled,
@@ -255,13 +251,30 @@ impl WaylandBackend {
255251
action_map,
256252
config.session.max_shapes_per_frame,
257253
ClickHighlightSettings::from(&config.ui.click_highlight),
254+
config.history.undo_all_delay_ms,
255+
config.history.redo_all_delay_ms,
256+
config.history.custom_section_enabled,
257+
config.history.custom_undo_delay_ms,
258+
config.history.custom_redo_delay_ms,
259+
config.history.custom_undo_steps,
260+
config.history.custom_redo_steps,
258261
);
259262

260263
input_state.set_hit_test_tolerance(config.drawing.hit_test_tolerance);
261264
input_state.set_hit_test_threshold(config.drawing.hit_test_linear_threshold);
262265
input_state.set_undo_stack_limit(config.drawing.undo_stack_limit);
263266
input_state.set_context_menu_enabled(config.ui.context_menu.enabled);
264267

268+
// Initialize toolbar visibility from pinned config
269+
input_state.init_toolbar_from_config(
270+
config.ui.toolbar.top_pinned,
271+
config.ui.toolbar.side_pinned,
272+
config.ui.toolbar.use_icons,
273+
config.ui.toolbar.show_more_colors,
274+
config.ui.toolbar.show_actions_section,
275+
config.ui.toolbar.show_delay_sliders,
276+
);
277+
265278
// Apply initial mode from CLI (if provided) or config default (only if board modes enabled)
266279
if config.board.enabled {
267280
let initial_mode_str = self
@@ -370,17 +383,16 @@ impl WaylandBackend {
370383

371384
// Configure the layer surface for fullscreen overlay
372385
layer_surface.set_anchor(Anchor::all());
373-
// NOTE: Using Exclusive keyboard interactivity for complete input capture
374-
// If clipboard operations are interrupted during overlay toggle, consider switching
375-
// to KeyboardInteractivity::OnDemand which cooperates better with other applications
376-
layer_surface.set_keyboard_interactivity(KeyboardInteractivity::Exclusive);
386+
let desired_keyboard_mode = state.desired_keyboard_interactivity();
387+
layer_surface.set_keyboard_interactivity(desired_keyboard_mode);
377388
layer_surface.set_size(0, 0); // Use full screen size
378389
layer_surface.set_exclusive_zone(-1);
379390

380391
// Commit the surface
381392
layer_surface.commit();
382393

383394
state.surface.set_layer_surface(layer_surface);
395+
state.current_keyboard_interactivity = Some(desired_keyboard_mode);
384396
info!("Layer shell surface created");
385397
} else if let Some(xdg_shell) = state.xdg_shell.as_ref() {
386398
info!("Layer shell missing; creating xdg-shell window");
@@ -408,12 +420,6 @@ impl WaylandBackend {
408420
));
409421
}
410422

411-
// Freeze on start if requested
412-
if self.freeze_on_start {
413-
info!("Freeze-on-start enabled, requesting frozen capture");
414-
state.input_state.request_frozen_toggle();
415-
}
416-
417423
// Track consecutive render failures for error recovery
418424
let mut consecutive_render_failures = 0u32;
419425
const MAX_RENDER_FAILURES: u32 = 10;
@@ -448,6 +454,20 @@ impl WaylandBackend {
448454
info!("Exit requested after dispatch, breaking event loop");
449455
break;
450456
}
457+
// Adjust keyboard interactivity if toolbar visibility changed.
458+
state.sync_toolbar_visibility(&qh);
459+
460+
// Advance any delayed history playback (undo/redo with delay).
461+
if state
462+
.input_state
463+
.tick_delayed_history(std::time::Instant::now())
464+
{
465+
state.toolbar.mark_dirty();
466+
state.input_state.needs_redraw = true;
467+
}
468+
if state.input_state.has_pending_history() {
469+
state.input_state.needs_redraw = true;
470+
}
451471
}
452472
Err(e) => {
453473
warn!("Event queue error: {}", e);
@@ -458,7 +478,9 @@ impl WaylandBackend {
458478

459479
if state.input_state.take_pending_frozen_toggle() {
460480
if !state.frozen_enabled {
461-
warn!("Frozen mode disabled on this compositor (xdg fallback); ignoring toggle");
481+
warn!(
482+
"Frozen mode disabled on this compositor (xdg fallback); ignoring toggle"
483+
);
462484
} else if state.frozen.is_in_progress() {
463485
warn!("Frozen capture already in progress; ignoring toggle");
464486
} else if state.input_state.frozen_active() {
@@ -562,7 +584,8 @@ impl WaylandBackend {
562584
Ok(keep_rendering) => {
563585
// Reset failure counter on successful render
564586
consecutive_render_failures = 0;
565-
state.input_state.needs_redraw = keep_rendering;
587+
state.input_state.needs_redraw =
588+
keep_rendering || state.input_state.has_pending_history();
566589
// Only set frame_callback_pending if vsync is enabled
567590
if state.config.performance.enable_vsync {
568591
state.surface.set_frame_callback_pending(true);

src/backend/wayland/frozen.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -643,6 +643,7 @@ mod tests {
643643
let mut input = InputState::with_defaults(
644644
crate::draw::color::RED,
645645
1.0,
646+
false,
646647
12.0,
647648
crate::draw::FontDescriptor::default(),
648649
false,
@@ -653,6 +654,13 @@ mod tests {
653654
std::collections::HashMap::new(),
654655
usize::MAX,
655656
crate::input::ClickHighlightSettings::disabled(),
657+
0,
658+
0,
659+
true,
660+
0,
661+
0,
662+
5,
663+
5,
656664
);
657665

658666
// Simulate an in-flight portal capture

src/backend/wayland/handlers/compositor.rs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,18 @@ impl CompositorHandler for WaylandState {
1616
_conn: &Connection,
1717
_qh: &QueueHandle<Self>,
1818
_surface: &wl_surface::WlSurface,
19-
_new_factor: i32,
19+
new_factor: i32,
2020
) {
21-
debug!("Scale factor changed");
21+
let scale = new_factor.max(1);
22+
debug!("Scale factor changed to {}", scale);
23+
self.surface.set_scale(scale);
24+
let (phys_w, phys_h) = self.surface.physical_dimensions();
25+
self.frozen
26+
.handle_resize(phys_w, phys_h, &mut self.input_state);
27+
self.toolbar
28+
.maybe_update_scale(self.surface.current_output().as_ref(), scale);
29+
self.toolbar.mark_dirty();
30+
self.input_state.needs_redraw = true;
2231
}
2332

2433
fn transform_changed(
@@ -65,6 +74,8 @@ impl CompositorHandler for WaylandState {
6574
if let Some(info) = self.output_state.info(output) {
6675
let scale = info.scale_factor.max(1);
6776
self.surface.set_scale(scale);
77+
self.toolbar.maybe_update_scale(Some(output), scale);
78+
self.toolbar.mark_dirty();
6879
let (logical_w, logical_h) = info
6980
.logical_size
7081
.unwrap_or((self.surface.width() as i32, self.surface.height() as i32));

src/backend/wayland/handlers/keyboard.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,9 @@ fn keysym_to_key(keysym: Keysym) -> Key {
157157
Keysym::K => Key::Char('K'),
158158
Keysym::z => Key::Char('z'),
159159
Keysym::Z => Key::Char('Z'),
160+
Keysym::F1 => Key::F1,
161+
Keysym::F2 => Key::F2,
162+
Keysym::F9 => Key::F9,
160163
Keysym::F10 => Key::F10,
161164
Keysym::F11 => Key::F11,
162165
Keysym::F12 => Key::F12,

0 commit comments

Comments
 (0)