Merge pull request #306 from cmeissl/feature/surface-enter-leave
This commit is contained in:
commit
f27658b759
|
@ -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) {
|
||||
if !output_rect.overlaps(bounding_box) {
|
||||
return;
|
||||
}
|
||||
initial_place.0 -= output.x;
|
||||
}
|
||||
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
|
||||
let geometry = self
|
||||
.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)
|
||||
.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,23 @@ 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 output_map = self.output_map.borrow();
|
||||
let max_x = output_map.width();
|
||||
let clamped_x = pos_x.max(0.0).min(max_x as f64);
|
||||
let max_y = output_map.height(clamped_x as i32);
|
||||
|
||||
// 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);
|
||||
if let Some(max_y) = max_y {
|
||||
let clamped_y = pos_y.max(0.0).min(max_y as f64);
|
||||
|
||||
(x, y)
|
||||
(clamped_x, clamped_y)
|
||||
} else {
|
||||
(clamped_x, pos_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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -26,6 +26,8 @@ mod winit;
|
|||
#[cfg(feature = "xwayland")]
|
||||
mod xwayland;
|
||||
|
||||
mod output_map;
|
||||
|
||||
use state::AnvilState;
|
||||
|
||||
static POSSIBLE_BACKENDS: &[&str] = &[
|
||||
|
|
|
@ -0,0 +1,397 @@
|
|||
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, x: i32) -> Option<u32> {
|
||||
// This is a simplification, we only arrange the outputs on the y axis side-by-side
|
||||
self.outputs
|
||||
.iter()
|
||||
.find(|output| x >= output.geometry.x && x < (output.geometry.x + output.geometry.width))
|
||||
.map(|output| output.geometry.height as u32)
|
||||
}
|
||||
|
||||
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,43 @@ 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 {
|
||||
fn fullscreen_output_geometry(
|
||||
wl_surface: &wl_surface::WlSurface,
|
||||
wl_output: Option<&wl_output::WlOutput>,
|
||||
window_map: &WindowMap,
|
||||
output_map: &OutputMap,
|
||||
) -> Option<Rectangle> {
|
||||
// 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();
|
||||
}
|
||||
|
||||
// There is no output preference, try to find the output
|
||||
// where the window is currently active
|
||||
let window_location = window_map
|
||||
.find(wl_surface)
|
||||
.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();
|
||||
|
||||
if let Some(result) = window_output {
|
||||
return Some(result);
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to primary output
|
||||
output_map.with_primary(|_, geometry| geometry).ok()
|
||||
}
|
||||
|
||||
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 +341,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
|
||||
|
@ -493,18 +545,41 @@ pub fn init_shell<BackendData: 'static>(display: &mut Display, log: ::slog::Logg
|
|||
}
|
||||
}
|
||||
XdgRequest::Fullscreen { surface, 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;
|
||||
};
|
||||
|
||||
let output_geometry = fullscreen_output_geometry(
|
||||
wl_surface,
|
||||
output.as_ref(),
|
||||
&xdg_window_map.borrow(),
|
||||
&xdg_output_map.borrow(),
|
||||
);
|
||||
|
||||
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| {
|
||||
// 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.size = Some((geometry.width, geometry.height));
|
||||
state.fullscreen_output = output;
|
||||
});
|
||||
if ret.is_ok() {
|
||||
surface.send_configure();
|
||||
}
|
||||
}
|
||||
}
|
||||
XdgRequest::UnFullscreen { surface } => {
|
||||
let ret = surface.with_pending_state(|state| {
|
||||
state.states.unset(xdg_toplevel::State::Fullscreen);
|
||||
|
@ -516,15 +591,38 @@ pub fn init_shell<BackendData: 'static>(display: &mut Display, log: ::slog::Logg
|
|||
}
|
||||
}
|
||||
XdgRequest::Maximize { surface } => {
|
||||
// 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| {
|
||||
// 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));
|
||||
state.size = Some((geometry.width, geometry.height));
|
||||
});
|
||||
if ret.is_ok() {
|
||||
surface.send_configure();
|
||||
}
|
||||
}
|
||||
}
|
||||
XdgRequest::UnMaximize { surface } => {
|
||||
let ret = surface.with_pending_state(|state| {
|
||||
state.states.unset(xdg_toplevel::State::Maximized);
|
||||
|
@ -541,24 +639,66 @@ 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;
|
||||
};
|
||||
|
||||
let output_geometry = fullscreen_output_geometry(
|
||||
wl_surface,
|
||||
output.as_ref(),
|
||||
&shell_window_map.borrow(),
|
||||
&shell_output_map.borrow(),
|
||||
);
|
||||
|
||||
if let Some(geometry) = output_geometry {
|
||||
shell_window_map
|
||||
.borrow_mut()
|
||||
.insert(SurfaceKind::Wl(surface), (geometry.x, geometry.y));
|
||||
}
|
||||
}
|
||||
ShellRequest::Move {
|
||||
surface,
|
||||
seat,
|
||||
|
@ -669,6 +809,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();
|
||||
|
@ -629,7 +613,8 @@ impl AnvilState<UdevData> {
|
|||
device_backend.dev_id,
|
||||
crtc,
|
||||
&mut *self.window_map.borrow_mut(),
|
||||
&mut self.backend_data.output_map,
|
||||
&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: &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,10 +53,13 @@ use std::{
|
|||
sync::{Arc, Mutex},
|
||||
};
|
||||
|
||||
use wayland_server::protocol::wl_output::{Subpixel, Transform};
|
||||
use wayland_server::protocol::{
|
||||
wl_output::{Subpixel, Transform},
|
||||
wl_surface,
|
||||
};
|
||||
use wayland_server::{
|
||||
protocol::wl_output::{Mode as WMode, WlOutput},
|
||||
Display, Filter, Global, Main,
|
||||
Client, Display, Filter, Global, Main,
|
||||
};
|
||||
|
||||
use slog::{info, o, trace, warn};
|
||||
|
@ -314,4 +317,38 @@ impl Output {
|
|||
.iter()
|
||||
.any(|o| o.as_ref().equals(output.as_ref()))
|
||||
}
|
||||
|
||||
/// This function allows to run a [FnMut] on every
|
||||
/// [WlOutput] matching the same [Client] as provided
|
||||
pub fn with_client_outputs<F>(&self, client: Client, mut f: F)
|
||||
where
|
||||
F: FnMut(&WlOutput),
|
||||
{
|
||||
self.inner
|
||||
.lock()
|
||||
.unwrap()
|
||||
.instances
|
||||
.iter()
|
||||
.filter(|output| match output.as_ref().client() {
|
||||
Some(output_client) => output_client.equals(&client),
|
||||
None => false,
|
||||
})
|
||||
.for_each(|output| f(output))
|
||||
}
|
||||
|
||||
/// Sends `wl_surface.enter` for the provided surface
|
||||
/// with the matching client output
|
||||
pub fn enter(&self, surface: &wl_surface::WlSurface) {
|
||||
if let Some(client) = surface.as_ref().client() {
|
||||
self.with_client_outputs(client, |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) {
|
||||
if let Some(client) = surface.as_ref().client() {
|
||||
self.with_client_outputs(client, |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