add an output_map which handles...
the positioning of outputs, tracks the surfaces on the outputs and sends enter/leave for the surfaces Additionally the output_map will handle re-location of windows if a output mode is changed, an output is added or removed. The shell has been updated to use the primary output for the initial placement. The fullscreen/maximize requests will now handle the shell part correctly. For real fullscreen output the rendering has to be changed. The output name is considered unique and an output can be retrieved from the map by location, output or name. This can be used as the base for HiDPI handling.
This commit is contained in:
parent
5cfb05cde8
commit
0d88a392fa
|
@ -185,7 +185,7 @@ pub fn draw_windows<R, E, F, T>(
|
|||
renderer: &mut R,
|
||||
frame: &mut F,
|
||||
window_map: &WindowMap,
|
||||
output_rect: Option<Rectangle>,
|
||||
output_rect: Rectangle,
|
||||
log: &::slog::Logger,
|
||||
) -> Result<(), SwapBuffersError>
|
||||
where
|
||||
|
@ -199,12 +199,10 @@ where
|
|||
// redraw the frame, in a simple but inneficient way
|
||||
window_map.with_windows_from_bottom_to_top(|toplevel_surface, mut initial_place, bounding_box| {
|
||||
// skip windows that do not overlap with a given output
|
||||
if let Some(output) = output_rect {
|
||||
if !output.overlaps(bounding_box) {
|
||||
return;
|
||||
}
|
||||
initial_place.0 -= output.x;
|
||||
if !output_rect.overlaps(bounding_box) {
|
||||
return;
|
||||
}
|
||||
initial_place.0 -= output_rect.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) {
|
||||
|
|
|
@ -13,6 +13,7 @@ use smithay::{
|
|||
},
|
||||
reexports::wayland_server::protocol::wl_pointer,
|
||||
wayland::{
|
||||
output::Mode,
|
||||
seat::{keysyms as xkb, AxisFrame, Keysym, ModifiersState},
|
||||
SERIAL_COUNTER as SCOUNTER,
|
||||
},
|
||||
|
@ -137,7 +138,12 @@ impl<Backend> AnvilState<Backend> {
|
|||
|
||||
#[cfg(feature = "winit")]
|
||||
impl AnvilState<WinitData> {
|
||||
pub fn process_input_event<B: InputBackend>(&mut self, event: InputEvent<B>) {
|
||||
pub fn process_input_event<B>(&mut self, event: InputEvent<B>)
|
||||
where
|
||||
B: InputBackend<SpecialEvent = smithay::backend::winit::WinitEvent>,
|
||||
{
|
||||
use smithay::backend::winit::WinitEvent;
|
||||
|
||||
match event {
|
||||
InputEvent::Keyboard { event, .. } => match self.keyboard_key_to_action::<B>(event) {
|
||||
KeyAction::None | KeyAction::Forward => {}
|
||||
|
@ -162,6 +168,16 @@ impl AnvilState<WinitData> {
|
|||
InputEvent::PointerMotionAbsolute { event, .. } => self.on_pointer_move_absolute::<B>(event),
|
||||
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,
|
||||
Mode {
|
||||
width: size.0 as i32,
|
||||
height: size.1 as i32,
|
||||
refresh: 60_000,
|
||||
},
|
||||
);
|
||||
}
|
||||
_ => {
|
||||
// other events are not handled in anvil (yet)
|
||||
}
|
||||
|
@ -206,17 +222,16 @@ impl AnvilState<UdevData> {
|
|||
}
|
||||
}
|
||||
KeyAction::Screen(num) => {
|
||||
if let Some(output) = self.backend_data.output_map.get(num) {
|
||||
let x = self
|
||||
.backend_data
|
||||
.output_map
|
||||
.iter()
|
||||
.take(num)
|
||||
.fold(0, |acc, output| acc + output.size.0)
|
||||
as f64
|
||||
+ (output.size.0 as f64 / 2.0);
|
||||
let y = output.size.1 as f64 / 2.0;
|
||||
self.pointer_location = (x as f64, y as f64)
|
||||
let geometry = self
|
||||
.output_map
|
||||
.borrow()
|
||||
.find_by_index(num, |_, geometry| geometry)
|
||||
.ok();
|
||||
|
||||
if let Some(geometry) = geometry {
|
||||
let x = geometry.x as f64 + geometry.width as f64 / 2.0;
|
||||
let y = geometry.height as f64 / 2.0;
|
||||
self.pointer_location = (x, y)
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -245,47 +260,16 @@ impl AnvilState<UdevData> {
|
|||
}
|
||||
|
||||
fn clamp_coords(&self, pos: (f64, f64)) -> (f64, f64) {
|
||||
if self.backend_data.output_map.is_empty() {
|
||||
if self.output_map.borrow().is_empty() {
|
||||
return pos;
|
||||
}
|
||||
|
||||
let (mut x, mut y) = pos;
|
||||
// max_x is the sum of the width of all outputs
|
||||
let max_x = self
|
||||
.backend_data
|
||||
.output_map
|
||||
.iter()
|
||||
.fold(0u32, |acc, output| acc + output.size.0);
|
||||
x = x.max(0.0).min(max_x as f64);
|
||||
let (pos_x, pos_y) = pos;
|
||||
let (max_x, max_y) = self.output_map.borrow().size();
|
||||
let clamped_x = pos_x.max(0.0).min(max_x as f64);
|
||||
let clamped_y = pos_y.max(0.0).min(max_y as f64);
|
||||
|
||||
// max y depends on the current output
|
||||
let max_y = self.current_output_size(x).1;
|
||||
y = y.max(0.0).min(max_y as f64);
|
||||
|
||||
(x, y)
|
||||
}
|
||||
|
||||
fn current_output_idx(&self, x: f64) -> usize {
|
||||
self.backend_data
|
||||
.output_map
|
||||
.iter()
|
||||
// map each output to their x position
|
||||
.scan(0u32, |acc, output| {
|
||||
let curr_x = *acc;
|
||||
*acc += output.size.0;
|
||||
Some(curr_x)
|
||||
})
|
||||
// get an index
|
||||
.enumerate()
|
||||
// find the first one with a greater x
|
||||
.find(|(_idx, x_pos)| *x_pos as f64 > x)
|
||||
// the previous output is the one we are on
|
||||
.map(|(idx, _)| idx - 1)
|
||||
.unwrap_or(self.backend_data.output_map.len() - 1)
|
||||
}
|
||||
|
||||
fn current_output_size(&self, x: f64) -> (u32, u32) {
|
||||
self.backend_data.output_map[self.current_output_idx(x)].size
|
||||
(clamped_x, clamped_y)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -26,6 +26,8 @@ mod winit;
|
|||
#[cfg(feature = "xwayland")]
|
||||
mod xwayland;
|
||||
|
||||
mod output_map;
|
||||
|
||||
use state::AnvilState;
|
||||
|
||||
static POSSIBLE_BACKENDS: &[&str] = &[
|
||||
|
|
|
@ -0,0 +1,403 @@
|
|||
use std::{cell::RefCell, rc::Rc};
|
||||
|
||||
use smithay::{
|
||||
reexports::{
|
||||
wayland_protocols::xdg_shell::server::xdg_toplevel,
|
||||
wayland_server::{
|
||||
protocol::{wl_output, wl_surface::WlSurface},
|
||||
Display, Global,
|
||||
},
|
||||
},
|
||||
utils::Rectangle,
|
||||
wayland::{
|
||||
compositor::{with_surface_tree_downward, SubsurfaceCachedState, TraversalAction},
|
||||
output::{self, Mode, PhysicalProperties},
|
||||
},
|
||||
};
|
||||
|
||||
use crate::shell::SurfaceData;
|
||||
|
||||
struct Output {
|
||||
name: String,
|
||||
output: output::Output,
|
||||
global: Option<Global<wl_output::WlOutput>>,
|
||||
geometry: Rectangle,
|
||||
surfaces: Vec<WlSurface>,
|
||||
current_mode: Mode,
|
||||
}
|
||||
|
||||
impl Output {
|
||||
fn new<N>(
|
||||
name: N,
|
||||
location: (i32, i32),
|
||||
display: &mut Display,
|
||||
physical: PhysicalProperties,
|
||||
mode: Mode,
|
||||
logger: slog::Logger,
|
||||
) -> Self
|
||||
where
|
||||
N: AsRef<str>,
|
||||
{
|
||||
let (output, global) = output::Output::new(display, name.as_ref().into(), physical, logger);
|
||||
|
||||
output.change_current_state(Some(mode), None, None);
|
||||
output.set_preferred(mode);
|
||||
|
||||
Self {
|
||||
name: name.as_ref().to_owned(),
|
||||
global: Some(global),
|
||||
output,
|
||||
geometry: Rectangle {
|
||||
x: location.0,
|
||||
y: location.1,
|
||||
width: mode.width,
|
||||
height: mode.height,
|
||||
},
|
||||
surfaces: Vec::new(),
|
||||
current_mode: mode,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Output {
|
||||
fn drop(&mut self) {
|
||||
self.global.take().unwrap().destroy();
|
||||
}
|
||||
}
|
||||
|
||||
#[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>,
|
||||
window_map: Rc<RefCell<crate::window_map::WindowMap>>,
|
||||
logger: slog::Logger,
|
||||
}
|
||||
|
||||
impl OutputMap {
|
||||
pub fn new(
|
||||
display: Rc<RefCell<Display>>,
|
||||
window_map: Rc<RefCell<crate::window_map::WindowMap>>,
|
||||
logger: ::slog::Logger,
|
||||
) -> Self {
|
||||
Self {
|
||||
display,
|
||||
outputs: Vec::new(),
|
||||
window_map,
|
||||
logger,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn arrange(&mut self) {
|
||||
// First recalculate the outputs location
|
||||
let mut output_x = 0;
|
||||
for output in self.outputs.iter_mut() {
|
||||
output.geometry.x = output_x;
|
||||
output.geometry.y = 0;
|
||||
output_x += output.geometry.width;
|
||||
}
|
||||
|
||||
// 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.x, o.y))
|
||||
.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));
|
||||
|
||||
if !within_outputs {
|
||||
windows_to_move.push((kind.to_owned(), primary_output_location));
|
||||
}
|
||||
});
|
||||
for (window, location) in windows_to_move.drain(..) {
|
||||
window_map.set_location(&window, location);
|
||||
}
|
||||
|
||||
// Update the size and location for maximized and fullscreen windows
|
||||
window_map.with_windows_from_bottom_to_top(|kind, location, _| {
|
||||
if let crate::window_map::Kind::Xdg(xdg) = kind {
|
||||
if let Some(state) = xdg.current_state() {
|
||||
if state.states.contains(xdg_toplevel::State::Maximized)
|
||||
|| 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()
|
||||
} else {
|
||||
self.find_by_position(location, |_, geometry| geometry).ok()
|
||||
};
|
||||
|
||||
if let Some(geometry) = output_geometry {
|
||||
if location != (geometry.x, geometry.y) {
|
||||
windows_to_move.push((kind.to_owned(), (geometry.x, geometry.y)));
|
||||
}
|
||||
|
||||
let res = xdg.with_pending_state(|pending_state| {
|
||||
pending_state.size = Some((geometry.width, geometry.height));
|
||||
});
|
||||
|
||||
if res.is_ok() {
|
||||
xdg.send_configure();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
for (window, location) in windows_to_move.drain(..) {
|
||||
window_map.set_location(&window, location);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add<N>(&mut self, name: N, physical: PhysicalProperties, mode: Mode)
|
||||
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 output = Output::new(
|
||||
name,
|
||||
location,
|
||||
&mut *self.display.borrow_mut(),
|
||||
physical,
|
||||
mode,
|
||||
self.logger.clone(),
|
||||
);
|
||||
|
||||
self.outputs.push(output);
|
||||
|
||||
// We call arrange here albeit the output is only appended and
|
||||
// this would not affect windows, but arrange could re-organize
|
||||
// outputs from a configuration.
|
||||
self.arrange();
|
||||
}
|
||||
|
||||
pub fn remove<N: AsRef<str>>(&mut self, name: N) {
|
||||
let removed_outputs = self.outputs.iter_mut().filter(|o| o.name == name.as_ref());
|
||||
|
||||
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) -> u32 {
|
||||
// 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(0u32, |acc, output| acc + output.geometry.width as u32)
|
||||
}
|
||||
|
||||
pub fn height(&self) -> u32 {
|
||||
// This is a simplification, we only arrange the outputs on the y axis side-by-side
|
||||
// so that the max height is simply the max of all output heights.
|
||||
self.outputs
|
||||
.iter()
|
||||
.map(|output| output.geometry.height as u32)
|
||||
.max()
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
pub fn size(&self) -> (u32, u32) {
|
||||
(self.width(), self.height())
|
||||
}
|
||||
|
||||
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) -> 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>
|
||||
where
|
||||
F: FnOnce(&output::Output, Rectangle) -> T,
|
||||
{
|
||||
let output = self
|
||||
.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>
|
||||
where
|
||||
N: AsRef<str>,
|
||||
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))
|
||||
}
|
||||
|
||||
pub fn find_by_position<F, T>(&self, position: (i32, i32), f: F) -> Result<T, OutputNotFound>
|
||||
where
|
||||
F: FnOnce(&output::Output, Rectangle) -> T,
|
||||
{
|
||||
let output = self
|
||||
.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) -> 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 {
|
||||
output.geometry.width = mode.width;
|
||||
output.geometry.height = mode.height;
|
||||
|
||||
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;
|
||||
|
||||
// Re-arrange outputs cause the size of one output changed
|
||||
self.arrange();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn refresh(&mut self) {
|
||||
// Clean-up dead surfaces
|
||||
self.outputs
|
||||
.iter_mut()
|
||||
.for_each(|o| o.surfaces.retain(|s| s.as_ref().is_alive()));
|
||||
|
||||
let window_map = self.window_map.clone();
|
||||
|
||||
window_map
|
||||
.borrow()
|
||||
.with_windows_from_bottom_to_top(|kind, location, bbox| {
|
||||
for output in self.outputs.iter_mut() {
|
||||
// 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 let Some(surface) = kind.get_surface() {
|
||||
with_surface_tree_downward(
|
||||
surface,
|
||||
(),
|
||||
|_, _, _| TraversalAction::DoChildren(()),
|
||||
|wl_surface, _, _| {
|
||||
if output.surfaces.contains(wl_surface) {
|
||||
output.output.leave(wl_surface);
|
||||
output.surfaces.retain(|s| s != wl_surface);
|
||||
}
|
||||
},
|
||||
|_, _, _| true,
|
||||
)
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Some(surface) = kind.get_surface() {
|
||||
with_surface_tree_downward(
|
||||
surface,
|
||||
location,
|
||||
|_, states, &(mut x, mut y)| {
|
||||
let data = states.data_map.get::<RefCell<SurfaceData>>();
|
||||
|
||||
if data.is_some() {
|
||||
if states.role == Some("subsurface") {
|
||||
let current = states.cached_state.current::<SubsurfaceCachedState>();
|
||||
x += current.location.0;
|
||||
y += current.location.1;
|
||||
}
|
||||
|
||||
TraversalAction::DoChildren((x, y))
|
||||
} else {
|
||||
// If the parent surface is unmapped, then the child surfaces are hidden as
|
||||
// well, no need to consider them here.
|
||||
TraversalAction::SkipChildren
|
||||
}
|
||||
},
|
||||
|wl_surface, states, &(x, y)| {
|
||||
let data = states.data_map.get::<RefCell<SurfaceData>>();
|
||||
|
||||
if let Some((width, height)) = data.and_then(|d| d.borrow().size()) {
|
||||
let surface_rectangle = Rectangle { x, y, width, height };
|
||||
|
||||
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);
|
||||
output.surfaces.push(wl_surface.clone());
|
||||
}
|
||||
} else {
|
||||
// Surface does not match output, if we sent enter earlier
|
||||
// we should now send leave
|
||||
if output.surfaces.contains(wl_surface) {
|
||||
output.output.leave(wl_surface);
|
||||
output.surfaces.retain(|s| s != wl_surface);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Maybe the the surface got unmapped, send leave on output
|
||||
if output.surfaces.contains(wl_surface) {
|
||||
output.output.leave(wl_surface);
|
||||
output.surfaces.retain(|s| s != wl_surface);
|
||||
}
|
||||
}
|
||||
},
|
||||
|_, _, _| true,
|
||||
)
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -9,7 +9,7 @@ use smithay::{
|
|||
reexports::{
|
||||
wayland_protocols::xdg_shell::server::xdg_toplevel,
|
||||
wayland_server::{
|
||||
protocol::{wl_buffer, wl_pointer::ButtonState, wl_shell_surface, wl_surface},
|
||||
protocol::{wl_buffer, wl_output, wl_pointer::ButtonState, wl_shell_surface, wl_surface},
|
||||
Display,
|
||||
},
|
||||
},
|
||||
|
@ -32,6 +32,7 @@ use smithay::{
|
|||
};
|
||||
|
||||
use crate::{
|
||||
output_map::OutputMap,
|
||||
state::AnvilState,
|
||||
window_map::{Kind as SurfaceKind, PopupKind, WindowMap},
|
||||
};
|
||||
|
@ -293,12 +294,13 @@ pub struct ShellHandles {
|
|||
pub xdg_state: Arc<Mutex<XdgShellState>>,
|
||||
pub wl_state: Arc<Mutex<WlShellState>>,
|
||||
pub window_map: Rc<RefCell<WindowMap>>,
|
||||
pub output_map: Rc<RefCell<OutputMap>>,
|
||||
}
|
||||
|
||||
pub fn init_shell<BackendData: 'static>(display: &mut Display, log: ::slog::Logger) -> ShellHandles {
|
||||
pub fn init_shell<BackendData: 'static>(display: Rc<RefCell<Display>>, log: ::slog::Logger) -> ShellHandles {
|
||||
// Create the compositor
|
||||
compositor_init(
|
||||
display,
|
||||
&mut *display.borrow_mut(),
|
||||
move |surface, mut ddata| {
|
||||
let anvil_state = ddata.get::<AnvilState<BackendData>>().unwrap();
|
||||
let window_map = anvil_state.window_map.as_ref();
|
||||
|
@ -309,19 +311,39 @@ pub fn init_shell<BackendData: 'static>(display: &mut Display, log: ::slog::Logg
|
|||
|
||||
// Init a window map, to track the location of our windows
|
||||
let window_map = Rc::new(RefCell::new(WindowMap::new()));
|
||||
let output_map = Rc::new(RefCell::new(OutputMap::new(
|
||||
display.clone(),
|
||||
window_map.clone(),
|
||||
log.clone(),
|
||||
)));
|
||||
|
||||
// init the xdg_shell
|
||||
let xdg_window_map = window_map.clone();
|
||||
let xdg_output_map = output_map.clone();
|
||||
let (xdg_shell_state, _, _) = xdg_shell_init(
|
||||
display,
|
||||
&mut *display.borrow_mut(),
|
||||
move |shell_event| match shell_event {
|
||||
XdgRequest::NewToplevel { surface } => {
|
||||
// place the window at a random location in the [0;800]x[0;800] square
|
||||
// place the window at a random location on the primary output
|
||||
// or if there is not output in a [0;800]x[0;800] square
|
||||
use rand::distributions::{Distribution, Uniform};
|
||||
let range = Uniform::new(0, 800);
|
||||
|
||||
let output_geometry = xdg_output_map
|
||||
.borrow()
|
||||
.with_primary(|_, geometry| geometry)
|
||||
.ok()
|
||||
.unwrap_or_else(|| Rectangle {
|
||||
width: 800,
|
||||
height: 800,
|
||||
..Default::default()
|
||||
});
|
||||
let max_x = output_geometry.x + (((output_geometry.width as f32) / 3.0) * 2.0) as i32;
|
||||
let max_y = output_geometry.y + (((output_geometry.height as f32) / 3.0) * 2.0) as i32;
|
||||
let x_range = Uniform::new(output_geometry.x, max_x);
|
||||
let y_range = Uniform::new(output_geometry.y, max_y);
|
||||
let mut rng = rand::thread_rng();
|
||||
let x = range.sample(&mut rng);
|
||||
let y = range.sample(&mut rng);
|
||||
let x = x_range.sample(&mut rng);
|
||||
let y = y_range.sample(&mut rng);
|
||||
// Do not send a configure here, the initial configure
|
||||
// of a xdg_surface has to be sent during the commit if
|
||||
// the surface is not already configured
|
||||
|
@ -478,16 +500,78 @@ pub fn init_shell<BackendData: 'static>(display: &mut Display, log: ::slog::Logg
|
|||
}
|
||||
}
|
||||
XdgRequest::Fullscreen { surface, output, .. } => {
|
||||
let ret = surface.with_pending_state(|state| {
|
||||
// TODO: Use size of current output the window is on and set position to (0,0)
|
||||
state.states.set(xdg_toplevel::State::Fullscreen);
|
||||
state.size = Some((800, 600));
|
||||
// TODO: If the provided output is None, use the output where
|
||||
// the toplevel is currently shown
|
||||
state.fullscreen_output = output;
|
||||
});
|
||||
if ret.is_ok() {
|
||||
surface.send_configure();
|
||||
// NOTE: This is only one part of the solution. We can set the
|
||||
// location and configure size here, but the surface should be rendered fullscreen
|
||||
// independently from its buffer size
|
||||
let wl_surface = if let Some(surface) = surface.get_surface() {
|
||||
surface
|
||||
} else {
|
||||
// If there is no underlying surface just ignore the request
|
||||
return;
|
||||
};
|
||||
|
||||
// Use the specified preferred output or else the current output the window
|
||||
// is shown or the primary output
|
||||
let output = output
|
||||
.or_else(|| {
|
||||
let xdg_window_map = xdg_window_map.borrow();
|
||||
let mut window_output: Option<wl_output::WlOutput> = None;
|
||||
xdg_window_map
|
||||
.find(wl_surface)
|
||||
.and_then(|kind| xdg_window_map.location(&kind))
|
||||
.and_then(|position| {
|
||||
xdg_output_map
|
||||
.borrow()
|
||||
.find_by_position(position, |output, _| {
|
||||
output.with_output(wl_surface, |_, output| {
|
||||
window_output = Some(output.to_owned());
|
||||
})
|
||||
})
|
||||
.ok()
|
||||
});
|
||||
window_output
|
||||
})
|
||||
.or_else(|| {
|
||||
let mut primary_output: Option<wl_output::WlOutput> = None;
|
||||
xdg_output_map
|
||||
.borrow()
|
||||
.with_primary(|output, _| {
|
||||
output.with_output(wl_surface, |_, output| {
|
||||
primary_output = Some(output.to_owned());
|
||||
})
|
||||
})
|
||||
.ok();
|
||||
primary_output
|
||||
});
|
||||
|
||||
let fullscreen_output = if let Some(output) = output {
|
||||
output
|
||||
} else {
|
||||
// If we have no output ignore the request
|
||||
return;
|
||||
};
|
||||
|
||||
let geometry = xdg_output_map
|
||||
.borrow()
|
||||
.find(&fullscreen_output, |_, geometry| geometry)
|
||||
.ok();
|
||||
|
||||
if let Some(geometry) = geometry {
|
||||
if let Some(surface) = surface.get_surface() {
|
||||
let mut xdg_window_map = xdg_window_map.borrow_mut();
|
||||
if let Some(kind) = xdg_window_map.find(surface) {
|
||||
xdg_window_map.set_location(&kind, (geometry.x, geometry.y));
|
||||
}
|
||||
}
|
||||
|
||||
let ret = surface.with_pending_state(|state| {
|
||||
state.states.set(xdg_toplevel::State::Fullscreen);
|
||||
state.size = Some((geometry.width, geometry.height));
|
||||
state.fullscreen_output = Some(fullscreen_output);
|
||||
});
|
||||
if ret.is_ok() {
|
||||
surface.send_configure();
|
||||
}
|
||||
}
|
||||
}
|
||||
XdgRequest::UnFullscreen { surface } => {
|
||||
|
@ -501,13 +585,36 @@ pub fn init_shell<BackendData: 'static>(display: &mut Display, log: ::slog::Logg
|
|||
}
|
||||
}
|
||||
XdgRequest::Maximize { surface } => {
|
||||
let ret = surface.with_pending_state(|state| {
|
||||
// TODO: Use size of current output the window is on and set position to (0,0)
|
||||
state.states.set(xdg_toplevel::State::Maximized);
|
||||
state.size = Some((800, 600));
|
||||
});
|
||||
if ret.is_ok() {
|
||||
surface.send_configure();
|
||||
// NOTE: This should use layer-shell when it is implemented to
|
||||
// get the correct maximum size
|
||||
let output_geometry = {
|
||||
let xdg_window_map = xdg_window_map.borrow();
|
||||
surface
|
||||
.get_surface()
|
||||
.and_then(|s| xdg_window_map.find(s))
|
||||
.and_then(|k| xdg_window_map.location(&k))
|
||||
.and_then(|position| {
|
||||
xdg_output_map
|
||||
.borrow()
|
||||
.find_by_position(position, |_, geometry| geometry)
|
||||
.ok()
|
||||
})
|
||||
};
|
||||
|
||||
if let Some(geometry) = output_geometry {
|
||||
if let Some(surface) = surface.get_surface() {
|
||||
let mut xdg_window_map = xdg_window_map.borrow_mut();
|
||||
if let Some(kind) = xdg_window_map.find(surface) {
|
||||
xdg_window_map.set_location(&kind, (geometry.x, geometry.y));
|
||||
}
|
||||
}
|
||||
let ret = surface.with_pending_state(|state| {
|
||||
state.states.set(xdg_toplevel::State::Maximized);
|
||||
state.size = Some((geometry.width, geometry.height));
|
||||
});
|
||||
if ret.is_ok() {
|
||||
surface.send_configure();
|
||||
}
|
||||
}
|
||||
}
|
||||
XdgRequest::UnMaximize { surface } => {
|
||||
|
@ -526,24 +633,105 @@ pub fn init_shell<BackendData: 'static>(display: &mut Display, log: ::slog::Logg
|
|||
|
||||
// init the wl_shell
|
||||
let shell_window_map = window_map.clone();
|
||||
let shell_output_map = output_map.clone();
|
||||
let (wl_shell_state, _) = wl_shell_init(
|
||||
display,
|
||||
&mut *display.borrow_mut(),
|
||||
move |req: ShellRequest| {
|
||||
match req {
|
||||
ShellRequest::SetKind {
|
||||
surface,
|
||||
kind: ShellSurfaceKind::Toplevel,
|
||||
} => {
|
||||
// place the window at a random location in the [0;800]x[0;800] square
|
||||
// place the window at a random location on the primary output
|
||||
// or if there is not output in a [0;800]x[0;800] square
|
||||
use rand::distributions::{Distribution, Uniform};
|
||||
let range = Uniform::new(0, 800);
|
||||
|
||||
let output_geometry = shell_output_map
|
||||
.borrow()
|
||||
.with_primary(|_, geometry| geometry)
|
||||
.ok()
|
||||
.unwrap_or_else(|| Rectangle {
|
||||
width: 800,
|
||||
height: 800,
|
||||
..Default::default()
|
||||
});
|
||||
let max_x = output_geometry.x + (((output_geometry.width as f32) / 3.0) * 2.0) as i32;
|
||||
let max_y = output_geometry.y + (((output_geometry.height as f32) / 3.0) * 2.0) as i32;
|
||||
let x_range = Uniform::new(output_geometry.x, max_x);
|
||||
let y_range = Uniform::new(output_geometry.y, max_y);
|
||||
let mut rng = rand::thread_rng();
|
||||
let x = range.sample(&mut rng);
|
||||
let y = range.sample(&mut rng);
|
||||
let x = x_range.sample(&mut rng);
|
||||
let y = y_range.sample(&mut rng);
|
||||
shell_window_map
|
||||
.borrow_mut()
|
||||
.insert(SurfaceKind::Wl(surface), (x, y));
|
||||
}
|
||||
ShellRequest::SetKind {
|
||||
surface,
|
||||
kind: ShellSurfaceKind::Fullscreen { output, .. },
|
||||
} => {
|
||||
// NOTE: This is only one part of the solution. We can set the
|
||||
// location and configure size here, but the surface should be rendered fullscreen
|
||||
// independently from its buffer size
|
||||
let wl_surface = if let Some(surface) = surface.get_surface() {
|
||||
surface
|
||||
} else {
|
||||
// If there is no underlying surface just ignore the request
|
||||
return;
|
||||
};
|
||||
|
||||
// Use the specified preferred output or else the current output the window
|
||||
// is shown or the primary output
|
||||
let output = output
|
||||
.or_else(|| {
|
||||
let shell_window_map = shell_window_map.borrow();
|
||||
let mut window_output: Option<wl_output::WlOutput> = None;
|
||||
shell_window_map
|
||||
.find(wl_surface)
|
||||
.and_then(|kind| shell_window_map.location(&kind))
|
||||
.and_then(|position| {
|
||||
shell_output_map
|
||||
.borrow()
|
||||
.find_by_position(position, |output, _| {
|
||||
output.with_output(wl_surface, |_, output| {
|
||||
window_output = Some(output.to_owned());
|
||||
})
|
||||
})
|
||||
.ok()
|
||||
});
|
||||
window_output
|
||||
})
|
||||
.or_else(|| {
|
||||
let mut primary_output: Option<wl_output::WlOutput> = None;
|
||||
shell_output_map
|
||||
.borrow()
|
||||
.with_primary(|output, _| {
|
||||
output.with_output(wl_surface, |_, output| {
|
||||
primary_output = Some(output.to_owned());
|
||||
})
|
||||
})
|
||||
.ok();
|
||||
primary_output
|
||||
});
|
||||
|
||||
let fullscreen_output = if let Some(output) = output {
|
||||
output
|
||||
} else {
|
||||
// If we have no output ignore the request
|
||||
return;
|
||||
};
|
||||
|
||||
let geometry = shell_output_map
|
||||
.borrow()
|
||||
.find(&fullscreen_output, |_, geometry| geometry)
|
||||
.ok();
|
||||
|
||||
let location = geometry.map(|g| (g.x, g.y)).unwrap_or_default();
|
||||
|
||||
shell_window_map
|
||||
.borrow_mut()
|
||||
.insert(SurfaceKind::Wl(surface), location);
|
||||
}
|
||||
ShellRequest::Move {
|
||||
surface,
|
||||
seat,
|
||||
|
@ -654,6 +842,7 @@ pub fn init_shell<BackendData: 'static>(display: &mut Display, log: ::slog::Logg
|
|||
xdg_state: xdg_shell_state,
|
||||
wl_state: wl_shell_state,
|
||||
window_map,
|
||||
output_map,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -31,6 +31,7 @@ pub struct AnvilState<BackendData> {
|
|||
pub display: Rc<RefCell<Display>>,
|
||||
pub handle: LoopHandle<'static, AnvilState<BackendData>>,
|
||||
pub window_map: Rc<RefCell<crate::window_map::WindowMap>>,
|
||||
pub output_map: Rc<RefCell<crate::output_map::OutputMap>>,
|
||||
pub dnd_icon: Arc<Mutex<Option<WlSurface>>>,
|
||||
pub log: slog::Logger,
|
||||
// input-related fields
|
||||
|
@ -76,7 +77,7 @@ impl<BackendData: Backend + 'static> AnvilState<BackendData> {
|
|||
|
||||
init_shm_global(&mut (*display).borrow_mut(), vec![], log.clone());
|
||||
|
||||
let shell_handles = init_shell::<BackendData>(&mut display.borrow_mut(), log.clone());
|
||||
let shell_handles = init_shell::<BackendData>(display.clone(), log.clone());
|
||||
|
||||
let socket_name = display
|
||||
.borrow_mut()
|
||||
|
@ -148,6 +149,7 @@ impl<BackendData: Backend + 'static> AnvilState<BackendData> {
|
|||
display,
|
||||
handle,
|
||||
window_map: shell_handles.window_map,
|
||||
output_map: shell_handles.output_map,
|
||||
dnd_icon,
|
||||
log,
|
||||
socket_name,
|
||||
|
|
|
@ -45,13 +45,12 @@ use smithay::{
|
|||
nix::{fcntl::OFlag, sys::stat::dev_t},
|
||||
wayland_server::{
|
||||
protocol::{wl_output, wl_surface},
|
||||
Display, Global,
|
||||
Display,
|
||||
},
|
||||
},
|
||||
signaling::{Linkable, SignalToken, Signaler},
|
||||
utils::Rectangle,
|
||||
wayland::{
|
||||
output::{Mode, Output, PhysicalProperties},
|
||||
output::{Mode, PhysicalProperties},
|
||||
seat::CursorImageStatus,
|
||||
},
|
||||
};
|
||||
|
@ -76,8 +75,14 @@ impl AsRawFd for SessionFd {
|
|||
}
|
||||
}
|
||||
|
||||
struct UdevOutputMap {
|
||||
pub device_id: dev_t,
|
||||
pub crtc: crtc::Handle,
|
||||
pub output_name: String,
|
||||
}
|
||||
|
||||
pub struct UdevData {
|
||||
pub output_map: Vec<MyOutput>,
|
||||
output_map: Vec<UdevOutputMap>,
|
||||
pub session: AutoSession,
|
||||
#[cfg(feature = "egl")]
|
||||
primary_gpu: Option<PathBuf>,
|
||||
|
@ -233,6 +238,7 @@ pub fn run_udev(
|
|||
} else {
|
||||
display.borrow_mut().flush_clients(&mut state);
|
||||
state.window_map.borrow_mut().refresh();
|
||||
state.output_map.borrow_mut().refresh();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -246,68 +252,6 @@ pub fn run_udev(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub struct MyOutput {
|
||||
pub device_id: dev_t,
|
||||
pub crtc: crtc::Handle,
|
||||
pub size: (u32, u32),
|
||||
_wl: Output,
|
||||
global: Option<Global<wl_output::WlOutput>>,
|
||||
}
|
||||
|
||||
impl MyOutput {
|
||||
fn new(
|
||||
display: &mut Display,
|
||||
device_id: dev_t,
|
||||
crtc: crtc::Handle,
|
||||
conn: ConnectorInfo,
|
||||
logger: ::slog::Logger,
|
||||
) -> MyOutput {
|
||||
let (output, global) = Output::new(
|
||||
display,
|
||||
format!("{:?}", conn.interface()),
|
||||
PhysicalProperties {
|
||||
width: conn.size().unwrap_or((0, 0)).0 as i32,
|
||||
height: conn.size().unwrap_or((0, 0)).1 as i32,
|
||||
subpixel: wl_output::Subpixel::Unknown,
|
||||
make: "Smithay".into(),
|
||||
model: "Generic DRM".into(),
|
||||
},
|
||||
logger,
|
||||
);
|
||||
|
||||
let mode = conn.modes()[0];
|
||||
let (w, h) = mode.size();
|
||||
output.change_current_state(
|
||||
Some(Mode {
|
||||
width: w as i32,
|
||||
height: h as i32,
|
||||
refresh: (mode.vrefresh() * 1000) as i32,
|
||||
}),
|
||||
None,
|
||||
None,
|
||||
);
|
||||
output.set_preferred(Mode {
|
||||
width: w as i32,
|
||||
height: h as i32,
|
||||
refresh: (mode.vrefresh() * 1000) as i32,
|
||||
});
|
||||
|
||||
MyOutput {
|
||||
device_id,
|
||||
crtc,
|
||||
size: (w as u32, h as u32),
|
||||
_wl: output,
|
||||
global: Some(global),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for MyOutput {
|
||||
fn drop(&mut self) {
|
||||
self.global.take().unwrap().destroy();
|
||||
}
|
||||
}
|
||||
|
||||
pub type RenderSurface = GbmBufferedSurface<SessionFd>;
|
||||
|
||||
struct BackendData {
|
||||
|
@ -321,12 +265,12 @@ struct BackendData {
|
|||
dev_id: u64,
|
||||
}
|
||||
|
||||
pub fn scan_connectors(
|
||||
fn scan_connectors(
|
||||
device: &mut DrmDevice<SessionFd>,
|
||||
gbm: &GbmDevice<SessionFd>,
|
||||
renderer: &mut Gles2Renderer,
|
||||
display: &mut Display,
|
||||
output_map: &mut Vec<MyOutput>,
|
||||
backend_output_map: &mut Vec<UdevOutputMap>,
|
||||
output_map: &mut crate::output_map::OutputMap,
|
||||
signaler: &Signaler<SessionSignal>,
|
||||
logger: &::slog::Logger,
|
||||
) -> HashMap<crtc::Handle, Rc<RefCell<RenderSurface>>> {
|
||||
|
@ -388,13 +332,37 @@ pub fn scan_connectors(
|
|||
}
|
||||
};
|
||||
|
||||
output_map.push(MyOutput::new(
|
||||
display,
|
||||
device.device_id(),
|
||||
let mode = connector_info.modes()[0];
|
||||
let size = mode.size();
|
||||
let mode = Mode {
|
||||
width: size.0 as i32,
|
||||
height: size.1 as i32,
|
||||
refresh: (mode.vrefresh() * 1000) as i32,
|
||||
};
|
||||
|
||||
let output_name = format!(
|
||||
"{:?}-{}",
|
||||
connector_info.interface(),
|
||||
connector_info.interface_id()
|
||||
);
|
||||
|
||||
output_map.add(
|
||||
&output_name,
|
||||
PhysicalProperties {
|
||||
width: connector_info.size().unwrap_or((0, 0)).0 as i32,
|
||||
height: connector_info.size().unwrap_or((0, 0)).1 as i32,
|
||||
subpixel: wl_output::Subpixel::Unknown,
|
||||
make: "Smithay".into(),
|
||||
model: "Generic DRM".into(),
|
||||
},
|
||||
mode,
|
||||
);
|
||||
|
||||
backend_output_map.push(UdevOutputMap {
|
||||
crtc,
|
||||
connector_info,
|
||||
logger.clone(),
|
||||
));
|
||||
device_id: device.device_id(),
|
||||
output_name,
|
||||
});
|
||||
|
||||
entry.insert(Rc::new(RefCell::new(renderer)));
|
||||
break 'outer;
|
||||
|
@ -483,8 +451,8 @@ impl AnvilState<UdevData> {
|
|||
&mut device,
|
||||
&gbm,
|
||||
&mut *renderer.borrow_mut(),
|
||||
&mut *self.display.borrow_mut(),
|
||||
&mut self.backend_data.output_map,
|
||||
&mut *self.output_map.borrow_mut(),
|
||||
&self.backend_data.signaler,
|
||||
&self.log,
|
||||
)));
|
||||
|
@ -543,8 +511,18 @@ impl AnvilState<UdevData> {
|
|||
if let Some(ref mut backend_data) = self.backend_data.backends.get_mut(&device) {
|
||||
let logger = self.log.clone();
|
||||
let loop_handle = self.handle.clone();
|
||||
let mut display = self.display.borrow_mut();
|
||||
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);
|
||||
|
@ -555,8 +533,8 @@ impl AnvilState<UdevData> {
|
|||
&mut *source,
|
||||
&backend_data.gbm,
|
||||
&mut *backend_data.renderer.borrow_mut(),
|
||||
&mut *display,
|
||||
&mut self.backend_data.output_map,
|
||||
&mut *self.output_map.borrow_mut(),
|
||||
&signaler,
|
||||
&logger,
|
||||
);
|
||||
|
@ -580,10 +558,16 @@ impl AnvilState<UdevData> {
|
|||
// drop surfaces
|
||||
backend_data.surfaces.borrow_mut().clear();
|
||||
debug!(self.log, "Surfaces dropped");
|
||||
// clear outputs
|
||||
self.backend_data
|
||||
let removed_outputs = self
|
||||
.backend_data
|
||||
.output_map
|
||||
.retain(|output| output.device_id != device);
|
||||
.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);
|
||||
|
||||
let _device = self.handle.remove(backend_data.registration_token);
|
||||
let _device = backend_data.event_dispatcher.into_source_inner();
|
||||
|
@ -630,6 +614,7 @@ impl AnvilState<UdevData> {
|
|||
crtc,
|
||||
&mut *self.window_map.borrow_mut(),
|
||||
&mut self.backend_data.output_map,
|
||||
&*self.output_map.borrow(),
|
||||
&self.pointer_location,
|
||||
&device_backend.pointer_image,
|
||||
&*self.dnd_icon.lock().unwrap(),
|
||||
|
@ -676,7 +661,8 @@ fn render_surface(
|
|||
device_id: dev_t,
|
||||
crtc: crtc::Handle,
|
||||
window_map: &mut WindowMap,
|
||||
output_map: &mut Vec<MyOutput>,
|
||||
backend_output_map: &[UdevOutputMap],
|
||||
output_map: &crate::output_map::OutputMap,
|
||||
pointer_location: &(f64, f64),
|
||||
pointer_image: &Gles2Texture,
|
||||
dnd_icon: &Option<wl_surface::WlSurface>,
|
||||
|
@ -685,48 +671,40 @@ fn render_surface(
|
|||
) -> Result<(), SwapBuffersError> {
|
||||
surface.frame_submitted()?;
|
||||
|
||||
// get output coordinates
|
||||
let (x, y) = output_map
|
||||
let output_geometry = backend_output_map
|
||||
.iter()
|
||||
.take_while(|output| output.device_id != device_id || output.crtc != crtc)
|
||||
.fold((0u32, 0u32), |pos, output| (pos.0 + output.size.0, pos.1));
|
||||
let (width, height) = output_map
|
||||
.iter()
|
||||
.find(|output| output.device_id == device_id && output.crtc == crtc)
|
||||
.map(|output| output.size)
|
||||
.unwrap_or((0, 0)); // in this case the output will be removed.
|
||||
.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_geometry = if let Some(geometry) = output_geometry {
|
||||
geometry
|
||||
} else {
|
||||
// Somehow we got called with a non existing output
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
let dmabuf = surface.next_buffer()?;
|
||||
renderer.bind(dmabuf)?;
|
||||
// and draw to our buffer
|
||||
match renderer
|
||||
.render(
|
||||
width,
|
||||
height,
|
||||
output_geometry.width as u32,
|
||||
output_geometry.height as u32,
|
||||
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,
|
||||
Some(Rectangle {
|
||||
x: x as i32,
|
||||
y: y as i32,
|
||||
width: width as i32,
|
||||
height: height as i32,
|
||||
}),
|
||||
logger,
|
||||
)?;
|
||||
draw_windows(renderer, frame, window_map, output_geometry, logger)?;
|
||||
|
||||
// get pointer coordinates
|
||||
let (ptr_x, ptr_y) = *pointer_location;
|
||||
let ptr_x = ptr_x.trunc().abs() as i32 - x as i32;
|
||||
let ptr_y = ptr_y.trunc().abs() as i32 - y as i32;
|
||||
let ptr_x = ptr_x.trunc().abs() as i32 - output_geometry.x;
|
||||
let ptr_y = ptr_y.trunc().abs() as i32 - output_geometry.y;
|
||||
|
||||
// set cursor
|
||||
if ptr_x >= 0 && ptr_x < width as i32 && ptr_y >= 0 && ptr_y < height as i32 {
|
||||
if ptr_x >= 0 && ptr_x < output_geometry.width && ptr_y >= 0 && ptr_y < output_geometry.height
|
||||
{
|
||||
// draw the dnd icon if applicable
|
||||
{
|
||||
if let Some(ref wl_surface) = dnd_icon.as_ref() {
|
||||
|
|
|
@ -11,9 +11,8 @@ use smithay::{
|
|||
calloop::EventLoop,
|
||||
wayland_server::{protocol::wl_output, Display},
|
||||
},
|
||||
utils::Rectangle,
|
||||
wayland::{
|
||||
output::{Mode, Output, PhysicalProperties},
|
||||
output::{Mode, PhysicalProperties},
|
||||
seat::CursorImageStatus,
|
||||
},
|
||||
};
|
||||
|
@ -23,6 +22,8 @@ use slog::Logger;
|
|||
use crate::drawing::*;
|
||||
use crate::state::{AnvilState, Backend};
|
||||
|
||||
pub const OUTPUT_NAME: &'static str = "winit";
|
||||
|
||||
pub struct WinitData;
|
||||
|
||||
impl Backend for WinitData {
|
||||
|
@ -72,9 +73,14 @@ pub fn run_winit(
|
|||
|
||||
let mut state = AnvilState::init(display.clone(), event_loop.handle(), WinitData, log.clone());
|
||||
|
||||
let (output, _) = Output::new(
|
||||
&mut display.borrow_mut(),
|
||||
"Winit".into(),
|
||||
let mode = Mode {
|
||||
width: w as i32,
|
||||
height: h as i32,
|
||||
refresh: 60_000,
|
||||
};
|
||||
|
||||
state.output_map.borrow_mut().add(
|
||||
OUTPUT_NAME,
|
||||
PhysicalProperties {
|
||||
width: 0,
|
||||
height: 0,
|
||||
|
@ -82,24 +88,9 @@ pub fn run_winit(
|
|||
make: "Smithay".into(),
|
||||
model: "Winit".into(),
|
||||
},
|
||||
log.clone(),
|
||||
mode,
|
||||
);
|
||||
|
||||
output.change_current_state(
|
||||
Some(Mode {
|
||||
width: w as i32,
|
||||
height: h as i32,
|
||||
refresh: 60_000,
|
||||
}),
|
||||
None,
|
||||
None,
|
||||
);
|
||||
output.set_preferred(Mode {
|
||||
width: w as i32,
|
||||
height: h as i32,
|
||||
refresh: 60_000,
|
||||
});
|
||||
|
||||
let start_time = std::time::Instant::now();
|
||||
let mut cursor_visible = true;
|
||||
|
||||
|
@ -120,16 +111,13 @@ pub fn run_winit(
|
|||
// drawing logic
|
||||
{
|
||||
let mut renderer = renderer.borrow_mut();
|
||||
|
||||
let output_rect = {
|
||||
let (width, height) = renderer.window_size().physical_size.into();
|
||||
Rectangle {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width,
|
||||
height,
|
||||
}
|
||||
};
|
||||
// This is safe to do as with winit we are guaranteed to have exactly one output
|
||||
let output_geometry = state
|
||||
.output_map
|
||||
.borrow()
|
||||
.find_by_name(OUTPUT_NAME, |_, geometry| geometry)
|
||||
.ok()
|
||||
.unwrap();
|
||||
|
||||
let result = renderer
|
||||
.render(|renderer, frame| {
|
||||
|
@ -140,7 +128,7 @@ pub fn run_winit(
|
|||
renderer,
|
||||
frame,
|
||||
&*state.window_map.borrow(),
|
||||
Some(output_rect),
|
||||
output_geometry,
|
||||
&log,
|
||||
)?;
|
||||
|
||||
|
@ -203,6 +191,7 @@ pub fn run_winit(
|
|||
} else {
|
||||
display.borrow_mut().flush_clients(&mut state);
|
||||
state.window_map.borrow_mut().refresh();
|
||||
state.output_map.borrow_mut().refresh();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -53,11 +53,17 @@ use std::{
|
|||
sync::{Arc, Mutex},
|
||||
};
|
||||
|
||||
use wayland_server::protocol::wl_output::{Subpixel, Transform};
|
||||
use wayland_server::{
|
||||
protocol::wl_output::{Mode as WMode, WlOutput},
|
||||
Display, Filter, Global, Main,
|
||||
};
|
||||
use wayland_server::{
|
||||
protocol::{
|
||||
wl_output::{Subpixel, Transform},
|
||||
wl_surface,
|
||||
},
|
||||
Resource,
|
||||
};
|
||||
|
||||
use slog::{info, o, trace, warn};
|
||||
|
||||
|
@ -314,4 +320,34 @@ impl Output {
|
|||
.iter()
|
||||
.any(|o| o.as_ref().equals(output.as_ref()))
|
||||
}
|
||||
|
||||
/// This function allows to run a `Fn` for the matching
|
||||
/// client outputs for a specific `Resource`.
|
||||
pub fn with_output<F, R, II>(&self, resource: &R, mut f: F)
|
||||
where
|
||||
R: AsRef<Resource<II>>,
|
||||
II: wayland_server::Interface,
|
||||
F: FnMut(&R, &WlOutput),
|
||||
{
|
||||
let tmp = resource.as_ref();
|
||||
self.inner
|
||||
.lock()
|
||||
.unwrap()
|
||||
.instances
|
||||
.iter()
|
||||
.filter(|output| output.as_ref().same_client_as(tmp))
|
||||
.for_each(|output| f(resource, output))
|
||||
}
|
||||
|
||||
/// Sends `wl_surface.enter` for the provided surface
|
||||
/// with the matching client output
|
||||
pub fn enter(&self, surface: &wl_surface::WlSurface) {
|
||||
self.with_output(surface, |surface, output| surface.enter(output))
|
||||
}
|
||||
|
||||
/// Sends `wl_surface.leave` for the provided surface
|
||||
/// with the matching client output
|
||||
pub fn leave(&self, surface: &wl_surface::WlSurface) {
|
||||
self.with_output(surface, |surface, output| surface.leave(output))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1113,6 +1113,30 @@ impl ToplevelSurface {
|
|||
})
|
||||
.unwrap())
|
||||
}
|
||||
|
||||
/// Gets a copy of the current state of this toplevel
|
||||
///
|
||||
/// Returns `None` if the underlying surface has been
|
||||
/// destroyed
|
||||
pub fn current_state(&self) -> Option<ToplevelState> {
|
||||
if !self.alive() {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(
|
||||
compositor::with_states(&self.wl_surface, |states| {
|
||||
let attributes = states
|
||||
.data_map
|
||||
.get::<Mutex<XdgToplevelSurfaceRoleAttributes>>()
|
||||
.unwrap()
|
||||
.lock()
|
||||
.unwrap();
|
||||
|
||||
attributes.current.clone()
|
||||
})
|
||||
.unwrap(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
|
Loading…
Reference in New Issue