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,
surface: &wl_surface::WlSurface,
location: Point<i32, Logical>,
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<R, E, F, T>(
@ -77,6 +78,7 @@ fn draw_surface_tree<R, E, F, T>(
frame: &mut F,
root: &wl_surface::WlSurface,
location: Point<i32, Logical>,
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::<RefCell<SurfaceData>>() {
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::<SubsurfaceCachedState>();
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
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<R, E, F, T>(
frame: &mut F,
window_map: &WindowMap,
output_rect: Rectangle<i32, Logical>,
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<R, E, F, T>(
frame: &mut F,
surface: &wl_surface::WlSurface,
location: Point<i32, Logical>,
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)
}

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 => {
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::PointerAxis { event, .. } => self.on_pointer_axis::<B>(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<WinitData> {
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<UdevData> {
}
}
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<UdevData> {
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::PointerButton { event, .. } => self.on_pointer_button::<B>(event),
@ -292,8 +362,9 @@ impl AnvilState<UdevData> {
let tablet_seat = self.seat.tablet_seat();
let window_map = self.window_map.borrow();
output_map
.with_primary(|_, rect| {
let output_geometry = output_map.with_primary().map(|o| o.geometry());
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();
@ -329,8 +400,7 @@ impl AnvilState<UdevData> {
evt.time(),
);
}
})
.unwrap();
}
}
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 window_map = self.window_map.borrow();
output_map
.with_primary(|_, rect| {
let output_geometry = output_map.with_primary().map(|o| o.geometry());
if let Some(rect) = output_geometry {
let tool = evt.tool();
tablet_seat.add_tool(&tool);
@ -363,8 +434,7 @@ impl AnvilState<UdevData> {
ProximityState::Out => tool.proximity_out(evt.time()),
}
}
})
.unwrap();
}
}
fn on_tablet_tool_tip<B: InputBackend>(&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
}

View File

@ -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<Global<wl_output::WlOutput>>,
geometry: Rectangle<i32, Logical>,
surfaces: Vec<WlSurface>,
current_mode: Mode,
scale: f32,
output_scale: i32,
location: Point<i32, Logical>,
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::<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);
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<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 {
@ -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<RefCell<Display>>,
outputs: Vec<Output>,
@ -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<N>(&mut self, name: N, physical: PhysicalProperties, mode: Mode)
pub fn add<N>(&mut self, name: N, physical: PhysicalProperties, mode: Mode) -> &Output
where
N: AsRef<str>,
{
// 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<N: AsRef<str>>(&mut self, name: N) {
let removed_outputs = self.outputs.iter_mut().filter(|o| o.name == name.as_ref());
pub fn retain<F>(&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<i32> {
// 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<F, T>(&self, f: F) -> Result<T, OutputNotFound>
where
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 with_primary(&self) -> Option<&Output> {
self.outputs.get(0)
}
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
F: FnOnce(&output::Output, Rectangle<i32, Logical>) -> 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<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
N: AsRef<str>,
F: FnOnce(&output::Output, Rectangle<i32, Logical>) -> 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<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
F: FnOnce(&output::Output, Rectangle<i32, Logical>) -> 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<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 {
// 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, None);
output
.output
.change_current_state(Some(mode), None, Some(output.output_scale));
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;
{
// 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();
}
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) {
@ -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);

View File

@ -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<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
.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<BackendData: 'static>(display: Rc<RefCell<Display>>, 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<BackendData: 'static>(display: Rc<RefCell<Display>>, 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;

View File

@ -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<UdevOutputMap>,
pub session: AutoSession,
#[cfg(feature = "egl")]
primary_gpu: Option<PathBuf>,
@ -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<SessionFd>,
gbm: &GbmDevice<SessionFd>,
renderer: &mut Gles2Renderer,
backend_output_map: &mut Vec<UdevOutputMap>,
output_map: &mut crate::output_map::OutputMap,
signaler: &Signaler<SessionSignal>,
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<UdevData> {
&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<UdevData> {
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::<UdevOutputId>()
.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<UdevData> {
&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<UdevData> {
// 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::<UdevOutputId>()
.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<UdevData> {
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<f64, Logical>,
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::<UdevOutputId>() == 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,
)?;
}

View File

@ -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;
}

View File

@ -185,6 +185,7 @@ pub trait Frame {
texture: &Self::TextureId,
pos: Point<i32, Physical>,
transform: Transform,
scale: f32,
alpha: f32,
) -> Result<(), Self::Error> {
let mut mat = Matrix3::<f32>::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));