Commit c663ae15e5bc
tarball7 files changed+166-0
@@ -104,6 +104,7 @@ impl CompositorHandler for State {
104104
let _ = popup.send_configure();105105
}106106
self.layer_commit(surface);107
+ self.send_scale_once(surface);107108
self.dirty.redraw = true;108109
}109110
}@@ -13,6 +13,7 @@ use smithay::wayland::compositor::CompositorState;
1313
use smithay::wayland::cursor_shape::CursorShapeManagerState;1414
use smithay::wayland::dmabuf::{DmabufFeedbackBuilder, DmabufGlobal, DmabufState};1515
use smithay::wayland::foreign_toplevel_list::ForeignToplevelListState;16
+use smithay::wayland::fractional_scale::FractionalScaleManagerState;1617
use smithay::wayland::idle_inhibit::IdleInhibitManagerState;1718
use smithay::wayland::image_capture_source::{1819
ImageCaptureSourceState, OutputCaptureSourceState, ToplevelCaptureSourceState,@@ -59,6 +60,7 @@ pub struct Protocols {
5960
_output_manager: OutputManagerState,6061
_cursor_shape: CursorShapeManagerState,6162
_viewporter: ViewporterState,63
+ _fractional_scale: FractionalScaleManagerState,6264
}6365
6466
impl Protocols {@@ -118,6 +120,7 @@ impl Protocols {
118120
_output_manager: OutputManagerState::new_with_xdg_output::<State>(dh),119121
_cursor_shape: CursorShapeManagerState::new::<State>(dh),120122
_viewporter: ViewporterState::new::<State>(dh),123
+ _fractional_scale: FractionalScaleManagerState::new::<State>(dh),121124
}122125
}123126
}@@ -39,6 +39,20 @@ impl Lock {
3939
.map(|(_, s)| s)4040
}4141
42
+ pub fn surfaces(&self) -> impl Iterator<Item = (&Output, &LockSurface)> {43
+ self.surfaces44
+ .iter()45
+ .map(|(o, s)| (o, s))46
+ .filter(|(_, s)| s.alive())47
+ }48
+49
+ pub fn output_of(&self, surface: &WlSurface) -> Option<Output> {50
+ self.surfaces51
+ .iter()52
+ .find(|(_, s)| s.wl_surface() == surface)53
+ .map(|(o, _)| o.clone())54
+ }55
+4256
/// Drops what belonged to an output that was unplugged.4357
pub fn forget(&mut self, output: &Output) {4458
self.surfaces.retain(|(o, _)| o != output);@@ -21,6 +21,7 @@ mod nested;
2121
mod pacing;2222
mod popups;2323
mod power;24
+mod scale;2425
mod scene;2526
mod seat;2627
mod signals;@@ -0,0 +1,144 @@
1
+//! Tells clients how many pixels make a logical one where their surfaces are2
+//! shown, through wl_surface and wp_fractional_scale. X11 windows are left3
+//! alone, since Xwayland draws at scale 1 and the renderer stretches it.4
+5
+use crate::state::State;6
+use smithay::desktop::utils::with_surfaces_surface_tree;7
+use smithay::desktop::{WindowSurfaceType, find_popup_root_surface, layer_map_for_output};8
+use smithay::output::{Output, Scale};9
+use smithay::reexports::wayland_server::Resource;10
+use smithay::reexports::wayland_server::protocol::wl_surface::WlSurface;11
+use smithay::utils::Transform;12
+use smithay::wayland::compositor::{SurfaceData, send_surface_state, with_states};13
+use smithay::wayland::fractional_scale::{FractionalScaleHandler, with_fractional_scale};14
+15
+/// Set on a surface once it was told a scale, so a commit only pays for the16
+/// first one.17
+struct Told;18
+19
+impl FractionalScaleHandler for State {20
+ fn new_fractional_scale(&mut self, surface: WlSurface) {21
+ self.send_scale(&surface);22
+ }23
+}24
+25
+fn apply(surface: &WlSurface, states: &SurfaceData, scale: Scale) {26
+ send_surface_state(surface, states, scale.integer_scale(), Transform::Normal);27
+ with_fractional_scale(states, |f| f.set_preferred_scale(scale.fractional_scale()));28
+ states.data_map.insert_if_missing(|| Told);29
+}30
+31
+impl State {32
+ /// Tells a surface that has not heard yet, which every commit asks of33
+ /// the surfaces it may have just created.34
+ pub fn send_scale_once(&self, surface: &WlSurface) {35
+ if with_states(surface, |s| s.data_map.get::<Told>().is_none()) {36
+ self.send_scale(surface);37
+ }38
+ }39
+40
+ pub fn send_scale(&self, surface: &WlSurface) {41
+ let xwayland = surface42
+ .client()43
+ .is_some_and(|c| c.get_data::<smithay::xwayland::XWaylandClientData>().is_some());44
+ match self.scale_output(surface).filter(|_| !xwayland) {45
+ Some(output) => {46
+ let scale = output.current_scale();47
+ with_states(surface, |states| apply(surface, states, scale));48
+ }49
+ None => with_states(surface, |states| {50
+ states.data_map.insert_if_missing(|| Told);51
+ }),52
+ }53
+ }54
+55
+ /// Walks every surface the compositor shows, for when a scale or a56
+ /// window's output changed.57
+ pub fn send_scales(&self) {58
+ let layout = &self.wm.layout;59
+ for (id, state) in self.wm.iter() {60
+ if state.window.x11_surface().is_some() {61
+ continue;62
+ }63
+ let output = state64
+ .placed65
+ .map(|(_, _, o)| o)66
+ .or_else(|| layout.window(id)?.output)67
+ .and_then(|o| self.backend.output(o))68
+ .or_else(|| self.pointer_output());69
+ if let Some(scale) = output.map(|o| o.current_scale()) {70
+ state71
+ .window72
+ .with_surfaces(|surface, states| apply(surface, states, scale));73
+ }74
+ }75
+ for output in self.wm.space.outputs() {76
+ let scale = output.current_scale();77
+ for l in layer_map_for_output(output).layers() {78
+ l.with_surfaces(|surface, states| apply(surface, states, scale));79
+ }80
+ }81
+ for (output, surface) in self.services.lock.surfaces() {82
+ let scale = output.current_scale();83
+ with_surfaces_surface_tree(surface.wl_surface(), |surface, states| {84
+ apply(surface, states, scale);85
+ });86
+ }87
+ self.send_pointer_scales();88
+ }89
+90
+ /// The cursor image and the drag icon belong to the pointer's output.91
+ pub fn send_pointer_scales(&self) {92
+ let Some(scale) = self.pointer_output().map(|o| o.current_scale()) else {93
+ return;94
+ };95
+ let cursor = match &self.input.cursor_status {96
+ smithay::input::pointer::CursorImageStatus::Surface(s) => Some(s),97
+ _ => None,98
+ };99
+ let icon = self.input.dnd_icon.as_ref().map(|i| &i.surface);100
+ for surface in cursor.into_iter().chain(icon) {101
+ with_surfaces_surface_tree(surface, |surface, states| apply(surface, states, scale));102
+ }103
+ }104
+105
+ pub fn pointer_output(&self) -> Option<Output> {106
+ self.wm107
+ .space108
+ .output_under(self.input.pointer_pos)109
+ .next()110
+ .or_else(|| self.wm.space.outputs().next())111
+ .cloned()112
+ }113
+114
+ /// The output a surface is mainly shown on.115
+ fn scale_output(&self, surface: &WlSurface) -> Option<Output> {116
+ let mut root = crate::idle::root_surface(surface);117
+ if let Some(popup) = self.wm.popups.find_popup(&root)118
+ && let Ok(parent) = find_popup_root_surface(&popup)119
+ {120
+ root = parent;121
+ }122
+ if let Some(id) = self.window_id(&root) {123
+ if self.wm.window(id)?.x11_surface().is_some() {124
+ return None;125
+ }126
+ let o = self127
+ .wm128
+ .get(id)?129
+ .placed130
+ .map(|(_, _, o)| o)131
+ .or_else(|| self.wm.layout.window(id)?.output)?;132
+ return self.backend.output(o);133
+ }134
+ let on_layer = self.wm.space.outputs().find(|o| {135
+ layer_map_for_output(o)136
+ .layer_for_surface(&root, WindowSurfaceType::TOPLEVEL)137
+ .is_some()138
+ });139
+ on_layer140
+ .cloned()141
+ .or_else(|| self.services.lock.output_of(&root))142
+ .or_else(|| self.pointer_output())143
+ }144
+}@@ -27,6 +27,7 @@ impl SeatHandler for State {
2727
2828
fn cursor_image(&mut self, _seat: &Seat<Self>, image: CursorImageStatus) {2929
self.input.cursor_status = image;30
+ self.send_pointer_scales();3031
self.dirty.redraw = true;3132
}3233
@@ -144,6 +145,7 @@ impl WaylandDndGrabHandler for State {
144145
surface,145146
offset: Point::default(),146147
});148
+ self.send_pointer_scales();147149
self.dirty.redraw = true;148150
let dh = self.core.display.clone();149151
match type_ {@@ -418,6 +418,7 @@ impl State {
418418
self.sync_focus();419419
self.follow_focus();420420
self.update_inhibit();421
+ self.send_scales();421422
self.dirty.redraw = true;422423
self.dirty.snapshot = true;423424
}