Skip to content

Commit

Permalink
Merge pull request #131 from federico-terzi/dev
Browse files Browse the repository at this point in the history
Version 0.4.0
  • Loading branch information
federico-terzi committed Nov 30, 2019
2 parents 6d1f157 + 3873bcc commit 7e2c6ac
Show file tree
Hide file tree
Showing 25 changed files with 465 additions and 117 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "espanso"
version = "0.3.5"
version = "0.4.0"
authors = ["Federico Terzi <federicoterzi96@gmail.com>"]
license = "GPL-3.0"
description = "Cross-platform Text Expander written in Rust"
Expand Down
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,13 @@ ___

Visit the [official documentation](https://espanso.org/docs/).

## Support

If you need some help to setup espanso, want to ask a question or simply get involved
in the community, [Join the official Subreddit](https://www.reddit.com/r/espanso/)! :)



## Donations

espanso is a free, open source software developed in my (little) spare time.
Expand All @@ -57,6 +64,7 @@ Many people helped the project along the way, thanks to all of you. In particula
* [Luca Antognetti](https://github.com/luca-ant) - Linux and Windows Tester
* [Matteo Pellegrino](https://www.matteopellegrino.me/) - MacOS Tester
* [Timo Runge](http://timorunge.com/) - MacOS contributor
* [NickSeagull](http://nickseagull.github.io/) - Contributor

## Remarks

Expand Down
6 changes: 6 additions & 0 deletions native/liblinuxbridge/bridge.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,10 @@ void trigger_terminal_paste() {
xdo_send_keysequence_window(xdo_context, CURRENTWINDOW, "Control_L+Shift+v", 8000);
}

void trigger_shift_ins_paste() {
xdo_send_keysequence_window(xdo_context, CURRENTWINDOW, "Shift+Insert", 8000);
}

// SYSTEM MODULE

// Function taken from the wmlib tool source code
Expand Down Expand Up @@ -470,3 +474,5 @@ int32_t is_current_window_terminal() {

return 0;
}


5 changes: 5 additions & 0 deletions native/liblinuxbridge/bridge.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,11 @@ extern "C" void trigger_paste();
*/
extern "C" void trigger_terminal_paste();

/*
* Trigger shift ins pasting( Pressing SHIFT+INS )
*/
extern "C" void trigger_shift_ins_paste();


// SYSTEM MODULE

Expand Down
6 changes: 6 additions & 0 deletions native/libmacbridge/bridge.h
Original file line number Diff line number Diff line change
Expand Up @@ -148,5 +148,11 @@ int32_t get_clipboard(char * buffer, int32_t size);
*/
int32_t set_clipboard(char * text);

/*
* Set the clipboard image to the given file
*/
int32_t set_clipboard_image(char * path);


};
#endif //ESPANSO_BRIDGE_H
20 changes: 19 additions & 1 deletion native/libmacbridge/bridge.mm
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,24 @@ int32_t set_clipboard(char * text) {
[pasteboard setString:nsText forType:NSPasteboardTypeString];
}

int32_t set_clipboard_image(char *path) {
NSString *pathString = [NSString stringWithUTF8String:path];
NSImage *image = [[NSImage alloc] initWithContentsOfFile:pathString];
int result = 0;

if (image != nil) {
NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
[pasteboard clearContents];
NSArray *copiedObjects = [NSArray arrayWithObject:image];
[pasteboard writeObjects:copiedObjects];
result = 1;
}
[image release];

return result;
}


// CONTEXT MENU

int32_t show_context_menu(MenuItem * items, int32_t count) {
Expand Down Expand Up @@ -273,4 +291,4 @@ int32_t prompt_accessibility() {
void open_settings_panel() {
NSString *urlString = @"x-apple.systempreferences:com.apple.preference.security?Privacy_Accessibility";
[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:urlString]];
}
}
43 changes: 43 additions & 0 deletions native/libwinbridge/bridge.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@
#include <strsafe.h>
#include <shellapi.h>

#pragma comment( lib, "gdiplus.lib" )
#include <gdiplus.h>

// How many milliseconds must pass between keystrokes to refresh the keyboard layout
const long refreshKeyboardLayoutInterval = 2000;

Expand Down Expand Up @@ -656,3 +659,43 @@ int32_t get_clipboard(wchar_t *buffer, int32_t size) {

CloseClipboard();
}

int32_t set_clipboard_image(wchar_t *path) {
bool result = false;

Gdiplus::GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);

Gdiplus::Bitmap *gdibmp = Gdiplus::Bitmap::FromFile(path);
if (gdibmp)
{
HBITMAP hbitmap;
gdibmp->GetHBITMAP(0, &hbitmap);
if (OpenClipboard(NULL))
{
EmptyClipboard();
DIBSECTION ds;
if (GetObject(hbitmap, sizeof(DIBSECTION), &ds))
{
HDC hdc = GetDC(HWND_DESKTOP);
//create compatible bitmap (get DDB from DIB)
HBITMAP hbitmap_ddb = CreateDIBitmap(hdc, &ds.dsBmih, CBM_INIT,
ds.dsBm.bmBits, (BITMAPINFO*)&ds.dsBmih, DIB_RGB_COLORS);
ReleaseDC(HWND_DESKTOP, hdc);
SetClipboardData(CF_BITMAP, hbitmap_ddb);
DeleteObject(hbitmap_ddb);
result = true;
}
CloseClipboard();
}

//cleanup:
DeleteObject(hbitmap);
delete gdibmp;
}

Gdiplus::GdiplusShutdown(gdiplusToken);

return result;
}
5 changes: 5 additions & 0 deletions native/libwinbridge/bridge.h
Original file line number Diff line number Diff line change
Expand Up @@ -146,4 +146,9 @@ extern "C" int32_t get_clipboard(wchar_t * buffer, int32_t size);
*/
extern "C" int32_t set_clipboard(wchar_t * text);

/*
* Set the clipboard image to the given path
*/
extern "C" int32_t set_clipboard_image(wchar_t * path);

#endif //ESPANSO_BRIDGE_H
1 change: 1 addition & 0 deletions src/bridge/linux.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,5 @@ extern {
pub fn left_arrow(count: i32);
pub fn trigger_paste();
pub fn trigger_terminal_paste();
pub fn trigger_shift_ins_paste();
}
1 change: 1 addition & 0 deletions src/bridge/macos.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ extern {
// Clipboard
pub fn get_clipboard(buffer: *mut c_char, size: i32) -> i32;
pub fn set_clipboard(text: *const c_char) -> i32;
pub fn set_clipboard_image(path: *const c_char) -> i32;

// UI
pub fn register_icon_click_callback(cb: extern fn(_self: *mut c_void));
Expand Down
1 change: 1 addition & 0 deletions src/bridge/windows.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ extern {
// CLIPBOARD
pub fn get_clipboard(buffer: *mut u16, size: i32) -> i32;
pub fn set_clipboard(payload: *const u16) -> i32;
pub fn set_clipboard_image(path: *const u16) -> i32;

// KEYBOARD
pub fn register_keypress_callback(cb: extern fn(_self: *mut c_void, *const u16,
Expand Down
26 changes: 25 additions & 1 deletion src/clipboard/linux.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@

use std::process::{Command, Stdio};
use std::io::{Write};
use log::error;
use log::{error, warn};
use std::path::Path;

pub struct LinuxClipboardManager {}

Expand Down Expand Up @@ -63,6 +64,29 @@ impl super::ClipboardManager for LinuxClipboardManager {
}
}
}

fn set_clipboard_image(&self, image_path: &Path) {
let extension = image_path.extension();
let mime = match extension {
Some(ext) => {
let ext = ext.to_string_lossy().to_lowercase();
match ext.as_ref() {
"png" => {"image/png"},
"jpg" | "jpeg" => {"image/jpeg"},
"gif" => {"image/gif"},
"svg" => {"image/svg"},
_ => {"image/png"},
}
},
None => {"image/png"},
};

let image_path = image_path.to_string_lossy().into_owned();

let res = Command::new("xclip")
.args(&["-selection", "clipboard", "-t", mime, "-i", &image_path])
.spawn();
}
}

impl LinuxClipboardManager {
Expand Down
17 changes: 16 additions & 1 deletion src/clipboard/macos.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,10 @@
*/

use std::os::raw::c_char;
use crate::bridge::macos::{get_clipboard, set_clipboard};
use crate::bridge::macos::*;
use std::ffi::{CStr, CString};
use std::path::Path;
use log::{error, warn};

pub struct MacClipboardManager {

Expand Down Expand Up @@ -52,6 +54,19 @@ impl super::ClipboardManager for MacClipboardManager {
}
}
}

fn set_clipboard_image(&self, image_path: &Path) {
let path_string = image_path.to_string_lossy().into_owned();
let res = CString::new(path_string);
if let Ok(path) = res {
unsafe {
let result = set_clipboard_image(path.as_ptr());
if result != 1 {
warn!("Couldn't set clipboard for image: {:?}", image_path)
}
}
}
}
}

impl MacClipboardManager {
Expand Down
3 changes: 3 additions & 0 deletions src/clipboard/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
*/

use std::path::Path;

#[cfg(target_os = "windows")]
mod windows;

Expand All @@ -29,6 +31,7 @@ mod macos;
pub trait ClipboardManager {
fn get_clipboard(&self) -> Option<String>;
fn set_clipboard(&self, payload: &str);
fn set_clipboard_image(&self, image_path: &Path);
}

// LINUX IMPLEMENTATION
Expand Down
11 changes: 10 additions & 1 deletion src/clipboard/windows.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
*/

use widestring::U16CString;
use crate::bridge::windows::{set_clipboard, get_clipboard};
use crate::bridge::windows::{set_clipboard, get_clipboard, set_clipboard_image};
use std::path::Path;

pub struct WindowsClipboardManager {

Expand Down Expand Up @@ -53,4 +54,12 @@ impl super::ClipboardManager for WindowsClipboardManager {
set_clipboard(payload_c.as_ptr());
}
}

fn set_clipboard_image(&self, image_path: &Path) {
let path_string = image_path.to_string_lossy().into_owned();
unsafe {
let payload_c = U16CString::from_str(path_string).unwrap();
set_clipboard_image(payload_c.as_ptr());
}
}
}
29 changes: 26 additions & 3 deletions src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ use std::fs::{File, create_dir_all};
use std::io::Read;
use serde::{Serialize, Deserialize};
use crate::event::KeyModifier;
use crate::keyboard::PasteShortcut;
use std::collections::{HashSet, HashMap};
use log::{error};
use std::fmt;
Expand Down Expand Up @@ -97,6 +98,9 @@ pub struct Configs {
#[serde(default = "default_toggle_interval")]
pub toggle_interval: u32,

#[serde(default)]
pub paste_shortcut: PasteShortcut,

#[serde(default = "default_backspace_limit")]
pub backspace_limit: i32,

Expand Down Expand Up @@ -426,6 +430,7 @@ mod tests {
use std::io::Write;
use tempfile::{NamedTempFile, TempDir};
use std::any::Any;
use crate::matcher::{TextContent, MatchContentType};

const TEST_WORKING_CONFIG_FILE : &str = include_str!("../res/test/working_config.yml");
const TEST_CONFIG_FILE_WITH_BAD_YAML : &str = include_str!("../res/test/config_with_bad_yaml.yml");
Expand Down Expand Up @@ -727,7 +732,13 @@ mod tests {
assert_eq!(config_set.default.matches.len(), 2);
assert_eq!(config_set.specific[0].matches.len(), 2);

assert!(config_set.specific[0].matches.iter().find(|x| x.trigger == ":lol" && x.replace == "newstring").is_some());
assert!(config_set.specific[0].matches.iter().find(|x| {
if let MatchContentType::Text(content) = &x.content {
x.trigger == ":lol" && content.replace == "newstring"
}else{
false
}
}).is_some());
assert!(config_set.specific[0].matches.iter().find(|x| x.trigger == ":yess").is_some());
}

Expand Down Expand Up @@ -755,7 +766,13 @@ mod tests {
assert_eq!(config_set.default.matches.len(), 2);
assert_eq!(config_set.specific[0].matches.len(), 1);

assert!(config_set.specific[0].matches.iter().find(|x| x.trigger == "hello" && x.replace == "newstring").is_some());
assert!(config_set.specific[0].matches.iter().find(|x| {
if let MatchContentType::Text(content) = &x.content {
x.trigger == "hello" && content.replace == "newstring"
}else{
false
}
}).is_some());
}

#[test]
Expand Down Expand Up @@ -897,7 +914,13 @@ mod tests {
let config_set = ConfigSet::load(data_dir.path(), package_dir.path()).unwrap();
assert_eq!(config_set.specific.len(), 0);
assert_eq!(config_set.default.matches.len(), 1);
assert!(config_set.default.matches.iter().any(|m| m.trigger == "hasta" && m.replace == "world"));
assert!(config_set.default.matches.iter().any(|m| {
if let MatchContentType::Text(content) = &m.content {
m.trigger == "hasta" && content.replace == "world"
}else{
false
}
}));
}

#[test]
Expand Down

0 comments on commit 7e2c6ac

Please sign in to comment.