mirror of
https://github.com/feschber/lan-mouse.git
synced 2026-04-24 19:33:20 +03:00
Compare commits
1 Commits
dns-fix
...
remove-git
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a2fe5fa892 |
34
.github/workflows/pre-release.yml
vendored
34
.github/workflows/pre-release.yml
vendored
@@ -84,56 +84,36 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: install dependencies
|
||||
run: brew install gtk4 libadwaita imagemagick
|
||||
run: brew install gtk4 libadwaita
|
||||
- name: Release Build
|
||||
run: |
|
||||
cargo build --release
|
||||
cp target/release/lan-mouse lan-mouse-macos-intel
|
||||
- name: Make icns
|
||||
run: scripts/makeicns.sh
|
||||
- name: Install cargo bundle
|
||||
run: cargo install cargo-bundle
|
||||
- name: Bundle
|
||||
run: cargo bundle --release
|
||||
- name: Zip bundle
|
||||
run: |
|
||||
cd target/release/bundle/osx
|
||||
zip -r "lan-mouse-macos-intel.zip" "Lan Mouse.app"
|
||||
- name: Upload build artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: lan-mouse-macos-intel
|
||||
path: target/release/bundle/osx/lan-mouse-macos-intel.zip
|
||||
path: lan-mouse-macos-intel
|
||||
|
||||
macos-aarch64-release-build:
|
||||
runs-on: macos-14
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: install dependencies
|
||||
run: brew install gtk4 libadwaita imagemagick
|
||||
run: brew install gtk4 libadwaita
|
||||
- name: Release Build
|
||||
run: |
|
||||
cargo build --release
|
||||
cp target/release/lan-mouse lan-mouse-macos-aarch64
|
||||
- name: Make icns
|
||||
run: scripts/makeicns.sh
|
||||
- name: Install cargo bundle
|
||||
run: cargo install cargo-bundle
|
||||
- name: Bundle
|
||||
run: cargo bundle --release
|
||||
- name: Zip bundle
|
||||
run: |
|
||||
cd target/release/bundle/osx
|
||||
zip -r "lan-mouse-macos-aarch64.zip" "Lan Mouse.app"
|
||||
- name: Upload build artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: lan-mouse-macos-aarch64
|
||||
path: target/release/bundle/osx/lan-mouse-macos-aarch64.zip
|
||||
path: lan-mouse-macos-aarch64
|
||||
|
||||
pre-release:
|
||||
name: "Pre Release"
|
||||
needs: [windows-release-build, linux-release-build, macos-release-build, macos-aarch64-release-build]
|
||||
needs: [windows-release-build, linux-release-build, macos-release-build]
|
||||
runs-on: "ubuntu-latest"
|
||||
steps:
|
||||
- name: Download build artifacts
|
||||
@@ -147,6 +127,6 @@ jobs:
|
||||
title: "Development Build"
|
||||
files: |
|
||||
lan-mouse-linux/lan-mouse
|
||||
lan-mouse-macos-intel/lan-mouse-macos-intel.zip
|
||||
lan-mouse-macos-aarch64/lan-mouse-macos-aarch64.zip
|
||||
lan-mouse-macos-intel/lan-mouse-macos-intel
|
||||
lan-mouse-macos-aarch64/lan-mouse-macos-aarch64
|
||||
lan-mouse-windows/lan-mouse-windows.zip
|
||||
|
||||
32
.github/workflows/rust.yml
vendored
32
.github/workflows/rust.yml
vendored
@@ -98,7 +98,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: install dependencies
|
||||
run: brew install gtk4 libadwaita imagemagick
|
||||
run: brew install gtk4 libadwaita
|
||||
- name: Build
|
||||
run: cargo build --verbose
|
||||
- name: Run tests
|
||||
@@ -107,28 +107,18 @@ jobs:
|
||||
run: cargo fmt --check
|
||||
- name: Clippy
|
||||
run: cargo clippy --all-features --all-targets -- --deny warnings
|
||||
- name: Make icns
|
||||
run: scripts/makeicns.sh
|
||||
- name: Install cargo bundle
|
||||
run: cargo install cargo-bundle
|
||||
- name: Bundle
|
||||
run: cargo bundle
|
||||
- name: Zip bundle
|
||||
run: |
|
||||
cd target/debug/bundle/osx
|
||||
zip -r "Lan Mouse macOS (Intel).zip" "Lan Mouse.app"
|
||||
- name: Upload build artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: Lan Mouse macOS (Intel)
|
||||
path: target/debug/bundle/osx/Lan Mouse macOS (Intel).zip
|
||||
name: lan-mouse-macos
|
||||
path: target/debug/lan-mouse
|
||||
|
||||
build-macos-aarch64:
|
||||
runs-on: macos-14
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: install dependencies
|
||||
run: brew install gtk4 libadwaita imagemagick
|
||||
run: brew install gtk4 libadwaita
|
||||
- name: Build
|
||||
run: cargo build --verbose
|
||||
- name: Run tests
|
||||
@@ -137,18 +127,8 @@ jobs:
|
||||
run: cargo fmt --check
|
||||
- name: Clippy
|
||||
run: cargo clippy --all-features --all-targets -- --deny warnings
|
||||
- name: Make icns
|
||||
run: scripts/makeicns.sh
|
||||
- name: Install cargo bundle
|
||||
run: cargo install cargo-bundle
|
||||
- name: Bundle
|
||||
run: cargo bundle
|
||||
- name: Zip bundle
|
||||
run: |
|
||||
cd target/debug/bundle/osx
|
||||
zip -r "Lan Mouse macOS (ARM).zip" "Lan Mouse.app"
|
||||
- name: Upload build artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: Lan Mouse macOS (ARM)
|
||||
path: target/debug/bundle/osx/Lan Mouse macOS (ARM).zip
|
||||
name: lan-mouse-macos-aarch64
|
||||
path: target/debug/lan-mouse
|
||||
|
||||
38
.github/workflows/tagged-release.yml
vendored
38
.github/workflows/tagged-release.yml
vendored
@@ -80,56 +80,36 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: install dependencies
|
||||
run: brew install gtk4 libadwaita imagemagick
|
||||
run: brew install gtk4 libadwaita
|
||||
- name: Release Build
|
||||
run: |
|
||||
cargo build --release
|
||||
cp target/release/lan-mouse lan-mouse-macos-intel
|
||||
- name: Make icns
|
||||
run: scripts/makeicns.sh
|
||||
- name: Install cargo bundle
|
||||
run: cargo install cargo-bundle
|
||||
- name: Bundle
|
||||
run: cargo bundle --release
|
||||
- name: Zip bundle
|
||||
run: |
|
||||
cd target/release/bundle/osx
|
||||
zip -r "lan-mouse-macos-intel.zip" "Lan Mouse.app"
|
||||
- name: Upload build artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: lan-mouse-macos-intel.zip
|
||||
path: target/release/bundle/osx/lan-mouse-macos-intel.zip
|
||||
name: lan-mouse-macos-intel
|
||||
path: lan-mouse-macos-intel
|
||||
|
||||
macos-aarch64-release-build:
|
||||
runs-on: macos-14
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: install dependencies
|
||||
run: brew install gtk4 libadwaita imagemagick
|
||||
run: brew install gtk4 libadwaita
|
||||
- name: Release Build
|
||||
run: |
|
||||
cargo build --release
|
||||
cp target/release/lan-mouse lan-mouse-macos-aarch64
|
||||
- name: Make icns
|
||||
run: scripts/makeicns.sh
|
||||
- name: Install cargo bundle
|
||||
run: cargo install cargo-bundle
|
||||
- name: Bundle
|
||||
run: cargo bundle --release
|
||||
- name: Zip bundle
|
||||
run: |
|
||||
cd target/release/bundle/osx
|
||||
zip -r "lan-mouse-macos-aarch64.zip" "Lan Mouse.app"
|
||||
- name: Upload build artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: lan-mouse-macos-aarch64.zip
|
||||
path: target/release/bundle/osx/lan-mouse-macos-aarch64.zip
|
||||
name: lan-mouse-macos-aarch64
|
||||
path: lan-mouse-macos-aarch64
|
||||
|
||||
tagged-release:
|
||||
name: "Tagged Release"
|
||||
needs: [windows-release-build, linux-release-build, macos-release-build, macos-aarch64-release-build]
|
||||
needs: [windows-release-build, linux-release-build, macos-release-build]
|
||||
runs-on: "ubuntu-latest"
|
||||
steps:
|
||||
- name: Download build artifacts
|
||||
@@ -141,6 +121,6 @@ jobs:
|
||||
prerelease: false
|
||||
files: |
|
||||
lan-mouse-linux/lan-mouse
|
||||
lan-mouse-macos-intel/lan-mouse-macos-intel.zip
|
||||
lan-mouse-macos-aarch64/lan-mouse-macos-aarch64.zip
|
||||
lan-mouse-macos-intel/lan-mouse-macos-intel
|
||||
lan-mouse-macos-aarch64/lan-mouse-macos-aarch64
|
||||
lan-mouse-windows/lan-mouse-windows.zip
|
||||
|
||||
@@ -89,8 +89,3 @@ libei_emulation = ["input-event/libei", "input-emulation/libei"]
|
||||
wlroots_emulation = ["input-emulation/wlroots"]
|
||||
x11_emulation = ["input-emulation/x11"]
|
||||
rdp_emulation = ["input-emulation/remote_desktop_portal"]
|
||||
|
||||
[package.metadata.bundle]
|
||||
name = "Lan Mouse"
|
||||
icon = ["target/icon.icns"]
|
||||
identifier = "de.feschber.LanMouse"
|
||||
|
||||
12
flake.lock
generated
12
flake.lock
generated
@@ -2,11 +2,11 @@
|
||||
"nodes": {
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1740560979,
|
||||
"narHash": "sha256-Vr3Qi346M+8CjedtbyUevIGDZW8LcA1fTG0ugPY/Hic=",
|
||||
"lastModified": 1728018373,
|
||||
"narHash": "sha256-NOiTvBbRLIOe5F6RbHaAh6++BNjsb149fGZd1T4+KBg=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "5135c59491985879812717f4c9fea69604e7f26f",
|
||||
"rev": "bc947f541ae55e999ffdb4013441347d83b00feb",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -29,11 +29,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1740623427,
|
||||
"narHash": "sha256-3SdPQrZoa4odlScFDUHd4CUPQ/R1gtH4Mq9u8CBiK8M=",
|
||||
"lastModified": 1728181869,
|
||||
"narHash": "sha256-sQXHXsjIcGEoIHkB+RO6BZdrPfB+43V1TEpyoWRI3ww=",
|
||||
"owner": "oxalica",
|
||||
"repo": "rust-overlay",
|
||||
"rev": "d342e8b5fd88421ff982f383c853f0fc78a847ab",
|
||||
"rev": "cd46aa3906c14790ef5cbe278d9e54f2c38f95c0",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
use async_trait::async_trait;
|
||||
use futures_core::Stream;
|
||||
use std::{
|
||||
collections::{HashSet, VecDeque},
|
||||
collections::VecDeque,
|
||||
env,
|
||||
fmt::{self, Display},
|
||||
io::{self, ErrorKind},
|
||||
os::fd::{AsFd, RawFd},
|
||||
pin::Pin,
|
||||
@@ -47,15 +46,13 @@ use wayland_protocols_wlr::layer_shell::v1::client::{
|
||||
use wayland_client::{
|
||||
backend::{ReadEventsGuard, WaylandError},
|
||||
delegate_noop,
|
||||
globals::{registry_queue_init, Global, GlobalList, GlobalListContents},
|
||||
globals::{registry_queue_init, GlobalListContents},
|
||||
protocol::{
|
||||
wl_buffer, wl_compositor,
|
||||
wl_keyboard::{self, WlKeyboard},
|
||||
wl_output::{self, WlOutput},
|
||||
wl_pointer::{self, WlPointer},
|
||||
wl_region,
|
||||
wl_registry::{self, WlRegistry},
|
||||
wl_seat, wl_shm, wl_shm_pool,
|
||||
wl_region, wl_registry, wl_seat, wl_shm, wl_shm_pool,
|
||||
wl_surface::WlSurface,
|
||||
},
|
||||
Connection, Dispatch, DispatchError, EventQueue, QueueHandle, WEnum,
|
||||
@@ -78,42 +75,28 @@ struct Globals {
|
||||
seat: wl_seat::WlSeat,
|
||||
shm: wl_shm::WlShm,
|
||||
layer_shell: ZwlrLayerShellV1,
|
||||
outputs: Vec<WlOutput>,
|
||||
xdg_output_manager: ZxdgOutputManagerV1,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct Output {
|
||||
wl_output: WlOutput,
|
||||
global: Global,
|
||||
info: Option<OutputInfo>,
|
||||
pending_info: OutputInfo,
|
||||
has_xdg_info: bool,
|
||||
}
|
||||
|
||||
impl Display for Output {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
if let Some(info) = &self.info {
|
||||
write!(
|
||||
f,
|
||||
"{} {}x{} @pos {:?} ({})",
|
||||
info.name, info.size.0, info.size.1, info.position, info.description
|
||||
)
|
||||
} else {
|
||||
write!(f, "unknown output")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
#[derive(Debug, Clone)]
|
||||
struct OutputInfo {
|
||||
description: String,
|
||||
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 {
|
||||
active_positions: HashSet<Position>,
|
||||
pointer: Option<WlPointer>,
|
||||
keyboard: Option<WlKeyboard>,
|
||||
pointer_lock: Option<ZwpLockedPointerV1>,
|
||||
@@ -121,13 +104,12 @@ struct State {
|
||||
shortcut_inhibitor: Option<ZwpKeyboardShortcutsInhibitorV1>,
|
||||
active_windows: Vec<Arc<Window>>,
|
||||
focused: Option<Arc<Window>>,
|
||||
global_list: GlobalList,
|
||||
globals: Globals,
|
||||
g: Globals,
|
||||
wayland_fd: RawFd,
|
||||
read_guard: Option<ReadEventsGuard>,
|
||||
qh: QueueHandle<Self>,
|
||||
pending_events: VecDeque<(Position, CaptureEvent)>,
|
||||
outputs: Vec<Output>,
|
||||
output_info: Vec<(WlOutput, OutputInfo)>,
|
||||
scroll_discrete_pending: bool,
|
||||
}
|
||||
|
||||
@@ -160,7 +142,7 @@ impl Window {
|
||||
size: (i32, i32),
|
||||
) -> Window {
|
||||
log::debug!("creating window output: {output:?}, size: {size:?}");
|
||||
let g = &state.globals;
|
||||
let g = &state.g;
|
||||
|
||||
let (width, height) = match pos {
|
||||
Position::Left | Position::Right => (1, size.1 as u32),
|
||||
@@ -221,36 +203,41 @@ impl Drop for Window {
|
||||
}
|
||||
}
|
||||
|
||||
fn get_edges(outputs: &[Output], pos: Position) -> Vec<(Output, i32)> {
|
||||
fn get_edges(outputs: &[(WlOutput, OutputInfo)], pos: Position) -> Vec<(WlOutput, i32)> {
|
||||
outputs
|
||||
.iter()
|
||||
.filter_map(|output| {
|
||||
output.info.as_ref().map(|info| {
|
||||
(
|
||||
output.clone(),
|
||||
match pos {
|
||||
Position::Left => info.position.0,
|
||||
Position::Right => info.position.0 + info.size.0,
|
||||
Position::Top => info.position.1,
|
||||
Position::Bottom => info.position.1 + info.size.1,
|
||||
},
|
||||
)
|
||||
})
|
||||
.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<Output> {
|
||||
fn get_output_configuration(state: &State, pos: Position) -> Vec<(WlOutput, OutputInfo)> {
|
||||
// get all output edges corresponding to the position
|
||||
let edges = get_edges(&state.outputs, pos);
|
||||
let opposite_edges = get_edges(&state.outputs, pos.opposite());
|
||||
let edges = get_edges(&state.output_info, pos);
|
||||
log::debug!("edges: {edges:?}");
|
||||
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
|
||||
edges
|
||||
let outputs: Vec<WlOutput> = edges
|
||||
.iter()
|
||||
.filter(|(_, edge)| !opposite_edges.iter().map(|(_, e)| *e).any(|e| &e == edge))
|
||||
.map(|(o, _)| o.clone())
|
||||
.collect();
|
||||
state
|
||||
.output_info
|
||||
.iter()
|
||||
.filter(|(o, _)| outputs.contains(o))
|
||||
.map(|(o, i)| (o.clone(), i.clone()))
|
||||
.collect()
|
||||
}
|
||||
|
||||
@@ -272,36 +259,36 @@ fn draw(f: &mut File, (width, height): (u32, u32)) {
|
||||
impl LayerShellInputCapture {
|
||||
pub fn new() -> std::result::Result<Self, LayerShellCaptureCreationError> {
|
||||
let conn = Connection::connect_to_env()?;
|
||||
let (global_list, mut queue) = registry_queue_init::<State>(&conn)?;
|
||||
let (g, mut queue) = registry_queue_init::<State>(&conn)?;
|
||||
|
||||
let qh = queue.handle();
|
||||
|
||||
let compositor: wl_compositor::WlCompositor = global_list
|
||||
let compositor: wl_compositor::WlCompositor = g
|
||||
.bind(&qh, 4..=5, ())
|
||||
.map_err(|e| WaylandBindError::new(e, "wl_compositor 4..=5"))?;
|
||||
let xdg_output_manager: ZxdgOutputManagerV1 = global_list
|
||||
let xdg_output_manager: ZxdgOutputManagerV1 = g
|
||||
.bind(&qh, 1..=3, ())
|
||||
.map_err(|e| WaylandBindError::new(e, "xdg_output_manager 1..=3"))?;
|
||||
let shm: wl_shm::WlShm = global_list
|
||||
let shm: wl_shm::WlShm = g
|
||||
.bind(&qh, 1..=1, ())
|
||||
.map_err(|e| WaylandBindError::new(e, "wl_shm"))?;
|
||||
let layer_shell: ZwlrLayerShellV1 = global_list
|
||||
let layer_shell: ZwlrLayerShellV1 = g
|
||||
.bind(&qh, 3..=4, ())
|
||||
.map_err(|e| WaylandBindError::new(e, "wlr_layer_shell 3..=4"))?;
|
||||
let seat: wl_seat::WlSeat = global_list
|
||||
let seat: wl_seat::WlSeat = g
|
||||
.bind(&qh, 7..=8, ())
|
||||
.map_err(|e| WaylandBindError::new(e, "wl_seat 7..=8"))?;
|
||||
|
||||
let pointer_constraints: ZwpPointerConstraintsV1 = global_list
|
||||
let pointer_constraints: ZwpPointerConstraintsV1 = g
|
||||
.bind(&qh, 1..=1, ())
|
||||
.map_err(|e| WaylandBindError::new(e, "zwp_pointer_constraints_v1"))?;
|
||||
let relative_pointer_manager: ZwpRelativePointerManagerV1 = global_list
|
||||
let relative_pointer_manager: ZwpRelativePointerManagerV1 = g
|
||||
.bind(&qh, 1..=1, ())
|
||||
.map_err(|e| WaylandBindError::new(e, "zwp_relative_pointer_manager_v1"))?;
|
||||
let shortcut_inhibit_manager: Result<
|
||||
ZwpKeyboardShortcutsInhibitManagerV1,
|
||||
WaylandBindError,
|
||||
> = global_list
|
||||
> = g
|
||||
.bind(&qh, 1..=1, ())
|
||||
.map_err(|e| WaylandBindError::new(e, "zwp_keyboard_shortcuts_inhibit_manager_v1"));
|
||||
// layer-shell backend still works without this protocol so we make it an optional dependency
|
||||
@@ -310,41 +297,65 @@ impl LayerShellInputCapture {
|
||||
to the client");
|
||||
}
|
||||
let shortcut_inhibit_manager = shortcut_inhibit_manager.ok();
|
||||
let outputs = vec![];
|
||||
|
||||
let g = Globals {
|
||||
compositor,
|
||||
shm,
|
||||
layer_shell,
|
||||
seat,
|
||||
pointer_constraints,
|
||||
relative_pointer_manager,
|
||||
shortcut_inhibit_manager,
|
||||
outputs,
|
||||
xdg_output_manager,
|
||||
};
|
||||
|
||||
// flush outgoing events
|
||||
queue.flush()?;
|
||||
|
||||
let wayland_fd = queue.as_fd().as_raw_fd();
|
||||
|
||||
let mut state = State {
|
||||
active_positions: Default::default(),
|
||||
pointer: None,
|
||||
keyboard: None,
|
||||
global_list,
|
||||
globals: Globals {
|
||||
compositor,
|
||||
shm,
|
||||
layer_shell,
|
||||
seat,
|
||||
pointer_constraints,
|
||||
relative_pointer_manager,
|
||||
shortcut_inhibit_manager,
|
||||
xdg_output_manager,
|
||||
},
|
||||
g,
|
||||
pointer_lock: None,
|
||||
rel_pointer: None,
|
||||
shortcut_inhibitor: None,
|
||||
active_windows: Vec::new(),
|
||||
focused: None,
|
||||
qh,
|
||||
wayland_fd: queue.as_fd().as_raw_fd(),
|
||||
wayland_fd,
|
||||
read_guard: None,
|
||||
pending_events: VecDeque::new(),
|
||||
outputs: vec![],
|
||||
output_info: vec![],
|
||||
scroll_discrete_pending: false,
|
||||
};
|
||||
|
||||
for global in state.global_list.contents().clone_list() {
|
||||
state.register_global(global);
|
||||
// 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());
|
||||
}
|
||||
|
||||
// flush outgoing events
|
||||
queue.flush()?;
|
||||
// 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 = loop {
|
||||
match queue.prepare_read() {
|
||||
@@ -368,7 +379,6 @@ impl LayerShellInputCapture {
|
||||
|
||||
fn delete_client(&mut self, pos: Position) {
|
||||
let inner = self.0.get_mut();
|
||||
inner.state.active_positions.remove(&pos);
|
||||
// remove all windows corresponding to this client
|
||||
while let Some(i) = inner.state.active_windows.iter().position(|w| w.pos == pos) {
|
||||
inner.state.active_windows.remove(i);
|
||||
@@ -378,52 +388,6 @@ impl LayerShellInputCapture {
|
||||
}
|
||||
|
||||
impl State {
|
||||
fn update_output_info(&mut self, name: u32) {
|
||||
let output = self
|
||||
.outputs
|
||||
.iter_mut()
|
||||
.find(|o| o.global.name == name)
|
||||
.expect("output not found");
|
||||
if output.has_xdg_info {
|
||||
output.info.replace(output.pending_info.clone());
|
||||
self.update_windows();
|
||||
}
|
||||
}
|
||||
|
||||
fn register_global(&mut self, global: Global) {
|
||||
if global.interface.as_str() == "wl_output" {
|
||||
log::debug!("new output global: wl_output {}", global.name);
|
||||
let wl_output = self.global_list.registry().bind::<WlOutput, _, _>(
|
||||
global.name,
|
||||
4,
|
||||
&self.qh,
|
||||
global.name,
|
||||
);
|
||||
self.globals
|
||||
.xdg_output_manager
|
||||
.get_xdg_output(&wl_output, &self.qh, global.name);
|
||||
self.outputs.push(Output {
|
||||
wl_output,
|
||||
global,
|
||||
info: None,
|
||||
has_xdg_info: false,
|
||||
pending_info: Default::default(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn deregister_global(&mut self, name: u32) {
|
||||
self.outputs.retain(|o| {
|
||||
if o.global.name == name {
|
||||
log::debug!("{o} (global {:?}) removed", o.global);
|
||||
o.wl_output.release();
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn grab(
|
||||
&mut self,
|
||||
surface: &WlSurface,
|
||||
@@ -444,7 +408,7 @@ impl State {
|
||||
|
||||
// lock pointer
|
||||
if self.pointer_lock.is_none() {
|
||||
self.pointer_lock = Some(self.globals.pointer_constraints.lock_pointer(
|
||||
self.pointer_lock = Some(self.g.pointer_constraints.lock_pointer(
|
||||
surface,
|
||||
pointer,
|
||||
None,
|
||||
@@ -456,7 +420,7 @@ impl State {
|
||||
|
||||
// request relative input
|
||||
if self.rel_pointer.is_none() {
|
||||
self.rel_pointer = Some(self.globals.relative_pointer_manager.get_relative_pointer(
|
||||
self.rel_pointer = Some(self.g.relative_pointer_manager.get_relative_pointer(
|
||||
pointer,
|
||||
qh,
|
||||
(),
|
||||
@@ -464,14 +428,10 @@ impl State {
|
||||
}
|
||||
|
||||
// capture modifier keys
|
||||
if let Some(shortcut_inhibit_manager) = &self.globals.shortcut_inhibit_manager {
|
||||
if let Some(shortcut_inhibit_manager) = &self.g.shortcut_inhibit_manager {
|
||||
if self.shortcut_inhibitor.is_none() {
|
||||
self.shortcut_inhibitor = Some(shortcut_inhibit_manager.inhibit_shortcuts(
|
||||
surface,
|
||||
&self.globals.seat,
|
||||
qh,
|
||||
(),
|
||||
));
|
||||
self.shortcut_inhibitor =
|
||||
Some(shortcut_inhibit_manager.inhibit_shortcuts(surface, &self.g.seat, qh, ()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -509,39 +469,21 @@ impl State {
|
||||
}
|
||||
|
||||
fn add_client(&mut self, pos: Position) {
|
||||
self.active_positions.insert(pos);
|
||||
let outputs = get_output_configuration(self, pos);
|
||||
|
||||
log::info!(
|
||||
"adding capture for position {pos} - using outputs: {:?}",
|
||||
outputs
|
||||
.iter()
|
||||
.map(|o| o
|
||||
.info
|
||||
.as_ref()
|
||||
.map(|i| i.name.to_owned())
|
||||
.unwrap_or("unknown output".to_owned()))
|
||||
.collect::<Vec<_>>()
|
||||
);
|
||||
outputs.iter().for_each(|o| {
|
||||
if let Some(info) = o.info.as_ref() {
|
||||
let window = Window::new(self, &self.qh, &o.wl_output, pos, info.size);
|
||||
let window = Arc::new(window);
|
||||
self.active_windows.push(window);
|
||||
}
|
||||
log::debug!("outputs: {outputs:?}");
|
||||
outputs.iter().for_each(|(o, i)| {
|
||||
let window = Window::new(self, &self.qh, o, pos, i.size);
|
||||
let window = Arc::new(window);
|
||||
self.active_windows.push(window);
|
||||
});
|
||||
}
|
||||
|
||||
fn update_windows(&mut self) {
|
||||
log::info!("active outputs: ");
|
||||
for output in self.outputs.iter().filter(|o| o.info.is_some()) {
|
||||
log::info!(" * {}", output);
|
||||
}
|
||||
|
||||
self.active_windows.clear();
|
||||
|
||||
let active_positions = self.active_positions.iter().cloned().collect::<Vec<_>>();
|
||||
for pos in active_positions {
|
||||
log::debug!("updating windows");
|
||||
log::debug!("output info: {:?}", self.output_info);
|
||||
let clients: Vec<_> = self.active_windows.drain(..).map(|w| w.pos).collect();
|
||||
for pos in clients {
|
||||
self.add_client(pos);
|
||||
}
|
||||
}
|
||||
@@ -893,7 +835,7 @@ impl Dispatch<ZwpRelativePointerV1, ()> for State {
|
||||
} = event
|
||||
{
|
||||
if let Some(window) = &app.focused {
|
||||
let time = ((((utime_hi as u64) << 32) | utime_lo as u64) / 1000) as u32;
|
||||
let time = (((utime_hi as u64) << 32 | utime_lo as u64) / 1000) as u32;
|
||||
app.pending_events.push_back((
|
||||
window.pos,
|
||||
CaptureEvent::Input(Event::Pointer(PointerEvent::Motion { time, dx, dy })),
|
||||
@@ -930,89 +872,94 @@ impl Dispatch<ZwlrLayerSurfaceV1, ()> for State {
|
||||
}
|
||||
|
||||
// delegate wl_registry events to App itself
|
||||
impl Dispatch<WlRegistry, GlobalListContents> for State {
|
||||
impl Dispatch<wl_registry::WlRegistry, GlobalListContents> for State {
|
||||
fn event(
|
||||
state: &mut Self,
|
||||
_registry: &WlRegistry,
|
||||
event: <WlRegistry as wayland_client::Proxy>::Event,
|
||||
_state: &mut Self,
|
||||
_proxy: &wl_registry::WlRegistry,
|
||||
_event: <wl_registry::WlRegistry as wayland_client::Proxy>::Event,
|
||||
_data: &GlobalListContents,
|
||||
_conn: &Connection,
|
||||
_qh: &QueueHandle<Self>,
|
||||
_qhandle: &QueueHandle<Self>,
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
version: _,
|
||||
} => {
|
||||
state.register_global(Global {
|
||||
name,
|
||||
interface,
|
||||
version,
|
||||
});
|
||||
}
|
||||
wl_registry::Event::GlobalRemove { name } => {
|
||||
state.deregister_global(name);
|
||||
if interface.as_str() == "wl_output" {
|
||||
log::debug!("wl_output global");
|
||||
state
|
||||
.g
|
||||
.outputs
|
||||
.push(registry.bind::<WlOutput, _, _>(name, 4, qh, ()))
|
||||
}
|
||||
}
|
||||
wl_registry::Event::GlobalRemove { .. } => {}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Dispatch<ZxdgOutputV1, u32> for State {
|
||||
impl Dispatch<ZxdgOutputV1, WlOutput> for State {
|
||||
fn event(
|
||||
state: &mut Self,
|
||||
_: &ZxdgOutputV1,
|
||||
event: <ZxdgOutputV1 as wayland_client::Proxy>::Event,
|
||||
name: &u32,
|
||||
wl_output: &WlOutput,
|
||||
_: &Connection,
|
||||
_: &QueueHandle<Self>,
|
||||
) {
|
||||
let output = state
|
||||
.outputs
|
||||
.iter_mut()
|
||||
.find(|o| o.global.name == *name)
|
||||
.expect("output");
|
||||
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
|
||||
}
|
||||
};
|
||||
|
||||
log::debug!("xdg_output {name} - {:?}", event);
|
||||
match event {
|
||||
zxdg_output_v1::Event::LogicalPosition { x, y } => {
|
||||
output.pending_info.position = (x, y);
|
||||
output.has_xdg_info = true;
|
||||
output_info.position = (x, y);
|
||||
}
|
||||
zxdg_output_v1::Event::LogicalSize { width, height } => {
|
||||
output.pending_info.size = (width, height);
|
||||
output.has_xdg_info = true;
|
||||
}
|
||||
zxdg_output_v1::Event::Done => {
|
||||
log::warn!("Use of deprecated xdg-output event \"done\"");
|
||||
state.update_output_info(*name);
|
||||
output_info.size = (width, height);
|
||||
}
|
||||
zxdg_output_v1::Event::Done => {}
|
||||
zxdg_output_v1::Event::Name { name } => {
|
||||
output.pending_info.name = name;
|
||||
output.has_xdg_info = true;
|
||||
output_info.name = name;
|
||||
}
|
||||
zxdg_output_v1::Event::Description { description } => {
|
||||
output.pending_info.description = description;
|
||||
output.has_xdg_info = true;
|
||||
}
|
||||
_ => todo!(),
|
||||
zxdg_output_v1::Event::Description { .. } => {}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Dispatch<WlOutput, u32> for State {
|
||||
impl Dispatch<WlOutput, ()> for State {
|
||||
fn event(
|
||||
state: &mut Self,
|
||||
_wl_output: &WlOutput,
|
||||
_proxy: &WlOutput,
|
||||
event: <WlOutput as wayland_client::Proxy>::Event,
|
||||
name: &u32,
|
||||
_data: &(),
|
||||
_conn: &Connection,
|
||||
_qhandle: &QueueHandle<Self>,
|
||||
) {
|
||||
log::debug!("wl_output {name} - {:?}", event);
|
||||
if let wl_output::Event::Done = event {
|
||||
state.update_output_info(*name);
|
||||
state.update_windows();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,11 +49,7 @@ fn gtk_main() -> glib::ExitCode {
|
||||
.application_id("de.feschber.LanMouse")
|
||||
.build();
|
||||
|
||||
app.connect_startup(|app| {
|
||||
load_icons();
|
||||
setup_actions(app);
|
||||
setup_menu(app);
|
||||
});
|
||||
app.connect_startup(|_| load_icons());
|
||||
app.connect_activate(build_ui);
|
||||
|
||||
let args: Vec<&'static str> = vec![];
|
||||
@@ -66,33 +62,6 @@ fn load_icons() {
|
||||
icon_theme.add_resource_path("/de/feschber/LanMouse/icons");
|
||||
}
|
||||
|
||||
// Add application actions
|
||||
fn setup_actions(app: &adw::Application) {
|
||||
// Quit action
|
||||
// This is important on macOS, where users expect a File->Quit action with a Cmd+Q shortcut.
|
||||
let quit_action = gio::SimpleAction::new("quit", None);
|
||||
quit_action.connect_activate({
|
||||
let app = app.clone();
|
||||
move |_, _| {
|
||||
app.quit();
|
||||
}
|
||||
});
|
||||
app.add_action(&quit_action);
|
||||
}
|
||||
|
||||
// Set up a global menu
|
||||
//
|
||||
// Currently this is used only on macOS
|
||||
fn setup_menu(app: &adw::Application) {
|
||||
let menu = gio::Menu::new();
|
||||
|
||||
let file_menu = gio::Menu::new();
|
||||
file_menu.append(Some("Quit"), Some("app.quit"));
|
||||
menu.append_submenu(Some("_File"), &file_menu);
|
||||
|
||||
app.set_menubar(Some(&menu))
|
||||
}
|
||||
|
||||
fn build_ui(app: &Application) {
|
||||
log::debug!("connecting to lan-mouse-socket");
|
||||
let (mut frontend_rx, frontend_tx) = match lan_mouse_ipc::connect() {
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
usage() {
|
||||
cat <<EOF
|
||||
$0: Make a macOS icns file from an SVG with ImageMagick and iconutil.
|
||||
usage: $0 [SVG [ICNS [ICONSET]]
|
||||
|
||||
ARGUMENTS
|
||||
SVG The SVG file to convert
|
||||
Defaults to ./lan-mouse-gtk/resources/de.feschber.LanMouse.svg
|
||||
ICNS The icns file to create
|
||||
Defaults to ./target/icon.icns
|
||||
ICONSET The iconset directory to create
|
||||
Defaults to ./target/icon.iconset
|
||||
This is just a temporary directory
|
||||
EOF
|
||||
}
|
||||
|
||||
if [ "$1" = "-h" ] || [ "$1" = "--help" ]; then
|
||||
usage
|
||||
exit 0
|
||||
fi
|
||||
|
||||
svg="${1:-./lan-mouse-gtk/resources/de.feschber.LanMouse.svg}"
|
||||
icns="${2:-./target/icon.icns}"
|
||||
iconset="${3:-./target/icon.iconset}"
|
||||
|
||||
set -u
|
||||
|
||||
mkdir -p "$iconset"
|
||||
magick convert -background none -resize 1024x1024 "$svg" "$iconset"/icon_512x512@2x.png
|
||||
magick convert -background none -resize 512x512 "$svg" "$iconset"/icon_512x512.png
|
||||
magick convert -background none -resize 256x256 "$svg" "$iconset"/icon_256x256.png
|
||||
magick convert -background none -resize 128x128 "$svg" "$iconset"/icon_128x128.png
|
||||
magick convert -background none -resize 64x64 "$svg" "$iconset"/icon_32x32@2x.png
|
||||
magick convert -background none -resize 32x32 "$svg" "$iconset"/icon_32x32.png
|
||||
magick convert -background none -resize 16x16 "$svg" "$iconset"/icon_16x16.png
|
||||
cp "$iconset"/icon_512x512.png "$iconset"/icon_256x256@2x.png
|
||||
cp "$iconset"/icon_256x256.png "$iconset"/icon_128x128@2x.png
|
||||
cp "$iconset"/icon_32x32.png "$iconset"/icon_16x16@2x.png
|
||||
iconutil -c icns "$iconset" -o "$icns"
|
||||
15
src/dns.rs
15
src/dns.rs
@@ -1,4 +1,4 @@
|
||||
use std::{collections::HashMap, net::IpAddr};
|
||||
use std::net::IpAddr;
|
||||
|
||||
use local_channel::mpsc::{channel, Receiver, Sender};
|
||||
use tokio::task::{spawn_local, JoinHandle};
|
||||
@@ -30,7 +30,6 @@ struct DnsTask {
|
||||
request_rx: Receiver<DnsRequest>,
|
||||
event_tx: Sender<DnsEvent>,
|
||||
cancellation_token: CancellationToken,
|
||||
active_tasks: HashMap<ClientHandle, JoinHandle<()>>,
|
||||
}
|
||||
|
||||
impl DnsResolver {
|
||||
@@ -40,7 +39,6 @@ impl DnsResolver {
|
||||
let (event_tx, event_rx) = channel();
|
||||
let cancellation_token = CancellationToken::new();
|
||||
let dns_task = DnsTask {
|
||||
active_tasks: Default::default(),
|
||||
resolver,
|
||||
request_rx,
|
||||
event_tx,
|
||||
@@ -83,14 +81,6 @@ impl DnsTask {
|
||||
while let Some(dns_request) = self.request_rx.recv().await {
|
||||
let DnsRequest { handle, hostname } = dns_request;
|
||||
|
||||
/* abort previous dns task */
|
||||
let previous_task = self.active_tasks.remove(&handle);
|
||||
if let Some(task) = previous_task {
|
||||
if !task.is_finished() {
|
||||
task.abort();
|
||||
}
|
||||
}
|
||||
|
||||
self.event_tx
|
||||
.send(DnsEvent::Resolving(handle))
|
||||
.expect("channel closed");
|
||||
@@ -100,7 +90,7 @@ impl DnsTask {
|
||||
let resolver = self.resolver.clone();
|
||||
let cancellation_token = self.cancellation_token.clone();
|
||||
|
||||
let task = tokio::task::spawn_local(async move {
|
||||
tokio::task::spawn_local(async move {
|
||||
tokio::select! {
|
||||
ips = resolver.lookup_ip(&hostname) => {
|
||||
let ips = ips.map(|ips| ips.iter().collect::<Vec<_>>());
|
||||
@@ -111,7 +101,6 @@ impl DnsTask {
|
||||
_ = cancellation_token.cancelled() => {},
|
||||
}
|
||||
});
|
||||
self.active_tasks.insert(handle, task);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user