feat(clipboard): implement wayland clipboard manager
This commit is contained in:
		
							parent
							
								
									aa64f11950
								
							
						
					
					
						commit
						4038dd0cf3
					
				
							
								
								
									
										13
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										13
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							| 
						 | 
				
			
			@ -240,13 +240,11 @@ version = "0.1.0"
 | 
			
		|||
dependencies = [
 | 
			
		||||
 "anyhow",
 | 
			
		||||
 "cc",
 | 
			
		||||
 "itertools",
 | 
			
		||||
 "lazy_static",
 | 
			
		||||
 "lazycell",
 | 
			
		||||
 "libc",
 | 
			
		||||
 "log",
 | 
			
		||||
 "scopeguard",
 | 
			
		||||
 "thiserror",
 | 
			
		||||
 "wait-timeout",
 | 
			
		||||
 "widestring",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -857,6 +855,15 @@ version = "0.9.2"
 | 
			
		|||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "wait-timeout"
 | 
			
		||||
version = "0.2.0"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "libc",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "wasi"
 | 
			
		||||
version = "0.9.0+wasi-snapshot-preview1"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -8,7 +8,7 @@ build="build.rs"
 | 
			
		|||
[features]
 | 
			
		||||
# If the wayland feature is enabled, all X11 dependencies will be dropped
 | 
			
		||||
# and wayland support will be enabled
 | 
			
		||||
wayland = []
 | 
			
		||||
wayland = ["wait-timeout"]
 | 
			
		||||
 | 
			
		||||
[dependencies]
 | 
			
		||||
log = "0.4.14"
 | 
			
		||||
| 
						 | 
				
			
			@ -20,10 +20,8 @@ lazy_static = "1.4.0"
 | 
			
		|||
[target.'cfg(windows)'.dependencies]
 | 
			
		||||
widestring = "0.4.3"
 | 
			
		||||
 | 
			
		||||
[target.'cfg(target_os="linux")'.dependencies]
 | 
			
		||||
libc = "0.2.85"
 | 
			
		||||
scopeguard = "1.1.0"
 | 
			
		||||
itertools = "0.10.0"
 | 
			
		||||
[target.'cfg(target_os = "linux")'.dependencies]
 | 
			
		||||
wait-timeout = { version = "0.2.0", optional = true }
 | 
			
		||||
 | 
			
		||||
[build-dependencies]
 | 
			
		||||
cc = "1.0.66"
 | 
			
		||||
| 
						 | 
				
			
			@ -29,9 +29,9 @@ mod win32;
 | 
			
		|||
#[cfg(not(feature = "wayland"))]
 | 
			
		||||
mod x11;
 | 
			
		||||
 | 
			
		||||
//#[cfg(target_os = "linux")]
 | 
			
		||||
//#[cfg(feature = "wayland")]
 | 
			
		||||
//mod wayland;
 | 
			
		||||
#[cfg(target_os = "linux")]
 | 
			
		||||
#[cfg(feature = "wayland")]
 | 
			
		||||
mod wayland;
 | 
			
		||||
 | 
			
		||||
#[cfg(target_os = "macos")]
 | 
			
		||||
mod mac;
 | 
			
		||||
| 
						 | 
				
			
			@ -45,11 +45,17 @@ pub trait Clipboard {
 | 
			
		|||
 | 
			
		||||
#[allow(dead_code)]
 | 
			
		||||
pub struct ClipboardOptions {
 | 
			
		||||
  // Wayland-only
 | 
			
		||||
  // The number of milliseconds the wl-clipboard commands are allowed
 | 
			
		||||
  // to run before triggering a time-out event.
 | 
			
		||||
  wayland_command_timeout_ms: u64,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Default for ClipboardOptions {
 | 
			
		||||
  fn default() -> Self {
 | 
			
		||||
    Self {}
 | 
			
		||||
    Self {
 | 
			
		||||
      wayland_command_timeout_ms: 2000,
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -74,7 +80,16 @@ pub fn get_clipboard(_: ClipboardOptions) -> Result<Box<dyn Clipboard>> {
 | 
			
		|||
 | 
			
		||||
#[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)?))
 | 
			
		||||
}
 | 
			
		||||
pub fn get_clipboard(options: ClipboardOptions) -> Result<Box<dyn Clipboard>> {
 | 
			
		||||
  // TODO: On some Wayland compositors (currently sway), the "wlr-data-control" protocol 
 | 
			
		||||
  // could enable the use of a much more efficient implementation relying on the "wl-clipboard-rs" crate.
 | 
			
		||||
  // Useful links: https://github.com/YaLTeR/wl-clipboard-rs/issues/8
 | 
			
		||||
  //
 | 
			
		||||
  // We could even decide the correct implementation at runtime by checking if the 
 | 
			
		||||
  // required protocol is available, if so use the efficient implementation
 | 
			
		||||
  // instead of the fallback one, which calls the wl-copy and wl-paste binaries, and is thus
 | 
			
		||||
  // less efficient 
 | 
			
		||||
 | 
			
		||||
  info!("using WaylandFallbackClipboard");
 | 
			
		||||
  Ok(Box::new(wayland::fallback::WaylandFallbackClipboard::new(options)?))
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										33
									
								
								espanso-clipboard/src/wayland/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								espanso-clipboard/src/wayland/README.md
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,33 @@
 | 
			
		|||
# Notes on Wayland and clipboard support
 | 
			
		||||
 | 
			
		||||
### Running espanso as another user
 | 
			
		||||
 | 
			
		||||
When running espanso as another user, we need to set up a couple of permissions
 | 
			
		||||
in order to enable the clipboard tools to correctly connect to the Wayland desktop.
 | 
			
		||||
 | 
			
		||||
In particular, we need to add the `espanso` user to the same group as the current user
 | 
			
		||||
so that it can access the `/run/user/X` directory (with X depending on the user).
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
# Find the current user wayland dir with
 | 
			
		||||
echo $XDG_RUNTIME_DIR  # in my case output: /run/user/1000
 | 
			
		||||
 | 
			
		||||
ls -la /run/user/1000
 | 
			
		||||
 | 
			
		||||
# Now add the `espanso` user to the current user group
 | 
			
		||||
sudo usermod -a -G freddy espanso
 | 
			
		||||
 | 
			
		||||
# Give permissions to the group
 | 
			
		||||
chmod g+rwx /run/user/1000
 | 
			
		||||
 | 
			
		||||
# Give write permission to the wayland socket
 | 
			
		||||
chmod g+w /run/user/1000/wayland-0
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Now the clipboard should work as expected
 | 
			
		||||
 | 
			
		||||
## Better implementation
 | 
			
		||||
 | 
			
		||||
On some Wayland compositors (currently sway), the "wlr-data-control" protocol could enable the use of a much more efficient implementation relying on the "wl-clipboard-rs" crate.
 | 
			
		||||
 | 
			
		||||
Useful links: https://github.com/YaLTeR/wl-clipboard-rs/issues/8
 | 
			
		||||
							
								
								
									
										191
									
								
								espanso-clipboard/src/wayland/fallback/mod.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										191
									
								
								espanso-clipboard/src/wayland/fallback/mod.rs
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,191 @@
 | 
			
		|||
/*
 | 
			
		||||
 * This file is part of espanso.
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2019-2021 Federico Terzi
 | 
			
		||||
 *
 | 
			
		||||
 * espanso is free software: you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU General Public License as published by
 | 
			
		||||
 * the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
 * (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * espanso is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with espanso.  If not, see <https://www.gnu.org/licenses/>.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
use std::{
 | 
			
		||||
  io::{Read, Write},
 | 
			
		||||
  os::unix::net::UnixStream,
 | 
			
		||||
  path::PathBuf,
 | 
			
		||||
  process::Stdio,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
use crate::{Clipboard, ClipboardOptions};
 | 
			
		||||
use anyhow::Result;
 | 
			
		||||
use log::error;
 | 
			
		||||
use std::process::Command;
 | 
			
		||||
use thiserror::Error;
 | 
			
		||||
use wait_timeout::ChildExt;
 | 
			
		||||
 | 
			
		||||
pub(crate) struct WaylandFallbackClipboard {
 | 
			
		||||
  command_timeout: u64,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl WaylandFallbackClipboard {
 | 
			
		||||
  pub fn new(options: ClipboardOptions) -> Result<Self> {
 | 
			
		||||
    // Make sure wl-paste and wl-copy are available
 | 
			
		||||
    if Command::new("wl-paste").arg("--version").output().is_err() {
 | 
			
		||||
      error!("unable to call 'wl-paste' binary, please install the wl-clipboard package.");
 | 
			
		||||
      return Err(WaylandFallbackClipboardError::MissingWLClipboard().into());
 | 
			
		||||
    }
 | 
			
		||||
    if Command::new("wl-copy").arg("--version").output().is_err() {
 | 
			
		||||
      error!("unable to call 'wl-copy' binary, please install the wl-clipboard package.");
 | 
			
		||||
      return Err(WaylandFallbackClipboardError::MissingWLClipboard().into());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Try to connect to the wayland display
 | 
			
		||||
    let wayland_socket = if let Ok(runtime_dir) = std::env::var("XDG_RUNTIME_DIR") {
 | 
			
		||||
      PathBuf::from(runtime_dir).join("wayland-0")
 | 
			
		||||
    } else {
 | 
			
		||||
      error!("environment variable XDG_RUNTIME_DIR is missing, can't initialize the clipboard");
 | 
			
		||||
      return Err(WaylandFallbackClipboardError::MissingEnvVariable().into());
 | 
			
		||||
    };
 | 
			
		||||
    if UnixStream::connect(wayland_socket).is_err() {
 | 
			
		||||
      error!("failed to connect to Wayland display");
 | 
			
		||||
      return Err(WaylandFallbackClipboardError::ConnectionFailed().into());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Ok(Self {
 | 
			
		||||
      command_timeout: options.wayland_command_timeout_ms,
 | 
			
		||||
    })
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Clipboard for WaylandFallbackClipboard {
 | 
			
		||||
  fn get_text(&self) -> Option<String> {
 | 
			
		||||
    let timeout = std::time::Duration::from_millis(self.command_timeout);
 | 
			
		||||
    match Command::new("wl-paste")
 | 
			
		||||
      .arg("--no-newline")
 | 
			
		||||
      .stdout(Stdio::piped())
 | 
			
		||||
      .spawn()
 | 
			
		||||
    {
 | 
			
		||||
      Ok(mut child) => match child.wait_timeout(timeout) {
 | 
			
		||||
        Ok(status_code) => {
 | 
			
		||||
          if let Some(status) = status_code {
 | 
			
		||||
            if status.success() {
 | 
			
		||||
              if let Some(mut io) = child.stdout {
 | 
			
		||||
                let mut output = Vec::new();
 | 
			
		||||
                io.read_to_end(&mut output).ok()?;
 | 
			
		||||
                Some(String::from_utf8_lossy(&output).to_string())
 | 
			
		||||
              } else {
 | 
			
		||||
                None
 | 
			
		||||
              }
 | 
			
		||||
            } else {
 | 
			
		||||
              error!("error, wl-paste exited with non-zero exit code");
 | 
			
		||||
              None
 | 
			
		||||
            }
 | 
			
		||||
          } else {
 | 
			
		||||
            error!("error, wl-paste has timed-out, killing the process");
 | 
			
		||||
            if child.kill().is_err() {
 | 
			
		||||
              error!("unable to kill wl-paste");
 | 
			
		||||
            }
 | 
			
		||||
            None
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
        Err(err) => {
 | 
			
		||||
          error!("error while executing 'wl-paste': {}", err);
 | 
			
		||||
          None
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      Err(err) => {
 | 
			
		||||
        error!("could not invoke 'wl-paste': {}", err);
 | 
			
		||||
        None
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  fn set_text(&self, text: &str) -> anyhow::Result<()> {
 | 
			
		||||
    self.invoke_command_with_timeout(&mut Command::new("wl-copy"), text.as_bytes(), "wl-copy")
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  fn set_image(&self, image_path: &std::path::Path) -> anyhow::Result<()> {
 | 
			
		||||
    if !image_path.exists() || !image_path.is_file() {
 | 
			
		||||
      return Err(WaylandFallbackClipboardError::ImageNotFound(image_path.to_path_buf()).into());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Load the image data
 | 
			
		||||
    let mut file = std::fs::File::open(image_path)?;
 | 
			
		||||
    let mut data = Vec::new();
 | 
			
		||||
    file.read_to_end(&mut data)?;
 | 
			
		||||
 | 
			
		||||
    self.invoke_command_with_timeout(&mut Command::new("wl-copy").arg("--type").arg("image/png"), &data, "wl-copy")
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  fn set_html(&self, html: &str, _fallback_text: Option<&str>) -> anyhow::Result<()> {
 | 
			
		||||
    self.invoke_command_with_timeout(&mut Command::new("wl-copy").arg("--type").arg("text/html"), html.as_bytes(), "wl-copy")
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl WaylandFallbackClipboard {
 | 
			
		||||
  fn invoke_command_with_timeout(&self, command: &mut Command, data: &[u8], name: &str) -> Result<()> {
 | 
			
		||||
    let timeout = std::time::Duration::from_millis(self.command_timeout);
 | 
			
		||||
    match command
 | 
			
		||||
      .stdin(Stdio::piped())
 | 
			
		||||
      .spawn()
 | 
			
		||||
    {
 | 
			
		||||
      Ok(mut child) => {
 | 
			
		||||
        if let Some(stdin) = child.stdin.as_mut() {
 | 
			
		||||
          stdin.write_all(data)?;
 | 
			
		||||
        }
 | 
			
		||||
        match child.wait_timeout(timeout) {
 | 
			
		||||
          Ok(status_code) => {
 | 
			
		||||
            if let Some(status) = status_code {
 | 
			
		||||
              if status.success() {
 | 
			
		||||
                Ok(())
 | 
			
		||||
              } else {
 | 
			
		||||
                error!("error, {} exited with non-zero exit code", name);
 | 
			
		||||
                Err(WaylandFallbackClipboardError::SetOperationFailed().into())
 | 
			
		||||
              }
 | 
			
		||||
            } else {
 | 
			
		||||
              error!("error, {} has timed-out, killing the process", name);
 | 
			
		||||
              if child.kill().is_err() {
 | 
			
		||||
                error!("unable to kill {}", name);
 | 
			
		||||
              }
 | 
			
		||||
              Err(WaylandFallbackClipboardError::SetOperationFailed().into())
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
          Err(err) => {
 | 
			
		||||
            error!("error while executing '{}': {}", name, err);
 | 
			
		||||
            Err(WaylandFallbackClipboardError::SetOperationFailed().into())
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      Err(err) => {
 | 
			
		||||
        error!("could not invoke '{}': {}", name, err);
 | 
			
		||||
        Err(WaylandFallbackClipboardError::SetOperationFailed().into())
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Error, Debug)]
 | 
			
		||||
pub(crate) enum WaylandFallbackClipboardError {
 | 
			
		||||
  #[error("wl-clipboard binaries are missing")]
 | 
			
		||||
  MissingWLClipboard(),
 | 
			
		||||
 | 
			
		||||
  #[error("missing XDG_RUNTIME_DIR env variable")]
 | 
			
		||||
  MissingEnvVariable(),
 | 
			
		||||
 | 
			
		||||
  #[error("can't connect to Wayland display")]
 | 
			
		||||
  ConnectionFailed(),
 | 
			
		||||
 | 
			
		||||
  #[error("clipboard set operation failed")]
 | 
			
		||||
  SetOperationFailed(),
 | 
			
		||||
 | 
			
		||||
  #[error("image not found: `{0}`")]
 | 
			
		||||
  ImageNotFound(PathBuf),
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										20
									
								
								espanso-clipboard/src/wayland/mod.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								espanso-clipboard/src/wayland/mod.rs
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,20 @@
 | 
			
		|||
/*
 | 
			
		||||
 * This file is part of espanso.
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2019-2021 Federico Terzi
 | 
			
		||||
 *
 | 
			
		||||
 * espanso is free software: you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU General Public License as published by
 | 
			
		||||
 * the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
 * (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * espanso is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with espanso.  If not, see <https://www.gnu.org/licenses/>.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
pub(crate) mod fallback;
 | 
			
		||||
| 
						 | 
				
			
			@ -17,4 +17,4 @@
 | 
			
		|||
 * along with espanso.  If not, see <https://www.gnu.org/licenses/>.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
pub mod native;
 | 
			
		||||
pub(crate) mod native;
 | 
			
		||||
| 
						 | 
				
			
			@ -73,18 +73,22 @@ impl HotKey {
 | 
			
		|||
    })
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  #[allow(dead_code)]
 | 
			
		||||
  pub(crate) fn has_ctrl(&self) -> bool {
 | 
			
		||||
    self.modifiers.contains(&ShortcutKey::Control)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  #[allow(dead_code)]
 | 
			
		||||
  pub(crate) fn has_meta(&self) -> bool {
 | 
			
		||||
    self.modifiers.contains(&ShortcutKey::Meta)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  #[allow(dead_code)]
 | 
			
		||||
  pub(crate) fn has_alt(&self) -> bool {
 | 
			
		||||
    self.modifiers.contains(&ShortcutKey::Alt)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  #[allow(dead_code)]
 | 
			
		||||
  pub(crate) fn has_shift(&self) -> bool {
 | 
			
		||||
    self.modifiers.contains(&ShortcutKey::Shift)
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -10,7 +10,7 @@ edition = "2018"
 | 
			
		|||
 | 
			
		||||
[features]
 | 
			
		||||
# If the wayland feature is enabled, all X11 dependencies will be dropped
 | 
			
		||||
# and only EVDEV-based methods will be supported.
 | 
			
		||||
# and only methods suitable for Wayland will be used
 | 
			
		||||
wayland = ["espanso-detect/wayland", "espanso-inject/wayland", "espanso-clipboard/wayland"]
 | 
			
		||||
 | 
			
		||||
[dependencies]
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -12,6 +12,7 @@ use simplelog::{CombinedLogger, Config, LevelFilter, TermLogger, TerminalMode};
 | 
			
		|||
 | 
			
		||||
fn main() {
 | 
			
		||||
  println!("Hello, world!z");
 | 
			
		||||
 | 
			
		||||
  CombinedLogger::init(vec![
 | 
			
		||||
    TermLogger::new(LevelFilter::Debug, Config::default(), TerminalMode::Mixed),
 | 
			
		||||
    // WriteLogger::new(
 | 
			
		||||
| 
						 | 
				
			
			@ -22,6 +23,9 @@ fn main() {
 | 
			
		|||
  ])
 | 
			
		||||
  .unwrap();
 | 
			
		||||
 | 
			
		||||
  let clipboard = espanso_clipboard::get_clipboard(Default::default()).unwrap();
 | 
			
		||||
  println!("clipboard: {:?}", clipboard.get_text());
 | 
			
		||||
 | 
			
		||||
  // let icon_paths = vec![
 | 
			
		||||
  //   (
 | 
			
		||||
  //     espanso_ui::icons::TrayIcon::Normal,
 | 
			
		||||
| 
						 | 
				
			
			@ -68,7 +72,8 @@ fn main() {
 | 
			
		|||
    let mut source = get_source(SourceCreationOptions {
 | 
			
		||||
      //use_evdev: true,
 | 
			
		||||
      hotkeys: vec![
 | 
			
		||||
        HotKey::new(1, "OPTION+SPACE").unwrap(),
 | 
			
		||||
        HotKey::new(1, "CTRL+SPACE").unwrap(),
 | 
			
		||||
        //HotKey::new(1, "OPTION+SPACE").unwrap(),
 | 
			
		||||
        HotKey::new(2, "CTRL+OPTION+3").unwrap(),
 | 
			
		||||
      ],
 | 
			
		||||
      ..Default::default()
 | 
			
		||||
| 
						 | 
				
			
			@ -96,9 +101,9 @@ fn main() {
 | 
			
		|||
            if hotkey.hotkey_id == 2 {
 | 
			
		||||
              println!("clip {:?}", clipboard.get_text());
 | 
			
		||||
            } else if hotkey.hotkey_id == 1 {
 | 
			
		||||
              //clipboard.set_text("test text").unwrap();
 | 
			
		||||
              clipboard.set_text("test text").unwrap();
 | 
			
		||||
              //clipboard.set_html("<i>test text</i>", Some("test text fallback")).unwrap();
 | 
			
		||||
              clipboard.set_image(&PathBuf::from("/home/freddy/insync/Development/Espanso/Images/icongreen.png")).unwrap();
 | 
			
		||||
              //clipboard.set_image(&PathBuf::from("/home/freddy/insync/Development/Espanso/Images/icongreen.png")).unwrap();
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue
	
	Block a user