Commit 1576c9318a4a
tarball5 files changed+720-694
@@ -1,190 +1,111 @@
1
-//! Host input forwarded into monolith's seat.1
+//! Input handling shared by every backend. Backends translate their events2
+//! into these calls, with pointer positions in global logical coordinates.23
34
use crate::state::State;45
use monolith_config::Modifier;56
use monolith_layout::{Rect, WindowId};6
-use smithay::backend::input::{Axis, ButtonState, InputTime, KeyState};7
+use smithay::backend::input::{ButtonState, InputTime, KeyState};78
use smithay::input::keyboard::{FilterResult, Keycode};89
use smithay::input::pointer::{AxisFrame, ButtonEvent, MotionEvent};9
-use smithay::utils::Logical;10
-use smithay::utils::{Point, SERIAL_COUNTER};11
-use wayland_client::protocol::{wl_keyboard, wl_pointer, wl_seat};12
-use wayland_client::{Connection, Dispatch, QueueHandle, WEnum};10
+use smithay::utils::{Logical, Point, SERIAL_COUNTER};1311
14
-impl Dispatch<wl_seat::WlSeat, ()> for State {15
- fn event(16
- _: &mut Self,17
- seat: &wl_seat::WlSeat,18
- event: wl_seat::Event,19
- _: &(),20
- _: &Connection,21
- qh: &QueueHandle<Self>,22
- ) {23
- if let wl_seat::Event::Capabilities {24
- capabilities: WEnum::Value(caps),25
- } = event26
- {27
- if caps.contains(wl_seat::Capability::Pointer) {28
- seat.get_pointer(qh, ());29
- }30
- if caps.contains(wl_seat::Capability::Keyboard) {31
- seat.get_keyboard(qh, ());32
- }12
+impl State {13
+ pub fn pointer_to(&mut self, pos: Point<f64, Logical>, time: InputTime) {14
+ self.pointer_pos = pos;15
+ if drag_to(self, pos) {16
+ return;3317
}18
+ let Some(pointer) = self.seat.get_pointer() else {19
+ return;20
+ };21
+ let under = self.surface_under(pos);22
+ pointer.motion(23
+ self,24
+ under,25
+ &MotionEvent {26
+ location: pos,27
+ serial: SERIAL_COUNTER.next_serial(),28
+ time,29
+ },30
+ );3431
}35
-}3632
37
-impl Dispatch<wl_pointer::WlPointer, ()> for State {38
- fn event(39
- state: &mut Self,40
- _: &wl_pointer::WlPointer,41
- event: wl_pointer::Event,42
- _: &(),43
- _: &Connection,44
- _: &QueueHandle<Self>,45
- ) {46
- let Some(pointer) = state.seat.get_pointer() else {33
+ pub fn pointer_button(&mut self, button: u32, pressed: ButtonState, time: InputTime) {34
+ if pressed == ButtonState::Released && self.drag.take().is_some() {4735
return;48
- };49
- let time = InputTime::now();50
- match event {51
- wl_pointer::Event::Enter {52
- surface_x,53
- surface_y,54
- ..55
- }56
- | wl_pointer::Event::Motion {57
- surface_x,58
- surface_y,59
- ..60
- } => {61
- let pos = Point::from((surface_x, surface_y));62
- if let Some(host) = state.host.as_mut() {63
- host.pointer_pos = pos;64
- }65
- if drag_to(state, pos) {66
- return;67
- }68
- let under = state.surface_under(pos);69
- pointer.motion(70
- state,71
- under,72
- &MotionEvent {73
- location: pos,74
- serial: SERIAL_COUNTER.next_serial(),75
- time,76
- },77
- );36
+ }37
+ if pressed == ButtonState::Pressed {38
+ focus_under_pointer(self);39
+ if start_drag(self, button) {40
+ return;7841
}79
- wl_pointer::Event::Button {42
+ }43
+ let Some(pointer) = self.seat.get_pointer() else {44
+ return;45
+ };46
+ pointer.button(47
+ self,48
+ &ButtonEvent {8049
button,81
- state: WEnum::Value(pressed),82
- ..83
- } => {84
- let serial = SERIAL_COUNTER.next_serial();85
- let button_state = match pressed {86
- wl_pointer::ButtonState::Pressed => ButtonState::Pressed,87
- _ => ButtonState::Released,88
- };89
- if button_state == ButtonState::Released && state.drag.take().is_some() {90
- return;91
- }92
- if button_state == ButtonState::Pressed {93
- focus_under_pointer(state);94
- if start_drag(state, button) {95
- return;96
- }97
- }98
- pointer.button(99
- state,100
- &ButtonEvent {101
- button,102
- state: button_state,103
- serial,104
- time,105
- },106
- );107
- }108
- wl_pointer::Event::Axis {109
- axis: WEnum::Value(axis),110
- value,111
- ..112
- } => {113
- let axis = match axis {114
- wl_pointer::Axis::HorizontalScroll => Axis::Horizontal,115
- _ => Axis::Vertical,116
- };117
- pointer.axis(state, AxisFrame::new(time).value(axis, value));118
- }119
- wl_pointer::Event::Frame => pointer.frame(state),120
- _ => {}50
+ state: pressed,51
+ serial: SERIAL_COUNTER.next_serial(),52
+ time,53
+ },54
+ );55
+ }56
+57
+ pub fn pointer_axis(&mut self, frame: AxisFrame) {58
+ if let Some(pointer) = self.seat.get_pointer() {59
+ pointer.axis(self, frame);12160
}12261
}123
-}12462
125
-impl Dispatch<wl_keyboard::WlKeyboard, ()> for State {126
- fn event(127
- state: &mut Self,128
- _: &wl_keyboard::WlKeyboard,129
- event: wl_keyboard::Event,130
- _: &(),131
- _: &Connection,132
- _: &QueueHandle<Self>,133
- ) {134
- let Some(keyboard) = state.seat.get_keyboard() else {63
+ pub fn pointer_frame(&mut self) {64
+ if let Some(pointer) = self.seat.get_pointer() {65
+ pointer.frame(self);66
+ }67
+ }68
+69
+ pub fn key(&mut self, code: Keycode, pressed: KeyState, time: InputTime) {70
+ let Some(keyboard) = self.seat.get_keyboard() else {13571
return;13672
};137
- if let wl_keyboard::Event::Key {138
- key,139
- state: WEnum::Value(pressed),140
- ..141
- } = event142
- {143
- let key_state = match pressed {144
- wl_keyboard::KeyState::Pressed => KeyState::Pressed,145
- _ => KeyState::Released,146
- };147
- let time = InputTime::now();148
- let code = Keycode::new(key + 8);149
- let action = keyboard.input(150
- state,151
- code,152
- key_state,153
- SERIAL_COUNTER.next_serial(),154
- time,155
- |state, mods, handle| {156
- if key_state == KeyState::Released {157
- let before = state.suppressed.len();158
- state.suppressed.retain(|k| *k != code);159
- return if state.suppressed.len() < before {160
- FilterResult::Intercept(None)161
- } else {162
- FilterResult::Forward163
- };164
- }165
- match state.binds.find(mods, &handle.raw_syms()) {166
- Some(cmd) => {167
- tracing::debug!(?cmd, "bind");168
- state.suppressed.push(code);169
- FilterResult::Intercept(Some(cmd))170
- }171
- None => FilterResult::Forward,73
+ let action = keyboard.input(74
+ self,75
+ code,76
+ pressed,77
+ SERIAL_COUNTER.next_serial(),78
+ time,79
+ |state, mods, handle| {80
+ if pressed == KeyState::Released {81
+ let before = state.suppressed.len();82
+ state.suppressed.retain(|k| *k != code);83
+ return if state.suppressed.len() < before {84
+ FilterResult::Intercept(None)85
+ } else {86
+ FilterResult::Forward87
+ };88
+ }89
+ match state.binds.find(mods, &handle.raw_syms()) {90
+ Some(cmd) => {91
+ tracing::debug!(?cmd, "bind");92
+ state.suppressed.push(code);93
+ FilterResult::Intercept(Some(cmd))17294
}173
- },174
- );175
- if let Some(Some(cmd)) = action176
- && let Err(e) = state.run(cmd)177
- {178
- tracing::warn!("bind failed: {e}");179
- }95
+ None => FilterResult::Forward,96
+ }97
+ },98
+ );99
+ if let Some(Some(cmd)) = action100
+ && let Err(e) = self.run(cmd)101
+ {102
+ tracing::warn!("bind failed: {e}");180103
}181104
}182105
}183106
184107
fn focus_under_pointer(state: &mut State) {185
- let Some(pos) = state.host.as_ref().map(|h| h.pointer_pos) else {186
- return;187
- };108
+ let pos = state.pointer_pos;188109
let id = state189110
.space190111
.element_under(pos)@@ -223,9 +144,6 @@ fn start_drag(state: &mut State, button: u32) -> bool {
223144
Modifier::Ctrl => m.ctrl,224145
Modifier::Shift => m.shift,225146
});226
- let Some(from) = state.host.as_ref().map(|h| h.pointer_pos) else {227
- return false;228
- };229147
let Some(id) = state.layout.focused() else {230148
return false;231149
};@@ -242,7 +160,7 @@ fn start_drag(state: &mut State, button: u32) -> bool {
242160
}243161
state.drag = Some(Drag {244162
window: id,245
- from,163
+ from: state.pointer_pos,246164
rect,247165
resize: button == BTN_RIGHT,248166
});@@ -1,527 +0,0 @@
1
-//! Runs monolith as a window inside another Wayland compositor. Frames are2
-//! rendered into dmabufs from our own swapchain and handed to the host3
-//! through linux-dmabuf, so the nested path exercises the same4
-//! render-into-dmabuf code as bare metal.5
-6
-use crate::state::State;7
-use calloop_wayland_source::WaylandSource;8
-use monolith_layout::Rect;9
-use monolith_render_vk::VulkanAllocator;10
-use smithay::backend::allocator::dmabuf::Dmabuf;11
-use smithay::backend::allocator::{Format, Fourcc, Modifier, Slot, Swapchain};12
-use smithay::backend::renderer::Bind;13
-use smithay::backend::renderer::damage::OutputDamageTracker;14
-use smithay::output::{Mode, Output, PhysicalProperties, Subpixel};15
-use smithay::reexports::calloop::EventLoop;16
-use smithay::utils::{Logical, Point, Size, Transform};17
-use std::time::Duration;18
-use wayland_client::globals::{GlobalListContents, registry_queue_init};19
-use wayland_client::protocol::{wl_buffer, wl_callback, wl_compositor, wl_registry, wl_surface};20
-use wayland_client::{Connection, Dispatch, QueueHandle, delegate_noop};21
-use wayland_protocols::wp::linux_dmabuf::zv1::client::{22
- zwp_linux_buffer_params_v1, zwp_linux_dmabuf_v1,23
-};24
-use wayland_protocols::wp::presentation_time::client::{wp_presentation, wp_presentation_feedback};25
-use wayland_protocols::xdg::decoration::zv1::client::{26
- zxdg_decoration_manager_v1, zxdg_toplevel_decoration_v1,27
-};28
-use wayland_protocols::xdg::shell::client::{xdg_surface, xdg_toplevel, xdg_wm_base};29
-30
-const CLEAR: [f32; 4] = [0.07, 0.075, 0.09, 1.0];31
-const DEFAULT_SIZE: (i32, i32) = (1280, 800);32
-const CLOCK_MONOTONIC: u32 = 1;33
-34
-pub struct Host {35
- pub size: Size<i32, Logical>,36
- pub pointer_pos: Point<f64, Logical>,37
- pub output: Output,38
- conn: Connection,39
- qh: QueueHandle<State>,40
- surface: wl_surface::WlSurface,41
- _xdg_surface: xdg_surface::XdgSurface,42
- _toplevel: xdg_toplevel::XdgToplevel,43
- dmabuf: zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1,44
- formats: Vec<Format>,45
- swapchain: Option<Swapchain<VulkanAllocator>>,46
- in_flight: Vec<(wl_buffer::WlBuffer, Slot<Dmabuf>)>,47
- damage: OutputDamageTracker,48
- configured: bool,49
- frame_pending: bool,50
- pending_size: Option<Size<i32, Logical>>,51
- presentation: Option<wp_presentation::WpPresentation>,52
- /// The host clock is CLOCK_MONOTONIC, so its timestamps share our timeline.53
- monotonic: bool,54
- last_present_ns: Option<u64>,55
- refresh_ns: u64,56
-}57
-58
-impl Host {59
- /// When a frame committed now will reach the screen, the next host60
- /// vblank after `now`.61
- pub fn predict(&self, now: u64) -> u64 {62
- match self.last_present_ns.filter(|_| self.monotonic) {63
- Some(last) if self.refresh_ns > 0 && last <= now => {64
- last + ((now - last) / self.refresh_ns + 1) * self.refresh_ns65
- }66
- _ => now,67
- }68
- }69
-}70
-71
-/// Destroys the host-side buffer when its swapchain slot goes away.72
-struct HostBuffer(wl_buffer::WlBuffer);73
-74
-impl Drop for HostBuffer {75
- fn drop(&mut self) {76
- self.0.destroy();77
- }78
-}79
-80
-pub fn init(81
- event_loop: &mut EventLoop<State>,82
- state: &mut State,83
-) -> Result<(), Box<dyn std::error::Error>> {84
- let conn = Connection::connect_to_env()?;85
- let (globals, mut queue) = registry_queue_init::<State>(&conn)?;86
- let qh = queue.handle();87
- let compositor: wl_compositor::WlCompositor = globals.bind(&qh, 4..=6, ())?;88
- let wm_base: xdg_wm_base::XdgWmBase = globals.bind(&qh, 1..=6, ())?;89
- let dmabuf: zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1 = globals.bind(&qh, 3..=3, ())?;90
- let presentation = globals91
- .bind::<wp_presentation::WpPresentation, _, _>(&qh, 1..=2, ())92
- .ok();93
- // Dropping a proxy handle does not destroy it, so seat events keep reaching input.rs.94
- let _ = globals.bind::<wayland_client::protocol::wl_seat::WlSeat, _, _>(&qh, 1..=7, ());95
-96
- let surface = compositor.create_surface(&qh, ());97
- let xdg_surface = wm_base.get_xdg_surface(&surface, &qh, ());98
- let toplevel = xdg_surface.get_toplevel(&qh, ());99
- toplevel.set_title("monolith".into());100
- toplevel.set_app_id("monolith".into());101
- if let Ok(deco) =102
- globals.bind::<zxdg_decoration_manager_v1::ZxdgDecorationManagerV1, _, _>(&qh, 1..=1, ())103
- {104
- deco.get_toplevel_decoration(&toplevel, &qh, ())105
- .set_mode(zxdg_toplevel_decoration_v1::Mode::ServerSide);106
- }107
- surface.commit();108
-109
- let output = Output::new(110
- "nested".into(),111
- PhysicalProperties {112
- size: (0, 0).into(),113
- subpixel: Subpixel::Unknown,114
- make: "monolith".into(),115
- model: "nested".into(),116
- serial_number: "0".into(),117
- },118
- );119
- let _global = output.create_global::<State>(&state.display);120
- let mode = Mode {121
- size: DEFAULT_SIZE.into(),122
- refresh: 60_000,123
- };124
- output.change_current_state(125
- Some(mode),126
- Some(Transform::Normal),127
- None,128
- Some((0, 0).into()),129
- );130
- output.set_preferred(mode);131
- state.space.map_output(&output, (0, 0));132
- let area = Rect::new(133
- 0.0,134
- 0.0,135
- f64::from(DEFAULT_SIZE.0),136
- f64::from(DEFAULT_SIZE.1),137
- );138
- state.output_id = Some(state.layout.add_output("nested", area));139
-140
- state.host = Some(Host {141
- size: DEFAULT_SIZE.into(),142
- pointer_pos: (0.0, 0.0).into(),143
- damage: OutputDamageTracker::from_output(&output),144
- output,145
- conn: conn.clone(),146
- qh,147
- surface,148
- _xdg_surface: xdg_surface,149
- _toplevel: toplevel,150
- dmabuf,151
- formats: Vec::new(),152
- swapchain: None,153
- in_flight: Vec::new(),154
- configured: false,155
- frame_pending: false,156
- pending_size: None,157
- presentation,158
- monotonic: false,159
- last_present_ns: None,160
- refresh_ns: 16_666_667,161
- });162
- queue.roundtrip(state)?;163
- queue.roundtrip(state)?;164
- WaylandSource::new(conn, queue).insert(event_loop.handle())?;165
- Ok(())166
-}167
-168
-/// Renders a frame if something changed and the host is ready for one.169
-pub fn render(state: &mut State) {170
- let State {171
- host,172
- renderer,173
- space,174
- needs_redraw,175
- start,176
- layout,177
- windows,178
- anim,179
- ..180
- } = state;181
- let Some(host) = host.as_mut() else { return };182
- if !host.configured || host.frame_pending || !*needs_redraw {183
- return;184
- }185
- if let Some(size) = host.pending_size.take() {186
- host.size = size;187
- let mode = Mode {188
- size: (size.w, size.h).into(),189
- refresh: (1_000_000_000_000 / host.refresh_ns) as i32,190
- };191
- host.output192
- .change_current_state(Some(mode), None, None, None);193
- host.output.set_preferred(mode);194
- if let Some(sc) = host.swapchain.as_mut() {195
- sc.resize(size.w as u32, size.h as u32);196
- }197
- }198
- let now = host.predict(crate::state::now_ns());199
- if host.swapchain.is_none() {200
- host.swapchain = make_swapchain(renderer, &host.formats, host.size);201
- }202
- let Some(swapchain) = host.swapchain.as_mut() else {203
- return;204
- };205
- let slot = match swapchain.acquire() {206
- Ok(Some(slot)) => slot,207
- Ok(None) => return,208
- Err(e) => {209
- tracing::warn!("failed to allocate a frame buffer: {e}");210
- return;211
- }212
- };213
- let mut dmabuf = (*slot).clone();214
- let damage = {215
- let mut fb = match renderer.bind(&mut dmabuf) {216
- Ok(fb) => fb,217
- Err(e) => {218
- tracing::warn!("cannot render into the swapchain: {e}");219
- return;220
- }221
- };222
- let elements = crate::scene::build(layout, windows, anim, renderer, now);223
- match host224
- .damage225
- .render_output(renderer, &mut fb, slot.age() as usize, &elements, CLEAR)226
- {227
- Ok(result) => {228
- if let Err(e) = result.sync.wait() {229
- tracing::warn!("waiting for the frame failed: {e:?}");230
- }231
- result.damage.cloned()232
- }233
- Err(e) => {234
- tracing::warn!("rendering failed: {e:?}");235
- return;236
- }237
- }238
- };239
- *needs_redraw = anim.active(now);240
- anim.prune(now);241
- let elapsed = start.elapsed();242
- for window in space.elements() {243
- window.send_frame(&host.output, elapsed, Some(Duration::ZERO), |_, _| {244
- Some(host.output.clone())245
- });246
- }247
- let Some(damage) = damage else {248
- if *needs_redraw {249
- host.surface.frame(&host.qh, ());250
- host.surface.commit();251
- host.frame_pending = true;252
- let _ = host.conn.flush();253
- }254
- return;255
- };256
- let buffer = match slot.userdata().get::<HostBuffer>() {257
- Some(b) => b.0.clone(),258
- None => {259
- let b = host_buffer(&host.dmabuf, &host.qh, &dmabuf);260
- slot.userdata().insert_if_missing(|| HostBuffer(b.clone()));261
- b262
- }263
- };264
- host.surface.attach(Some(&buffer), 0, 0);265
- for r in &damage {266
- host.surface267
- .damage_buffer(r.loc.x, r.loc.y, r.size.w, r.size.h);268
- }269
- host.surface.frame(&host.qh, ());270
- if let Some(p) = &host.presentation {271
- p.feedback(&host.surface, &host.qh, ());272
- }273
- host.surface.commit();274
- swapchain.submitted(&slot);275
- host.in_flight.push((buffer, slot));276
- host.frame_pending = true;277
- let _ = host.conn.flush();278
-}279
-280
-fn make_swapchain(281
- renderer: &monolith_render_vk::VulkanRenderer,282
- host: &[Format],283
- size: Size<i32, Logical>,284
-) -> Option<Swapchain<VulkanAllocator>> {285
- let ours = renderer.render_formats();286
- for fourcc in [Fourcc::Xrgb8888, Fourcc::Argb8888] {287
- let modifiers: Vec<Modifier> = host288
- .iter()289
- .filter(|f| f.code == fourcc && ours.contains(f))290
- .map(|f| f.modifier)291
- .collect();292
- if !modifiers.is_empty() {293
- return Some(Swapchain::new(294
- renderer.allocator(),295
- size.w as u32,296
- size.h as u32,297
- fourcc,298
- modifiers,299
- ));300
- }301
- }302
- tracing::error!("the host compositor and the renderer share no dmabuf format");303
- None304
-}305
-306
-fn host_buffer(307
- dmabuf: &zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1,308
- qh: &QueueHandle<State>,309
- buf: &Dmabuf,310
-) -> wl_buffer::WlBuffer {311
- use smithay::backend::allocator::Buffer;312
- let params = dmabuf.create_params(qh, ());313
- let modifier = u64::from(buf.format().modifier);314
- for (i, ((fd, offset), stride)) in buf315
- .handles()316
- .zip(buf.offsets())317
- .zip(buf.strides())318
- .enumerate()319
- {320
- params.add(321
- fd,322
- i as u32,323
- offset,324
- stride,325
- (modifier >> 32) as u32,326
- modifier as u32,327
- );328
- }329
- let buffer = params.create_immed(330
- buf.width() as i32,331
- buf.height() as i32,332
- buf.format().code as u32,333
- zwp_linux_buffer_params_v1::Flags::empty(),334
- qh,335
- (),336
- );337
- params.destroy();338
- buffer339
-}340
-341
-impl Dispatch<wl_registry::WlRegistry, GlobalListContents> for State {342
- fn event(343
- _: &mut Self,344
- _: &wl_registry::WlRegistry,345
- _: wl_registry::Event,346
- _: &GlobalListContents,347
- _: &Connection,348
- _: &QueueHandle<Self>,349
- ) {350
- }351
-}352
-353
-impl Dispatch<xdg_wm_base::XdgWmBase, ()> for State {354
- fn event(355
- _: &mut Self,356
- base: &xdg_wm_base::XdgWmBase,357
- event: xdg_wm_base::Event,358
- _: &(),359
- _: &Connection,360
- _: &QueueHandle<Self>,361
- ) {362
- if let xdg_wm_base::Event::Ping { serial } = event {363
- base.pong(serial);364
- }365
- }366
-}367
-368
-impl Dispatch<xdg_surface::XdgSurface, ()> for State {369
- fn event(370
- state: &mut Self,371
- surface: &xdg_surface::XdgSurface,372
- event: xdg_surface::Event,373
- _: &(),374
- _: &Connection,375
- _: &QueueHandle<Self>,376
- ) {377
- if let xdg_surface::Event::Configure { serial } = event {378
- surface.ack_configure(serial);379
- if let Some(host) = state.host.as_mut() {380
- host.configured = true;381
- }382
- state.needs_redraw = true;383
- }384
- }385
-}386
-387
-impl Dispatch<xdg_toplevel::XdgToplevel, ()> for State {388
- fn event(389
- state: &mut Self,390
- _: &xdg_toplevel::XdgToplevel,391
- event: xdg_toplevel::Event,392
- _: &(),393
- _: &Connection,394
- _: &QueueHandle<Self>,395
- ) {396
- match event {397
- xdg_toplevel::Event::Configure { width, height, .. } if width > 0 && height > 0 => {398
- let size = Size::from((width, height));399
- if let Some(host) = state.host.as_mut()400
- && host.size != size401
- {402
- host.pending_size = Some(size);403
- host.size = size;404
- state.resize_output(size);405
- }406
- }407
- xdg_toplevel::Event::Close => state.signal.stop(),408
- _ => {}409
- }410
- }411
-}412
-413
-impl Dispatch<zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1, ()> for State {414
- fn event(415
- state: &mut Self,416
- _: &zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1,417
- event: zwp_linux_dmabuf_v1::Event,418
- _: &(),419
- _: &Connection,420
- _: &QueueHandle<Self>,421
- ) {422
- if let zwp_linux_dmabuf_v1::Event::Modifier {423
- format,424
- modifier_hi,425
- modifier_lo,426
- } = event427
- && let (Ok(code), Some(host)) = (Fourcc::try_from(format), state.host.as_mut())428
- {429
- let modifier = Modifier::from((u64::from(modifier_hi) << 32) | u64::from(modifier_lo));430
- host.formats.push(Format { code, modifier });431
- }432
- }433
-}434
-435
-impl Dispatch<wl_buffer::WlBuffer, ()> for State {436
- fn event(437
- state: &mut Self,438
- buffer: &wl_buffer::WlBuffer,439
- event: wl_buffer::Event,440
- _: &(),441
- _: &Connection,442
- _: &QueueHandle<Self>,443
- ) {444
- if let wl_buffer::Event::Release = event445
- && let Some(host) = state.host.as_mut()446
- {447
- host.in_flight.retain(|(b, _)| b != buffer);448
- }449
- }450
-}451
-452
-impl Dispatch<wl_callback::WlCallback, ()> for State {453
- fn event(454
- state: &mut Self,455
- _: &wl_callback::WlCallback,456
- event: wl_callback::Event,457
- _: &(),458
- _: &Connection,459
- _: &QueueHandle<Self>,460
- ) {461
- if let wl_callback::Event::Done { .. } = event462
- && let Some(host) = state.host.as_mut()463
- {464
- host.frame_pending = false;465
- }466
- }467
-}468
-469
-delegate_noop!(State: wl_compositor::WlCompositor);470
-delegate_noop!(State: ignore wl_surface::WlSurface);471
-delegate_noop!(State: ignore zwp_linux_buffer_params_v1::ZwpLinuxBufferParamsV1);472
-delegate_noop!(State: zxdg_decoration_manager_v1::ZxdgDecorationManagerV1);473
-delegate_noop!(State: ignore zxdg_toplevel_decoration_v1::ZxdgToplevelDecorationV1);474
-475
-impl Dispatch<wp_presentation::WpPresentation, ()> for State {476
- fn event(477
- state: &mut Self,478
- _: &wp_presentation::WpPresentation,479
- event: wp_presentation::Event,480
- _: &(),481
- _: &Connection,482
- _: &QueueHandle<Self>,483
- ) {484
- if let wp_presentation::Event::ClockId { clk_id } = event485
- && let Some(host) = state.host.as_mut()486
- {487
- host.monotonic = clk_id == CLOCK_MONOTONIC;488
- }489
- }490
-}491
-492
-impl Dispatch<wp_presentation_feedback::WpPresentationFeedback, ()> for State {493
- fn event(494
- state: &mut Self,495
- _: &wp_presentation_feedback::WpPresentationFeedback,496
- event: wp_presentation_feedback::Event,497
- _: &(),498
- _: &Connection,499
- _: &QueueHandle<Self>,500
- ) {501
- let wp_presentation_feedback::Event::Presented {502
- tv_sec_hi,503
- tv_sec_lo,504
- tv_nsec,505
- refresh,506
- ..507
- } = event508
- else {509
- return;510
- };511
- let Some(host) = state.host.as_mut() else {512
- return;513
- };514
- let secs = (u64::from(tv_sec_hi) << 32) | u64::from(tv_sec_lo);515
- host.last_present_ns = Some(secs * 1_000_000_000 + u64::from(tv_nsec));516
- if refresh > 0 && u64::from(refresh) != host.refresh_ns {517
- host.refresh_ns = u64::from(refresh);518
- let mode = Mode {519
- size: (host.size.w, host.size.h).into(),520
- refresh: (1_000_000_000_000 / u64::from(refresh)) as i32,521
- };522
- host.output523
- .change_current_state(Some(mode), None, None, None);524
- host.output.set_preferred(mode);525
- }526
- }527
-}@@ -0,0 +1,105 @@
1
+//! The host seat, forwarded into monolith's own.2
+3
+use crate::state::State;4
+use smithay::backend::input::{Axis, ButtonState, InputTime, KeyState};5
+use smithay::input::keyboard::Keycode;6
+use smithay::input::pointer::AxisFrame;7
+use smithay::utils::Point;8
+use wayland_client::protocol::{wl_keyboard, wl_pointer, wl_seat};9
+use wayland_client::{Connection, Dispatch, QueueHandle, WEnum};10
+11
+impl Dispatch<wl_seat::WlSeat, ()> for State {12
+ fn event(13
+ _: &mut Self,14
+ seat: &wl_seat::WlSeat,15
+ event: wl_seat::Event,16
+ _: &(),17
+ _: &Connection,18
+ qh: &QueueHandle<Self>,19
+ ) {20
+ if let wl_seat::Event::Capabilities {21
+ capabilities: WEnum::Value(caps),22
+ } = event23
+ {24
+ if caps.contains(wl_seat::Capability::Pointer) {25
+ seat.get_pointer(qh, ());26
+ }27
+ if caps.contains(wl_seat::Capability::Keyboard) {28
+ seat.get_keyboard(qh, ());29
+ }30
+ }31
+ }32
+}33
+34
+impl Dispatch<wl_pointer::WlPointer, ()> for State {35
+ fn event(36
+ state: &mut Self,37
+ _: &wl_pointer::WlPointer,38
+ event: wl_pointer::Event,39
+ _: &(),40
+ _: &Connection,41
+ _: &QueueHandle<Self>,42
+ ) {43
+ let time = InputTime::now();44
+ match event {45
+ wl_pointer::Event::Enter {46
+ surface_x,47
+ surface_y,48
+ ..49
+ }50
+ | wl_pointer::Event::Motion {51
+ surface_x,52
+ surface_y,53
+ ..54
+ } => state.pointer_to(Point::from((surface_x, surface_y)), time),55
+ wl_pointer::Event::Button {56
+ button,57
+ state: WEnum::Value(pressed),58
+ ..59
+ } => {60
+ let pressed = match pressed {61
+ wl_pointer::ButtonState::Pressed => ButtonState::Pressed,62
+ _ => ButtonState::Released,63
+ };64
+ state.pointer_button(button, pressed, time);65
+ }66
+ wl_pointer::Event::Axis {67
+ axis: WEnum::Value(axis),68
+ value,69
+ ..70
+ } => {71
+ let axis = match axis {72
+ wl_pointer::Axis::HorizontalScroll => Axis::Horizontal,73
+ _ => Axis::Vertical,74
+ };75
+ state.pointer_axis(AxisFrame::new(time).value(axis, value));76
+ }77
+ wl_pointer::Event::Frame => state.pointer_frame(),78
+ _ => {}79
+ }80
+ }81
+}82
+83
+impl Dispatch<wl_keyboard::WlKeyboard, ()> for State {84
+ fn event(85
+ state: &mut Self,86
+ _: &wl_keyboard::WlKeyboard,87
+ event: wl_keyboard::Event,88
+ _: &(),89
+ _: &Connection,90
+ _: &QueueHandle<Self>,91
+ ) {92
+ if let wl_keyboard::Event::Key {93
+ key,94
+ state: WEnum::Value(pressed),95
+ ..96
+ } = event97
+ {98
+ let pressed = match pressed {99
+ wl_keyboard::KeyState::Pressed => KeyState::Pressed,100
+ _ => KeyState::Released,101
+ };102
+ state.key(Keycode::new(key + 8), pressed, InputTime::now());103
+ }104
+ }105
+}@@ -0,0 +1,527 @@
1
+//! Runs monolith as a window inside another Wayland compositor. Frames are2
+//! rendered into dmabufs from our own swapchain and handed to the host3
+//! through linux-dmabuf, so the nested path exercises the same4
+//! render-into-dmabuf code as bare metal.5
+6
+mod input;7
+8
+use crate::state::State;9
+use calloop_wayland_source::WaylandSource;10
+use monolith_layout::Rect;11
+use monolith_render_vk::VulkanAllocator;12
+use smithay::backend::allocator::dmabuf::Dmabuf;13
+use smithay::backend::allocator::{Format, Fourcc, Modifier, Slot, Swapchain};14
+use smithay::backend::renderer::Bind;15
+use smithay::backend::renderer::damage::OutputDamageTracker;16
+use smithay::output::{Mode, Output, PhysicalProperties, Subpixel};17
+use smithay::reexports::calloop::EventLoop;18
+use smithay::utils::{Logical, Size, Transform};19
+use std::time::Duration;20
+use wayland_client::globals::{GlobalListContents, registry_queue_init};21
+use wayland_client::protocol::{wl_buffer, wl_callback, wl_compositor, wl_registry, wl_surface};22
+use wayland_client::{Connection, Dispatch, QueueHandle, delegate_noop};23
+use wayland_protocols::wp::linux_dmabuf::zv1::client::{24
+ zwp_linux_buffer_params_v1, zwp_linux_dmabuf_v1,25
+};26
+use wayland_protocols::wp::presentation_time::client::{wp_presentation, wp_presentation_feedback};27
+use wayland_protocols::xdg::decoration::zv1::client::{28
+ zxdg_decoration_manager_v1, zxdg_toplevel_decoration_v1,29
+};30
+use wayland_protocols::xdg::shell::client::{xdg_surface, xdg_toplevel, xdg_wm_base};31
+32
+const CLEAR: [f32; 4] = [0.07, 0.075, 0.09, 1.0];33
+const DEFAULT_SIZE: (i32, i32) = (1280, 800);34
+const CLOCK_MONOTONIC: u32 = 1;35
+36
+pub struct Host {37
+ pub size: Size<i32, Logical>,38
+ pub output: Output,39
+ conn: Connection,40
+ qh: QueueHandle<State>,41
+ surface: wl_surface::WlSurface,42
+ _xdg_surface: xdg_surface::XdgSurface,43
+ _toplevel: xdg_toplevel::XdgToplevel,44
+ dmabuf: zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1,45
+ formats: Vec<Format>,46
+ swapchain: Option<Swapchain<VulkanAllocator>>,47
+ in_flight: Vec<(wl_buffer::WlBuffer, Slot<Dmabuf>)>,48
+ damage: OutputDamageTracker,49
+ configured: bool,50
+ frame_pending: bool,51
+ pending_size: Option<Size<i32, Logical>>,52
+ presentation: Option<wp_presentation::WpPresentation>,53
+ /// The host clock is CLOCK_MONOTONIC, so its timestamps share our timeline.54
+ monotonic: bool,55
+ last_present_ns: Option<u64>,56
+ refresh_ns: u64,57
+}58
+59
+impl Host {60
+ /// When a frame committed now will reach the screen, the next host61
+ /// vblank after `now`.62
+ pub fn predict(&self, now: u64) -> u64 {63
+ match self.last_present_ns.filter(|_| self.monotonic) {64
+ Some(last) if self.refresh_ns > 0 && last <= now => {65
+ last + ((now - last) / self.refresh_ns + 1) * self.refresh_ns66
+ }67
+ _ => now,68
+ }69
+ }70
+}71
+72
+/// Destroys the host-side buffer when its swapchain slot goes away.73
+struct HostBuffer(wl_buffer::WlBuffer);74
+75
+impl Drop for HostBuffer {76
+ fn drop(&mut self) {77
+ self.0.destroy();78
+ }79
+}80
+81
+pub fn init(82
+ event_loop: &mut EventLoop<State>,83
+ state: &mut State,84
+) -> Result<(), Box<dyn std::error::Error>> {85
+ let conn = Connection::connect_to_env()?;86
+ let (globals, mut queue) = registry_queue_init::<State>(&conn)?;87
+ let qh = queue.handle();88
+ let compositor: wl_compositor::WlCompositor = globals.bind(&qh, 4..=6, ())?;89
+ let wm_base: xdg_wm_base::XdgWmBase = globals.bind(&qh, 1..=6, ())?;90
+ let dmabuf: zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1 = globals.bind(&qh, 3..=3, ())?;91
+ let presentation = globals92
+ .bind::<wp_presentation::WpPresentation, _, _>(&qh, 1..=2, ())93
+ .ok();94
+ // Dropping a proxy handle does not destroy it, so seat events keep reaching input.rs.95
+ let _ = globals.bind::<wayland_client::protocol::wl_seat::WlSeat, _, _>(&qh, 1..=7, ());96
+97
+ let surface = compositor.create_surface(&qh, ());98
+ let xdg_surface = wm_base.get_xdg_surface(&surface, &qh, ());99
+ let toplevel = xdg_surface.get_toplevel(&qh, ());100
+ toplevel.set_title("monolith".into());101
+ toplevel.set_app_id("monolith".into());102
+ if let Ok(deco) =103
+ globals.bind::<zxdg_decoration_manager_v1::ZxdgDecorationManagerV1, _, _>(&qh, 1..=1, ())104
+ {105
+ deco.get_toplevel_decoration(&toplevel, &qh, ())106
+ .set_mode(zxdg_toplevel_decoration_v1::Mode::ServerSide);107
+ }108
+ surface.commit();109
+110
+ let output = Output::new(111
+ "nested".into(),112
+ PhysicalProperties {113
+ size: (0, 0).into(),114
+ subpixel: Subpixel::Unknown,115
+ make: "monolith".into(),116
+ model: "nested".into(),117
+ serial_number: "0".into(),118
+ },119
+ );120
+ let _global = output.create_global::<State>(&state.display);121
+ let mode = Mode {122
+ size: DEFAULT_SIZE.into(),123
+ refresh: 60_000,124
+ };125
+ output.change_current_state(126
+ Some(mode),127
+ Some(Transform::Normal),128
+ None,129
+ Some((0, 0).into()),130
+ );131
+ output.set_preferred(mode);132
+ state.space.map_output(&output, (0, 0));133
+ let area = Rect::new(134
+ 0.0,135
+ 0.0,136
+ f64::from(DEFAULT_SIZE.0),137
+ f64::from(DEFAULT_SIZE.1),138
+ );139
+ state.output_id = Some(state.layout.add_output("nested", area));140
+141
+ state.host = Some(Host {142
+ size: DEFAULT_SIZE.into(),143
+ damage: OutputDamageTracker::from_output(&output),144
+ output,145
+ conn: conn.clone(),146
+ qh,147
+ surface,148
+ _xdg_surface: xdg_surface,149
+ _toplevel: toplevel,150
+ dmabuf,151
+ formats: Vec::new(),152
+ swapchain: None,153
+ in_flight: Vec::new(),154
+ configured: false,155
+ frame_pending: false,156
+ pending_size: None,157
+ presentation,158
+ monotonic: false,159
+ last_present_ns: None,160
+ refresh_ns: 16_666_667,161
+ });162
+ queue.roundtrip(state)?;163
+ queue.roundtrip(state)?;164
+ WaylandSource::new(conn, queue).insert(event_loop.handle())?;165
+ Ok(())166
+}167
+168
+/// Renders a frame if something changed and the host is ready for one.169
+pub fn render(state: &mut State) {170
+ let State {171
+ host,172
+ renderer,173
+ space,174
+ needs_redraw,175
+ start,176
+ layout,177
+ windows,178
+ anim,179
+ ..180
+ } = state;181
+ let Some(host) = host.as_mut() else { return };182
+ if !host.configured || host.frame_pending || !*needs_redraw {183
+ return;184
+ }185
+ if let Some(size) = host.pending_size.take() {186
+ host.size = size;187
+ let mode = Mode {188
+ size: (size.w, size.h).into(),189
+ refresh: (1_000_000_000_000 / host.refresh_ns) as i32,190
+ };191
+ host.output192
+ .change_current_state(Some(mode), None, None, None);193
+ host.output.set_preferred(mode);194
+ if let Some(sc) = host.swapchain.as_mut() {195
+ sc.resize(size.w as u32, size.h as u32);196
+ }197
+ }198
+ let now = host.predict(crate::state::now_ns());199
+ if host.swapchain.is_none() {200
+ host.swapchain = make_swapchain(renderer, &host.formats, host.size);201
+ }202
+ let Some(swapchain) = host.swapchain.as_mut() else {203
+ return;204
+ };205
+ let slot = match swapchain.acquire() {206
+ Ok(Some(slot)) => slot,207
+ Ok(None) => return,208
+ Err(e) => {209
+ tracing::warn!("failed to allocate a frame buffer: {e}");210
+ return;211
+ }212
+ };213
+ let mut dmabuf = (*slot).clone();214
+ let damage = {215
+ let mut fb = match renderer.bind(&mut dmabuf) {216
+ Ok(fb) => fb,217
+ Err(e) => {218
+ tracing::warn!("cannot render into the swapchain: {e}");219
+ return;220
+ }221
+ };222
+ let elements = crate::scene::build(layout, windows, anim, renderer, now);223
+ match host224
+ .damage225
+ .render_output(renderer, &mut fb, slot.age() as usize, &elements, CLEAR)226
+ {227
+ Ok(result) => {228
+ if let Err(e) = result.sync.wait() {229
+ tracing::warn!("waiting for the frame failed: {e:?}");230
+ }231
+ result.damage.cloned()232
+ }233
+ Err(e) => {234
+ tracing::warn!("rendering failed: {e:?}");235
+ return;236
+ }237
+ }238
+ };239
+ *needs_redraw = anim.active(now);240
+ anim.prune(now);241
+ let elapsed = start.elapsed();242
+ for window in space.elements() {243
+ window.send_frame(&host.output, elapsed, Some(Duration::ZERO), |_, _| {244
+ Some(host.output.clone())245
+ });246
+ }247
+ let Some(damage) = damage else {248
+ if *needs_redraw {249
+ host.surface.frame(&host.qh, ());250
+ host.surface.commit();251
+ host.frame_pending = true;252
+ let _ = host.conn.flush();253
+ }254
+ return;255
+ };256
+ let buffer = match slot.userdata().get::<HostBuffer>() {257
+ Some(b) => b.0.clone(),258
+ None => {259
+ let b = host_buffer(&host.dmabuf, &host.qh, &dmabuf);260
+ slot.userdata().insert_if_missing(|| HostBuffer(b.clone()));261
+ b262
+ }263
+ };264
+ host.surface.attach(Some(&buffer), 0, 0);265
+ for r in &damage {266
+ host.surface267
+ .damage_buffer(r.loc.x, r.loc.y, r.size.w, r.size.h);268
+ }269
+ host.surface.frame(&host.qh, ());270
+ if let Some(p) = &host.presentation {271
+ p.feedback(&host.surface, &host.qh, ());272
+ }273
+ host.surface.commit();274
+ swapchain.submitted(&slot);275
+ host.in_flight.push((buffer, slot));276
+ host.frame_pending = true;277
+ let _ = host.conn.flush();278
+}279
+280
+fn make_swapchain(281
+ renderer: &monolith_render_vk::VulkanRenderer,282
+ host: &[Format],283
+ size: Size<i32, Logical>,284
+) -> Option<Swapchain<VulkanAllocator>> {285
+ let ours = renderer.render_formats();286
+ for fourcc in [Fourcc::Xrgb8888, Fourcc::Argb8888] {287
+ let modifiers: Vec<Modifier> = host288
+ .iter()289
+ .filter(|f| f.code == fourcc && ours.contains(f))290
+ .map(|f| f.modifier)291
+ .collect();292
+ if !modifiers.is_empty() {293
+ return Some(Swapchain::new(294
+ renderer.allocator(),295
+ size.w as u32,296
+ size.h as u32,297
+ fourcc,298
+ modifiers,299
+ ));300
+ }301
+ }302
+ tracing::error!("the host compositor and the renderer share no dmabuf format");303
+ None304
+}305
+306
+fn host_buffer(307
+ dmabuf: &zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1,308
+ qh: &QueueHandle<State>,309
+ buf: &Dmabuf,310
+) -> wl_buffer::WlBuffer {311
+ use smithay::backend::allocator::Buffer;312
+ let params = dmabuf.create_params(qh, ());313
+ let modifier = u64::from(buf.format().modifier);314
+ for (i, ((fd, offset), stride)) in buf315
+ .handles()316
+ .zip(buf.offsets())317
+ .zip(buf.strides())318
+ .enumerate()319
+ {320
+ params.add(321
+ fd,322
+ i as u32,323
+ offset,324
+ stride,325
+ (modifier >> 32) as u32,326
+ modifier as u32,327
+ );328
+ }329
+ let buffer = params.create_immed(330
+ buf.width() as i32,331
+ buf.height() as i32,332
+ buf.format().code as u32,333
+ zwp_linux_buffer_params_v1::Flags::empty(),334
+ qh,335
+ (),336
+ );337
+ params.destroy();338
+ buffer339
+}340
+341
+impl Dispatch<wl_registry::WlRegistry, GlobalListContents> for State {342
+ fn event(343
+ _: &mut Self,344
+ _: &wl_registry::WlRegistry,345
+ _: wl_registry::Event,346
+ _: &GlobalListContents,347
+ _: &Connection,348
+ _: &QueueHandle<Self>,349
+ ) {350
+ }351
+}352
+353
+impl Dispatch<xdg_wm_base::XdgWmBase, ()> for State {354
+ fn event(355
+ _: &mut Self,356
+ base: &xdg_wm_base::XdgWmBase,357
+ event: xdg_wm_base::Event,358
+ _: &(),359
+ _: &Connection,360
+ _: &QueueHandle<Self>,361
+ ) {362
+ if let xdg_wm_base::Event::Ping { serial } = event {363
+ base.pong(serial);364
+ }365
+ }366
+}367
+368
+impl Dispatch<xdg_surface::XdgSurface, ()> for State {369
+ fn event(370
+ state: &mut Self,371
+ surface: &xdg_surface::XdgSurface,372
+ event: xdg_surface::Event,373
+ _: &(),374
+ _: &Connection,375
+ _: &QueueHandle<Self>,376
+ ) {377
+ if let xdg_surface::Event::Configure { serial } = event {378
+ surface.ack_configure(serial);379
+ if let Some(host) = state.host.as_mut() {380
+ host.configured = true;381
+ }382
+ state.needs_redraw = true;383
+ }384
+ }385
+}386
+387
+impl Dispatch<xdg_toplevel::XdgToplevel, ()> for State {388
+ fn event(389
+ state: &mut Self,390
+ _: &xdg_toplevel::XdgToplevel,391
+ event: xdg_toplevel::Event,392
+ _: &(),393
+ _: &Connection,394
+ _: &QueueHandle<Self>,395
+ ) {396
+ match event {397
+ xdg_toplevel::Event::Configure { width, height, .. } if width > 0 && height > 0 => {398
+ let size = Size::from((width, height));399
+ if let Some(host) = state.host.as_mut()400
+ && host.size != size401
+ {402
+ host.pending_size = Some(size);403
+ host.size = size;404
+ state.resize_output(size);405
+ }406
+ }407
+ xdg_toplevel::Event::Close => state.signal.stop(),408
+ _ => {}409
+ }410
+ }411
+}412
+413
+impl Dispatch<zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1, ()> for State {414
+ fn event(415
+ state: &mut Self,416
+ _: &zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1,417
+ event: zwp_linux_dmabuf_v1::Event,418
+ _: &(),419
+ _: &Connection,420
+ _: &QueueHandle<Self>,421
+ ) {422
+ if let zwp_linux_dmabuf_v1::Event::Modifier {423
+ format,424
+ modifier_hi,425
+ modifier_lo,426
+ } = event427
+ && let (Ok(code), Some(host)) = (Fourcc::try_from(format), state.host.as_mut())428
+ {429
+ let modifier = Modifier::from((u64::from(modifier_hi) << 32) | u64::from(modifier_lo));430
+ host.formats.push(Format { code, modifier });431
+ }432
+ }433
+}434
+435
+impl Dispatch<wl_buffer::WlBuffer, ()> for State {436
+ fn event(437
+ state: &mut Self,438
+ buffer: &wl_buffer::WlBuffer,439
+ event: wl_buffer::Event,440
+ _: &(),441
+ _: &Connection,442
+ _: &QueueHandle<Self>,443
+ ) {444
+ if let wl_buffer::Event::Release = event445
+ && let Some(host) = state.host.as_mut()446
+ {447
+ host.in_flight.retain(|(b, _)| b != buffer);448
+ }449
+ }450
+}451
+452
+impl Dispatch<wl_callback::WlCallback, ()> for State {453
+ fn event(454
+ state: &mut Self,455
+ _: &wl_callback::WlCallback,456
+ event: wl_callback::Event,457
+ _: &(),458
+ _: &Connection,459
+ _: &QueueHandle<Self>,460
+ ) {461
+ if let wl_callback::Event::Done { .. } = event462
+ && let Some(host) = state.host.as_mut()463
+ {464
+ host.frame_pending = false;465
+ }466
+ }467
+}468
+469
+delegate_noop!(State: wl_compositor::WlCompositor);470
+delegate_noop!(State: ignore wl_surface::WlSurface);471
+delegate_noop!(State: ignore zwp_linux_buffer_params_v1::ZwpLinuxBufferParamsV1);472
+delegate_noop!(State: zxdg_decoration_manager_v1::ZxdgDecorationManagerV1);473
+delegate_noop!(State: ignore zxdg_toplevel_decoration_v1::ZxdgToplevelDecorationV1);474
+475
+impl Dispatch<wp_presentation::WpPresentation, ()> for State {476
+ fn event(477
+ state: &mut Self,478
+ _: &wp_presentation::WpPresentation,479
+ event: wp_presentation::Event,480
+ _: &(),481
+ _: &Connection,482
+ _: &QueueHandle<Self>,483
+ ) {484
+ if let wp_presentation::Event::ClockId { clk_id } = event485
+ && let Some(host) = state.host.as_mut()486
+ {487
+ host.monotonic = clk_id == CLOCK_MONOTONIC;488
+ }489
+ }490
+}491
+492
+impl Dispatch<wp_presentation_feedback::WpPresentationFeedback, ()> for State {493
+ fn event(494
+ state: &mut Self,495
+ _: &wp_presentation_feedback::WpPresentationFeedback,496
+ event: wp_presentation_feedback::Event,497
+ _: &(),498
+ _: &Connection,499
+ _: &QueueHandle<Self>,500
+ ) {501
+ let wp_presentation_feedback::Event::Presented {502
+ tv_sec_hi,503
+ tv_sec_lo,504
+ tv_nsec,505
+ refresh,506
+ ..507
+ } = event508
+ else {509
+ return;510
+ };511
+ let Some(host) = state.host.as_mut() else {512
+ return;513
+ };514
+ let secs = (u64::from(tv_sec_hi) << 32) | u64::from(tv_sec_lo);515
+ host.last_present_ns = Some(secs * 1_000_000_000 + u64::from(tv_nsec));516
+ if refresh > 0 && u64::from(refresh) != host.refresh_ns {517
+ host.refresh_ns = u64::from(refresh);518
+ let mode = Mode {519
+ size: (host.size.w, host.size.h).into(),520
+ refresh: (1_000_000_000_000 / u64::from(refresh)) as i32,521
+ };522
+ host.output523
+ .change_current_state(Some(mode), None, None, None);524
+ host.output.set_preferred(mode);525
+ }526
+ }527
+}@@ -56,6 +56,8 @@ pub struct State {
5656
pub suppressed: Vec<Keycode>,5757
pub move_inverted: bool,5858
pub drag: Option<crate::input::Drag>,59
+ /// Global logical position, shared by every backend.60
+ pub pointer_pos: Point<f64, Logical>,5961
pub ipc: crate::ipc::Server,6062
pub launches: crate::launch::Launches,6163
pub workspaces: WorkspaceState,@@ -140,6 +142,7 @@ impl State {
140142
suppressed: Vec::new(),141143
move_inverted: false,142144
drag: None,145
+ pointer_pos: Point::default(),143146
ipc: crate::ipc::Server::default(),144147
launches: Default::default(),145148
workspaces: WorkspaceState::new::<State>(&dh),