implement fractional HiDPI scaling

expose output from output map and return a reference
in find

change scale with keyboard shortcut

scale input coordinates and render location
according to output_scale

scale buffers during rendering

adapt window locations on output scale
change to that the location will appear
to be stable

scale udev pointer location on output scale
change to make the pointer location appear stable

Use a UserDataMap to store the udev output id

Short names for udev outputs for easier output
scale configuration
This commit is contained in:
Christian Meissl 2021-07-01 19:51:06 +02:00 committed by Victor Berger
parent e60374a459
commit 72e4d910fe
7 changed files with 442 additions and 256 deletions

View File

@ -39,6 +39,7 @@ pub fn draw_cursor<R, E, F, T>(
frame: &mut F, frame: &mut F,
surface: &wl_surface::WlSurface, surface: &wl_surface::WlSurface,
location: Point<i32, Logical>, location: Point<i32, Logical>,
output_scale: f32,
log: &Logger, log: &Logger,
) -> Result<(), SwapBuffersError> ) -> Result<(), SwapBuffersError>
where where
@ -69,7 +70,7 @@ where
(0, 0).into() (0, 0).into()
} }
}; };
draw_surface_tree(renderer, frame, surface, location - delta, log) draw_surface_tree(renderer, frame, surface, location - delta, output_scale, log)
} }
fn draw_surface_tree<R, E, F, T>( fn draw_surface_tree<R, E, F, T>(
@ -77,6 +78,7 @@ fn draw_surface_tree<R, E, F, T>(
frame: &mut F, frame: &mut F,
root: &wl_surface::WlSurface, root: &wl_surface::WlSurface,
location: Point<i32, Logical>, location: Point<i32, Logical>,
output_scale: f32,
log: &Logger, log: &Logger,
) -> Result<(), SwapBuffersError> ) -> Result<(), SwapBuffersError>
where where
@ -153,6 +155,7 @@ where
let mut location = *location; let mut location = *location;
if let Some(ref data) = states.data_map.get::<RefCell<SurfaceData>>() { if let Some(ref data) = states.data_map.get::<RefCell<SurfaceData>>() {
let mut data = data.borrow_mut(); let mut data = data.borrow_mut();
let buffer_scale = data.buffer_scale as f32;
if let Some(texture) = data if let Some(texture) = data
.texture .texture
.as_mut() .as_mut()
@ -164,10 +167,12 @@ where
let current = states.cached_state.current::<SubsurfaceCachedState>(); let current = states.cached_state.current::<SubsurfaceCachedState>();
location += current.location; location += current.location;
} }
let render_scale = output_scale as f32 / buffer_scale;
if let Err(err) = frame.render_texture_at( if let Err(err) = frame.render_texture_at(
&texture.texture, &texture.texture,
location.to_physical(1), // TODO: handle output scaling factor location.to_f64().to_physical(output_scale as f64).to_i32_round(),
Transform::Normal, /* TODO */ Transform::Normal, /* TODO */
render_scale,
1.0, 1.0,
) { ) {
result = Err(err.into()); result = Err(err.into());
@ -186,6 +191,7 @@ pub fn draw_windows<R, E, F, T>(
frame: &mut F, frame: &mut F,
window_map: &WindowMap, window_map: &WindowMap,
output_rect: Rectangle<i32, Logical>, output_rect: Rectangle<i32, Logical>,
output_scale: f32,
log: &::slog::Logger, log: &::slog::Logger,
) -> Result<(), SwapBuffersError> ) -> Result<(), SwapBuffersError>
where where
@ -205,7 +211,9 @@ where
initial_place.x -= output_rect.loc.x; initial_place.x -= output_rect.loc.x;
if let Some(wl_surface) = toplevel_surface.get_surface() { if let Some(wl_surface) = toplevel_surface.get_surface() {
// this surface is a root of a subsurface tree that needs to be drawn // this surface is a root of a subsurface tree that needs to be drawn
if let Err(err) = draw_surface_tree(renderer, frame, &wl_surface, initial_place, log) { if let Err(err) =
draw_surface_tree(renderer, frame, &wl_surface, initial_place, output_scale, log)
{
result = Err(err); result = Err(err);
} }
// furthermore, draw its popups // furthermore, draw its popups
@ -217,7 +225,9 @@ where
let location = popup.location(); let location = popup.location();
let draw_location = initial_place + location + toplevel_geometry_offset; let draw_location = initial_place + location + toplevel_geometry_offset;
if let Some(wl_surface) = popup.get_surface() { if let Some(wl_surface) = popup.get_surface() {
if let Err(err) = draw_surface_tree(renderer, frame, &wl_surface, draw_location, log) { if let Err(err) =
draw_surface_tree(renderer, frame, &wl_surface, draw_location, output_scale, log)
{
result = Err(err); result = Err(err);
} }
} }
@ -233,6 +243,7 @@ pub fn draw_dnd_icon<R, E, F, T>(
frame: &mut F, frame: &mut F,
surface: &wl_surface::WlSurface, surface: &wl_surface::WlSurface,
location: Point<i32, Logical>, location: Point<i32, Logical>,
output_scale: f32,
log: &::slog::Logger, log: &::slog::Logger,
) -> Result<(), SwapBuffersError> ) -> Result<(), SwapBuffersError>
where where
@ -247,5 +258,5 @@ where
"Trying to display as a dnd icon a surface that does not have the DndIcon role." "Trying to display as a dnd icon a surface that does not have the DndIcon role."
); );
} }
draw_surface_tree(renderer, frame, surface, location, log) draw_surface_tree(renderer, frame, surface, location, output_scale, log)
} }

View File

@ -164,6 +164,32 @@ impl AnvilState<WinitData> {
); );
} }
} }
KeyAction::ScaleUp => {
let current_scale = {
self.output_map
.borrow()
.find_by_name(crate::winit::OUTPUT_NAME)
.map(|o| o.scale())
.unwrap_or(1.0)
};
self.output_map
.borrow_mut()
.update_scale_by_name(current_scale + 0.25f32, crate::winit::OUTPUT_NAME);
}
KeyAction::ScaleDown => {
let current_scale = {
self.output_map
.borrow()
.find_by_name(crate::winit::OUTPUT_NAME)
.map(|o| o.scale())
.unwrap_or(1.0)
};
self.output_map.borrow_mut().update_scale_by_name(
f32::max(1.0f32, current_scale - 0.25f32),
crate::winit::OUTPUT_NAME,
);
}
action => { action => {
warn!(self.log, "Key action {:?} unsupported on winit backend.", action); warn!(self.log, "Key action {:?} unsupported on winit backend.", action);
} }
@ -172,12 +198,12 @@ impl AnvilState<WinitData> {
InputEvent::PointerButton { event, .. } => self.on_pointer_button::<B>(event), InputEvent::PointerButton { event, .. } => self.on_pointer_button::<B>(event),
InputEvent::PointerAxis { event, .. } => self.on_pointer_axis::<B>(event), InputEvent::PointerAxis { event, .. } => self.on_pointer_axis::<B>(event),
InputEvent::Special(WinitEvent::Resized { size, .. }) => { InputEvent::Special(WinitEvent::Resized { size, .. }) => {
self.output_map.borrow_mut().update_mode( self.output_map.borrow_mut().update_mode_by_name(
crate::winit::OUTPUT_NAME,
Mode { Mode {
size, size,
refresh: 60_000, refresh: 60_000,
}, },
crate::winit::OUTPUT_NAME,
); );
} }
_ => { _ => {
@ -190,7 +216,8 @@ impl AnvilState<WinitData> {
let output_size = self let output_size = self
.output_map .output_map
.borrow() .borrow()
.with_primary(|_, rect| (rect.size.w as u32, rect.size.h as u32).into()) .find_by_name(crate::winit::OUTPUT_NAME)
.map(|o| (o.size().w as u32, o.size().h as u32).into())
.unwrap(); .unwrap();
let pos = evt.position_transformed(output_size); let pos = evt.position_transformed(output_size);
self.pointer_location = pos; self.pointer_location = pos;
@ -228,11 +255,7 @@ impl AnvilState<UdevData> {
} }
} }
KeyAction::Screen(num) => { KeyAction::Screen(num) => {
let geometry = self let geometry = self.output_map.borrow().find_by_index(num).map(|o| o.geometry());
.output_map
.borrow()
.find_by_index(num, |_, geometry| geometry)
.ok();
if let Some(geometry) = geometry { if let Some(geometry) = geometry {
let x = geometry.loc.x as f64 + geometry.size.w as f64 / 2.0; let x = geometry.loc.x as f64 + geometry.size.w as f64 / 2.0;
@ -240,6 +263,53 @@ impl AnvilState<UdevData> {
self.pointer_location = (x, y).into() self.pointer_location = (x, y).into()
} }
} }
KeyAction::ScaleUp => {
let mut output_map = self.output_map.borrow_mut();
let output = output_map
.find_by_position(self.pointer_location.to_i32_round())
.map(|o| (o.name().to_owned(), o.location(), o.scale()));
if let Some((name, output_location, scale)) = output {
let new_scale = scale + 0.25;
output_map.update_scale_by_name(new_scale, name);
let rescale = scale as f64 / new_scale as f64;
let output_location = output_location.to_f64();
let mut pointer_output_location = self.pointer_location - output_location;
pointer_output_location.x *= rescale;
pointer_output_location.y *= rescale;
self.pointer_location = output_location + pointer_output_location;
let under = self.window_map.borrow().get_surface_under(self.pointer_location);
self.pointer
.motion(self.pointer_location, under, SCOUNTER.next_serial(), 0);
}
}
KeyAction::ScaleDown => {
let mut output_map = self.output_map.borrow_mut();
let output = output_map
.find_by_position(self.pointer_location.to_i32_round())
.map(|o| (o.name().to_owned(), o.location(), o.scale()));
if let Some((name, output_location, scale)) = output {
let new_scale = f32::max(1.0, scale - 0.25);
output_map.update_scale_by_name(new_scale, name);
let rescale = scale as f64 / new_scale as f64;
let output_location = output_location.to_f64();
let mut pointer_output_location = self.pointer_location - output_location;
pointer_output_location.x *= rescale;
pointer_output_location.y *= rescale;
self.pointer_location = output_location + pointer_output_location;
let under = self.window_map.borrow().get_surface_under(self.pointer_location);
self.pointer
.motion(self.pointer_location, under, SCOUNTER.next_serial(), 0);
}
}
}, },
InputEvent::PointerMotion { event, .. } => self.on_pointer_move::<B>(event), InputEvent::PointerMotion { event, .. } => self.on_pointer_move::<B>(event),
InputEvent::PointerButton { event, .. } => self.on_pointer_button::<B>(event), InputEvent::PointerButton { event, .. } => self.on_pointer_button::<B>(event),
@ -292,8 +362,9 @@ impl AnvilState<UdevData> {
let tablet_seat = self.seat.tablet_seat(); let tablet_seat = self.seat.tablet_seat();
let window_map = self.window_map.borrow(); let window_map = self.window_map.borrow();
output_map let output_geometry = output_map.with_primary().map(|o| o.geometry());
.with_primary(|_, rect| {
if let Some(rect) = output_geometry {
let rect_size = (rect.size.w as u32, rect.size.h as u32).into(); let rect_size = (rect.size.w as u32, rect.size.h as u32).into();
*pointer_location = evt.position_transformed(rect_size) + rect.loc.to_f64(); *pointer_location = evt.position_transformed(rect_size) + rect.loc.to_f64();
@ -329,8 +400,7 @@ impl AnvilState<UdevData> {
evt.time(), evt.time(),
); );
} }
}) }
.unwrap();
} }
fn on_tablet_tool_proximity<B: InputBackend>(&mut self, evt: B::TabletToolProximityEvent) { fn on_tablet_tool_proximity<B: InputBackend>(&mut self, evt: B::TabletToolProximityEvent) {
@ -339,8 +409,9 @@ impl AnvilState<UdevData> {
let tablet_seat = self.seat.tablet_seat(); let tablet_seat = self.seat.tablet_seat();
let window_map = self.window_map.borrow(); let window_map = self.window_map.borrow();
output_map let output_geometry = output_map.with_primary().map(|o| o.geometry());
.with_primary(|_, rect| {
if let Some(rect) = output_geometry {
let tool = evt.tool(); let tool = evt.tool();
tablet_seat.add_tool(&tool); tablet_seat.add_tool(&tool);
@ -363,8 +434,7 @@ impl AnvilState<UdevData> {
ProximityState::Out => tool.proximity_out(evt.time()), ProximityState::Out => tool.proximity_out(evt.time()),
} }
} }
}) }
.unwrap();
} }
fn on_tablet_tool_tip<B: InputBackend>(&mut self, evt: B::TabletToolTipEvent) { fn on_tablet_tool_tip<B: InputBackend>(&mut self, evt: B::TabletToolTipEvent) {
@ -439,6 +509,8 @@ enum KeyAction {
Run(String), Run(String),
/// Switch the current screen /// Switch the current screen
Screen(usize), Screen(usize),
ScaleUp,
ScaleDown,
/// Forward the key to the client /// Forward the key to the client
Forward, Forward,
/// Do nothing more /// Do nothing more
@ -460,6 +532,10 @@ fn process_keyboard_shortcut(modifiers: ModifiersState, keysym: Keysym) -> KeyAc
KeyAction::Run("weston-terminal".into()) KeyAction::Run("weston-terminal".into())
} else if modifiers.logo && keysym >= xkb::KEY_1 && keysym <= xkb::KEY_9 { } else if modifiers.logo && keysym >= xkb::KEY_1 && keysym <= xkb::KEY_9 {
KeyAction::Screen((keysym - xkb::KEY_1) as usize) KeyAction::Screen((keysym - xkb::KEY_1) as usize)
} else if modifiers.logo && modifiers.shift && keysym == xkb::KEY_M {
KeyAction::ScaleDown
} else if modifiers.logo && modifiers.shift && keysym == xkb::KEY_P {
KeyAction::ScaleUp
} else { } else {
KeyAction::Forward KeyAction::Forward
} }

View File

@ -5,10 +5,10 @@ use smithay::{
wayland_protocols::xdg_shell::server::xdg_toplevel, wayland_protocols::xdg_shell::server::xdg_toplevel,
wayland_server::{ wayland_server::{
protocol::{wl_output, wl_surface::WlSurface}, protocol::{wl_output, wl_surface::WlSurface},
Display, Global, Display, Global, UserDataMap,
}, },
}, },
utils::{Logical, Point, Rectangle}, utils::{Logical, Point, Rectangle, Size},
wayland::{ wayland::{
compositor::{with_surface_tree_downward, SubsurfaceCachedState, TraversalAction}, compositor::{with_surface_tree_downward, SubsurfaceCachedState, TraversalAction},
output::{self, Mode, PhysicalProperties}, output::{self, Mode, PhysicalProperties},
@ -17,13 +17,16 @@ use smithay::{
use crate::shell::SurfaceData; use crate::shell::SurfaceData;
struct Output { pub struct Output {
name: String, name: String,
output: output::Output, output: output::Output,
global: Option<Global<wl_output::WlOutput>>, global: Option<Global<wl_output::WlOutput>>,
geometry: Rectangle<i32, Logical>,
surfaces: Vec<WlSurface>, surfaces: Vec<WlSurface>,
current_mode: Mode, current_mode: Mode,
scale: f32,
output_scale: i32,
location: Point<i32, Logical>,
userdata: UserDataMap,
} }
impl Output { impl Output {
@ -40,22 +43,64 @@ impl Output {
{ {
let (output, global) = output::Output::new(display, name.as_ref().into(), physical, logger); let (output, global) = output::Output::new(display, name.as_ref().into(), physical, logger);
output.change_current_state(Some(mode), None, None); let scale = std::env::var(format!("ANVIL_SCALE_{}", name.as_ref()))
.ok()
.and_then(|s| s.parse::<f32>().ok())
.unwrap_or(1.0)
.max(1.0);
let output_scale = scale.round() as i32;
output.change_current_state(Some(mode), None, Some(output_scale));
output.set_preferred(mode); output.set_preferred(mode);
Self { Self {
name: name.as_ref().to_owned(), name: name.as_ref().to_owned(),
global: Some(global), global: Some(global),
output, output,
geometry: Rectangle { location,
loc: location,
// TODO: handle scaling factor
size: mode.size.to_logical(1),
},
surfaces: Vec::new(), surfaces: Vec::new(),
current_mode: mode, current_mode: mode,
scale,
output_scale,
userdata: Default::default(),
} }
} }
pub fn userdata(&self) -> &UserDataMap {
&self.userdata
}
pub fn geometry(&self) -> Rectangle<i32, Logical> {
let loc = self.location();
let size = self.size();
Rectangle { loc, size }
}
pub fn size(&self) -> Size<i32, Logical> {
self.current_mode
.size
.to_f64()
.to_logical(self.scale as f64)
.to_i32_round()
}
pub fn location(&self) -> Point<i32, Logical> {
self.location
}
pub fn scale(&self) -> f32 {
self.scale
}
pub fn name(&self) -> &str {
self.name.as_str()
}
pub fn current_mode(&self) -> Mode {
self.current_mode
}
} }
impl Drop for Output { impl Drop for Output {
@ -64,17 +109,6 @@ impl Drop for Output {
} }
} }
#[derive(Debug)]
pub struct OutputNotFound;
impl std::fmt::Display for OutputNotFound {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("The output could not be found")
}
}
impl std::error::Error for OutputNotFound {}
pub struct OutputMap { pub struct OutputMap {
display: Rc<RefCell<Display>>, display: Rc<RefCell<Display>>,
outputs: Vec<Output>, outputs: Vec<Output>,
@ -100,24 +134,43 @@ impl OutputMap {
// First recalculate the outputs location // First recalculate the outputs location
let mut output_x = 0; let mut output_x = 0;
for output in self.outputs.iter_mut() { for output in self.outputs.iter_mut() {
output.geometry.loc.x = output_x; let output_x_shift = output_x - output.location.x;
output.geometry.loc.y = 0;
output_x += output.geometry.loc.x; // If the scale changed we shift all windows on that output
// so that the location of the window will stay the same on screen
if output_x_shift != 0 {
let mut window_map = self.window_map.borrow_mut();
for surface in output.surfaces.iter() {
let toplevel = window_map.find(surface);
if let Some(toplevel) = toplevel {
let current_location = window_map.location(&toplevel);
if let Some(mut location) = current_location {
if output.geometry().contains(location) {
location.x += output_x_shift;
window_map.set_location(&toplevel, location);
}
}
}
}
}
output.location.x = output_x;
output.location.y = 0;
output_x += output.size().w;
} }
// Check if any windows are now out of outputs range // Check if any windows are now out of outputs range
// and move them to the primary output // and move them to the primary output
let primary_output_location = self let primary_output_location = self.with_primary().map(|o| o.location()).unwrap_or_default();
.with_primary(|_, geometry| geometry)
.ok()
.map(|o| o.loc)
.unwrap_or_default();
let mut window_map = self.window_map.borrow_mut(); let mut window_map = self.window_map.borrow_mut();
// TODO: This is a bit unfortunate, we save the windows in a temp vector // TODO: This is a bit unfortunate, we save the windows in a temp vector
// cause we can not call window_map.set_location within the closure. // cause we can not call window_map.set_location within the closure.
let mut windows_to_move = Vec::new(); let mut windows_to_move = Vec::new();
window_map.with_windows_from_bottom_to_top(|kind, _, &bbox| { window_map.with_windows_from_bottom_to_top(|kind, _, &bbox| {
let within_outputs = self.outputs.iter().any(|o| o.geometry.overlaps(bbox)); let within_outputs = self.outputs.iter().any(|o| o.geometry().overlaps(bbox));
if !within_outputs { if !within_outputs {
windows_to_move.push((kind.to_owned(), primary_output_location)); windows_to_move.push((kind.to_owned(), primary_output_location));
@ -135,9 +188,9 @@ impl OutputMap {
|| state.states.contains(xdg_toplevel::State::Fullscreen) || state.states.contains(xdg_toplevel::State::Fullscreen)
{ {
let output_geometry = if let Some(output) = state.fullscreen_output.as_ref() { let output_geometry = if let Some(output) = state.fullscreen_output.as_ref() {
self.find(output, |_, geometry| geometry).ok() self.find_by_output(output).map(|o| o.geometry())
} else { } else {
self.find_by_position(location, |_, geometry| geometry).ok() self.find_by_position(location).map(|o| o.geometry())
}; };
if let Some(geometry) = output_geometry { if let Some(geometry) = output_geometry {
@ -162,14 +215,14 @@ impl OutputMap {
} }
} }
pub fn add<N>(&mut self, name: N, physical: PhysicalProperties, mode: Mode) pub fn add<N>(&mut self, name: N, physical: PhysicalProperties, mode: Mode) -> &Output
where where
N: AsRef<str>, N: AsRef<str>,
{ {
// Append the output to the end of the existing // Append the output to the end of the existing
// outputs by placing it after the current overall // outputs by placing it after the current overall
// width // width
let location = (self.width() as i32, 0); let location = (self.width(), 0);
let output = Output::new( let output = Output::new(
name, name,
@ -186,123 +239,143 @@ impl OutputMap {
// this would not affect windows, but arrange could re-organize // this would not affect windows, but arrange could re-organize
// outputs from a configuration. // outputs from a configuration.
self.arrange(); self.arrange();
self.outputs.last().unwrap()
} }
pub fn remove<N: AsRef<str>>(&mut self, name: N) { pub fn retain<F>(&mut self, f: F)
let removed_outputs = self.outputs.iter_mut().filter(|o| o.name == name.as_ref()); where
F: FnMut(&Output) -> bool,
{
self.outputs.retain(f);
for output in removed_outputs {
for surface in output.surfaces.drain(..) {
output.output.leave(&surface);
}
}
self.outputs.retain(|o| o.name != name.as_ref());
// Re-arrange outputs cause one or more outputs have
// been removed
self.arrange(); self.arrange();
} }
pub fn width(&self) -> i32 { pub fn width(&self) -> i32 {
// This is a simplification, we only arrange the outputs on the y axis side-by-side // This is a simplification, we only arrange the outputs on the y axis side-by-side
// so that the total width is simply the sum of all output widths. // so that the total width is simply the sum of all output widths.
self.outputs self.outputs.iter().fold(0, |acc, output| acc + output.size().w)
.iter()
.fold(0, |acc, output| acc + output.geometry.size.w)
} }
pub fn height(&self, x: i32) -> Option<i32> { pub fn height(&self, x: i32) -> Option<i32> {
// This is a simplification, we only arrange the outputs on the y axis side-by-side // This is a simplification, we only arrange the outputs on the y axis side-by-side
self.outputs self.outputs
.iter() .iter()
.find(|output| x >= output.geometry.loc.x && x < (output.geometry.loc.x + output.geometry.size.w)) .find(|output| {
.map(|output| output.geometry.size.h) let geometry = output.geometry();
x >= geometry.loc.x && x < (geometry.loc.x + geometry.size.w)
})
.map(|output| output.size().h)
} }
pub fn is_empty(&self) -> bool { pub fn is_empty(&self) -> bool {
self.outputs.is_empty() self.outputs.is_empty()
} }
pub fn with_primary<F, T>(&self, f: F) -> Result<T, OutputNotFound> pub fn with_primary(&self) -> Option<&Output> {
where self.outputs.get(0)
F: FnOnce(&output::Output, Rectangle<i32, Logical>) -> T,
{
let output = self.outputs.get(0).ok_or(OutputNotFound)?;
Ok(f(&output.output, output.geometry))
} }
pub fn find<F, T>(&self, output: &wl_output::WlOutput, f: F) -> Result<T, OutputNotFound> pub fn find<F>(&self, f: F) -> Option<&Output>
where where
F: FnOnce(&output::Output, Rectangle<i32, Logical>) -> T, F: FnMut(&&Output) -> bool,
{ {
let output = self self.outputs.iter().find(f)
.outputs
.iter()
.find(|o| o.output.owns(output))
.ok_or(OutputNotFound)?;
Ok(f(&output.output, output.geometry))
} }
pub fn find_by_name<N, F, T>(&self, name: N, f: F) -> Result<T, OutputNotFound> pub fn find_by_output(&self, output: &wl_output::WlOutput) -> Option<&Output> {
self.find(|o| o.output.owns(output))
}
pub fn find_by_name<N>(&self, name: N) -> Option<&Output>
where where
N: AsRef<str>, N: AsRef<str>,
F: FnOnce(&output::Output, Rectangle<i32, Logical>) -> T,
{ {
let output = self self.find(|o| o.name == name.as_ref())
.outputs
.iter()
.find(|o| o.name == name.as_ref())
.ok_or(OutputNotFound)?;
Ok(f(&output.output, output.geometry))
} }
pub fn find_by_position<F, T>(&self, position: Point<i32, Logical>, f: F) -> Result<T, OutputNotFound> pub fn find_by_position(&self, position: Point<i32, Logical>) -> Option<&Output> {
self.find(|o| o.geometry().contains(position))
}
pub fn find_by_index(&self, index: usize) -> Option<&Output> {
self.outputs.get(index)
}
pub fn update<F>(&mut self, mode: Option<Mode>, scale: Option<f32>, mut f: F)
where where
F: FnOnce(&output::Output, Rectangle<i32, Logical>) -> T, F: FnMut(&Output) -> bool,
{ {
let output = self let output = self.outputs.iter_mut().find(|o| f(&**o));
.outputs
.iter()
.find(|o| o.geometry.contains(position))
.ok_or(OutputNotFound)?;
Ok(f(&output.output, output.geometry))
}
pub fn find_by_index<F, T>(&self, index: usize, f: F) -> Result<T, OutputNotFound>
where
F: FnOnce(&output::Output, Rectangle<i32, Logical>) -> T,
{
let output = self.outputs.get(index).ok_or(OutputNotFound)?;
Ok(f(&output.output, output.geometry))
}
pub fn update_mode<N: AsRef<str>>(&mut self, name: N, mode: Mode) {
let output = self.outputs.iter_mut().find(|o| o.name == name.as_ref());
// NOTE: This will just simply shift all outputs after
// the output who's mode has changed left or right depending
// on if the mode width increased or decreased.
// We could also re-configure toplevels here.
// If a surface is now visible on an additional output because
// the output width decreased the refresh method will take
// care and will send enter for the output.
if let Some(output) = output { if let Some(output) = output {
// TODO: handle scale factors if let Some(mode) = mode {
output.geometry.size = mode.size.to_logical(1);
output.output.delete_mode(output.current_mode); output.output.delete_mode(output.current_mode);
output.output.change_current_state(Some(mode), None, None); output
.output
.change_current_state(Some(mode), None, Some(output.output_scale));
output.output.set_preferred(mode); output.output.set_preferred(mode);
output.current_mode = mode; output.current_mode = mode;
}
if let Some(scale) = scale {
// Calculate in which direction the scale changed
let rescale = output.scale() / scale;
{
// We take the current location of our toplevels and move them
// to the same location using the new scale
let mut window_map = self.window_map.borrow_mut();
for surface in output.surfaces.iter() {
let toplevel = window_map.find(surface);
if let Some(toplevel) = toplevel {
let current_location = window_map.location(&toplevel);
if let Some(location) = current_location {
let output_geometry = output.geometry();
if output_geometry.contains(location) {
let mut toplevel_output_location =
(location - output_geometry.loc).to_f64();
toplevel_output_location.x *= rescale as f64;
toplevel_output_location.y *= rescale as f64;
window_map.set_location(
&toplevel,
output_geometry.loc + toplevel_output_location.to_i32_round(),
);
}
}
}
}
}
let output_scale = scale.round() as i32;
output.scale = scale;
if output.output_scale != output_scale {
output.output_scale = output_scale;
output
.output
.change_current_state(Some(output.current_mode), None, Some(output_scale));
}
}
}
// Re-arrange outputs cause the size of one output changed
self.arrange(); self.arrange();
} }
pub fn update_by_name<N: AsRef<str>>(&mut self, mode: Option<Mode>, scale: Option<f32>, name: N) {
self.update(mode, scale, |o| o.name() == name.as_ref())
}
pub fn update_scale_by_name<N: AsRef<str>>(&mut self, scale: f32, name: N) {
self.update_by_name(None, Some(scale), name)
}
pub fn update_mode_by_name<N: AsRef<str>>(&mut self, mode: Mode, name: N) {
self.update_by_name(Some(mode), None, name)
} }
pub fn refresh(&mut self) { pub fn refresh(&mut self) {
@ -320,7 +393,7 @@ impl OutputMap {
// Check if the bounding box of the toplevel intersects with // Check if the bounding box of the toplevel intersects with
// the output, if not no surface in the tree can intersect with // the output, if not no surface in the tree can intersect with
// the output. // the output.
if !output.geometry.overlaps(bbox) { if !output.geometry().overlaps(bbox) {
if let Some(surface) = kind.get_surface() { if let Some(surface) = kind.get_surface() {
with_surface_tree_downward( with_surface_tree_downward(
surface, surface,
@ -365,7 +438,7 @@ impl OutputMap {
if let Some(size) = data.and_then(|d| d.borrow().size()) { if let Some(size) = data.and_then(|d| d.borrow().size()) {
let surface_rectangle = Rectangle { loc, size }; let surface_rectangle = Rectangle { loc, size };
if output.geometry.overlaps(surface_rectangle) { if output.geometry().overlaps(surface_rectangle) {
// We found a matching output, check if we already sent enter // We found a matching output, check if we already sent enter
if !output.surfaces.contains(wl_surface) { if !output.surfaces.contains(wl_surface) {
output.output.enter(wl_surface); output.output.enter(wl_surface);

View File

@ -301,7 +301,7 @@ fn fullscreen_output_geometry(
// First test if a specific output has been requested // First test if a specific output has been requested
// if the requested output is not found ignore the request // if the requested output is not found ignore the request
if let Some(wl_output) = wl_output { if let Some(wl_output) = wl_output {
return output_map.find(&wl_output, |_, geometry| geometry).ok(); return output_map.find_by_output(&wl_output).map(|o| o.geometry());
} }
// There is no output preference, try to find the output // There is no output preference, try to find the output
@ -311,7 +311,7 @@ fn fullscreen_output_geometry(
.and_then(|kind| window_map.location(&kind)); .and_then(|kind| window_map.location(&kind));
if let Some(location) = window_location { if let Some(location) = window_location {
let window_output = output_map.find_by_position(location, |_, geometry| geometry).ok(); let window_output = output_map.find_by_position(location).map(|o| o.geometry());
if let Some(result) = window_output { if let Some(result) = window_output {
return Some(result); return Some(result);
@ -319,7 +319,7 @@ fn fullscreen_output_geometry(
} }
// Fallback to primary output // Fallback to primary output
output_map.with_primary(|_, geometry| geometry).ok() output_map.with_primary().map(|o| o.geometry())
} }
pub fn init_shell<BackendData: 'static>(display: Rc<RefCell<Display>>, log: ::slog::Logger) -> ShellHandles { pub fn init_shell<BackendData: 'static>(display: Rc<RefCell<Display>>, log: ::slog::Logger) -> ShellHandles {
@ -355,8 +355,8 @@ pub fn init_shell<BackendData: 'static>(display: Rc<RefCell<Display>>, log: ::sl
let output_geometry = xdg_output_map let output_geometry = xdg_output_map
.borrow() .borrow()
.with_primary(|_, geometry| geometry) .with_primary()
.ok() .map(|o| o.geometry())
.unwrap_or_else(|| Rectangle::from_loc_and_size((0, 0), (800, 800))); .unwrap_or_else(|| Rectangle::from_loc_and_size((0, 0), (800, 800)));
let max_x = output_geometry.loc.x + (((output_geometry.size.w as f32) / 3.0) * 2.0) as i32; let max_x = output_geometry.loc.x + (((output_geometry.size.w as f32) / 3.0) * 2.0) as i32;
let max_y = output_geometry.loc.y + (((output_geometry.size.h as f32) / 3.0) * 2.0) as i32; let max_y = output_geometry.loc.y + (((output_geometry.size.h as f32) / 3.0) * 2.0) as i32;
@ -593,8 +593,8 @@ pub fn init_shell<BackendData: 'static>(display: Rc<RefCell<Display>>, log: ::sl
.and_then(|position| { .and_then(|position| {
xdg_output_map xdg_output_map
.borrow() .borrow()
.find_by_position(position, |_, geometry| geometry) .find_by_position(position)
.ok() .map(|o| o.geometry())
}) })
}; };
@ -645,8 +645,8 @@ pub fn init_shell<BackendData: 'static>(display: Rc<RefCell<Display>>, log: ::sl
let output_geometry = shell_output_map let output_geometry = shell_output_map
.borrow() .borrow()
.with_primary(|_, geometry| geometry) .with_primary()
.ok() .map(|o| o.geometry())
.unwrap_or_else(|| Rectangle::from_loc_and_size((0, 0), (800, 800))); .unwrap_or_else(|| Rectangle::from_loc_and_size((0, 0), (800, 800)));
let max_x = let max_x =
output_geometry.loc.x + (((output_geometry.size.w as f32) / 3.0) * 2.0) as i32; output_geometry.loc.x + (((output_geometry.size.w as f32) / 3.0) * 2.0) as i32;

View File

@ -78,14 +78,13 @@ impl AsRawFd for SessionFd {
} }
} }
struct UdevOutputMap { #[derive(Debug, PartialEq)]
pub device_id: dev_t, struct UdevOutputId {
pub crtc: crtc::Handle, device_id: dev_t,
pub output_name: String, crtc: crtc::Handle,
} }
pub struct UdevData { pub struct UdevData {
output_map: Vec<UdevOutputMap>,
pub session: AutoSession, pub session: AutoSession,
#[cfg(feature = "egl")] #[cfg(feature = "egl")]
primary_gpu: Option<PathBuf>, primary_gpu: Option<PathBuf>,
@ -132,7 +131,6 @@ pub fn run_udev(
let data = UdevData { let data = UdevData {
session, session,
output_map: Vec::new(),
#[cfg(feature = "egl")] #[cfg(feature = "egl")]
primary_gpu, primary_gpu,
backends: HashMap::new(), backends: HashMap::new(),
@ -272,7 +270,6 @@ fn scan_connectors(
device: &mut DrmDevice<SessionFd>, device: &mut DrmDevice<SessionFd>,
gbm: &GbmDevice<SessionFd>, gbm: &GbmDevice<SessionFd>,
renderer: &mut Gles2Renderer, renderer: &mut Gles2Renderer,
backend_output_map: &mut Vec<UdevOutputMap>,
output_map: &mut crate::output_map::OutputMap, output_map: &mut crate::output_map::OutputMap,
signaler: &Signaler<SessionSignal>, signaler: &Signaler<SessionSignal>,
logger: &::slog::Logger, logger: &::slog::Logger,
@ -342,14 +339,26 @@ fn scan_connectors(
refresh: (mode.vrefresh() * 1000) as i32, refresh: (mode.vrefresh() * 1000) as i32,
}; };
let output_name = format!( let other_short_name;
"{:?}-{}", let interface_short_name = match connector_info.interface() {
connector_info.interface(), drm::control::connector::Interface::DVII => "DVI-I",
connector_info.interface_id() drm::control::connector::Interface::DVID => "DVI-D",
); drm::control::connector::Interface::DVIA => "DVI-A",
drm::control::connector::Interface::SVideo => "S-VIDEO",
drm::control::connector::Interface::DisplayPort => "DP",
drm::control::connector::Interface::HDMIA => "HDMI-A",
drm::control::connector::Interface::HDMIB => "HDMI-B",
drm::control::connector::Interface::EmbeddedDisplayPort => "eDP",
other => {
other_short_name = format!("{:?}", other);
&other_short_name
}
};
let output_name = format!("{}-{}", interface_short_name, connector_info.interface_id());
let (phys_w, phys_h) = connector_info.size().unwrap_or((0, 0)); let (phys_w, phys_h) = connector_info.size().unwrap_or((0, 0));
output_map.add( let output = output_map.add(
&output_name, &output_name,
PhysicalProperties { PhysicalProperties {
size: (phys_w as i32, phys_h as i32).into(), size: (phys_w as i32, phys_h as i32).into(),
@ -360,10 +369,9 @@ fn scan_connectors(
mode, mode,
); );
backend_output_map.push(UdevOutputMap { output.userdata().insert_if_missing(|| UdevOutputId {
crtc, crtc,
device_id: device.device_id(), device_id: device.device_id(),
output_name,
}); });
entry.insert(Rc::new(RefCell::new(renderer))); entry.insert(Rc::new(RefCell::new(renderer)));
@ -456,7 +464,6 @@ impl AnvilState<UdevData> {
&mut device, &mut device,
&gbm, &gbm,
&mut *renderer.borrow_mut(), &mut *renderer.borrow_mut(),
&mut self.backend_data.output_map,
&mut *self.output_map.borrow_mut(), &mut *self.output_map.borrow_mut(),
&self.backend_data.signaler, &self.backend_data.signaler,
&self.log, &self.log,
@ -517,20 +524,14 @@ impl AnvilState<UdevData> {
let logger = self.log.clone(); let logger = self.log.clone();
let loop_handle = self.handle.clone(); let loop_handle = self.handle.clone();
let signaler = self.backend_data.signaler.clone(); let signaler = self.backend_data.signaler.clone();
let removed_outputs = self
.backend_data
.output_map
.iter()
.filter(|o| o.device_id == device)
.map(|o| o.output_name.as_str());
for output in removed_outputs { self.output_map.borrow_mut().retain(|output| {
self.output_map.borrow_mut().remove(output); output
} .userdata()
.get::<UdevOutputId>()
self.backend_data .map(|id| id.device_id != device)
.output_map .unwrap_or(true)
.retain(|output| output.device_id != device); });
let mut source = backend_data.event_dispatcher.as_source_mut(); let mut source = backend_data.event_dispatcher.as_source_mut();
let mut backends = backend_data.surfaces.borrow_mut(); let mut backends = backend_data.surfaces.borrow_mut();
@ -538,7 +539,6 @@ impl AnvilState<UdevData> {
&mut *source, &mut *source,
&backend_data.gbm, &backend_data.gbm,
&mut *backend_data.renderer.borrow_mut(), &mut *backend_data.renderer.borrow_mut(),
&mut self.backend_data.output_map,
&mut *self.output_map.borrow_mut(), &mut *self.output_map.borrow_mut(),
&signaler, &signaler,
&logger, &logger,
@ -563,16 +563,14 @@ impl AnvilState<UdevData> {
// drop surfaces // drop surfaces
backend_data.surfaces.borrow_mut().clear(); backend_data.surfaces.borrow_mut().clear();
debug!(self.log, "Surfaces dropped"); debug!(self.log, "Surfaces dropped");
let removed_outputs = self
.backend_data self.output_map.borrow_mut().retain(|output| {
.output_map output
.iter() .userdata()
.filter(|o| o.device_id == device) .get::<UdevOutputId>()
.map(|o| o.output_name.as_str()); .map(|id| id.device_id != device)
for output_id in removed_outputs { .unwrap_or(true)
self.output_map.borrow_mut().remove(output_id); });
}
self.backend_data.output_map.retain(|o| o.device_id != device);
let _device = self.handle.remove(backend_data.registration_token); let _device = self.handle.remove(backend_data.registration_token);
let _device = backend_data.event_dispatcher.into_source_inner(); let _device = backend_data.event_dispatcher.into_source_inner();
@ -618,7 +616,6 @@ impl AnvilState<UdevData> {
device_backend.dev_id, device_backend.dev_id,
crtc, crtc,
&mut *self.window_map.borrow_mut(), &mut *self.window_map.borrow_mut(),
&self.backend_data.output_map,
&*self.output_map.borrow(), &*self.output_map.borrow(),
self.pointer_location, self.pointer_location,
&device_backend.pointer_image, &device_backend.pointer_image,
@ -666,7 +663,6 @@ fn render_surface(
device_id: dev_t, device_id: dev_t,
crtc: crtc::Handle, crtc: crtc::Handle,
window_map: &mut WindowMap, window_map: &mut WindowMap,
backend_output_map: &[UdevOutputMap],
output_map: &crate::output_map::OutputMap, output_map: &crate::output_map::OutputMap,
pointer_location: Point<f64, Logical>, pointer_location: Point<f64, Logical>,
pointer_image: &Gles2Texture, pointer_image: &Gles2Texture,
@ -676,14 +672,12 @@ fn render_surface(
) -> Result<(), SwapBuffersError> { ) -> Result<(), SwapBuffersError> {
surface.frame_submitted()?; surface.frame_submitted()?;
let output_geometry = backend_output_map let output = output_map
.iter() .find(|o| o.userdata().get::<UdevOutputId>() == Some(&UdevOutputId { device_id, crtc }))
.find(|o| o.device_id == device_id && o.crtc == crtc) .map(|output| (output.geometry(), output.scale(), output.current_mode()));
.map(|o| o.output_name.as_str())
.and_then(|name| output_map.find_by_name(name, |_, geometry| geometry).ok());
let output_geometry = if let Some(geometry) = output_geometry { let (output_geometry, output_scale, mode) = if let Some((geometry, scale, mode)) = output {
geometry (geometry, scale, mode)
} else { } else {
// Somehow we got called with a non existing output // Somehow we got called with a non existing output
return Ok(()); return Ok(());
@ -694,13 +688,12 @@ fn render_surface(
// and draw to our buffer // and draw to our buffer
match renderer match renderer
.render( .render(
// TODO: handle scale factor mode.size,
output_geometry.size.to_physical(1),
Transform::Flipped180, // Scanout is rotated Transform::Flipped180, // Scanout is rotated
|renderer, frame| { |renderer, frame| {
frame.clear([0.8, 0.8, 0.9, 1.0])?; frame.clear([0.8, 0.8, 0.9, 1.0])?;
// draw the surfaces // draw the surfaces
draw_windows(renderer, frame, window_map, output_geometry, logger)?; draw_windows(renderer, frame, window_map, output_geometry, output_scale, logger)?;
// set cursor // set cursor
if output_geometry.to_f64().contains(pointer_location) { if output_geometry.to_f64().contains(pointer_location) {
@ -711,7 +704,14 @@ fn render_surface(
{ {
if let Some(ref wl_surface) = dnd_icon.as_ref() { if let Some(ref wl_surface) = dnd_icon.as_ref() {
if wl_surface.as_ref().is_alive() { if wl_surface.as_ref().is_alive() {
draw_dnd_icon(renderer, frame, wl_surface, relative_ptr_location, logger)?; draw_dnd_icon(
renderer,
frame,
wl_surface,
relative_ptr_location,
output_scale,
logger,
)?;
} }
} }
} }
@ -727,13 +727,23 @@ fn render_surface(
} }
if let CursorImageStatus::Image(ref wl_surface) = *cursor_status { if let CursorImageStatus::Image(ref wl_surface) = *cursor_status {
draw_cursor(renderer, frame, wl_surface, relative_ptr_location, logger)?; draw_cursor(
renderer,
frame,
wl_surface,
relative_ptr_location,
output_scale,
logger,
)?;
} else { } else {
// TODO: handle output scale factor
frame.render_texture_at( frame.render_texture_at(
pointer_image, pointer_image,
relative_ptr_location.to_physical(1), relative_ptr_location
.to_f64()
.to_physical(output_scale as f64)
.to_i32_round(),
Transform::Normal, Transform::Normal,
output_scale,
1.0, 1.0,
)?; )?;
} }

View File

@ -110,11 +110,11 @@ pub fn run_winit(
{ {
let mut renderer = renderer.borrow_mut(); let mut renderer = renderer.borrow_mut();
// This is safe to do as with winit we are guaranteed to have exactly one output // This is safe to do as with winit we are guaranteed to have exactly one output
let output_geometry = state let (output_geometry, output_scale) = state
.output_map .output_map
.borrow() .borrow()
.find_by_name(OUTPUT_NAME, |_, geometry| geometry) .find_by_name(OUTPUT_NAME)
.ok() .map(|output| (output.geometry(), output.scale()))
.unwrap(); .unwrap();
let result = renderer let result = renderer
@ -127,6 +127,7 @@ pub fn run_winit(
frame, frame,
&*state.window_map.borrow(), &*state.window_map.borrow(),
output_geometry, output_geometry,
output_scale,
&log, &log,
)?; )?;
@ -136,7 +137,14 @@ pub fn run_winit(
let guard = state.dnd_icon.lock().unwrap(); let guard = state.dnd_icon.lock().unwrap();
if let Some(ref surface) = *guard { if let Some(ref surface) = *guard {
if surface.as_ref().is_alive() { if surface.as_ref().is_alive() {
draw_dnd_icon(renderer, frame, surface, (x as i32, y as i32).into(), &log)?; draw_dnd_icon(
renderer,
frame,
surface,
(x as i32, y as i32).into(),
output_scale,
&log,
)?;
} }
} }
} }
@ -155,7 +163,14 @@ pub fn run_winit(
// draw as relevant // draw as relevant
if let CursorImageStatus::Image(ref surface) = *guard { if let CursorImageStatus::Image(ref surface) = *guard {
cursor_visible = false; cursor_visible = false;
draw_cursor(renderer, frame, surface, (x as i32, y as i32).into(), &log)?; draw_cursor(
renderer,
frame,
surface,
(x as i32, y as i32).into(),
output_scale,
&log,
)?;
} else { } else {
cursor_visible = true; cursor_visible = true;
} }

View File

@ -185,6 +185,7 @@ pub trait Frame {
texture: &Self::TextureId, texture: &Self::TextureId,
pos: Point<i32, Physical>, pos: Point<i32, Physical>,
transform: Transform, transform: Transform,
scale: f32,
alpha: f32, alpha: f32,
) -> Result<(), Self::Error> { ) -> Result<(), Self::Error> {
let mut mat = Matrix3::<f32>::identity(); let mut mat = Matrix3::<f32>::identity();
@ -192,7 +193,7 @@ pub trait Frame {
// position and scale // position and scale
let size = texture.size(); let size = texture.size();
mat = mat * Matrix3::from_translation(Vector2::new(pos.x as f32, pos.y as f32)); mat = mat * Matrix3::from_translation(Vector2::new(pos.x as f32, pos.y as f32));
mat = mat * Matrix3::from_nonuniform_scale(size.0 as f32, size.1 as f32); mat = mat * Matrix3::from_nonuniform_scale(size.0 as f32 * scale, size.1 as f32 * scale);
//apply surface transformation //apply surface transformation
mat = mat * Matrix3::from_translation(Vector2::new(0.5, 0.5)); mat = mat * Matrix3::from_translation(Vector2::new(0.5, 0.5));