Introduce wayland feature

This commit is contained in:
Federico Terzi 2021-02-14 22:01:42 +01:00
parent a9d24d400d
commit 3737eed034
17 changed files with 292 additions and 93 deletions

View File

@ -5,6 +5,11 @@ authors = ["Federico Terzi <federico-terzi@users.noreply.github.com>"]
edition = "2018" edition = "2018"
build="build.rs" build="build.rs"
[features]
# If the wayland feature is enabled, all X11 dependencies will be dropped
# and only EVDEV-based methods will be supported.
wayland = []
[dependencies] [dependencies]
log = "0.4.14" log = "0.4.14"
lazycell = "1.3.0" lazycell = "1.3.0"

View File

@ -39,21 +39,27 @@ fn cc_config() {
println!("cargo:rerun-if-changed=src/x11/native.h"); println!("cargo:rerun-if-changed=src/x11/native.h");
println!("cargo:rerun-if-changed=src/evdev/native.cpp"); println!("cargo:rerun-if-changed=src/evdev/native.cpp");
println!("cargo:rerun-if-changed=src/evdev/native.h"); println!("cargo:rerun-if-changed=src/evdev/native.h");
cc::Build::new()
.cpp(true) if cfg!(not(feature = "wayland")) {
.include("src/x11/native.h") cc::Build::new()
.file("src/x11/native.cpp") .cpp(true)
.compile("espansodetect"); .include("src/x11/native.h")
.file("src/x11/native.cpp")
.compile("espansodetect");
println!("cargo:rustc-link-lib=static=espansodetect");
println!("cargo:rustc-link-lib=dylib=X11");
println!("cargo:rustc-link-lib=dylib=Xtst");
}
cc::Build::new() cc::Build::new()
.cpp(true) .cpp(true)
.include("src/evdev/native.h") .include("src/evdev/native.h")
.file("src/evdev/native.cpp") .file("src/evdev/native.cpp")
.compile("espansodetectevdev"); .compile("espansodetectevdev");
println!("cargo:rustc-link-search=native=/usr/lib/x86_64-linux-gnu/"); println!("cargo:rustc-link-search=native=/usr/lib/x86_64-linux-gnu/");
println!("cargo:rustc-link-lib=static=espansodetect");
println!("cargo:rustc-link-lib=static=espansodetectevdev"); println!("cargo:rustc-link-lib=static=espansodetectevdev");
println!("cargo:rustc-link-lib=dylib=X11");
println!("cargo:rustc-link-lib=dylib=Xtst");
println!("cargo:rustc-link-lib=dylib=xkbcommon"); println!("cargo:rustc-link-lib=dylib=xkbcommon");
} }

View File

@ -1,26 +1,32 @@
// This code is a port of the libxkbcommon "interactive-evdev.c" example // This code is a port of the libxkbcommon "interactive-evdev.c" example
// https://github.com/xkbcommon/libxkbcommon/blob/master/tools/interactive-evdev.c // https://github.com/xkbcommon/libxkbcommon/blob/master/tools/interactive-evdev.c
use std::ffi::CString;
use scopeguard::ScopeGuard; use scopeguard::ScopeGuard;
use anyhow::Result; use anyhow::Result;
use thiserror::Error; use thiserror::Error;
use super::{ use crate::KeyboardConfig;
context::Context,
ffi::{xkb_keymap, xkb_keymap_new_from_names, xkb_keymap_unref, XKB_KEYMAP_COMPILE_NO_FLAGS}, use super::{context::Context, ffi::{XKB_KEYMAP_COMPILE_NO_FLAGS, xkb_keymap, xkb_keymap_new_from_names, xkb_keymap_unref, xkb_rule_names}};
};
pub struct Keymap { pub struct Keymap {
keymap: *mut xkb_keymap, keymap: *mut xkb_keymap,
} }
impl Keymap { impl Keymap {
pub fn new(context: &Context) -> Result<Keymap> { pub fn new(context: &Context, rmlvo: Option<KeyboardConfig>) -> Result<Keymap> {
let names = rmlvo.map(|rmlvo| {
Self::generate_names(rmlvo)
});
let names_ptr = names.map_or(std::ptr::null(), |names| &names);
let raw_keymap = unsafe { let raw_keymap = unsafe {
xkb_keymap_new_from_names( xkb_keymap_new_from_names(
context.get_handle(), context.get_handle(),
std::ptr::null(), names_ptr,
XKB_KEYMAP_COMPILE_NO_FLAGS, XKB_KEYMAP_COMPILE_NO_FLAGS,
) )
}; };
@ -40,6 +46,22 @@ impl Keymap {
pub fn get_handle(&self) -> *mut xkb_keymap { pub fn get_handle(&self) -> *mut xkb_keymap {
self.keymap self.keymap
} }
fn generate_names(rmlvo: KeyboardConfig) -> xkb_rule_names {
let rules = rmlvo.rules.map(|s| { CString::new(s).expect("unable to create CString for keymap") });
let model = rmlvo.model.map(|s| { CString::new(s).expect("unable to create CString for keymap") });
let layout = rmlvo.layout.map(|s| { CString::new(s).expect("unable to create CString for keymap") });
let variant = rmlvo.variant.map(|s| { CString::new(s).expect("unable to create CString for keymap") });
let options = rmlvo.options.map(|s| { CString::new(s).expect("unable to create CString for keymap") });
xkb_rule_names {
rules: rules.map_or(std::ptr::null(), |s| s.as_ptr()),
model: model.map_or(std::ptr::null(), |s| s.as_ptr()),
layout: layout.map_or(std::ptr::null(), |s| s.as_ptr()),
variant: variant.map_or(std::ptr::null(), |s| s.as_ptr()),
options: options.map_or(std::ptr::null(), |s| s.as_ptr()),
}
}
} }
impl Drop for Keymap { impl Drop for Keymap {

View File

@ -36,7 +36,7 @@ use libc::{
use log::{error, trace}; use log::{error, trace};
use thiserror::Error; use thiserror::Error;
use crate::event::Status::*; use crate::{KeyboardConfig, Source, SourceCallback, SourceCreationOptions, event::Status::*};
use crate::event::Variant::*; use crate::event::Variant::*;
use crate::event::{InputEvent, Key, KeyboardEvent, Variant}; use crate::event::{InputEvent, Key, KeyboardEvent, Variant};
use crate::event::{Key::*, MouseButton, MouseEvent}; use crate::event::{Key::*, MouseButton, MouseEvent};
@ -49,27 +49,30 @@ const BTN_MIDDLE: u16 = 0x112;
const BTN_SIDE: u16 = 0x113; const BTN_SIDE: u16 = 0x113;
const BTN_EXTRA: u16 = 0x114; const BTN_EXTRA: u16 = 0x114;
pub type EVDEVSourceCallback = Box<dyn Fn(InputEvent)>;
pub struct EVDEVSource { pub struct EVDEVSource {
devices: Vec<Device>, devices: Vec<Device>,
_keyboard_rmlvo: Option<KeyboardConfig>,
_context: LazyCell<Context>, _context: LazyCell<Context>,
_keymap: LazyCell<Keymap>, _keymap: LazyCell<Keymap>,
} }
#[allow(clippy::new_without_default)] #[allow(clippy::new_without_default)]
impl EVDEVSource { impl EVDEVSource {
pub fn new() -> EVDEVSource { pub fn new(options: SourceCreationOptions) -> EVDEVSource {
Self { Self {
devices: Vec::new(), devices: Vec::new(),
_context: LazyCell::new(), _context: LazyCell::new(),
_keymap: LazyCell::new(), _keymap: LazyCell::new(),
_keyboard_rmlvo: options.evdev_keyboard_rmlvo,
} }
} }
}
pub fn initialize(&mut self) -> Result<()> { impl Source for EVDEVSource {
fn initialize(&mut self) -> Result<()> {
let context = Context::new().expect("unable to obtain xkb context"); let context = Context::new().expect("unable to obtain xkb context");
let keymap = Keymap::new(&context).expect("unable to create xkb keymap"); let keymap = Keymap::new(&context, self._keyboard_rmlvo.clone()).expect("unable to create xkb keymap");
match get_devices(&keymap) { match get_devices(&keymap) {
Ok(devices) => self.devices = devices, Ok(devices) => self.devices = devices,
@ -97,7 +100,7 @@ impl EVDEVSource {
Ok(()) Ok(())
} }
pub fn eventloop(&self, event_callback: EVDEVSourceCallback) { fn eventloop(&self, event_callback: SourceCallback) {
if self.devices.is_empty() { if self.devices.is_empty() {
panic!("can't start eventloop without evdev devices"); panic!("can't start eventloop without evdev devices");
} }

View File

@ -17,12 +17,16 @@
* along with espanso. If not, see <https://www.gnu.org/licenses/>. * along with espanso. If not, see <https://www.gnu.org/licenses/>.
*/ */
use anyhow::Result;
use log::info;
pub mod event; pub mod event;
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
pub mod win32; pub mod win32;
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
#[cfg(not(feature = "wayland"))]
pub mod x11; pub mod x11;
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
@ -34,3 +38,73 @@ pub mod mac;
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
#[macro_use] #[macro_use]
extern crate lazy_static; extern crate lazy_static;
pub type SourceCallback = Box<dyn Fn(event::InputEvent)>;
pub trait Source {
fn initialize(&mut self) -> Result<()>;
fn eventloop(&self, event_callback: SourceCallback);
}
#[allow(dead_code)]
pub struct SourceCreationOptions {
// Only relevant in X11 Linux systems, use the EVDEV backend instead of X11.
use_evdev: bool,
// Can be used to overwrite the keymap configuration
// used by espanso to inject key presses.
evdev_keyboard_rmlvo: Option<KeyboardConfig>,
}
// This struct identifies the keyboard layout that
// should be used by EVDEV when loading the keymap.
// For more information: https://xkbcommon.org/doc/current/structxkb__rule__names.html
#[derive(Debug, Clone)]
pub struct KeyboardConfig {
pub rules: Option<String>,
pub model: Option<String>,
pub layout: Option<String>,
pub variant: Option<String>,
pub options: Option<String>,
}
impl Default for SourceCreationOptions {
fn default() -> Self {
Self {
use_evdev: false,
evdev_keyboard_rmlvo: None,
}
}
}
#[cfg(target_os = "windows")]
pub fn get_source(_options: SourceCreationOptions) -> Result<Box<dyn Source>> {
info!("using Win32Source");
Ok(Box::new(win32::Win32Source::new()))
}
#[cfg(target_os = "macos")]
pub fn get_source(_options: SourceCreationOptions) -> Result<Box<dyn Source>> {
info!("using CocoaSource");
Ok(Box::new(mac::CocoaSource::new()))
}
#[cfg(target_os = "linux")]
#[cfg(not(feature = "wayland"))]
pub fn get_source(options: SourceCreationOptions) -> Result<Box<dyn Source>> {
if options.use_evdev {
info!("using EVDEVSource");
Ok(Box::new(evdev::EVDEVSource::new(options)))
} else {
info!("using X11Source");
Ok(Box::new(x11::X11Source::new()))
}
}
#[cfg(target_os = "linux")]
#[cfg(feature = "wayland")]
pub fn get_source(options: SourceCreationOptions) -> Result<Box<dyn Source>> {
info!("using EVDEVSource");
Ok(Box::new(evdev::EVDEVSource::new(options)))
}

View File

@ -86,7 +86,6 @@ extern "C" fn native_callback(raw_event: RawInputEvent) {
} }
} }
pub type CocoaSourceCallback = Box<dyn Fn(InputEvent)>;
pub struct CocoaSource { pub struct CocoaSource {
receiver: LazyCell<Receiver<InputEvent>>, receiver: LazyCell<Receiver<InputEvent>>,
} }
@ -99,7 +98,11 @@ impl CocoaSource {
} }
} }
pub fn initialize(&mut self) -> Result<()> {
}
impl Source for CocoaSource {
fn initialize(&mut self) -> Result<()> {
let (sender, receiver) = channel(); let (sender, receiver) = channel();
// Set the global sender // Set the global sender
@ -120,7 +123,7 @@ impl CocoaSource {
Ok(()) Ok(())
} }
pub fn eventloop(&self, event_callback: CocoaSourceCallback) { fn eventloop(&self, event_callback: SourceCallback) {
if let Some(receiver) = self.receiver.borrow() { if let Some(receiver) = self.receiver.borrow() {
loop { loop {
let event = receiver.recv(); let event = receiver.recv();

View File

@ -75,10 +75,9 @@ extern "C" {
pub fn detect_destroy(window: *const c_void) -> i32; pub fn detect_destroy(window: *const c_void) -> i32;
} }
pub type Win32SourceCallback = Box<dyn Fn(InputEvent)>;
pub struct Win32Source { pub struct Win32Source {
handle: *mut c_void, handle: *mut c_void,
callback: LazyCell<Win32SourceCallback>, callback: LazyCell<SourceCallback>,
} }
#[allow(clippy::new_without_default)] #[allow(clippy::new_without_default)]
@ -89,8 +88,10 @@ impl Win32Source {
callback: LazyCell::new(), callback: LazyCell::new(),
} }
} }
}
pub fn initialize(&mut self) -> Result<()> { impl Source for Win32Source {
fn initialize(&mut self) -> Result<()> {
let mut error_code = 0; let mut error_code = 0;
let handle = unsafe { detect_initialize(self as *const Win32Source, &mut error_code) }; let handle = unsafe { detect_initialize(self as *const Win32Source, &mut error_code) };
@ -108,7 +109,7 @@ impl Win32Source {
Ok(()) Ok(())
} }
pub fn eventloop(&self, event_callback: Win32SourceCallback) { fn eventloop(&self, event_callback: SourceCallback) {
if self.handle.is_null() { if self.handle.is_null() {
panic!("Attempt to start Win32Source eventloop without initialization"); panic!("Attempt to start Win32Source eventloop without initialization");
} }

View File

@ -25,7 +25,7 @@ use log::{error, trace, warn};
use anyhow::Result; use anyhow::Result;
use thiserror::Error; use thiserror::Error;
use crate::event::Status::*; use crate::{Source, SourceCallback, event::Status::*};
use crate::event::Variant::*; use crate::event::Variant::*;
use crate::event::{InputEvent, Key, KeyboardEvent, Variant}; use crate::event::{InputEvent, Key, KeyboardEvent, Variant};
use crate::event::{Key::*, MouseButton, MouseEvent}; use crate::event::{Key::*, MouseButton, MouseEvent};
@ -70,10 +70,9 @@ extern "C" {
pub fn detect_destroy(window: *const c_void) -> i32; pub fn detect_destroy(window: *const c_void) -> i32;
} }
pub type X11SourceCallback = Box<dyn Fn(InputEvent)>;
pub struct X11Source { pub struct X11Source {
handle: *mut c_void, handle: *mut c_void,
callback: LazyCell<X11SourceCallback>, callback: LazyCell<SourceCallback>,
} }
#[allow(clippy::new_without_default)] #[allow(clippy::new_without_default)]
@ -89,7 +88,11 @@ impl X11Source {
unsafe { detect_check_x11() != 0 } unsafe { detect_check_x11() != 0 }
} }
pub fn initialize(&mut self) -> Result<()> {
}
impl Source for X11Source {
fn initialize(&mut self) -> Result<()> {
let mut error_code = 0; let mut error_code = 0;
let handle = unsafe { detect_initialize(self as *const X11Source, &mut error_code) }; let handle = unsafe { detect_initialize(self as *const X11Source, &mut error_code) };
@ -111,7 +114,7 @@ impl X11Source {
Ok(()) Ok(())
} }
pub fn eventloop(&self, event_callback: X11SourceCallback) { fn eventloop(&self, event_callback: SourceCallback) {
if self.handle.is_null() { if self.handle.is_null() {
panic!("Attempt to start X11Source eventloop without initialization"); panic!("Attempt to start X11Source eventloop without initialization");
} }

View File

@ -5,6 +5,11 @@ authors = ["Federico Terzi <federico-terzi@users.noreply.github.com>"]
edition = "2018" edition = "2018"
build="build.rs" build="build.rs"
[features]
# If the wayland feature is enabled, all X11 dependencies will be dropped
# and only EVDEV-based methods will be supported.
wayland = []
[dependencies] [dependencies]
log = "0.4.14" log = "0.4.14"
lazycell = "1.3.0" lazycell = "1.3.0"

View File

@ -35,27 +35,21 @@ fn cc_config() {
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
fn cc_config() { fn cc_config() {
// println!("cargo:rerun-if-changed=src/x11/native.cpp");
// println!("cargo:rerun-if-changed=src/x11/native.h");
// println!("cargo:rerun-if-changed=src/evdev/native.cpp");
// println!("cargo:rerun-if-changed=src/evdev/native.h");
println!("cargo:rerun-if-changed=src/evdev/native.h"); println!("cargo:rerun-if-changed=src/evdev/native.h");
println!("cargo:rerun-if-changed=src/evdev/native.c"); println!("cargo:rerun-if-changed=src/evdev/native.c");
// cc::Build::new()
// .cpp(true)
// .include("src/x11/native.h")
// .file("src/x11/native.cpp")
// .compile("espansoinject");
cc::Build::new() cc::Build::new()
.include("src/evdev/native.h") .include("src/evdev/native.h")
.file("src/evdev/native.c") .file("src/evdev/native.c")
.compile("espansoinjectev"); .compile("espansoinjectev");
println!("cargo:rustc-link-search=native=/usr/lib/x86_64-linux-gnu/"); println!("cargo:rustc-link-search=native=/usr/lib/x86_64-linux-gnu/");
// println!("cargo:rustc-link-lib=static=espansoinject");
println!("cargo:rustc-link-lib=static=espansoinjectev"); println!("cargo:rustc-link-lib=static=espansoinjectev");
println!("cargo:rustc-link-lib=dylib=X11");
println!("cargo:rustc-link-lib=dylib=Xtst");
println!("cargo:rustc-link-lib=dylib=xkbcommon"); println!("cargo:rustc-link-lib=dylib=xkbcommon");
if cfg!(not(feature = "wayland")) {
println!("cargo:rustc-link-lib=dylib=X11");
println!("cargo:rustc-link-lib=dylib=Xtst");
}
} }
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]

View File

@ -18,6 +18,7 @@
*/ */
use anyhow::Result; use anyhow::Result;
use log::info;
pub mod keys; pub mod keys;
@ -25,6 +26,7 @@ pub mod keys;
mod win32; mod win32;
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
#[cfg(not(feature = "wayland"))]
mod x11; mod x11;
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
@ -77,7 +79,7 @@ impl Default for InjectionOptions {
#[allow(dead_code)] #[allow(dead_code)]
pub struct InjectorCreationOptions { pub struct InjectorCreationOptions {
// Only relevant in Linux systems // Only relevant in X11 Linux systems, use the EVDEV backend instead of X11.
use_evdev: bool, use_evdev: bool,
// Overwrite the list of modifiers to be scanned when // Overwrite the list of modifiers to be scanned when
@ -116,19 +118,33 @@ impl Default for InjectorCreationOptions {
} }
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
pub fn get_injector(_options: InjectorOptions) -> impl Injector { pub fn get_injector(_options: InjectorOptions) -> Result<Box<dyn Injector>> {
win32::Win32Injector::new() info!("using Win32Injector");
Ok(Box::new(win32::Win32Injector::new()))
} }
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
pub fn get_injector(_options: InjectorOptions) -> impl Injector { pub fn get_injector(_options: InjectorOptions) -> Result<Box<dyn Injector>> {
mac::MacInjector::new() info!("using MacInjector");
Ok(Box::new(mac::MacInjector::new()))
} }
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
pub fn get_injector(options: InjectorCreationOptions) -> Result<impl Injector> { #[cfg(not(feature = "wayland"))]
// TODO: differenciate based on the options pub fn get_injector(options: InjectorCreationOptions) -> Result<Box<dyn Injector>> {
//x11::X11Injector::new() if options.use_evdev {
evdev::EVDEVInjector::new(options) info!("using EVDEVInjector");
Ok(Box::new(evdev::EVDEVInjector::new(options)?))
} else {
info!("using X11Injector");
Ok(Box::new(x11::X11Injector::new()?))
}
}
#[cfg(target_os = "linux")]
#[cfg(feature = "wayland")]
pub fn get_injector(options: InjectorCreationOptions) -> Result<Box<dyn Injector>> {
info!("using EVDEVInjector");
Ok(Box::new(evdev::EVDEVInjector::new(options)?))
} }

View File

@ -30,7 +30,7 @@ use ffi::{Display, KeyCode, KeyPress, KeyRelease, KeySym, Window, XCloseDisplay,
use log::error; use log::error;
use anyhow::Result; use anyhow::Result;
use crate::linux::raw_keys::{convert_key_to_sym, convert_to_sym_array}; use crate::linux::raw_keys::{convert_to_sym_array};
use thiserror::Error; use thiserror::Error;
use crate::{keys, InjectionOptions, Injector}; use crate::{keys, InjectionOptions, Injector};

View File

@ -1,3 +1,6 @@
use icons::TrayIcon;
use anyhow::Result;
pub mod event; pub mod event;
pub mod icons; pub mod icons;
pub mod menu; pub mod menu;
@ -10,3 +13,52 @@ pub mod linux;
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
pub mod mac; pub mod mac;
pub trait UIRemote {
fn update_tray_icon(&self, icon: TrayIcon);
fn show_notification(&self, message: &str);
fn show_context_menu(&self, menu: &menu::Menu);
}
pub type UIEventCallback = Box<dyn Fn(event::UIEvent)>;
pub trait UIEventLoop {
fn initialize(&mut self);
fn run(&self, event_callback: UIEventCallback);
}
pub struct UIOptions {
pub show_icon: bool,
pub icon_paths: Vec<(TrayIcon, String)>,
pub notification_icon_path: Option<String>,
}
impl Default for UIOptions {
fn default() -> Self {
Self {
show_icon: true,
icon_paths: Vec::new(),
notification_icon_path: None,
}
}
}
#[cfg(target_os = "windows")]
pub fn create_ui(_options: UIOptions) -> Result<Box<dyn Injector>> {
// TODO: refactor
Ok(Box::new(win32::Win32Injector::new()))
}
#[cfg(target_os = "macos")]
pub fn create_ui(_options: UIOptions) -> Result<Box<dyn Injector>> {
// TODO: refactor
Ok(Box::new(mac::MacInjector::new()))
}
#[cfg(target_os = "linux")]
pub fn create_ui(options: UIOptions) -> Result<(Box<dyn UIRemote>, Box<dyn UIEventLoop>)> {
// TODO: here we could avoid panicking and instead return a good result
let (remote, eventloop) = linux::create(linux::LinuxUIOptions {
notification_icon_path: options.notification_icon_path.expect("missing notification icon path")
});
Ok((Box::new(remote), Box::new(eventloop)))
}

View File

@ -3,6 +3,8 @@ use notify_rust::Notification;
use std::sync::mpsc; use std::sync::mpsc;
use std::sync::mpsc::{Receiver, Sender}; use std::sync::mpsc::{Receiver, Sender};
use crate::{UIEventLoop, UIRemote};
pub struct LinuxUIOptions { pub struct LinuxUIOptions {
pub notification_icon_path: String, pub notification_icon_path: String,
} }
@ -30,8 +32,14 @@ impl LinuxRemote {
pub fn stop(&self) -> anyhow::Result<()> { pub fn stop(&self) -> anyhow::Result<()> {
Ok(self.tx.send(())?) Ok(self.tx.send(())?)
} }
}
pub fn show_notification(&self, message: &str) { impl UIRemote for LinuxRemote {
fn update_tray_icon(&self, _: crate::icons::TrayIcon) {
// NOOP on linux
}
fn show_notification(&self, message: &str) {
if let Err(error) = Notification::new() if let Err(error) = Notification::new()
.summary("Espanso") .summary("Espanso")
.body(message) .body(message)
@ -41,6 +49,10 @@ impl LinuxRemote {
error!("Unable to show notification: {}", error); error!("Unable to show notification: {}", error);
} }
} }
fn show_context_menu(&self, _: &crate::menu::Menu) {
// NOOP on linux
}
} }
pub struct LinuxEventLoop { pub struct LinuxEventLoop {
@ -51,12 +63,14 @@ impl LinuxEventLoop {
pub fn new(rx: Receiver<()>) -> Self { pub fn new(rx: Receiver<()>) -> Self {
Self { rx } Self { rx }
} }
}
pub fn initialize(&self) { impl UIEventLoop for LinuxEventLoop {
fn initialize(&mut self) {
// NOOP on linux // NOOP on linux
} }
pub fn run(&self) { fn run(&self, _: crate::UIEventCallback) {
// We don't run an event loop on Linux as there is no tray icon or application window needed. // We don't run an event loop on Linux as there is no tray icon or application window needed.
// Thad said, we still need a way to block this method, and thus we use a channel // Thad said, we still need a way to block this method, and thus we use a channel
self.rx.recv().expect("Unable to block the LinuxEventLoop"); self.rx.recv().expect("Unable to block the LinuxEventLoop");

View File

@ -110,8 +110,6 @@ pub fn create(options: Win32UIOptions) -> (Win32Remote, Win32EventLoop) {
(remote, eventloop) (remote, eventloop)
} }
pub type Win32UIEventCallback = Box<dyn Fn(UIEvent)>;
pub struct Win32EventLoop { pub struct Win32EventLoop {
handle: Arc<AtomicPtr<c_void>>, handle: Arc<AtomicPtr<c_void>>,

View File

@ -8,6 +8,11 @@ readme = "README.md"
homepage = "https://github.com/federico-terzi/espanso" homepage = "https://github.com/federico-terzi/espanso"
edition = "2018" edition = "2018"
[features]
# If the wayland feature is enabled, all X11 dependencies will be dropped
# and only EVDEV-based methods will be supported.
wayland = ["espanso-detect/wayland", "espanso-inject/wayland"]
[dependencies] [dependencies]
espanso-detect = { path = "../espanso-detect" } espanso-detect = { path = "../espanso-detect" }
espanso-ui = { path = "../espanso-ui" } espanso-ui = { path = "../espanso-ui" }

View File

@ -1,6 +1,6 @@
use std::time::Duration; use std::time::Duration;
use espanso_detect::event::{InputEvent, Status}; use espanso_detect::{event::{InputEvent, Status}, get_source};
use espanso_inject::{get_injector, Injector, keys}; use espanso_inject::{get_injector, Injector, keys};
use espanso_ui::{event::UIEvent::*, icons::TrayIcon, menu::*}; use espanso_ui::{event::UIEvent::*, icons::TrayIcon, menu::*};
use simplelog::{CombinedLogger, Config, LevelFilter, TermLogger, TerminalMode}; use simplelog::{CombinedLogger, Config, LevelFilter, TermLogger, TerminalMode};
@ -48,18 +48,17 @@ fn main() {
// show_icon: true, // show_icon: true,
// icon_paths: &icon_paths, // icon_paths: &icon_paths,
// }); // });
let (remote, mut eventloop) = espanso_ui::linux::create(espanso_ui::linux::LinuxUIOptions { let (remote, mut eventloop) = espanso_ui::create_ui(espanso_ui::UIOptions {
notification_icon_path: r"C:\Users\Freddy\Insync\Development\Espanso\Images\icongreensmall.png".to_string(), notification_icon_path: Some(r"C:\Users\Freddy\Insync\Development\Espanso\Images\icongreensmall.png".to_string()),
}); ..Default::default()
}).unwrap();
eventloop.initialize(); eventloop.initialize();
let handle = std::thread::spawn(move || { let handle = std::thread::spawn(move || {
let injector = get_injector(Default::default()).unwrap(); let injector = get_injector(Default::default()).unwrap();
//let mut source = espanso_detect::win32::Win32Source::new(); let mut source = get_source(Default::default()).unwrap();
let mut source = espanso_detect::x11::X11Source::new(); source.initialize().unwrap();
//let mut source = espanso_detect::mac::CocoaSource::new();
source.initialize();
source.eventloop(Box::new(move |event: InputEvent| { source.eventloop(Box::new(move |event: InputEvent| {
println!("ev {:?}", event); println!("ev {:?}", event);
match event { match event {
@ -77,30 +76,29 @@ fn main() {
})); }));
}); });
eventloop.run(); eventloop.run(Box::new(move |event| {
// eventloop.run(Box::new(move |event| { println!("ui {:?}", event);
// println!("ui {:?}", event); let menu = Menu::from(vec![
// let menu = Menu::from(vec![ MenuItem::Simple(SimpleMenuItem::new("open", "Open")),
// MenuItem::Simple(SimpleMenuItem::new("open", "Open")), MenuItem::Separator,
// MenuItem::Separator, MenuItem::Sub(SubMenuItem::new(
// MenuItem::Sub(SubMenuItem::new( "Sub",
// "Sub", vec![
// vec![ MenuItem::Simple(SimpleMenuItem::new("sub1", "Sub 1")),
// MenuItem::Simple(SimpleMenuItem::new("sub1", "Sub 1")), MenuItem::Simple(SimpleMenuItem::new("sub2", "Sub 2")),
// MenuItem::Simple(SimpleMenuItem::new("sub2", "Sub 2")), ],
// ], )),
// )), ])
// ]) .unwrap();
// .unwrap(); match event {
// match event { TrayIconClick => {
// TrayIconClick => { remote.show_context_menu(&menu);
// remote.show_context_menu(&menu); }
// } ContextMenuClick(raw_id) => {
// ContextMenuClick(raw_id) => { //remote.update_tray_icon(TrayIcon::Disabled);
// //remote.update_tray_icon(TrayIcon::Disabled); remote.show_notification("Hello there!");
// remote.show_notification("Hello there!"); println!("item {:?}", menu.get_item_id(raw_id));
// println!("item {:?}", menu.get_item_id(raw_id)); }
// } }
// } }));
// }));
} }