diff --git a/anvil/src/drawing.rs b/anvil/src/drawing.rs index 1e202c7..913c3c6 100644 --- a/anvil/src/drawing.rs +++ b/anvil/src/drawing.rs @@ -39,6 +39,7 @@ pub fn draw_cursor( frame: &mut F, surface: &wl_surface::WlSurface, location: Point, + output_scale: f32, log: &Logger, ) -> Result<(), SwapBuffersError> where @@ -69,7 +70,7 @@ where (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( @@ -77,6 +78,7 @@ fn draw_surface_tree( frame: &mut F, root: &wl_surface::WlSurface, location: Point, + output_scale: f32, log: &Logger, ) -> Result<(), SwapBuffersError> where @@ -153,6 +155,7 @@ where let mut location = *location; if let Some(ref data) = states.data_map.get::>() { let mut data = data.borrow_mut(); + let buffer_scale = data.buffer_scale as f32; if let Some(texture) = data .texture .as_mut() @@ -164,10 +167,12 @@ where let current = states.cached_state.current::(); location += current.location; } + let render_scale = output_scale as f32 / buffer_scale; if let Err(err) = frame.render_texture_at( &texture.texture, - location.to_physical(1), // TODO: handle output scaling factor - Transform::Normal, /* TODO */ + location.to_f64().to_physical(output_scale as f64).to_i32_round(), + Transform::Normal, /* TODO */ + render_scale, 1.0, ) { result = Err(err.into()); @@ -186,6 +191,7 @@ pub fn draw_windows( frame: &mut F, window_map: &WindowMap, output_rect: Rectangle, + output_scale: f32, log: &::slog::Logger, ) -> Result<(), SwapBuffersError> where @@ -205,7 +211,9 @@ where initial_place.x -= output_rect.loc.x; if let Some(wl_surface) = toplevel_surface.get_surface() { // 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); } // furthermore, draw its popups @@ -217,7 +225,9 @@ where let location = popup.location(); let draw_location = initial_place + location + toplevel_geometry_offset; 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); } } @@ -233,6 +243,7 @@ pub fn draw_dnd_icon( frame: &mut F, surface: &wl_surface::WlSurface, location: Point, + output_scale: f32, log: &::slog::Logger, ) -> Result<(), SwapBuffersError> where @@ -247,5 +258,5 @@ where "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) } diff --git a/anvil/src/input_handler.rs b/anvil/src/input_handler.rs index 0b7b07f..b6a4aa2 100644 --- a/anvil/src/input_handler.rs +++ b/anvil/src/input_handler.rs @@ -164,6 +164,32 @@ impl AnvilState { ); } } + 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 => { warn!(self.log, "Key action {:?} unsupported on winit backend.", action); } @@ -172,12 +198,12 @@ impl AnvilState { InputEvent::PointerButton { event, .. } => self.on_pointer_button::(event), InputEvent::PointerAxis { event, .. } => self.on_pointer_axis::(event), InputEvent::Special(WinitEvent::Resized { size, .. }) => { - self.output_map.borrow_mut().update_mode( - crate::winit::OUTPUT_NAME, + self.output_map.borrow_mut().update_mode_by_name( Mode { size, refresh: 60_000, }, + crate::winit::OUTPUT_NAME, ); } _ => { @@ -190,7 +216,8 @@ impl AnvilState { let output_size = self .output_map .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(); let pos = evt.position_transformed(output_size); self.pointer_location = pos; @@ -228,11 +255,7 @@ impl AnvilState { } } KeyAction::Screen(num) => { - let geometry = self - .output_map - .borrow() - .find_by_index(num, |_, geometry| geometry) - .ok(); + let geometry = self.output_map.borrow().find_by_index(num).map(|o| o.geometry()); if let Some(geometry) = geometry { let x = geometry.loc.x as f64 + geometry.size.w as f64 / 2.0; @@ -240,6 +263,53 @@ impl AnvilState { 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::(event), InputEvent::PointerButton { event, .. } => self.on_pointer_button::(event), @@ -292,45 +362,45 @@ impl AnvilState { let tablet_seat = self.seat.tablet_seat(); let window_map = self.window_map.borrow(); - output_map - .with_primary(|_, rect| { - 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(); + let output_geometry = output_map.with_primary().map(|o| o.geometry()); - let under = window_map.get_surface_under(*pointer_location); - let tablet = tablet_seat.get_tablet(&TabletDescriptor::from(&evt.device())); - let tool = tablet_seat.get_tool(&evt.tool()); + if let Some(rect) = output_geometry { + 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(); - if let (Some(tablet), Some(tool)) = (tablet, tool) { - if evt.pressure_has_changed() { - tool.pressure(evt.pressure()); - } - if evt.distance_has_changed() { - tool.distance(evt.distance()); - } - if evt.tilt_has_changed() { - tool.tilt(evt.tilt()); - } - if evt.slider_has_changed() { - tool.slider_position(evt.slider_position()); - } - if evt.rotation_has_changed() { - tool.rotation(evt.rotation()); - } - if evt.wheel_has_changed() { - tool.wheel(evt.wheel_delta(), evt.wheel_delta_discrete()); - } + let under = window_map.get_surface_under(*pointer_location); + let tablet = tablet_seat.get_tablet(&TabletDescriptor::from(&evt.device())); + let tool = tablet_seat.get_tool(&evt.tool()); - tool.motion( - *pointer_location, - under, - &tablet, - SCOUNTER.next_serial(), - evt.time(), - ); + if let (Some(tablet), Some(tool)) = (tablet, tool) { + if evt.pressure_has_changed() { + tool.pressure(evt.pressure()); } - }) - .unwrap(); + if evt.distance_has_changed() { + tool.distance(evt.distance()); + } + if evt.tilt_has_changed() { + tool.tilt(evt.tilt()); + } + if evt.slider_has_changed() { + tool.slider_position(evt.slider_position()); + } + if evt.rotation_has_changed() { + tool.rotation(evt.rotation()); + } + if evt.wheel_has_changed() { + tool.wheel(evt.wheel_delta(), evt.wheel_delta_discrete()); + } + + tool.motion( + *pointer_location, + under, + &tablet, + SCOUNTER.next_serial(), + evt.time(), + ); + } + } } fn on_tablet_tool_proximity(&mut self, evt: B::TabletToolProximityEvent) { @@ -339,32 +409,32 @@ impl AnvilState { let tablet_seat = self.seat.tablet_seat(); let window_map = self.window_map.borrow(); - output_map - .with_primary(|_, rect| { - let tool = evt.tool(); - tablet_seat.add_tool(&tool); + let output_geometry = output_map.with_primary().map(|o| o.geometry()); - let rect_size = (rect.size.h as u32, rect.size.w as u32).into(); - *pointer_location = evt.position_transformed(rect_size) + rect.loc.to_f64(); + if let Some(rect) = output_geometry { + let tool = evt.tool(); + tablet_seat.add_tool(&tool); - let under = window_map.get_surface_under(*pointer_location); - let tablet = tablet_seat.get_tablet(&TabletDescriptor::from(&evt.device())); - let tool = tablet_seat.get_tool(&tool); + let rect_size = (rect.size.h as u32, rect.size.w as u32).into(); + *pointer_location = evt.position_transformed(rect_size) + rect.loc.to_f64(); - if let (Some(under), Some(tablet), Some(tool)) = (under, tablet, tool) { - match evt.state() { - ProximityState::In => tool.proximity_in( - *pointer_location, - under, - &tablet, - SCOUNTER.next_serial(), - evt.time(), - ), - ProximityState::Out => tool.proximity_out(evt.time()), - } + let under = window_map.get_surface_under(*pointer_location); + let tablet = tablet_seat.get_tablet(&TabletDescriptor::from(&evt.device())); + let tool = tablet_seat.get_tool(&tool); + + if let (Some(under), Some(tablet), Some(tool)) = (under, tablet, tool) { + match evt.state() { + ProximityState::In => tool.proximity_in( + *pointer_location, + under, + &tablet, + SCOUNTER.next_serial(), + evt.time(), + ), + ProximityState::Out => tool.proximity_out(evt.time()), } - }) - .unwrap(); + } + } } fn on_tablet_tool_tip(&mut self, evt: B::TabletToolTipEvent) { @@ -439,6 +509,8 @@ enum KeyAction { Run(String), /// Switch the current screen Screen(usize), + ScaleUp, + ScaleDown, /// Forward the key to the client Forward, /// Do nothing more @@ -460,6 +532,10 @@ fn process_keyboard_shortcut(modifiers: ModifiersState, keysym: Keysym) -> KeyAc KeyAction::Run("weston-terminal".into()) } else if modifiers.logo && keysym >= xkb::KEY_1 && keysym <= xkb::KEY_9 { 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 { KeyAction::Forward } diff --git a/anvil/src/output_map.rs b/anvil/src/output_map.rs index c0515db..6ced0e1 100644 --- a/anvil/src/output_map.rs +++ b/anvil/src/output_map.rs @@ -5,10 +5,10 @@ use smithay::{ wayland_protocols::xdg_shell::server::xdg_toplevel, wayland_server::{ protocol::{wl_output, wl_surface::WlSurface}, - Display, Global, + Display, Global, UserDataMap, }, }, - utils::{Logical, Point, Rectangle}, + utils::{Logical, Point, Rectangle, Size}, wayland::{ compositor::{with_surface_tree_downward, SubsurfaceCachedState, TraversalAction}, output::{self, Mode, PhysicalProperties}, @@ -17,13 +17,16 @@ use smithay::{ use crate::shell::SurfaceData; -struct Output { +pub struct Output { name: String, output: output::Output, global: Option>, - geometry: Rectangle, surfaces: Vec, current_mode: Mode, + scale: f32, + output_scale: i32, + location: Point, + userdata: UserDataMap, } impl Output { @@ -40,22 +43,64 @@ impl Output { { 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::().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); Self { name: name.as_ref().to_owned(), global: Some(global), output, - geometry: Rectangle { - loc: location, - // TODO: handle scaling factor - size: mode.size.to_logical(1), - }, + location, surfaces: Vec::new(), current_mode: mode, + scale, + output_scale, + userdata: Default::default(), } } + + pub fn userdata(&self) -> &UserDataMap { + &self.userdata + } + + pub fn geometry(&self) -> Rectangle { + let loc = self.location(); + let size = self.size(); + + Rectangle { loc, size } + } + + pub fn size(&self) -> Size { + self.current_mode + .size + .to_f64() + .to_logical(self.scale as f64) + .to_i32_round() + } + + pub fn location(&self) -> Point { + 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 { @@ -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 { display: Rc>, outputs: Vec, @@ -100,24 +134,43 @@ impl OutputMap { // First recalculate the outputs location let mut output_x = 0; for output in self.outputs.iter_mut() { - output.geometry.loc.x = output_x; - output.geometry.loc.y = 0; - output_x += output.geometry.loc.x; + let output_x_shift = output_x - output.location.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 // and move them to the primary output - let primary_output_location = self - .with_primary(|_, geometry| geometry) - .ok() - .map(|o| o.loc) - .unwrap_or_default(); + let primary_output_location = self.with_primary().map(|o| o.location()).unwrap_or_default(); let mut window_map = self.window_map.borrow_mut(); // 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. let mut windows_to_move = Vec::new(); 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 { windows_to_move.push((kind.to_owned(), primary_output_location)); @@ -135,9 +188,9 @@ impl OutputMap { || state.states.contains(xdg_toplevel::State::Fullscreen) { 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 { - self.find_by_position(location, |_, geometry| geometry).ok() + self.find_by_position(location).map(|o| o.geometry()) }; if let Some(geometry) = output_geometry { @@ -162,14 +215,14 @@ impl OutputMap { } } - pub fn add(&mut self, name: N, physical: PhysicalProperties, mode: Mode) + pub fn add(&mut self, name: N, physical: PhysicalProperties, mode: Mode) -> &Output where N: AsRef, { // Append the output to the end of the existing // outputs by placing it after the current overall // width - let location = (self.width() as i32, 0); + let location = (self.width(), 0); let output = Output::new( name, @@ -186,123 +239,143 @@ impl OutputMap { // this would not affect windows, but arrange could re-organize // outputs from a configuration. self.arrange(); + + self.outputs.last().unwrap() } - pub fn remove>(&mut self, name: N) { - let removed_outputs = self.outputs.iter_mut().filter(|o| o.name == name.as_ref()); + pub fn retain(&mut self, f: F) + 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(); } pub fn width(&self) -> i32 { // 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. - self.outputs - .iter() - .fold(0, |acc, output| acc + output.geometry.size.w) + self.outputs.iter().fold(0, |acc, output| acc + output.size().w) } pub fn height(&self, x: i32) -> Option { // This is a simplification, we only arrange the outputs on the y axis side-by-side self.outputs .iter() - .find(|output| x >= output.geometry.loc.x && x < (output.geometry.loc.x + output.geometry.size.w)) - .map(|output| output.geometry.size.h) + .find(|output| { + 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 { self.outputs.is_empty() } - pub fn with_primary(&self, f: F) -> Result - where - F: FnOnce(&output::Output, Rectangle) -> T, - { - let output = self.outputs.get(0).ok_or(OutputNotFound)?; - - Ok(f(&output.output, output.geometry)) + pub fn with_primary(&self) -> Option<&Output> { + self.outputs.get(0) } - pub fn find(&self, output: &wl_output::WlOutput, f: F) -> Result + pub fn find(&self, f: F) -> Option<&Output> where - F: FnOnce(&output::Output, Rectangle) -> T, + F: FnMut(&&Output) -> bool, { - let output = self - .outputs - .iter() - .find(|o| o.output.owns(output)) - .ok_or(OutputNotFound)?; - - Ok(f(&output.output, output.geometry)) + self.outputs.iter().find(f) } - pub fn find_by_name(&self, name: N, f: F) -> Result + pub fn find_by_output(&self, output: &wl_output::WlOutput) -> Option<&Output> { + self.find(|o| o.output.owns(output)) + } + + pub fn find_by_name(&self, name: N) -> Option<&Output> where N: AsRef, - F: FnOnce(&output::Output, Rectangle) -> T, { - let output = self - .outputs - .iter() - .find(|o| o.name == name.as_ref()) - .ok_or(OutputNotFound)?; - - Ok(f(&output.output, output.geometry)) + self.find(|o| o.name == name.as_ref()) } - pub fn find_by_position(&self, position: Point, f: F) -> Result + pub fn find_by_position(&self, position: Point) -> 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(&mut self, mode: Option, scale: Option, mut f: F) where - F: FnOnce(&output::Output, Rectangle) -> T, + F: FnMut(&Output) -> bool, { - let output = self - .outputs - .iter() - .find(|o| o.geometry.contains(position)) - .ok_or(OutputNotFound)?; + let output = self.outputs.iter_mut().find(|o| f(&**o)); - Ok(f(&output.output, output.geometry)) - } - - pub fn find_by_index(&self, index: usize, f: F) -> Result - where - F: FnOnce(&output::Output, Rectangle) -> T, - { - let output = self.outputs.get(index).ok_or(OutputNotFound)?; - - Ok(f(&output.output, output.geometry)) - } - - pub fn update_mode>(&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 { - // TODO: handle scale factors - output.geometry.size = mode.size.to_logical(1); + if let Some(mode) = mode { + output.output.delete_mode(output.current_mode); + output + .output + .change_current_state(Some(mode), None, Some(output.output_scale)); + output.output.set_preferred(mode); + output.current_mode = mode; + } - output.output.delete_mode(output.current_mode); - output.output.change_current_state(Some(mode), None, None); - output.output.set_preferred(mode); - output.current_mode = mode; + if let Some(scale) = scale { + // Calculate in which direction the scale changed + let rescale = output.scale() / scale; - // Re-arrange outputs cause the size of one output changed - self.arrange(); + { + // 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)); + } + } } + + self.arrange(); + } + + pub fn update_by_name>(&mut self, mode: Option, scale: Option, name: N) { + self.update(mode, scale, |o| o.name() == name.as_ref()) + } + + pub fn update_scale_by_name>(&mut self, scale: f32, name: N) { + self.update_by_name(None, Some(scale), name) + } + + pub fn update_mode_by_name>(&mut self, mode: Mode, name: N) { + self.update_by_name(Some(mode), None, name) } pub fn refresh(&mut self) { @@ -320,7 +393,7 @@ impl OutputMap { // 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 !output.geometry.overlaps(bbox) { + if !output.geometry().overlaps(bbox) { if let Some(surface) = kind.get_surface() { with_surface_tree_downward( surface, @@ -365,7 +438,7 @@ impl OutputMap { if let Some(size) = data.and_then(|d| d.borrow().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 if !output.surfaces.contains(wl_surface) { output.output.enter(wl_surface); diff --git a/anvil/src/shell.rs b/anvil/src/shell.rs index 9803aad..0b4da6c 100644 --- a/anvil/src/shell.rs +++ b/anvil/src/shell.rs @@ -301,7 +301,7 @@ fn fullscreen_output_geometry( // First test if a specific output has been requested // if the requested output is not found ignore the request 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 @@ -311,7 +311,7 @@ fn fullscreen_output_geometry( .and_then(|kind| window_map.location(&kind)); 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 { return Some(result); @@ -319,7 +319,7 @@ fn fullscreen_output_geometry( } // Fallback to primary output - output_map.with_primary(|_, geometry| geometry).ok() + output_map.with_primary().map(|o| o.geometry()) } pub fn init_shell(display: Rc>, log: ::slog::Logger) -> ShellHandles { @@ -355,8 +355,8 @@ pub fn init_shell(display: Rc>, log: ::sl let output_geometry = xdg_output_map .borrow() - .with_primary(|_, geometry| geometry) - .ok() + .with_primary() + .map(|o| o.geometry()) .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_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(display: Rc>, log: ::sl .and_then(|position| { xdg_output_map .borrow() - .find_by_position(position, |_, geometry| geometry) - .ok() + .find_by_position(position) + .map(|o| o.geometry()) }) }; @@ -645,8 +645,8 @@ pub fn init_shell(display: Rc>, log: ::sl let output_geometry = shell_output_map .borrow() - .with_primary(|_, geometry| geometry) - .ok() + .with_primary() + .map(|o| o.geometry()) .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; diff --git a/anvil/src/udev.rs b/anvil/src/udev.rs index f8bbb61..f95473c 100644 --- a/anvil/src/udev.rs +++ b/anvil/src/udev.rs @@ -78,14 +78,13 @@ impl AsRawFd for SessionFd { } } -struct UdevOutputMap { - pub device_id: dev_t, - pub crtc: crtc::Handle, - pub output_name: String, +#[derive(Debug, PartialEq)] +struct UdevOutputId { + device_id: dev_t, + crtc: crtc::Handle, } pub struct UdevData { - output_map: Vec, pub session: AutoSession, #[cfg(feature = "egl")] primary_gpu: Option, @@ -132,7 +131,6 @@ pub fn run_udev( let data = UdevData { session, - output_map: Vec::new(), #[cfg(feature = "egl")] primary_gpu, backends: HashMap::new(), @@ -272,7 +270,6 @@ fn scan_connectors( device: &mut DrmDevice, gbm: &GbmDevice, renderer: &mut Gles2Renderer, - backend_output_map: &mut Vec, output_map: &mut crate::output_map::OutputMap, signaler: &Signaler, logger: &::slog::Logger, @@ -342,14 +339,26 @@ fn scan_connectors( refresh: (mode.vrefresh() * 1000) as i32, }; - let output_name = format!( - "{:?}-{}", - connector_info.interface(), - connector_info.interface_id() - ); + let other_short_name; + let interface_short_name = match connector_info.interface() { + drm::control::connector::Interface::DVII => "DVI-I", + 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)); - output_map.add( + let output = output_map.add( &output_name, PhysicalProperties { size: (phys_w as i32, phys_h as i32).into(), @@ -360,10 +369,9 @@ fn scan_connectors( mode, ); - backend_output_map.push(UdevOutputMap { + output.userdata().insert_if_missing(|| UdevOutputId { crtc, device_id: device.device_id(), - output_name, }); entry.insert(Rc::new(RefCell::new(renderer))); @@ -456,7 +464,6 @@ impl AnvilState { &mut device, &gbm, &mut *renderer.borrow_mut(), - &mut self.backend_data.output_map, &mut *self.output_map.borrow_mut(), &self.backend_data.signaler, &self.log, @@ -517,20 +524,14 @@ impl AnvilState { let logger = self.log.clone(); let loop_handle = self.handle.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().remove(output); - } - - self.backend_data - .output_map - .retain(|output| output.device_id != device); + self.output_map.borrow_mut().retain(|output| { + output + .userdata() + .get::() + .map(|id| id.device_id != device) + .unwrap_or(true) + }); let mut source = backend_data.event_dispatcher.as_source_mut(); let mut backends = backend_data.surfaces.borrow_mut(); @@ -538,7 +539,6 @@ impl AnvilState { &mut *source, &backend_data.gbm, &mut *backend_data.renderer.borrow_mut(), - &mut self.backend_data.output_map, &mut *self.output_map.borrow_mut(), &signaler, &logger, @@ -563,16 +563,14 @@ impl AnvilState { // drop surfaces backend_data.surfaces.borrow_mut().clear(); debug!(self.log, "Surfaces dropped"); - let removed_outputs = self - .backend_data - .output_map - .iter() - .filter(|o| o.device_id == device) - .map(|o| o.output_name.as_str()); - for output_id in removed_outputs { - self.output_map.borrow_mut().remove(output_id); - } - self.backend_data.output_map.retain(|o| o.device_id != device); + + self.output_map.borrow_mut().retain(|output| { + output + .userdata() + .get::() + .map(|id| id.device_id != device) + .unwrap_or(true) + }); let _device = self.handle.remove(backend_data.registration_token); let _device = backend_data.event_dispatcher.into_source_inner(); @@ -618,7 +616,6 @@ impl AnvilState { device_backend.dev_id, crtc, &mut *self.window_map.borrow_mut(), - &self.backend_data.output_map, &*self.output_map.borrow(), self.pointer_location, &device_backend.pointer_image, @@ -666,7 +663,6 @@ fn render_surface( device_id: dev_t, crtc: crtc::Handle, window_map: &mut WindowMap, - backend_output_map: &[UdevOutputMap], output_map: &crate::output_map::OutputMap, pointer_location: Point, pointer_image: &Gles2Texture, @@ -676,14 +672,12 @@ fn render_surface( ) -> Result<(), SwapBuffersError> { surface.frame_submitted()?; - let output_geometry = backend_output_map - .iter() - .find(|o| o.device_id == device_id && o.crtc == crtc) - .map(|o| o.output_name.as_str()) - .and_then(|name| output_map.find_by_name(name, |_, geometry| geometry).ok()); + let output = output_map + .find(|o| o.userdata().get::() == Some(&UdevOutputId { device_id, crtc })) + .map(|output| (output.geometry(), output.scale(), output.current_mode())); - let output_geometry = if let Some(geometry) = output_geometry { - geometry + let (output_geometry, output_scale, mode) = if let Some((geometry, scale, mode)) = output { + (geometry, scale, mode) } else { // Somehow we got called with a non existing output return Ok(()); @@ -694,13 +688,12 @@ fn render_surface( // and draw to our buffer match renderer .render( - // TODO: handle scale factor - output_geometry.size.to_physical(1), + mode.size, Transform::Flipped180, // Scanout is rotated |renderer, frame| { frame.clear([0.8, 0.8, 0.9, 1.0])?; // 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 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 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 { - draw_cursor(renderer, frame, wl_surface, relative_ptr_location, logger)?; + draw_cursor( + renderer, + frame, + wl_surface, + relative_ptr_location, + output_scale, + logger, + )?; } else { - // TODO: handle output scale factor frame.render_texture_at( pointer_image, - relative_ptr_location.to_physical(1), + relative_ptr_location + .to_f64() + .to_physical(output_scale as f64) + .to_i32_round(), Transform::Normal, + output_scale, 1.0, )?; } diff --git a/anvil/src/winit.rs b/anvil/src/winit.rs index 93484b4..589f1a5 100644 --- a/anvil/src/winit.rs +++ b/anvil/src/winit.rs @@ -110,11 +110,11 @@ pub fn run_winit( { let mut renderer = renderer.borrow_mut(); // 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 .borrow() - .find_by_name(OUTPUT_NAME, |_, geometry| geometry) - .ok() + .find_by_name(OUTPUT_NAME) + .map(|output| (output.geometry(), output.scale())) .unwrap(); let result = renderer @@ -127,6 +127,7 @@ pub fn run_winit( frame, &*state.window_map.borrow(), output_geometry, + output_scale, &log, )?; @@ -136,7 +137,14 @@ pub fn run_winit( let guard = state.dnd_icon.lock().unwrap(); if let Some(ref surface) = *guard { 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 if let CursorImageStatus::Image(ref surface) = *guard { 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 { cursor_visible = true; } diff --git a/src/backend/renderer/mod.rs b/src/backend/renderer/mod.rs index 2bad97b..96b78ca 100644 --- a/src/backend/renderer/mod.rs +++ b/src/backend/renderer/mod.rs @@ -185,6 +185,7 @@ pub trait Frame { texture: &Self::TextureId, pos: Point, transform: Transform, + scale: f32, alpha: f32, ) -> Result<(), Self::Error> { let mut mat = Matrix3::::identity(); @@ -192,7 +193,7 @@ pub trait Frame { // position and scale let size = texture.size(); 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 mat = mat * Matrix3::from_translation(Vector2::new(0.5, 0.5));