Add Windows backend on Image clipboard
This commit is contained in:
parent
283db379c8
commit
89d0bd8596
|
@ -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;
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
|
@ -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,
|
||||||
|
|
|
@ -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());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -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);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user