srdusr
aboutsummaryrefslogtreecommitdiffstats
path: root/crates/wayland/src
diff options
context:
space:
mode:
authorsrdusr <[email protected]>2024-05-29 14:40:00 +0200
committersrdusr <[email protected]>2024-05-29 14:40:00 +0200
commit3d3057ae384ef7389284af8988410889e99c6bb9 (patch)
tree57b05e9660afd8ed51b26e5db1ab541a811ccc16 /crates/wayland/src
parentdd31bf5ac2a692617d478478d36c733586b93cf8 (diff)
downloadsrdwm-3d3057ae384ef7389284af8988410889e99c6bb9.tar.gz
srdwm-3d3057ae384ef7389284af8988410889e99c6bb9.zip
Config at ~/.config/srd; cursor shapes, key repeat, pin, mouse defaults
Config path drops a level: ~/.config/srd, not ~/.config/srdwm/srd, which said the same thing twice. No other user-facing path had the same problem -- srdwm reads the config dir and writes nothing else. Cursor shapes. A client's own cursor surface is now rendered with the hotspot it declared, so an I-beam over text or a hand over a link shows the app's image instead of srdwm's arrow. The built-in arrow stays as the fallback when no client has set one, over decorations and the desktop. Named shapes still fall back to the arrow; most toolkits set a surface. Decorations and cursors now share one OverlayElement type, since render_output takes a single custom-element slice. Key repeat (srd.bind_repeat, Hyprland's binde). Held volume, brightness and switcher keys repeat at the seat's own rate rather than firing once. Driven from the poll loop, not a timer source: the winit backend has no calloop loop of its own, and poll_events already runs continuously in both backends. Repeat stops when *that* key is released, not when any key is. Always-on-top / pin, for the picture-in-picture and HUD rules that used it. Window::always_on_top was another declared-but-never-read field. Enforced in WindowManager's stacking order rather than at render time, so every consumer of stacking_order gets it and none can forget to honour it. Mouse-only window management, checked end to end: drag the titlebar to move, drag any edge or corner to resize, titlebar buttons to close/maximise/ minimise, click to focus, drag to a screen edge to snap, and now double-click the titlebar to maximise. The resize grab band went from 6px to 10px - a hairline is genuinely hard to hit with a mouse, which is why Hyprland ships extend_border_grab_area. Also removed the emoji status markers from docs/IMPLEMENTATION_STATUS.md.
Diffstat (limited to 'crates/wayland/src')
-rw-r--r--crates/wayland/src/cursor.rs59
-rw-r--r--crates/wayland/src/elements.rs20
-rw-r--r--crates/wayland/src/input.rs26
-rw-r--r--crates/wayland/src/lib.rs7
-rw-r--r--crates/wayland/src/state.rs94
-rw-r--r--crates/wayland/src/udev.rs11
-rw-r--r--crates/wayland/src/winit.rs15
7 files changed, 203 insertions, 29 deletions
diff --git a/crates/wayland/src/cursor.rs b/crates/wayland/src/cursor.rs
index 121e8e1..b489c03 100644
--- a/crates/wayland/src/cursor.rs
+++ b/crates/wayland/src/cursor.rs
@@ -7,13 +7,19 @@
//! can move it, click with it, and drag windows with it, but you cannot see
//! where it is. That makes the udev backend unusable as a real session.
//!
-//! **Scope, stated plainly:** this draws one built-in arrow, always. It
-//! honours `CursorImageStatus::Hidden` (so a client that hides the pointer
-//! still gets its way), but it does *not* yet render a client's own cursor
-//! surface or a named shape - an app asking for an I-beam or a resize arrow
-//! still sees this arrow. That is a real limitation, and a visible one over
-//! text fields; it is also strictly better than the previous behaviour of
-//! drawing nothing at all.
+//! Two sources, in priority order:
+//!
+//! 1. **The client's own cursor surface** (`CursorImageStatus::Surface`) --
+//! a terminal's I-beam, a browser's hand, an app's resize arrows. Drawn
+//! from its surface tree, offset by the hotspot the client declared.
+//! 2. **A built-in arrow**, for when no client has set an image (over
+//! srdwm's own decorations and the desktop) or asked for a named shape
+//! we have no art for.
+//!
+//! `CursorImageStatus::Hidden` is honoured, so a client that hides the
+//! pointer still gets its way. Named shapes (`CursorIcon::Text` etc.) fall
+//! back to the arrow rather than being drawn as the requested shape - most
+//! toolkits set a surface rather than a name, so this is rarely visible.
//!
//! The built-in arrow is deliberate rather than loading an XCursor theme:
//! theme loading pulls in a dependency, needs a theme to actually be
@@ -21,8 +27,11 @@
//! is always present beats a prettier one that sometimes isn't there - the
//! same reasoning as `decoration.rs`'s font fallback.
+use smithay::backend::renderer::element::memory::MemoryRenderBufferRenderElement;
use smithay::utils::{Logical, Point};
+use crate::elements::OverlayElement;
+
/// Side length of the built-in cursor bitmap, in pixels.
pub(crate) const CURSOR_SIZE: i32 = 24;
@@ -85,6 +94,10 @@ pub(crate) fn arrow_bitmap() -> Vec<u8> {
}
+/// One cursor render element, whatever the source.
+///
+/// Client cursor surfaces and the built-in bitmap are different element
+/// types, so they're unified here rather than forcing both through one.
/// Render elements for the pointer, to be drawn above everything else.
///
/// `pos` is in the global space and `origin` is the output's origin, since
@@ -99,14 +112,17 @@ pub(crate) fn render_elements<R>(
pos: Point<f64, Logical>,
origin: Point<i32, Logical>,
size: (i32, i32),
-) -> Vec<smithay::backend::renderer::element::memory::MemoryRenderBufferRenderElement<R>>
+) -> Vec<OverlayElement<R>>
where
- R: smithay::backend::renderer::Renderer + smithay::backend::renderer::ImportMem,
+ R: smithay::backend::renderer::Renderer
+ + smithay::backend::renderer::ImportAll
+ + smithay::backend::renderer::ImportMem,
R::TextureId: Clone + Send + 'static,
{
- use smithay::backend::renderer::element::memory::MemoryRenderBufferRenderElement;
+ use smithay::backend::renderer::element::surface::render_elements_from_surface_tree;
use smithay::backend::renderer::element::Kind;
- use smithay::input::pointer::CursorImageStatus;
+ use smithay::input::pointer::{CursorImageStatus, CursorImageSurfaceData};
+ use smithay::wayland::compositor::with_states;
if matches!(status, CursorImageStatus::Hidden) {
return Vec::new();
@@ -117,10 +133,26 @@ where
return Vec::new();
}
- // Built-in arrow: hotspot is the tip, so no offset.
+ if let CursorImageStatus::Surface(surface) = status {
+ // The client picked an image. Its hotspot is the point *inside* that
+ // image which tracks the pointer, so the surface is drawn offset by
+ // it - without this the image sits down-right of where clicks land.
+ let hotspot = with_states(surface, |states| {
+ states
+ .data_map
+ .get::<CursorImageSurfaceData>()
+ .map(|d| d.lock().unwrap().hotspot)
+ .unwrap_or_default()
+ });
+ let at = (local.0 - hotspot.x, local.1 - hotspot.y);
+ return render_elements_from_surface_tree(renderer, surface, at, 1.0, 1.0, Kind::Cursor);
+ }
+
+ // No client image (or a named shape we don't have art for): the
+ // built-in arrow, whose hotspot is its tip, so no offset.
let at = (local.0 as f64, local.1 as f64);
match MemoryRenderBufferRenderElement::from_buffer(renderer, at, buffer, None, None, None, Kind::Cursor) {
- Ok(e) => vec![e],
+ Ok(e) => vec![OverlayElement::Memory(e)],
Err(e) => {
log::warn!("cursor: failed to import bitmap: {e}");
Vec::new()
@@ -128,6 +160,7 @@ where
}
}
+
/// The built-in arrow as an uploadable buffer. Built once at startup rather
/// than per frame - the bitmap never changes.
pub(crate) fn make_buffer() -> smithay::backend::renderer::element::memory::MemoryRenderBuffer {
diff --git a/crates/wayland/src/elements.rs b/crates/wayland/src/elements.rs
new file mode 100644
index 0000000..f1fbfee
--- /dev/null
+++ b/crates/wayland/src/elements.rs
@@ -0,0 +1,20 @@
+//! The one render-element type for everything srdwm draws *itself*, on top
+//! of client windows: its titlebars and the mouse pointer.
+//!
+//! `render_output` takes a single `custom_elements` slice, so these have to
+//! be one type. They come from two different sources - an uploaded bitmap
+//! (titlebars, the built-in cursor arrow) and a client's own surface (a
+//! client-set cursor image) - hence the two variants.
+
+use smithay::backend::renderer::element::memory::MemoryRenderBufferRenderElement;
+use smithay::backend::renderer::element::surface::WaylandSurfaceRenderElement;
+use smithay::backend::renderer::{ImportAll, ImportMem};
+
+smithay::backend::renderer::element::render_elements! {
+ pub(crate) OverlayElement<R> where
+ R: ImportAll + ImportMem;
+ /// A client's own surface, used for client-set cursor images.
+ Surface=WaylandSurfaceRenderElement<R>,
+ /// A bitmap srdwm rasterised: a titlebar, or the built-in cursor arrow.
+ Memory=MemoryRenderBufferRenderElement<R>,
+}
diff --git a/crates/wayland/src/input.rs b/crates/wayland/src/input.rs
index 6959c8f..17e94d4 100644
--- a/crates/wayland/src/input.rs
+++ b/crates/wayland/src/input.rs
@@ -206,7 +206,17 @@ pub(crate) fn handle_pointer_button(state: &mut CompState, pos: Point<f64, Logic
if let Some((id, hit)) = hit {
focus_window(state, id);
match hit {
- TitlebarHit::Drag => state.wm.borrow_mut().start_drag(id, pos.x as i32, pos.y as i32),
+ TitlebarHit::Drag => {
+ // Double-click the titlebar to maximise, as every other
+ // desktop does - one of the few window operations that
+ // otherwise needs the keyboard or a precise button hit.
+ if state.is_double_click(id, time) {
+ state.wm.borrow_mut().toggle_maximize(id);
+ state.sync_geometry(id);
+ } else {
+ state.wm.borrow_mut().start_drag(id, pos.x as i32, pos.y as i32)
+ }
+ }
TitlebarHit::Close => {
if let Some(w) = state.id_to_window.get(&id) {
close_dwindow(w);
@@ -223,6 +233,8 @@ pub(crate) fn handle_pointer_button(state: &mut CompState, pos: Point<f64, Logic
if let Some((window, _loc)) = state.space.element_under(pos) {
let window = window.clone();
state.space.raise_element(&window, true);
+ // Clicking a normal window must not bury a pinned one.
+ state.raise_pinned();
if let Some(&id) = dwindow_wl_surface(&window).and_then(|s| state.surface_to_id.get(&s)) {
focus_window(state, id);
}
@@ -276,10 +288,16 @@ pub(crate) fn handle_keyboard_key_event<B: smithay::backend::input::InputBackend
}
});
- if key_state == BackendKeyState::Pressed {
- if let Some((key_name, modifiers)) = matched {
- state.pending.borrow_mut().push(CoreEvent::KeyPress { key_name, modifiers });
+ match key_state {
+ BackendKeyState::Pressed => {
+ if let Some((key_name, modifiers)) = matched {
+ state.begin_repeat(keycode, &key_name, modifiers);
+ state.pending.borrow_mut().push(CoreEvent::KeyPress { key_name, modifiers });
+ }
}
+ // Any release ends a repeat of *that* key; releasing an unrelated
+ // key must not stop it.
+ BackendKeyState::Released => state.end_repeat(keycode),
}
// Unmatched keys were already forwarded to the focused client by
// `FilterResult::Forward` inside the closure above.
diff --git a/crates/wayland/src/lib.rs b/crates/wayland/src/lib.rs
index 001c803..1d04d01 100644
--- a/crates/wayland/src/lib.rs
+++ b/crates/wayland/src/lib.rs
@@ -38,6 +38,7 @@
mod cursor;
mod decoration;
+mod elements;
mod input;
mod lock;
mod protocols;
@@ -67,13 +68,13 @@ pub(crate) fn err(e: impl std::fmt::Display) -> PlatformError {
/// nested-vs-native. Falls back to winit if udev initialization fails for
/// any reason (no seat access, no DRM device, ...), logging why rather than
/// failing outright.
-pub fn connect(wm: Rc<RefCell<WindowManager>>, bound_keys: &[String]) -> PlatformResult<Box<dyn Platform>> {
+pub fn connect(wm: Rc<RefCell<WindowManager>>, bound_keys: &[String], repeat_keys: &[String]) -> PlatformResult<Box<dyn Platform>> {
let no_host_display = std::env::var_os("WAYLAND_DISPLAY").is_none() && std::env::var_os("DISPLAY").is_none();
if no_host_display {
- match udev::UdevPlatform::connect(wm.clone(), bound_keys) {
+ match udev::UdevPlatform::connect(wm.clone(), bound_keys, repeat_keys) {
Ok(platform) => return Ok(Box::new(platform)),
Err(e) => log::warn!("udev/DRM backend unavailable ({e}); falling back to nested winit backend"),
}
}
- Ok(Box::new(WaylandPlatform::connect(wm, bound_keys)?))
+ Ok(Box::new(WaylandPlatform::connect(wm, bound_keys, repeat_keys)?))
}
diff --git a/crates/wayland/src/state.rs b/crates/wayland/src/state.rs
index a1704cb..bea3453 100644
--- a/crates/wayland/src/state.rs
+++ b/crates/wayland/src/state.rs
@@ -14,7 +14,7 @@
use std::cell::RefCell;
use std::collections::{HashMap, HashSet};
use std::rc::Rc;
-use std::time::Instant;
+use std::time::{Duration, Instant};
use smithay::backend::allocator::Fourcc;
use smithay::backend::renderer::element::memory::MemoryRenderBuffer;
@@ -127,12 +127,18 @@ pub(crate) struct CompState {
/// Bitmap for the built-in arrow, built once at startup rather than
/// per frame.
pub(crate) cursor_buffer: MemoryRenderBuffer,
+ /// Last titlebar press, for double-click detection.
+ pub(crate) last_titlebar_click: Option<(WindowId, u32)>,
pub(crate) wm: Rc<RefCell<WindowManager>>,
pub(crate) surface_to_id: HashMap<WlSurface, WindowId>,
pub(crate) id_to_window: HashMap<WindowId, DWindow>,
pub(crate) decorations: HashMap<WindowId, MemoryRenderBuffer>,
pub(crate) pending: Rc<RefCell<Vec<CoreEvent>>>,
pub(crate) bound_keys: Rc<HashSet<String>>,
+ /// Combos that repeat while held (`srd.bind_repeat`).
+ pub(crate) repeat_keys: Rc<HashSet<String>>,
+ /// The binding currently held down and repeating, if any.
+ pub(crate) repeat: Option<RepeatState>,
pub(crate) start_time: Instant,
/// `Some` only for the udev/DRM backend; see `udev.rs` module docs for
/// why its runtime state lives here rather than on a separate struct.
@@ -147,6 +153,61 @@ pub(crate) struct CompState {
pub(crate) xwayland_pending: Vec<smithay::xwayland::X11Surface>,
}
+/// A held keybinding that is firing repeatedly.
+///
+/// Driven from the poll loop rather than a timer source: the winit backend
+/// has no `calloop` loop of its own, and `poll_events` already runs
+/// continuously in both backends, so this works the same in each.
+pub(crate) struct RepeatState {
+ /// Which physical key is held - repeat stops when *this* key is
+ /// released, not when any key is.
+ pub(crate) keycode: smithay::input::keyboard::Keycode,
+ pub(crate) key_name: String,
+ pub(crate) modifiers: srdwm_core::Modifiers,
+ pub(crate) next_fire: Instant,
+}
+
+/// Matches the seat's own repeat settings (`add_keyboard(.., 200, 25)`), so
+/// held bindings feel the same as held keys in a text field.
+const REPEAT_DELAY: Duration = Duration::from_millis(200);
+const REPEAT_INTERVAL: Duration = Duration::from_millis(1000 / 25);
+
+impl CompState {
+ /// Starts repeating `combo` if it was registered with `srd.bind_repeat`.
+ pub(crate) fn begin_repeat(&mut self, keycode: smithay::input::keyboard::Keycode, key_name: &str, modifiers: srdwm_core::Modifiers) {
+ let combo = srdwm_core::key_combo_string(modifiers, key_name);
+ if !self.repeat_keys.contains(&combo) {
+ return;
+ }
+ self.repeat = Some(RepeatState {
+ keycode,
+ key_name: key_name.to_string(),
+ modifiers,
+ next_fire: Instant::now() + REPEAT_DELAY,
+ });
+ }
+
+ /// Stops repeating when the held key is released.
+ pub(crate) fn end_repeat(&mut self, keycode: smithay::input::keyboard::Keycode) {
+ if self.repeat.as_ref().is_some_and(|r| r.keycode == keycode) {
+ self.repeat = None;
+ }
+ }
+
+ /// Emits another `KeyPress` if the held binding is due. Called once per
+ /// poll from both backends.
+ pub(crate) fn tick_repeat(&mut self) {
+ let Some(repeat) = self.repeat.as_mut() else { return };
+ let now = Instant::now();
+ if now < repeat.next_fire {
+ return;
+ }
+ repeat.next_fire = now + REPEAT_INTERVAL;
+ let (key_name, modifiers) = (repeat.key_name.clone(), repeat.modifiers);
+ self.pending.borrow_mut().push(CoreEvent::KeyPress { key_name, modifiers });
+ }
+}
+
/// Titlebar background is the same regardless of focus (matching the X11
/// backend); only the title text color changes.
const TITLEBAR_BG: (u8, u8, u8) = (0x2e, 0x34, 0x40);
@@ -219,6 +280,8 @@ impl CompState {
// real Wayland keyboard/selection focus. (Same class of bug as the
// click-to-focus one fixed earlier; this is the creation path.)
self.set_keyboard_focus(Some(surface));
+ // A newly-mapped window goes on top, but not over a pinned one.
+ self.raise_pinned();
self.pending.borrow_mut().push(CoreEvent::WindowCreated(id));
}
@@ -339,6 +402,35 @@ impl CompState {
keyboard.set_focus(self, surface, serial);
}
+ /// True when this titlebar press is the second of a double-click on the
+ /// same window. Threshold is the usual 400ms.
+ pub(crate) fn is_double_click(&mut self, id: WindowId, time: u32) -> bool {
+ const DOUBLE_CLICK_MS: u32 = 400;
+ let doubled = match self.last_titlebar_click {
+ Some((last_id, last_time)) => last_id == id && time.saturating_sub(last_time) <= DOUBLE_CLICK_MS,
+ None => false,
+ };
+ // Reset after a double, so a third click starts a fresh pair rather
+ // than counting as another double.
+ self.last_titlebar_click = if doubled { None } else { Some((id, time)) };
+ doubled
+ }
+
+ /// Re-raises always-on-top windows in the `Space`.
+ ///
+ /// `WindowManager` keeps pinned windows last in its own stacking order,
+ /// but the `Space` has an order of its own that decides what actually
+ /// draws on top - so pinning is only real once it is pushed here.
+ /// Called after anything that raises a window.
+ pub(crate) fn raise_pinned(&mut self) {
+ let pinned: Vec<WindowId> = self.wm.borrow().stacking_order().filter(|w| w.always_on_top).map(|w| w.id).collect();
+ for id in pinned {
+ if let Some(w) = self.id_to_window.get(&id).cloned() {
+ self.space.raise_element(&w, false);
+ }
+ }
+ }
+
pub(crate) fn sync_geometry(&mut self, id: WindowId) {
let Some(geom) = self.wm.borrow().window(id).map(|w| w.geometry) else { return };
if let Some(w) = self.id_to_window.get(&id) {
diff --git a/crates/wayland/src/udev.rs b/crates/wayland/src/udev.rs
index 30517f8..95ef571 100644
--- a/crates/wayland/src/udev.rs
+++ b/crates/wayland/src/udev.rs
@@ -197,7 +197,7 @@ impl CompState {
let head = &mut udev.heads[index];
let back = 1 - head.front;
- let mut custom_elements: Vec<MemoryRenderBufferRenderElement<PixmanRenderer>> = Vec::new();
+ let mut custom_elements: Vec<crate::elements::OverlayElement<PixmanRenderer>> = Vec::new();
if !locked {
// Cursor first: `render_output` draws custom elements
// front-to-back, so the earliest element is topmost. On a
@@ -215,7 +215,7 @@ impl CompState {
for (geom, deco) in &decorations {
let pos = ((geom.x - origin.x) as f64, (geom.y - origin.y) as f64);
match MemoryRenderBufferRenderElement::from_buffer(&mut udev.renderer, pos, deco, None, None, None, Kind::Unspecified) {
- Ok(elem) => custom_elements.push(elem),
+ Ok(elem) => custom_elements.push(crate::elements::OverlayElement::Memory(elem)),
Err(e) => log::warn!("udev: failed to import titlebar buffer: {e}"),
}
}
@@ -451,7 +451,7 @@ pub struct UdevPlatform {
}
impl UdevPlatform {
- pub fn connect(wm: Rc<RefCell<WindowManager>>, bound_keys: &[String]) -> PlatformResult<Self> {
+ pub fn connect(wm: Rc<RefCell<WindowManager>>, bound_keys: &[String], repeat_keys: &[String]) -> PlatformResult<Self> {
let event_loop: EventLoop<'static, CompState> = EventLoop::try_new().map_err(err)?;
let (session, notifier) = LibSeatSession::new().map_err(err)?;
@@ -556,12 +556,15 @@ impl UdevPlatform {
lock: Default::default(),
cursor_status: smithay::input::pointer::CursorImageStatus::default_named(),
cursor_buffer: crate::cursor::make_buffer(),
+ last_titlebar_click: None,
wm: wm.clone(),
surface_to_id: HashMap::new(),
id_to_window: HashMap::new(),
decorations: HashMap::new(),
pending: pending.clone(),
bound_keys: Rc::new(bound_keys.iter().cloned().collect::<HashSet<_>>()),
+ repeat_keys: Rc::new(repeat_keys.iter().cloned().collect::<HashSet<_>>()),
+ repeat: None,
start_time: Instant::now(),
udev: Some(udev_state),
xwayland_shell_state: smithay::wayland::xwayland_shell::XWaylandShellState::new::<CompState>(&display_handle),
@@ -897,6 +900,8 @@ impl Platform for UdevPlatform {
fn poll_events(&mut self) -> PlatformResult<Vec<CoreEvent>> {
self.accept_clients()?;
self.event_loop.dispatch(Some(Duration::from_millis(16)), &mut self.state).map_err(err)?;
+ // Held bindings that repeat - see `CompState::tick_repeat`.
+ self.state.tick_repeat();
self.display.dispatch_clients(&mut self.state).map_err(err)?;
self.display.flush_clients().map_err(err)?;
self.state.render_udev_frame();
diff --git a/crates/wayland/src/winit.rs b/crates/wayland/src/winit.rs
index b21ca06..a11e263 100644
--- a/crates/wayland/src/winit.rs
+++ b/crates/wayland/src/winit.rs
@@ -64,7 +64,7 @@ impl WaylandPlatform {
/// strings (see `srdwm_core::key_combo_string`) - the same set the X11
/// backend grabs individually via `XGrabKey`. Only a keypress matching
/// one of these is withheld from the focused client.
- pub fn connect(wm: Rc<RefCell<WindowManager>>, bound_keys: &[String]) -> PlatformResult<Self> {
+ pub fn connect(wm: Rc<RefCell<WindowManager>>, bound_keys: &[String], repeat_keys: &[String]) -> PlatformResult<Self> {
let display: Display<CompState> = Display::new().map_err(err)?;
let dh = display.handle();
@@ -132,12 +132,15 @@ impl WaylandPlatform {
lock: SessionLock::default(),
cursor_status: smithay::input::pointer::CursorImageStatus::default_named(),
cursor_buffer: crate::cursor::make_buffer(),
+ last_titlebar_click: None,
wm: wm.clone(),
surface_to_id: HashMap::new(),
id_to_window: HashMap::new(),
decorations: HashMap::new(),
pending: pending.clone(),
bound_keys: Rc::new(bound_keys.iter().cloned().collect()),
+ repeat_keys: Rc::new(repeat_keys.iter().cloned().collect()),
+ repeat: None,
start_time: Instant::now(),
udev: None,
xwayland_shell_state: smithay::wayland::xwayland_shell::XWaylandShellState::new::<CompState>(&dh),
@@ -212,11 +215,11 @@ impl WaylandPlatform {
return Ok(());
}
- let mut custom_elements: Vec<MemoryRenderBufferRenderElement<GlesRenderer>> = Vec::new();
+ let mut custom_elements: Vec<crate::elements::OverlayElement<GlesRenderer>> = Vec::new();
for (&id, deco) in self.state.decorations.iter() {
let Some(geom) = self.wm.borrow().window(id).map(|w| w.geometry) else { continue };
match MemoryRenderBufferRenderElement::from_buffer(renderer, (geom.x as f64, geom.y as f64), deco, None, None, None, Kind::Unspecified) {
- Ok(elem) => custom_elements.push(elem),
+ Ok(elem) => custom_elements.push(crate::elements::OverlayElement::Memory(elem)),
Err(e) => log::warn!("failed to import titlebar buffer for window {id}: {e}"),
}
}
@@ -274,11 +277,11 @@ impl WaylandPlatform {
renderer.create_buffer(Fourcc::Abgr8888, (size.w, size.h).into()).map_err(err)?;
let mut framebuffer = renderer.bind(&mut target).map_err(err)?;
- let mut custom_elements: Vec<MemoryRenderBufferRenderElement<GlesRenderer>> = Vec::new();
+ let mut custom_elements: Vec<crate::elements::OverlayElement<GlesRenderer>> = Vec::new();
for (&id, deco) in self.state.decorations.iter() {
let Some(geom) = self.wm.borrow().window(id).map(|w| w.geometry) else { continue };
if let Ok(elem) = MemoryRenderBufferRenderElement::from_buffer(renderer, (geom.x as f64, geom.y as f64), deco, None, None, None, Kind::Unspecified) {
- custom_elements.push(elem);
+ custom_elements.push(crate::elements::OverlayElement::Memory(elem));
}
}
@@ -334,6 +337,8 @@ impl Platform for WaylandPlatform {
if closed {
return Err(PlatformError::Other("compositor window closed".into()));
}
+ // Held bindings that repeat - see `CompState::tick_repeat`.
+ self.state.tick_repeat();
self.display.dispatch_clients(&mut self.state).map_err(err)?;
self.display.flush_clients().map_err(err)?;
self.render_frame()?;