rime / squirrel

【鼠鬚管】Rime for macOS
https://rime.im
GNU General Public License v3.0
4.73k stars 415 forks source link

[Bug] 关于鼠须管在tauri调试中不能输入中文、也无法切换中西的问题 #977

Closed f91kdash closed 2 months ago

f91kdash commented 2 months ago

tauri(mac系用的是WKWebview)调试中input框输入不了中文,但编译后的app能正常输入。 mac自带输入法没有这个问题。

鼠须管版本是1.0.2,没有任何改动

ksqsf commented 2 months ago

可否给个复现方法,谢谢。

f91kdash commented 2 months ago

可否给个复现方法,谢谢。

做了多种尝试,xcode中无论是objc还是swift还是swiftui中使用webview都没有出现input框中无法输入中文的问题。但只要是rust,无论是用tauri还是通过objc crate创建的webview窗口就都会出现无法输入中文的问题,也不管是debug还是release。 编译出来的程序直接运行能正常输入。

ksqsf commented 2 months ago

可否给个可以无脑跟的步骤? 我甚至不知道从哪下手复现。你可以假设我已经安装了 Rust 工具链,但是具体要加什么 dependencies ,代码又是什么?

f91kdash commented 2 months ago

可否给个可以无脑跟的步骤? 我甚至不知道从哪下手复现。你可以假设我已经安装了 Rust 工具链,但是具体要加什么 dependencies ,代码又是什么?

随便做了个演示。 先在终端安装上cargo-bundle: cargo install cargo-bundle 然后下面是代码:

Cargo.toml

[package]
name = "demo"
version = "0.1.0"
edition = "2021"

[dependencies]
objc = "=0.2.7"

[package.metadata.bundle]
name = "demo"
identifier = "com.a.demo"
icon = ["icon/icon.png"]
version = "1.0.0"
short_description = "An example application."

main.rs

use objc::{class, declare::ClassDecl, msg_send, runtime::{Class, Object, Sel}, sel, sel_impl};

#[link(name = "Cocoa", kind = "framework")]
extern "C" {}

#[link(name = "WebKit", kind = "framework")]
extern "C" {}

type Id = *mut Object;

const NS_STRING_ENCODING_UTF8: usize = 4;

pub struct NSString(Id);

impl NSString {
    #[inline]
    pub fn from_str(s: &str) -> Self {
        unsafe {
            let ns_string: Id = msg_send![class!(NSString), alloc];
            let ns_string: NSString = msg_send![ns_string,
                                    initWithBytes: s.as_ptr()
                                    length: s.len()
                                    encoding: NS_STRING_ENCODING_UTF8];

            let _: () = msg_send![ns_string.0, autorelease];
            ns_string
        }
    }

    #[inline]
    pub fn len(&self) -> usize {
        unsafe { msg_send![self.0, lengthOfBytesUsingEncoding: NS_STRING_ENCODING_UTF8] }
    }

    #[inline]
    pub fn as_str(&self) -> &str {
        unsafe {
            let bytes: *const u8 = msg_send![self.0, UTF8String];
            let bytes = std::slice::from_raw_parts(bytes, self.len());
            std::str::from_utf8_unchecked(bytes)
        }
    }
}

#[derive(Clone, Copy)]
pub struct WebView {
    pub inner_webview: Id,
    pub content_controller: Id,
}

impl WebView {
    pub fn new() -> Self {
        unsafe {
            let inner_webview = Class::get("InnerWebView").unwrap_or_else(|| {
                let mut inner_webview = ClassDecl::new("InnerWebView", class!(WKWebView)).unwrap();

                extern "C" fn will_open_menu(_this: &Object, _cmd: Sel, menu: Id, _event: Id) {
                    unsafe {
                        let _: () = msg_send![menu, removeAllItems];
                    }
                }

                inner_webview.add_method(
                    sel!(willOpenMenu:withEvent:),
                    will_open_menu as extern "C" fn(&Object, Sel, Id, Id),
                );

                return inner_webview.register();
            });

            let config: Id = msg_send![class!(WKWebViewConfiguration), new];
            let content_controller: Id = msg_send![config, userContentController];
            let preferences: Id = msg_send![config, preferences];
            let yes_value: Id = msg_send![class!(NSNumber), numberWithBool: true];

            let _: () = msg_send![preferences,
                            setValue: yes_value
                            forKey: NSString::from_str("allowFileAccessFromFileURLs")];

            let _: () = msg_send![preferences,
                            setValue: yes_value
                            forKey: NSString::from_str("developerExtrasEnabled")];

            let webview: Id = msg_send![inner_webview, alloc];
            let webview: Id = msg_send![webview,
                                    initWithFrame: (0_f64, 0_f64, 0_f64, 0_f64)
                                    configuration: config];

            let _: () = msg_send![webview, autorelease];
            let _: () = msg_send![webview, setInspectable: true];
            let _: () = msg_send![webview, setNavigationDelegate: webview];
            let _: () = msg_send![webview, setUIDelegate: webview];

            WebView { inner_webview: webview, content_controller }
        }
    }

    pub fn load(&self, url: &str) {
        unsafe {
            match &url[0..6] {
                "http:/" | "https:" => {
                    let ns_url: Id = msg_send![class!(NSURL), alloc];
                    let ns_url: Id = msg_send![ns_url, initWithString: NSString::from_str(url)];

                    let ns_req: Id = msg_send![class!(NSURLRequest), alloc];
                    let ns_req: Id = msg_send![ns_req, initWithURL: ns_url];
                    let _: () = msg_send![self.inner_webview, loadRequest: ns_req];

                    let _: () = msg_send![ns_req, release];
                    let _: () = msg_send![ns_url, release];
                }
                _ => {
                    let last_sep =
                        url.chars().count() - url.chars().rev().position(|c| c == '/').unwrap() - 1;
                    let path = &url[0..last_sep];

                    let ns_path: Id = msg_send![class!(NSURL), alloc];
                    let ns_path: Id = msg_send![ns_path,
                                            initFileURLWithPath: NSString::from_str(path)
                                            isDirectory: true];
                    let ns_url: Id = msg_send![class!(NSURL), alloc];
                    let ns_url: Id = msg_send![ns_url,
                                    initFileURLWithPath: NSString::from_str(url)
                                    isDirectory: false];

                    let ns_req: Id = msg_send![class!(NSURLRequest), alloc];
                    let ns_req: Id = msg_send![ns_req, initWithURL: ns_url];

                    let _: () = msg_send![self.inner_webview,
                                    loadFileRequest: ns_req
                                    allowingReadAccessToURL: ns_path];

                    let _: () = msg_send![ns_path, release];
                    let _: () = msg_send![ns_url, release];
                    let _: () = msg_send![ns_req, release];
                }
            }
        }
    }
}

fn main() {
    unsafe {
        let app: Id = msg_send![class!(NSApplication), sharedApplication];
        let delegate = {
            let mut delegate_decl = ClassDecl::new("AppDelegate", class!(NSObject)).unwrap();

            extern "C" fn application_should_terminate_after_last_window_closed(
                _this: &Object,
                _cmd: objc::runtime::Sel,
                _sender: Id,
            ) -> bool {
                true
            }

            delegate_decl.add_method(
                sel!(applicationShouldTerminateAfterLastWindowClosed:),
                application_should_terminate_after_last_window_closed
                    as extern "C" fn(&Object, objc::runtime::Sel, Id) -> bool,
            );

            delegate_decl.register();
            let delegate: Id = msg_send![class!(AppDelegate), alloc];
            let delegate: Id = msg_send![delegate, init];
            let _: () = msg_send![delegate, autorelease];
            delegate
        };

        let _: () = msg_send![app, setDelegate: delegate];
        let _: () = msg_send![app, setActivationPolicy: 0];

        let ns_window: Id = msg_send![class!(NSWindow), alloc];
        let ns_window: Id = msg_send![ns_window,
                                        initWithContentRect: (0_f64, 0_f64, 1280_f64, 720_f64)
                                        styleMask: 32783_usize // titled + closable + miniaturizable + fullScreen + fullSizeContentView
                                        backing: 2_usize // buffered
                                        defer: true];

        let webview = WebView::new();
        let _: () = msg_send![ns_window, setContentView: webview.inner_webview];
        let _: () = msg_send![ns_window, makeFirstResponder: webview.inner_webview];
        let _: () = msg_send![ns_window, makeKeyAndOrderFront: ns_window];
        webview.load("https://www.bilibili.com/");

        let _: () = msg_send![ns_window, makeKeyAndOrderFront: ns_window];

        let running_app: Id = msg_send![class!(NSRunningApplication), currentApplication];
        let _: () = msg_send![running_app, activateWithOptions: 2_u64];
        let _: () = msg_send![app, run];
    }
}

随便load一个页面,本地也行,http也行,只要有input框就能复现。 cargo run和build,都会出现无法输入中文的情况,但运行cargo bundle打包出来的app直接运行就能正常输入。无论debug还是release都一样。 我还测试了直接在终端运行safari,没有能复现,说明不是由终端启动应用导致。 系统14.6.1,鼠须管1.0.2 注:bundle的时候随便往项目下icon文件夹塞个icon.png就行。

ksqsf commented 2 months ago

感谢!复现了。