Skip to content

Commit a5bdcd0

Browse files
committed
implement xdg-foreign to put capture dialog on top
1 parent be27e33 commit a5bdcd0

10 files changed

Lines changed: 166 additions & 43 deletions

File tree

‎Cargo.lock‎

Lines changed: 36 additions & 10 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎input-capture/src/lib.rs‎

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use std::{
22
collections::{HashMap, HashSet, VecDeque},
33
fmt::Display,
44
mem::swap,
5+
sync::{Arc, Mutex},
56
task::{Poll, ready},
67
};
78

@@ -129,6 +130,23 @@ pub struct InputCapture {
129130
pending: VecDeque<(CaptureHandle, CaptureEvent)>,
130131
}
131132

133+
#[derive(Clone, Debug)]
134+
pub enum WindowIdentifier {
135+
Wayland(String),
136+
X11(u32),
137+
}
138+
139+
impl Into<ashpd::WindowIdentifier> for WindowIdentifier {
140+
fn into(self) -> ashpd::WindowIdentifier {
141+
match self {
142+
WindowIdentifier::Wayland(handle) => {
143+
ashpd::WindowIdentifier::from_xdg_foreign_exported_v2(handle)
144+
}
145+
WindowIdentifier::X11(_) => todo!(),
146+
}
147+
}
148+
}
149+
132150
impl InputCapture {
133151
/// create a new client with the given id
134152
pub async fn create(&mut self, id: CaptureHandle, pos: Position) -> Result<(), CaptureError> {
@@ -177,8 +195,11 @@ impl InputCapture {
177195
}
178196

179197
/// creates a new [`InputCapture`]
180-
pub async fn new(backend: Option<Backend>) -> Result<Self, CaptureCreationError> {
181-
let capture = create(backend).await?;
198+
pub async fn new(
199+
backend: Option<Backend>,
200+
window_identifier: Arc<Mutex<Option<WindowIdentifier>>>,
201+
) -> Result<Self, CaptureCreationError> {
202+
let capture = create(backend, window_identifier).await?;
182203
Ok(Self {
183204
capture,
184205
id_map: Default::default(),
@@ -280,13 +301,16 @@ trait Capture: Stream<Item = Result<(Position, CaptureEvent), CaptureError>> + U
280301

281302
async fn create_backend(
282303
backend: Backend,
304+
window_identifier: Arc<Mutex<Option<WindowIdentifier>>>,
283305
) -> Result<
284306
Box<dyn Capture<Item = Result<(Position, CaptureEvent), CaptureError>>>,
285307
CaptureCreationError,
286308
> {
287309
match backend {
288310
#[cfg(all(unix, feature = "libei", not(target_os = "macos")))]
289-
Backend::InputCapturePortal => Ok(Box::new(libei::LibeiInputCapture::new().await?)),
311+
Backend::InputCapturePortal => Ok(Box::new(
312+
libei::LibeiInputCapture::new(window_identifier).await?,
313+
)),
290314
#[cfg(all(unix, feature = "layer_shell", not(target_os = "macos")))]
291315
Backend::LayerShell => Ok(Box::new(layer_shell::LayerShellInputCapture::new()?)),
292316
#[cfg(all(unix, feature = "x11", not(target_os = "macos")))]
@@ -301,12 +325,13 @@ async fn create_backend(
301325

302326
async fn create(
303327
backend: Option<Backend>,
328+
window_identifier: Arc<Mutex<Option<WindowIdentifier>>>,
304329
) -> Result<
305330
Box<dyn Capture<Item = Result<(Position, CaptureEvent), CaptureError>>>,
306331
CaptureCreationError,
307332
> {
308333
if let Some(backend) = backend {
309-
let b = create_backend(backend).await;
334+
let b = create_backend(backend, window_identifier).await;
310335
if b.is_ok() {
311336
log::info!("using capture backend: {backend}");
312337
}
@@ -325,7 +350,7 @@ async fn create(
325350
#[cfg(target_os = "macos")]
326351
Backend::MacOs,
327352
] {
328-
match create_backend(backend).await {
353+
match create_backend(backend, window_identifier.clone()).await {
329354
Ok(b) => {
330355
log::info!("using capture backend: {backend}");
331356
return Ok(b);

‎input-capture/src/libei.rs‎

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ use std::{
2323
os::unix::net::UnixStream,
2424
pin::Pin,
2525
rc::Rc,
26-
sync::Arc,
26+
sync::{Arc, Mutex},
2727
task::{Context, Poll},
2828
};
2929
use tokio::{
@@ -39,7 +39,7 @@ use futures_core::Stream;
3939

4040
use input_event::Event;
4141

42-
use crate::CaptureEvent;
42+
use crate::{CaptureEvent, WindowIdentifier};
4343

4444
use super::{
4545
Capture as LanMouseInputCapture, Position,
@@ -153,11 +153,15 @@ async fn update_barriers(
153153

154154
async fn create_session(
155155
input_capture: &InputCapture,
156+
window_identifier: Arc<Mutex<Option<WindowIdentifier>>>,
156157
) -> std::result::Result<(Session<InputCapture>, BitFlags<Capabilities>), ashpd::Error> {
157-
log::debug!("creating input capture session");
158+
let window_identifier = window_identifier.lock().unwrap().clone();
159+
log::debug!("creating input capture session: {window_identifier:?}");
160+
let ashpd_window_identifier: Option<ashpd::WindowIdentifier> =
161+
window_identifier.map(|i| i.into());
158162
input_capture
159163
.create_session(
160-
None,
164+
ashpd_window_identifier.as_ref(),
161165
Capabilities::Keyboard | Capabilities::Pointer | Capabilities::Touchscreen,
162166
)
163167
.await
@@ -202,10 +206,15 @@ async fn libei_event_handler(
202206
}
203207

204208
impl LibeiInputCapture {
205-
pub async fn new() -> std::result::Result<Self, LibeiCaptureCreationError> {
209+
/// creates a new libei input capture
210+
/// `window_id` is a window identifier for user prompts
211+
pub async fn new(
212+
window_identifier: Arc<Mutex<Option<WindowIdentifier>>>,
213+
) -> std::result::Result<Self, LibeiCaptureCreationError> {
206214
let input_capture = Box::pin(InputCapture::new().await?);
207215
let input_capture_ptr = input_capture.as_ref().get_ref() as *const InputCapture;
208-
let first_session = Some(create_session(unsafe { &*input_capture_ptr }).await?);
216+
let first_session =
217+
Some(create_session(unsafe { &*input_capture_ptr }, window_identifier.clone()).await?);
209218

210219
let (event_tx, event_rx) = mpsc::channel(1);
211220
let (notify_capture, notify_rx) = mpsc::channel(1);
@@ -220,6 +229,7 @@ impl LibeiInputCapture {
220229
first_session,
221230
event_tx,
222231
cancellation_token.clone(),
232+
window_identifier,
223233
);
224234
let capture_task = tokio::task::spawn_local(capture);
225235

@@ -244,6 +254,7 @@ async fn do_capture(
244254
session: Option<(Session<InputCapture>, BitFlags<Capabilities>)>,
245255
event_tx: Sender<(Position, CaptureEvent)>,
246256
cancellation_token: CancellationToken,
257+
window_identifier: Arc<Mutex<Option<WindowIdentifier>>>,
247258
) -> Result<(), CaptureError> {
248259
let mut session = session.map(|s| s.0);
249260

@@ -289,7 +300,11 @@ async fn do_capture(
289300
// create session
290301
let mut session = match session.take() {
291302
Some(s) => s,
292-
None => create_session(input_capture).await?.0,
303+
None => {
304+
create_session(input_capture, window_identifier.clone())
305+
.await?
306+
.0
307+
}
293308
};
294309

295310
let capture_session = do_capture_session(

‎lan-mouse-gtk/Cargo.toml‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,14 @@ repository = "https://github.com/feschber/lan-mouse"
88

99
[dependencies]
1010
gtk = { package = "gtk4", version = "0.9.0", features = ["v4_2"] }
11+
gdk4_wayland = { package = "gdk4-wayland", version="0.9.6" }
1112
adw = { package = "libadwaita", version = "0.7.0", features = ["v1_1"] }
1213
async-channel = { version = "2.1.1" }
1314
hostname = "0.4.0"
1415
log = "0.4.20"
1516
lan-mouse-ipc = { path = "../lan-mouse-ipc", version = "0.2.0" }
1617
thiserror = "2.0.0"
18+
wayland-client = "0.31.12"
1719

1820
[build-dependencies]
1921
glib-build-tools = { version = "0.20.0" }

‎lan-mouse-gtk/src/lib.rs‎

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use std::{env, process, str};
1010

1111
use window::Window;
1212

13-
use lan_mouse_ipc::FrontendEvent;
13+
use lan_mouse_ipc::{FrontendEvent, FrontendRequest, WindowIdentifier};
1414

1515
use adw::Application;
1616
use gtk::{IconTheme, gdk::Display, glib::clone, prelude::*};
@@ -19,6 +19,8 @@ use gtk::{gio, glib, prelude::ApplicationExt};
1919
use self::client_object::ClientObject;
2020
use self::key_object::KeyObject;
2121

22+
use gdk4_wayland::WaylandToplevel;
23+
2224
use thiserror::Error;
2325

2426
#[derive(Error, Debug)]
@@ -124,6 +126,27 @@ fn build_ui(app: &Application) {
124126

125127
let window = Window::new(app, frontend_tx);
126128

129+
// export TopLevel handle and send it to the service so that it can put the InpuCapture / RemoteDesktop
130+
// windows on top of it using xdg-foreign.
131+
window.connect_show(|window| {
132+
// needs the surface so we have to present first!
133+
if let Some(surface) = window.surface() {
134+
if surface.display().backend().is_wayland() {
135+
// let surface = surface.downcast::<WaylandSurface>();
136+
let toplevel = surface.downcast::<WaylandToplevel>().expect("xdg-toplevel");
137+
let window = window.clone();
138+
toplevel.export_handle(move |_toplevel, handle| {
139+
if let Ok(handle) = handle {
140+
let handle = handle.to_string();
141+
window.request(FrontendRequest::WindowIdentifier(
142+
WindowIdentifier::Wayland(handle),
143+
));
144+
}
145+
});
146+
}
147+
}
148+
});
149+
127150
glib::spawn_future_local(clone!(
128151
#[weak]
129152
window,

‎lan-mouse-gtk/src/window.rs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -422,7 +422,7 @@ impl Window {
422422
self.request(FrontendRequest::RemoveAuthorizedKey(fp));
423423
}
424424

425-
fn request(&self, request: FrontendRequest) {
425+
pub(crate) fn request(&self, request: FrontendRequest) {
426426
let mut requester = self.imp().frontend_request_writer.borrow_mut();
427427
let requester = requester.as_mut().unwrap();
428428
if let Err(e) = requester.request(request) {

‎lan-mouse-ipc/src/lib.rs‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,14 @@ pub enum FrontendRequest {
255255
UpdateEnterHook(u64, Option<String>),
256256
/// save config file
257257
SaveConfiguration,
258+
/// window identifier used to present input-capture / remote-desktop prompts
259+
WindowIdentifier(WindowIdentifier),
260+
}
261+
262+
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
263+
pub enum WindowIdentifier {
264+
Wayland(String),
265+
X11(u32),
258266
}
259267

260268
#[derive(Clone, Copy, PartialEq, Eq, Debug, Default, Serialize, Deserialize)]

0 commit comments

Comments
 (0)