Skip to content

Commit c75fbdb

Browse files
committed
feat(volume-keys): capture on hardware volume buttons
1 parent 5d059d6 commit c75fbdb

6 files changed

Lines changed: 401 additions & 1 deletion

File tree

io.github.cosmic_utils.camera.yml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,10 @@ finish-args:
1717
- --socket=fallback-x11
1818
# Webcam access (currently no better option than this)
1919
# https://docs.flatpak.org/en/latest/sandbox-permissions.html#device-access
20-
# Automatically includes GPU support since "dri" is part of "all"
20+
# Automatically includes GPU support since "dri" is part of "all".
21+
# Also exposes /dev/input/event*, which the haptic backend uses for
22+
# vibration and the volume_keys backend reads as a shutter trigger on
23+
# phones.
2124
- --device=all
2225
# Home directory access for saving photos/videos
2326
- --filesystem=xdg-pictures:rw

src/app/mod.rs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1588,6 +1588,43 @@ impl cosmic::Application for AppModel {
15881588
)
15891589
};
15901590

1591+
// Volume-key shutter on phones: the compositor swallows the keys
1592+
// for system audio so they never reach iced as keyboard events.
1593+
// The `volume_keys` backend reads `/dev/input/event*` directly,
1594+
// grabs each device exclusively while the window is focused
1595+
// (so the compositor doesn't *also* change the volume) and
1596+
// forwards presses as `Message::Capture` — same as the spacebar.
1597+
let volume_keys_sub = subscription_with_id(
1598+
"volume_keys",
1599+
cosmic::iced::stream::channel(8, async move |mut output| {
1600+
let mut rx = crate::backends::volume_keys::start();
1601+
while let Some(_key) = rx.recv().await {
1602+
if output.send(Message::Capture).await.is_err() {
1603+
break;
1604+
}
1605+
}
1606+
}),
1607+
);
1608+
1609+
// Translate Wayland keyboard-focus transitions for the camera
1610+
// window into `WindowFocusChanged` messages; the update handler
1611+
// forwards them into the volume_keys backend so we only grab the
1612+
// hardware shutter buttons while the camera is in focus.
1613+
let window_focus_sub = subscription::filter_map("window_focus", |event| {
1614+
let subscription::Event::Interaction { event, .. } = event else {
1615+
return None;
1616+
};
1617+
match event {
1618+
cosmic::iced::Event::Window(cosmic::iced::window::Event::Focused) => {
1619+
Some(Message::WindowFocusChanged(true))
1620+
}
1621+
cosmic::iced::Event::Window(cosmic::iced::window::Event::Unfocused) => {
1622+
Some(Message::WindowFocusChanged(false))
1623+
}
1624+
_ => None,
1625+
}
1626+
});
1627+
15911628
Subscription::batch([
15921629
config_sub,
15931630
camera_sub,
@@ -1602,6 +1639,8 @@ impl cosmic::Application for AppModel {
16021639
audio_level_sub,
16031640
portal_theme_sub,
16041641
keybind_sub,
1642+
volume_keys_sub,
1643+
window_focus_sub,
16051644
])
16061645
}
16071646

src/app/state.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1575,6 +1575,10 @@ pub enum Message {
15751575
FitAnimationTick,
15761576
/// Animation tick for the zoom-reset transition
15771577
ZoomAnimationTick,
1578+
/// Wayland keyboard-focus changed for the app window. Toggles the
1579+
/// volume-key `EVIOCGRAB` and dispatch gate so we only consume the
1580+
/// hardware shutter buttons while the camera is in focus.
1581+
WindowFocusChanged(bool),
15781582
/// Window control: close
15791583
WindowClose,
15801584
/// Window control: minimize

src/app/update.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,11 @@ impl AppModel {
215215
self.zoom_animation.map(|a| a.start.elapsed()),
216216
|s| s.zoom_animation = None,
217217
),
218+
Message::WindowFocusChanged(focused) => {
219+
info!(focused, "WindowFocusChanged");
220+
crate::backends::volume_keys::set_focused(focused);
221+
Task::none()
222+
}
218223
Message::WindowClose => self.handle_window_close(),
219224
Message::WindowMinimize => self.core.minimize(None),
220225
Message::WindowToggleMaximize => self.core.toggle_maximize(None),

src/backends/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,3 +40,4 @@ pub mod audio;
4040
pub mod camera;
4141
pub mod haptic;
4242
pub mod virtual_camera;
43+
pub mod volume_keys;

0 commit comments

Comments
 (0)