Add Windows backend on Image clipboard

This commit is contained in:
Federico Terzi 2019-11-29 00:01:26 +01:00
parent 283db379c8
commit 89d0bd8596
6 changed files with 87 additions and 8 deletions

View File

@ -31,6 +31,9 @@
#include <strsafe.h> #include <strsafe.h>
#include <shellapi.h> #include <shellapi.h>
#pragma comment( lib, "gdiplus.lib" )
#include <gdiplus.h>
// How many milliseconds must pass between keystrokes to refresh the keyboard layout // How many milliseconds must pass between keystrokes to refresh the keyboard layout
const long refreshKeyboardLayoutInterval = 2000; const long refreshKeyboardLayoutInterval = 2000;
@ -656,3 +659,43 @@ int32_t get_clipboard(wchar_t *buffer, int32_t size) {
CloseClipboard(); 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;
}

View File

@ -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); 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 #endif //ESPANSO_BRIDGE_H

View File

@ -47,6 +47,7 @@ extern {
// CLIPBOARD // CLIPBOARD
pub fn get_clipboard(buffer: *mut u16, size: i32) -> i32; pub fn get_clipboard(buffer: *mut u16, size: i32) -> i32;
pub fn set_clipboard(payload: *const u16) -> i32; pub fn set_clipboard(payload: *const u16) -> i32;
pub fn set_clipboard_image(path: *const u16) -> i32;
// KEYBOARD // KEYBOARD
pub fn register_keypress_callback(cb: extern fn(_self: *mut c_void, *const u16, pub fn register_keypress_callback(cb: extern fn(_self: *mut c_void, *const u16,

View File

@ -18,7 +18,8 @@
*/ */
use widestring::U16CString; 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 { pub struct WindowsClipboardManager {
@ -53,4 +54,12 @@ impl super::ClipboardManager for WindowsClipboardManager {
set_clipboard(payload_c.as_ptr()); 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());
}
}
} }

View File

@ -218,14 +218,12 @@ impl <'a, S: KeyboardManager, C: ClipboardManager, M: ConfigManager<'a>, U: UIMa
// Image Match // Image Match
MatchContentType::Image(content) => { MatchContentType::Image(content) => {
let image_path = PathBuf::from(&content.path);
// Make sure the image exist beforehand // Make sure the image exist beforehand
if image_path.exists() { if content.path.exists() {
self.clipboard_manager.set_clipboard_image(&image_path); self.clipboard_manager.set_clipboard_image(&content.path);
self.keyboard_manager.trigger_paste(); self.keyboard_manager.trigger_paste();
}else{ }else{
error!("Image not found in path: {:?}", image_path); error!("Image not found in path: {:?}", content.path);
} }
}, },
} }

View File

@ -22,6 +22,8 @@ use crate::event::{KeyEvent, KeyModifier};
use crate::event::KeyEventReceiver; use crate::event::KeyEventReceiver;
use serde_yaml::Mapping; use serde_yaml::Mapping;
use regex::Regex; use regex::Regex;
use std::path::PathBuf;
use std::fs;
pub(crate) mod scrolling; pub(crate) mod scrolling;
@ -53,7 +55,7 @@ pub struct TextContent {
#[derive(Debug, Serialize, Clone)] #[derive(Debug, Serialize, Clone)]
pub struct ImageContent { pub struct ImageContent {
pub path: String, pub path: PathBuf,
} }
impl <'de> serde::Deserialize<'de> for Match { impl <'de> serde::Deserialize<'de> for Match {
@ -97,8 +99,29 @@ impl<'a> From<&'a AutoMatch> for Match{
MatchContentType::Text(content) MatchContentType::Text(content)
}else if let Some(image_path) = &other.image_path { // Image match }else if let Some(image_path) = &other.image_path { // Image match
// On Windows, we have to replace the forward / with the backslash \ in the path
let new_path = if cfg!(target_os = "windows") {
image_path.replace("/", "\\")
}else{
image_path.to_owned()
};
// Calculate variables in path
let new_path = if new_path.contains("$CONFIG") {
let config_dir = crate::context::get_config_dir();
let config_path = fs::canonicalize(&config_dir);
let config_path = if let Ok(config_path) = config_path {
config_path.to_string_lossy().into_owned()
}else{
"".to_owned()
};
new_path.replace("$CONFIG", &config_path)
}else{
new_path.to_owned()
};
let content = ImageContent { let content = ImageContent {
path: image_path.clone() path: PathBuf::from(new_path)
}; };
MatchContentType::Image(content) MatchContentType::Image(content)