Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Closed
f91kdash opened this issue Sep 7, 2024 · 5 comments · Fixed by #979
Closed
Labels

Comments

@f91kdash
Copy link

f91kdash commented Sep 7, 2024

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

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

@ksqsf
Copy link
Member

ksqsf commented Sep 7, 2024

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

@f91kdash
Copy link
Author

f91kdash commented Sep 8, 2024

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

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

@ksqsf
Copy link
Member

ksqsf commented Sep 8, 2024

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

@f91kdash
Copy link
Author

f91kdash commented Sep 9, 2024

可否给个可以无脑跟的步骤? 我甚至不知道从哪下手复现。你可以假设我已经安装了 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 ksqsf added the bug label Sep 9, 2024
@ksqsf
Copy link
Member

ksqsf commented Sep 9, 2024

感谢!复现了。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants