From 1843a79c8280ad5ec7b0528839b172a5026990b4 Mon Sep 17 00:00:00 2001 From: yetone Date: Thu, 25 May 2023 15:10:19 +0800 Subject: [PATCH] fix: get selected text in macOS (#142) --- src-tauri/Cargo.lock | 11 +++++ src-tauri/Cargo.toml | 5 +- src-tauri/src/main.rs | 20 ++++++++ src-tauri/src/selection.rs | 93 +++++++++++--------------------------- src-tauri/src/window.rs | 11 +++-- 5 files changed, 67 insertions(+), 73 deletions(-) diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index fd9ebbd5f1..7b3c3cc992 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -1981,6 +1981,16 @@ dependencies = [ "time", ] +[[package]] +name = "macos-accessibility-client" +version = "0.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edf7710fbff50c24124331760978fb9086d6de6288dcdb38b25a97f8b1bdebbb" +dependencies = [ + "core-foundation", + "core-foundation-sys", +] + [[package]] name = "malloc_buf" version = "0.0.6" @@ -2664,6 +2674,7 @@ dependencies = [ "dirs 5.0.1", "dunce", "enigo", + "macos-accessibility-client", "mouse_position", "once_cell", "reqwest", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 90f5a21d9a..69aef83f98 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -31,12 +31,13 @@ windows = {version="0.44.0",features= ["Win32_UI_WindowsAndMessaging", "Win32_Fo window-shadows = "0.2" arboard = "3.2.0" -[target.'cfg(target_os = "macos")'.dependencies ] +[target.'cfg(target_os = "macos")'.dependencies] window-shadows = "0.2" core-graphics = "0.22.3" arboard = "3.2.0" +macos-accessibility-client = "0.0.1" -[target.'cfg(target_os = "linux")'.dependencies ] +[target.'cfg(target_os = "linux")'.dependencies] mouse_position = "0.1.3" x11-clipboard = "0.7.1" wl-clipboard-rs = "0.7.0" diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index b5981082fc..108cf8cc94 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -24,12 +24,32 @@ use trayicon::*; use utils::*; use window::*; +#[cfg(target_os = "macos")] +fn query_accessibility_permissions() -> bool { + let trusted = macos_accessibility_client::accessibility::application_is_trusted_with_prompt(); + if trusted { + print!("Application is totally trusted!"); + } else { + print!("Application isn't trusted :("); + } + trusted +} + +#[cfg(not(target_os = "macos"))] +fn query_accessibility_permissions() -> bool { + return true; +} + // 全局AppHandle pub static APP: OnceCell = OnceCell::new(); // 存待翻译文本 pub struct StringWrapper(pub Mutex); fn main() { + if !query_accessibility_permissions() { + return + } + tauri::Builder::default() // 单例运行 .plugin(tauri_plugin_single_instance::init(|app, argv, cwd| { diff --git a/src-tauri/src/selection.rs b/src-tauri/src/selection.rs index 0661fdf761..83ffc765c4 100644 --- a/src-tauri/src/selection.rs +++ b/src-tauri/src/selection.rs @@ -142,84 +142,43 @@ pub fn get_selection_text() -> Result { #[cfg(target_os = "macos")] pub fn get_selection_text() -> Result { let apple_script = r#" -use sys : application "System Events" +use AppleScript version "2.4" +use scripting additions +use framework "Foundation" +use framework "AppKit" --- Use the following delay to choose an application window --- and highlight some text. Then ensure that the window remains --- in focus until the script terminates. --- delay 5 +tell application "System Events" + set frontmostProcess to first process whose frontmost is true + set appName to name of frontmostProcess +end tell -set P to the first application process whose frontmost is true +if appName is equal to "OpenAI Translator" then + return +end if -set appName to name of P +-- Back up clipboard contents: +set savedClipboard to the clipboard -if appName is equal to "Mail" then - error "not support " & appName -end +set thePasteboard to current application's NSPasteboard's generalPasteboard() +set theCount to thePasteboard's changeCount() -if appName is equal to "Safari" then - try - tell application "Safari" - set theText to (do JavaScript "getSelection().toString()" in document 1) - end tell - return theText - end try - error "not support Safari" -end +-- Copy selected text to clipboard: +tell application "System Events" to keystroke "c" using {command down} +delay 0.1 -- Without this, the clipboard may have stale data. -if appName is equal to "Google Chrome" then - try - tell application "Google Chrome" to tell active tab of window 1 - set theText to (execute javascript "getSelection().toString()") - end tell - return theText - end try - error "not support Google Chrome" -end +if thePasteboard's changeCount() is theCount then + return "" +end if -if appName is equal to "Microsoft Edge" then - try - tell application "Microsoft Edge" to tell active tab of window 1 - set theText to (execute javascript "getSelection().toString()") - end tell - return theText - end try - error "not support Microsoft Edge" -end +set theSelectedText to the clipboard -set _W to a reference to the first window of P +set the clipboard to savedClipboard -set _U to a reference to ¬ - (UI elements of P whose ¬ - name of attributes contains "AXSelectedText" and ¬ - value of attribute "AXSelectedText" is not "" and ¬ - class of value of attribute "AXSelectedText" is not class) - -tell sys to if (count _U) ≠ 0 then ¬ - return the value of ¬ - attribute "AXSelectedText" of ¬ - _U's contents's first item - -set _U to a reference to UI elements of _W - -with timeout of 1 seconds - tell sys to repeat while (_U exists) - tell (a reference to ¬ - (_U whose ¬ - name of attributes contains "AXSelectedText" and ¬ - value of attribute "AXSelectedText" is not "" and ¬ - class of value of attribute "AXSelectedText" is not class)) ¬ - to if (count) ≠ 0 then return the value of ¬ - attribute "AXSelectedText" of its contents's first item - - set _U to a reference to (UI elements of _U) - end repeat -end timeout - -error "not found AXSelectedText" +theSelectedText "#; match std::process::Command::new("osascript") + .arg("-e") .arg(apple_script) .output() { @@ -228,7 +187,7 @@ error "not found AXSelectedText" if output.status.success() { // get output content let content = String::from_utf8(output.stdout) - .expect("failed to parse get-selected-text-by-ax.applescript output"); + .expect("failed to parse get-selected-text.applescript output"); // trim content let content = content.trim(); Ok(content.to_string()) diff --git a/src-tauri/src/window.rs b/src-tauri/src/window.rs index 3551e01690..8561d8773e 100644 --- a/src-tauri/src/window.rs +++ b/src-tauri/src/window.rs @@ -213,10 +213,13 @@ fn get_mouse_location() -> Result<(f64, f64), String> { // 划词翻译 pub fn translate_window() { // 获取选择文本 - let mut text = String::new(); - if let Ok(v) = get_selection_text() { - text = v; - } + let text = match get_selection_text() { + Ok(v) => v, + Err(err) => { + println!("get selection text error: {:?}", err); + String::new() + } + }; let handle = APP.get().unwrap(); // 写入状态备用 let state: tauri::State = handle.state();