mirror of
https://github.com/feschber/lan-mouse.git
synced 2026-03-22 12:40:54 +03:00
Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bae52eb9e7 | ||
|
|
0fbd09b07f | ||
|
|
96dd9c05a1 | ||
|
|
15c02ac505 | ||
|
|
08893a39be | ||
|
|
48b701b726 | ||
|
|
891e21d3e9 | ||
|
|
6a5de3f025 |
2
Cargo.lock
generated
2
Cargo.lock
generated
@@ -675,7 +675,7 @@ checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lan-mouse"
|
name = "lan-mouse"
|
||||||
version = "0.3.0"
|
version = "0.3.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"env_logger",
|
"env_logger",
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "lan-mouse"
|
name = "lan-mouse"
|
||||||
description = "Software KVM Switch / mouse & keyboard sharing software for Local Area Networks"
|
description = "Software KVM Switch / mouse & keyboard sharing software for Local Area Networks"
|
||||||
version = "0.3.0"
|
version = "0.3.2"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "GPL-3.0-or-later"
|
license = "GPL-3.0-or-later"
|
||||||
repository = "https://github.com/ferdinandschober/lan-mouse"
|
repository = "https://github.com/ferdinandschober/lan-mouse"
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
# Lan Mouse Share
|
# Lan Mouse Share
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
||||||
Goal of this project is to be an open-source replacement for proprietary tools like [Synergy](https://symless.com/synergy), [Share Mouse](https://www.sharemouse.com/de/).
|
Goal of this project is to be an open-source replacement for proprietary tools like [Synergy](https://symless.com/synergy), [Share Mouse](https://www.sharemouse.com/de/).
|
||||||
|
|
||||||
@@ -43,6 +44,8 @@ ips = ["192.168.178.156"]
|
|||||||
host_name = "thorium"
|
host_name = "thorium"
|
||||||
# ips for ethernet and wifi
|
# ips for ethernet and wifi
|
||||||
ips = ["192.168.178.189"]
|
ips = ["192.168.178.189"]
|
||||||
|
# optional port
|
||||||
|
port = 4242
|
||||||
```
|
```
|
||||||
|
|
||||||
Where `left` can be either `left`, `right`, `top` or `bottom`.
|
Where `left` can be either `left`, `right`, `top` or `bottom`.
|
||||||
|
|||||||
@@ -19,3 +19,5 @@ ips = ["192.168.178.156"]
|
|||||||
host_name = "thorium"
|
host_name = "thorium"
|
||||||
# ips for ethernet and wifi
|
# ips for ethernet and wifi
|
||||||
ips = ["192.168.178.189", "192.168.178.172"]
|
ips = ["192.168.178.189", "192.168.178.172"]
|
||||||
|
# optional port
|
||||||
|
port = 4242
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ use std::{
|
|||||||
rc::Rc,
|
rc::Rc,
|
||||||
};
|
};
|
||||||
|
|
||||||
use wayland_protocols::wp::{
|
use wayland_protocols::{wp::{
|
||||||
keyboard_shortcuts_inhibit::zv1::client::{
|
keyboard_shortcuts_inhibit::zv1::client::{
|
||||||
zwp_keyboard_shortcuts_inhibit_manager_v1::ZwpKeyboardShortcutsInhibitManagerV1,
|
zwp_keyboard_shortcuts_inhibit_manager_v1::ZwpKeyboardShortcutsInhibitManagerV1,
|
||||||
zwp_keyboard_shortcuts_inhibitor_v1::ZwpKeyboardShortcutsInhibitorV1,
|
zwp_keyboard_shortcuts_inhibitor_v1::ZwpKeyboardShortcutsInhibitorV1,
|
||||||
@@ -25,7 +25,7 @@ use wayland_protocols::wp::{
|
|||||||
zwp_relative_pointer_manager_v1::ZwpRelativePointerManagerV1,
|
zwp_relative_pointer_manager_v1::ZwpRelativePointerManagerV1,
|
||||||
zwp_relative_pointer_v1::{self, ZwpRelativePointerV1},
|
zwp_relative_pointer_v1::{self, ZwpRelativePointerV1},
|
||||||
},
|
},
|
||||||
};
|
}, xdg::xdg_output::zv1::client::{zxdg_output_manager_v1::ZxdgOutputManagerV1, zxdg_output_v1::{self, ZxdgOutputV1}}};
|
||||||
|
|
||||||
use wayland_protocols_wlr::layer_shell::v1::client::{
|
use wayland_protocols_wlr::layer_shell::v1::client::{
|
||||||
zwlr_layer_shell_v1::{Layer, ZwlrLayerShellV1},
|
zwlr_layer_shell_v1::{Layer, ZwlrLayerShellV1},
|
||||||
@@ -38,7 +38,7 @@ use wayland_client::{
|
|||||||
globals::{registry_queue_init, GlobalListContents},
|
globals::{registry_queue_init, GlobalListContents},
|
||||||
protocol::{
|
protocol::{
|
||||||
wl_buffer, wl_compositor, wl_keyboard, wl_pointer, wl_region, wl_registry, wl_seat, wl_shm,
|
wl_buffer, wl_compositor, wl_keyboard, wl_pointer, wl_region, wl_registry, wl_seat, wl_shm,
|
||||||
wl_shm_pool, wl_surface,
|
wl_shm_pool, wl_surface, wl_output::{self, WlOutput},
|
||||||
},
|
},
|
||||||
Connection, Dispatch, DispatchError, QueueHandle, WEnum, EventQueue,
|
Connection, Dispatch, DispatchError, QueueHandle, WEnum, EventQueue,
|
||||||
};
|
};
|
||||||
@@ -55,6 +55,25 @@ struct Globals {
|
|||||||
seat: wl_seat::WlSeat,
|
seat: wl_seat::WlSeat,
|
||||||
shm: wl_shm::WlShm,
|
shm: wl_shm::WlShm,
|
||||||
layer_shell: ZwlrLayerShellV1,
|
layer_shell: ZwlrLayerShellV1,
|
||||||
|
outputs: Vec<wl_output::WlOutput>,
|
||||||
|
xdg_output_manager: ZxdgOutputManagerV1,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
struct OutputInfo {
|
||||||
|
name: String,
|
||||||
|
position: (i32, i32),
|
||||||
|
size: (i32, i32),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OutputInfo {
|
||||||
|
fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
name: "".to_string(),
|
||||||
|
position: (0,0),
|
||||||
|
size: (0,0),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct State {
|
struct State {
|
||||||
@@ -68,6 +87,7 @@ struct State {
|
|||||||
read_guard: Option<ReadEventsGuard>,
|
read_guard: Option<ReadEventsGuard>,
|
||||||
qh: QueueHandle<Self>,
|
qh: QueueHandle<Self>,
|
||||||
pending_events: Vec<(ClientHandle, Event)>,
|
pending_events: Vec<(ClientHandle, Event)>,
|
||||||
|
output_info: Vec<(WlOutput, OutputInfo)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct WaylandEventProducer {
|
pub struct WaylandEventProducer {
|
||||||
@@ -82,8 +102,13 @@ struct Window {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Window {
|
impl Window {
|
||||||
fn new(g: &Globals, qh: &QueueHandle<State>, pos: Position) -> Window {
|
fn new(state: &State, qh: &QueueHandle<State>, output: &WlOutput, pos: Position, size: (i32, i32)) -> Window {
|
||||||
let (width, height) = (1, 1440);
|
let g = &state.g;
|
||||||
|
|
||||||
|
let (width, height) = match pos {
|
||||||
|
Position::Left | Position::Right => (1, size.1 as u32),
|
||||||
|
Position::Top | Position::Bottom => (size.0 as u32, 1),
|
||||||
|
};
|
||||||
let mut file = tempfile::tempfile().unwrap();
|
let mut file = tempfile::tempfile().unwrap();
|
||||||
draw(&mut file, (width, height));
|
draw(&mut file, (width, height));
|
||||||
let pool = g
|
let pool = g
|
||||||
@@ -102,8 +127,8 @@ impl Window {
|
|||||||
|
|
||||||
let layer_surface = g.layer_shell.get_layer_surface(
|
let layer_surface = g.layer_shell.get_layer_surface(
|
||||||
&surface,
|
&surface,
|
||||||
None,
|
Some(&output),
|
||||||
Layer::Top,
|
Layer::Overlay,
|
||||||
"LAN Mouse Sharing".into(),
|
"LAN Mouse Sharing".into(),
|
||||||
qh,
|
qh,
|
||||||
(),
|
(),
|
||||||
@@ -116,9 +141,9 @@ impl Window {
|
|||||||
};
|
};
|
||||||
|
|
||||||
layer_surface.set_anchor(anchor);
|
layer_surface.set_anchor(anchor);
|
||||||
layer_surface.set_size(1, 1440);
|
layer_surface.set_size(width, height);
|
||||||
layer_surface.set_exclusive_zone(0);
|
layer_surface.set_exclusive_zone(-1);
|
||||||
layer_surface.set_margin(0, 0, 0, 0);
|
layer_surface.set_margin(0,0,0,0);
|
||||||
surface.set_input_region(None);
|
surface.set_input_region(None);
|
||||||
surface.commit();
|
surface.commit();
|
||||||
Window {
|
Window {
|
||||||
@@ -138,20 +163,56 @@ impl Drop for Window {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_edges(outputs: &[(WlOutput, OutputInfo)], pos: Position) -> Vec<(WlOutput, i32)> {
|
||||||
|
outputs.iter().map(|(o, i)| {
|
||||||
|
(o.clone(),
|
||||||
|
match pos {
|
||||||
|
Position::Left => i.position.0,
|
||||||
|
Position::Right => i.position.0 + i.size.0,
|
||||||
|
Position::Top => i.position.1,
|
||||||
|
Position::Bottom => i.position.1 + i.size.1,
|
||||||
|
})
|
||||||
|
}).collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_output_configuration(state: &State, pos: Position) -> Vec<(WlOutput, OutputInfo)> {
|
||||||
|
// get all output edges corresponding to the position
|
||||||
|
let edges = get_edges(&state.output_info, pos);
|
||||||
|
let opposite_edges = get_edges(&state.output_info, pos.opposite());
|
||||||
|
|
||||||
|
// remove those edges that are at the same position
|
||||||
|
// as an opposite edge of a different output
|
||||||
|
let outputs: Vec<WlOutput> = edges.iter().filter(|(_,edge)| {
|
||||||
|
opposite_edges.iter().map(|(_,e)| *e).find(|e| e == edge).is_none()
|
||||||
|
}).map(|(o,_)| o.clone()).collect();
|
||||||
|
state.output_info
|
||||||
|
.iter()
|
||||||
|
.filter(|(o,_)| outputs.contains(o))
|
||||||
|
.map(|(o, i)| (o.clone(), i.clone()))
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
fn draw(f: &mut File, (width, height): (u32, u32)) {
|
fn draw(f: &mut File, (width, height): (u32, u32)) {
|
||||||
let mut buf = BufWriter::new(f);
|
let mut buf = BufWriter::new(f);
|
||||||
for _ in 0..height {
|
for _ in 0..height {
|
||||||
for _ in 0..width {
|
for _ in 0..width {
|
||||||
buf.write_all(&0x44FbF1C7u32.to_ne_bytes()).unwrap();
|
buf.write_all(&0x00000000u32.to_ne_bytes()).unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WaylandEventProducer {
|
impl WaylandEventProducer {
|
||||||
pub fn new() -> Result<Self> {
|
pub fn new() -> Result<Self> {
|
||||||
let conn = Connection::connect_to_env().expect("could not connect to wayland compositor");
|
let conn = match Connection::connect_to_env() {
|
||||||
let (g, queue) =
|
Ok(c) => c,
|
||||||
registry_queue_init::<State>(&conn).expect("failed to initialize wl_registry");
|
Err(e) => return Err(anyhow!("could not connect to wayland compositor: {e:?}")),
|
||||||
|
};
|
||||||
|
|
||||||
|
let (g, mut queue) = match registry_queue_init::<State>(&conn) {
|
||||||
|
Ok(q) => q,
|
||||||
|
Err(e) => return Err(anyhow!("failed to initialize wl_registry: {e:?}")),
|
||||||
|
};
|
||||||
|
|
||||||
let qh = queue.handle();
|
let qh = queue.handle();
|
||||||
|
|
||||||
let compositor: wl_compositor::WlCompositor = match g.bind(&qh, 4..=5, ()) {
|
let compositor: wl_compositor::WlCompositor = match g.bind(&qh, 4..=5, ()) {
|
||||||
@@ -159,6 +220,11 @@ impl WaylandEventProducer {
|
|||||||
Err(_) => return Err(anyhow!("wl_compositor >= v4 not supported")),
|
Err(_) => return Err(anyhow!("wl_compositor >= v4 not supported")),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let xdg_output_manager: ZxdgOutputManagerV1 = match g.bind(&qh, 1..=3, ()) {
|
||||||
|
Ok(xdg_output_manager) => xdg_output_manager,
|
||||||
|
Err(_) => return Err(anyhow!("xdg_output not supported!")),
|
||||||
|
};
|
||||||
|
|
||||||
let shm: wl_shm::WlShm = match g.bind(&qh, 1..=1, ()) {
|
let shm: wl_shm::WlShm = match g.bind(&qh, 1..=1, ()) {
|
||||||
Ok(wl_shm) => wl_shm,
|
Ok(wl_shm) => wl_shm,
|
||||||
Err(_) => return Err(anyhow!("wl_shm v1 not supported")),
|
Err(_) => return Err(anyhow!("wl_shm v1 not supported")),
|
||||||
@@ -189,6 +255,8 @@ impl WaylandEventProducer {
|
|||||||
Err(_) => return Err(anyhow!("zwp_keyboard_shortcuts_inhibit_manager_v1 not supported")),
|
Err(_) => return Err(anyhow!("zwp_keyboard_shortcuts_inhibit_manager_v1 not supported")),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let outputs = vec![];
|
||||||
|
|
||||||
let g = Globals {
|
let g = Globals {
|
||||||
compositor,
|
compositor,
|
||||||
shm,
|
shm,
|
||||||
@@ -197,6 +265,8 @@ impl WaylandEventProducer {
|
|||||||
pointer_constraints,
|
pointer_constraints,
|
||||||
relative_pointer_manager,
|
relative_pointer_manager,
|
||||||
shortcut_inhibit_manager,
|
shortcut_inhibit_manager,
|
||||||
|
outputs,
|
||||||
|
xdg_output_manager,
|
||||||
};
|
};
|
||||||
|
|
||||||
// flush outgoing events
|
// flush outgoing events
|
||||||
@@ -205,23 +275,47 @@ impl WaylandEventProducer {
|
|||||||
// prepare reading wayland events
|
// prepare reading wayland events
|
||||||
let read_guard = queue.prepare_read()?;
|
let read_guard = queue.prepare_read()?;
|
||||||
let wayland_fd = read_guard.connection_fd().as_raw_fd();
|
let wayland_fd = read_guard.connection_fd().as_raw_fd();
|
||||||
let read_guard = Some(read_guard);
|
std::mem::drop(read_guard);
|
||||||
|
|
||||||
Ok(WaylandEventProducer {
|
let mut state = State {
|
||||||
queue,
|
g,
|
||||||
state: State {
|
pointer_lock: None,
|
||||||
g,
|
rel_pointer: None,
|
||||||
pointer_lock: None,
|
shortcut_inhibitor: None,
|
||||||
rel_pointer: None,
|
client_for_window: Vec::new(),
|
||||||
shortcut_inhibitor: None,
|
focused: None,
|
||||||
client_for_window: Vec::new(),
|
qh,
|
||||||
focused: None,
|
wayland_fd,
|
||||||
qh,
|
read_guard: None,
|
||||||
wayland_fd,
|
pending_events: vec![],
|
||||||
read_guard,
|
output_info: vec![],
|
||||||
pending_events: vec![],
|
};
|
||||||
}
|
|
||||||
})
|
// dispatch registry to () again, in order to read all wl_outputs
|
||||||
|
conn.display().get_registry(&state.qh, ());
|
||||||
|
log::debug!("==============> requested registry");
|
||||||
|
|
||||||
|
// roundtrip to read wl_output globals
|
||||||
|
queue.roundtrip(&mut state)?;
|
||||||
|
log::debug!("==============> roundtrip 1 done");
|
||||||
|
|
||||||
|
// read outputs
|
||||||
|
for output in state.g.outputs.iter() {
|
||||||
|
state.g.xdg_output_manager.get_xdg_output(output, &state.qh, output.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
// roundtrip to read xdg_output events
|
||||||
|
queue.roundtrip(&mut state)?;
|
||||||
|
|
||||||
|
log::debug!("==============> roundtrip 2 done");
|
||||||
|
for i in &state.output_info {
|
||||||
|
log::debug!("{:#?}", i.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
let read_guard = queue.prepare_read()?;
|
||||||
|
state.read_guard = Some(read_guard);
|
||||||
|
|
||||||
|
Ok(WaylandEventProducer { queue, state })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -309,9 +403,15 @@ impl State {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn add_client(&mut self, client: ClientHandle, pos: Position) {
|
fn add_client(&mut self, client: ClientHandle, pos: Position) {
|
||||||
let window = Rc::new(Window::new(&self.g, &self.qh, pos));
|
|
||||||
assert!(Rc::strong_count(&window) == 1);
|
let outputs = get_output_configuration(self, pos);
|
||||||
self.client_for_window.push((window, client));
|
|
||||||
|
outputs.iter().for_each(|(o, i)| {
|
||||||
|
let window = Window::new(&self, &self.qh, &o, pos, i.size);
|
||||||
|
let window = Rc::new(window);
|
||||||
|
self.client_for_window.push((window, client));
|
||||||
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -426,10 +526,14 @@ impl EventProducer for WaylandEventProducer {
|
|||||||
self.state.add_client(handle, pos);
|
self.state.add_client(handle, pos);
|
||||||
}
|
}
|
||||||
if let ClientEvent::Destroy(handle) = client_event {
|
if let ClientEvent::Destroy(handle) = client_event {
|
||||||
if let Some(i) = self.state.client_for_window.iter().position(|(_,c)| *c == handle) {
|
loop {
|
||||||
let w = self.state.client_for_window.remove(i);
|
// remove all windows corresponding to this client
|
||||||
self.state.focused = None;
|
if let Some(i) = self.state.client_for_window.iter().position(|(_,c)| *c == handle) {
|
||||||
assert!(Rc::strong_count(&w.0) == 1);
|
self.state.client_for_window.remove(i);
|
||||||
|
self.state.focused = None;
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.flush_events();
|
self.flush_events();
|
||||||
@@ -675,7 +779,6 @@ impl Dispatch<ZwlrLayerSurfaceV1, ()> for State {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// delegate wl_registry events to App itself
|
// delegate wl_registry events to App itself
|
||||||
// delegate_dispatch!(App: [wl_registry::WlRegistry: GlobalListContents] => App);
|
|
||||||
impl Dispatch<wl_registry::WlRegistry, GlobalListContents> for State {
|
impl Dispatch<wl_registry::WlRegistry, GlobalListContents> for State {
|
||||||
fn event(
|
fn event(
|
||||||
_state: &mut Self,
|
_state: &mut Self,
|
||||||
@@ -688,6 +791,70 @@ impl Dispatch<wl_registry::WlRegistry, GlobalListContents> for State {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Dispatch<wl_registry::WlRegistry, ()> for State {
|
||||||
|
fn event(
|
||||||
|
state: &mut Self,
|
||||||
|
registry: &wl_registry::WlRegistry,
|
||||||
|
event: <wl_registry::WlRegistry as wayland_client::Proxy>::Event,
|
||||||
|
_: &(),
|
||||||
|
_: &Connection,
|
||||||
|
qh: &QueueHandle<Self>,
|
||||||
|
) {
|
||||||
|
match event {
|
||||||
|
wl_registry::Event::Global { name, interface, version: _ } => {
|
||||||
|
match interface.as_str() {
|
||||||
|
"wl_output" => {
|
||||||
|
log::debug!("wl_output global");
|
||||||
|
state.g.outputs.push(registry.bind::<wl_output::WlOutput, _, _>(name, 4, qh, ()))
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
wl_registry::Event::GlobalRemove { .. } => {},
|
||||||
|
_ => {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Dispatch<ZxdgOutputV1, WlOutput> for State {
|
||||||
|
fn event(
|
||||||
|
state: &mut Self,
|
||||||
|
_: &ZxdgOutputV1,
|
||||||
|
event: <ZxdgOutputV1 as wayland_client::Proxy>::Event,
|
||||||
|
wl_output: &WlOutput,
|
||||||
|
_: &Connection,
|
||||||
|
_: &QueueHandle<Self>,
|
||||||
|
) {
|
||||||
|
log::debug!("xdg-output - {event:?}");
|
||||||
|
let output_info = match state
|
||||||
|
.output_info
|
||||||
|
.iter_mut()
|
||||||
|
.find(|(o, _)| o == wl_output) {
|
||||||
|
Some((_,c)) => c,
|
||||||
|
None => {
|
||||||
|
let output_info = OutputInfo::new();
|
||||||
|
state.output_info.push((wl_output.clone(), output_info));
|
||||||
|
&mut state.output_info.last_mut().unwrap().1
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
match event {
|
||||||
|
zxdg_output_v1::Event::LogicalPosition { x, y } => {
|
||||||
|
output_info.position = (x, y);
|
||||||
|
},
|
||||||
|
zxdg_output_v1::Event::LogicalSize { width, height } => {
|
||||||
|
output_info.size = (width, height);
|
||||||
|
},
|
||||||
|
zxdg_output_v1::Event::Done => {},
|
||||||
|
zxdg_output_v1::Event::Name { name } => {
|
||||||
|
output_info.name = name;
|
||||||
|
},
|
||||||
|
zxdg_output_v1::Event::Description { .. } => {},
|
||||||
|
_ => {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// don't emit any events
|
// don't emit any events
|
||||||
delegate_noop!(State: wl_region::WlRegion);
|
delegate_noop!(State: wl_region::WlRegion);
|
||||||
delegate_noop!(State: wl_shm_pool::WlShmPool);
|
delegate_noop!(State: wl_shm_pool::WlShmPool);
|
||||||
@@ -698,6 +865,8 @@ delegate_noop!(State: ZwpKeyboardShortcutsInhibitManagerV1);
|
|||||||
delegate_noop!(State: ZwpPointerConstraintsV1);
|
delegate_noop!(State: ZwpPointerConstraintsV1);
|
||||||
|
|
||||||
// ignore events
|
// ignore events
|
||||||
|
delegate_noop!(State: ignore wl_output::WlOutput);
|
||||||
|
delegate_noop!(State: ignore ZxdgOutputManagerV1);
|
||||||
delegate_noop!(State: ignore wl_shm::WlShm);
|
delegate_noop!(State: ignore wl_shm::WlShm);
|
||||||
delegate_noop!(State: ignore wl_buffer::WlBuffer);
|
delegate_noop!(State: ignore wl_buffer::WlBuffer);
|
||||||
delegate_noop!(State: ignore wl_surface::WlSurface);
|
delegate_noop!(State: ignore wl_surface::WlSurface);
|
||||||
|
|||||||
@@ -9,6 +9,18 @@ pub enum Position {
|
|||||||
Top,
|
Top,
|
||||||
Bottom,
|
Bottom,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Position {
|
||||||
|
pub fn opposite(&self) -> Self {
|
||||||
|
match self {
|
||||||
|
Position::Left => Self::Right,
|
||||||
|
Position::Right => Self::Left,
|
||||||
|
Position::Top => Self::Bottom,
|
||||||
|
Position::Bottom => Self::Top,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Display for Position {
|
impl Display for Position {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
write!(f, "{}", match self {
|
write!(f, "{}", match self {
|
||||||
|
|||||||
Reference in New Issue
Block a user