rime / weasel

【小狼毫】Rime for Windows
https://rime.im
GNU General Public License v3.0
4.48k stars 547 forks source link

一些 Feature Request #1235

Closed mirtlebot closed 4 months ago

mirtlebot commented 5 months ago

非常感谢 fxliang 和其他开发者的近年来的奉献。

以下是一些 feature request,个人想法,不一定可行:

  1. 或许可以像 fcitx5-rime 那样,在用户不输入的时候,每隔一段时间同步用户数据,方便将用户词(至少 tabledb)从内存写入文件,保存数据。
  2. 在系统启用省电(能效)模式时,禁用一些特效(如各种阴影),节省开销,提升流畅度;
  3. https://github.com/rime/weasel/issues/616https://github.com/rime/weasel/issues/228 似乎已经不再适用,讨论区和主项目的的 issue 并未能有效跟进,word 的 bug 已经修复
  4. 在系统为暗色模式时(托盘颜色为暗色),将小狼毫的 icon 调整成白底,更显眼(类似 PIME 的那样)
  5. 支持在系统为暗色模式时,单独调整 ascii_icon 和 icon:,让它们更显眼(类似微软拼音)。当下,只能使用一层描边以适配深色浅色,且效果并不好;
  6. 是否能至少给予「进程守护」的选项,哪怕默认关闭?非常抱歉再度提到了这个问题。
    
    我已经阅读了 fxliang 给出的不添加进程守护的理由:发现崩溃原因,修复问题。

但作为用户,我从四五年前的 14.3 开始,就深受小狼毫毫无征兆崩溃的困扰,

我并无能力修复或找出原因,只想其能正常工作。

尝试了外部脚本、进程守护,发现还是类似 Techince 提供的那种写入程序内部的效果最好。



或者来说,我不太不熟悉 C++ 和 Weasel 的代码,是否有开发者愿意教教我,如何在在主仓的代码中,实现一个进程守护的功能,因为 Techince 的每次提交信息量大,我找不到其中关于进程守护的有效代码。

感激不尽。
fxliang commented 5 months ago

关于崩服务,可以看看#1234,我禁掉了服务上无用的ui更新,理论上会少些崩的机会,也可能让开机启动不成功的问题少些概率

fxliang commented 5 months ago

另外,如果在服务没有运行的时候左键点击一下语言栏可以运行服务这样会缓解一点疼痛吗?这样会比右键重启服务要少点击一下了

mirtlebot commented 5 months ago

谢谢,我会在 PR 合并后测试反馈的。

不过因为不太清楚崩溃的时机和机制,崩溃也是不定期的。很难给出「好一些了的定论」。之前我要用做安全测试,有各种版本的虚拟机,有两个屏幕,有软件和脚本。小狼毫一天可以崩 4 - 5 次。

直觉上,从虚拟机切回来、一个脚本执行完、换个屏幕,小狼毫偶尔就炸了。但仍不清楚如何复现。

如果在服务没有运行的时候左键点击一下语言栏可以运行服务这样会缓解一点疼痛吗

从体验说上,啥都不做它就能自己拉起来最好。

这个解决办法,和几年前有经验分享说 将小狼毫服务快捷方式固定到任务栏 差不多。

我可以写一个外部程序监控守护,提在这里只是想讨论下。

fxliang commented 5 months ago

我会提好点的初步判断,是之前做ThreadFocusSink事件的时候意外发现两个窗口之间高速切换会引发server内的空指针调用,但是报错的位置是dwrite.dll,然后才有后面限制了ui更新context,status的修改,但是那样还是有一定的概率在两个窗口之间快速切换中输入一个按键的时候崩

昨日有留意有用户试验到如果将小狼毫放到启动项最后一项最后启动,就可以在自启后不退出。进而想到如果小狼毫先于一些应用自启,之后的过程就会类似几个窗口之间快速切换,里面会有update ui的操作,就有机会引起我之前手工测出的崩溃。

于是我上面提到的那个pr里,限制了如果session是tsf session的话,只在message的状态下调用server里的update ui,其他都跳过。目前猜测的方向应该是有可能会减少一点触发我之前发现的那种崩溃的概率。

就差开机不能启动的用户来试了

mirtlecn commented 5 months ago

我之前都在评论底下直接 at 他们名字了,根本不理人的。

fxliang commented 5 months ago

至于守护,#1126 里他们有讨论过一些内容,大致流可用。要改一下目录获取的逻辑,还有我觉得用ipc判断server 有没有运行不够好,named mutex 应该更合理高效

ccyybn commented 5 months ago

其实进程守护的核心逻辑只需要几行代码,不过我为了实现重启Server后恢复不同窗口的状态,比如中英文,emoji开关这种,魔改了很多地方,甚至librime也改了下,免得恢复时弹出状态切换的通知

再者我比较完美主义,不仅希望崩溃后在我不输入的时候,能自动重启,如果在我输入时,刚好崩溃了,也能立刻重启,所以这里就有两个逻辑,要防止同时执行,就用到了进程间的mutex,你可以直接看我的仓库代码,我也是边学C++边查微软文档搞出来的

ccyybn commented 5 months ago

https://github.com/ccyybn/weasel/commit/88a02ddbdf385226e005f899fbdf259418177a3b

唯一的问题就是,如果server是以管理员身份运行的(第一次安装后),如果竞争到监控任务的TSF进程没有管理员权限,就无法OpenProcess 成功,不能原地等待server崩溃,只能定时检查进程列表,这时如果server崩溃了,而TSF刚好处于检查间隙,则是由输入逻辑触发重启

ccyybn commented 5 months ago

如果你只是需要简单的重启,就用当时Techince 教我的截图里的代码就好了,最好再加一个开关,免得关不掉服务 https://github.com/rime/weasel/issues/1126#issuecomment-1975203421

mirtlebot commented 5 months ago

如果你只是需要简单的重启,就用当时Techince 教我的截图里的代码就好了,最好再加一个开关,免得关不掉服务 https://github.com/rime/weasel/issues/1126#issuecomment-1975203421

这个代码我尝试过,引用了很多本库中不存在的函数。无法编译出来。

尝试添加这些本身不存在的函数和 moudle,diff 了一下,又发现涉及了很多别的文件。不太敢改了,可能超出了能力范围

ccyybn commented 5 months ago

引用的其他东西不用管,核心就是echo 不成功就调用server 启动他,然后sleep 一下,再echo 一下,最后connect ,因为是按键触发,实际用的时候会感觉小卡一下,所以我是用的另一种, WaitForSingleObject 等到崩溃就立刻重启,这个思路就和写外部的监控程序是一样的了,就是在里面开个线程,随便干什么都可以

mirtlecn commented 5 months ago

好吧,我尝试搞懂他的代码,但似乎发现比给出的要复杂不少。

不是专业搞这方面的,只知道里面引入了大量主仓没有的函数、对象之类,搜索的时候发现这些对象又在几个文件中出现,没敢往下改了,不然之后 merge 主仓代码的时候查冲突要花太长的时间。

+if (!m_client.Echo()) // Echo 未成功
+{
+   // UpdateGlobalCompartment(); // 这是做什么的?
+   if (GetBit(WeaselFlag::DAEMON_ENABLE)) // 判断服务是否运行?
+   {
+       execute(std::format(LR"({}\WeaselServer.exe)", WeaselRootPath())); // 启动服务
+       Sleep(150); // 等待
+       if (!m_client.Echo()) // 再次尝试 Echo
+       {
+           m_client.Disconnect();
+           m_client.Connect(NULL);
+           m_client.StartSession();
+       }
+   }
+}
'WeaselFlag': is not a class or namespace name
'DAEMON_ENABLE': undeclared identifier [D:\a\weasel\weasel\WeaselTSF\WeaselTSF.vcxproj]
'GetBit': identifier not found [D:\a\weasel\weasel\WeaselTSF\WeaselTSF.vcxproj]
'format': is not a member of 'std' [D:\a\weasel\weasel\WeaselTSF\WeaselTSF.vcxproj]
'WeaselRootPath': identifier not found [D:\a\weasel\weasel\WeaselTSF\WeaselTSF.vcxproj]
'format': identifier not found [D:\a\weasel\weasel\WeaselTSF\WeaselTSF.vcxproj]
'execute': identifier not found [D:\a\weasel\weasel\WeaselTSF\WeaselTSF.vcxproj]

我目前是给 WeaselServer 的监控程序(WinSW)做成系统服务用,以当前用户的权限运行,但有时候重启的不那么及时。

ccyybn commented 5 months ago
+if (!m_client.Echo()) // Echo 未成功
+{
+   // UpdateGlobalCompartment(); // 同步他加的一些全局的量,包括这里守护的开关
+   if (GetBit(WeaselFlag::DAEMON_ENABLE)) // 判断守护的开关
+   {
+       execute(std::format(LR"({}\WeaselServer.exe)", WeaselRootPath())); // 启动服务
+       Sleep(150); // 等待
+       if (!m_client.Echo()) // 再次尝试 Echo
+       {
+           m_client.Disconnect();
+           m_client.Connect(NULL);
+           m_client.StartSession();
+       }
+   }
+}

因为TSF是多进程,这取决于你在多少进程里用了输入法,所以需要全局共享这个守护的开关,这里的Compartment就是微软为输入法提供一个全局的工具,他这个函数封装的比较复杂,把全局变量在本地缓存了一下,实际上每次直接去Compartment取值就好了,右键菜单切换开关时,就去改Compartment里面的这个值

ccyybn commented 5 months ago

我没记错的话,就是这两个 setvalue 和 getvalue,我代码里也用到了的,你可以对比一下

https://learn.microsoft.com/en-us/windows/win32/api/msctf/nf-msctf-itfcompartment-getvalue https://learn.microsoft.com/en-us/windows/win32/api/msctf/nf-msctf-itfcompartment-setvalue

https://github.com/ccyybn/weasel/blob/d4180bfa5b5015f92c3e4fdde9d674c18f8c8a20/WeaselTSF/WeaselTSF.cpp#L26 https://github.com/ccyybn/weasel/blob/d4180bfa5b5015f92c3e4fdde9d674c18f8c8a20/WeaselTSF/WeaselTSF.cpp#L65

fxliang commented 5 months ago

1243 合入之后我就看怎么将这个守护的事情做进来 @mirtlebot @ccyybn

fxliang commented 5 months ago

https://github.com/fxliang/weasel/actions/runs/9140885344

自用试验分支上简单加了一点,基本能用了,没做开关,够用来切换输入法了(如果真不想拉起的话) 如果崩了,会在%TEMP%\rime.weasel下形成dmp文件

@mirtlebot

mirtlecn commented 5 months ago

效果很不错,但不太理解为什么要做 6 次重试才拉起来,感觉时间有些长。

https://github.com/fxliang/weasel/commit/680df09ed1eb75b6b50bd7081e932d326bb85bc5#diff-fcf1995c0aa5f5dc9534d787f174781f2d1888e1d623af3258154c7b5335003eR235

fxliang commented 5 months ago

效果很不错,但不太理解为什么要做 6 次重试才拉起来,感觉时间有些长。

fxliang@680df09#diff-fcf1995c0aa5f5dc9534d787f174781f2d1888e1d623af3258154c7b5335003eR235

给你机会键盘切换输入法之后做换Server之类的操作 3个按键down up

mirtlebot commented 4 months ago

这个守护方法,我碰到一个没法稳定复现的小问题:

是手动退出算法,然后测试的,不代表真实崩溃情况:

mirtlecn commented 4 months ago

我会提好点的初步判断,是之前做ThreadFocusSink事件的时候意外发现两个窗口之间高速切换会引发server内的空指针调用,但是报错的位置是dwrite.dll,然后才有后面限制了ui更新context,status的修改,但是那样还是有一定的概率在两个窗口之间快速切换中输入一个按键的时候崩

昨日有留意有用户试验到如果将小狼毫放到启动项最后一项最后启动,就可以在自启后不退出。进而想到如果小狼毫先于一些应用自启,之后的过程就会类似几个窗口之间快速切换,里面会有update ui的操作,就有机会引起我之前手工测出的崩溃。

于是我上面提到的那个pr里,限制了如果session是tsf session的话,只在message的状态下调用server里的update ui,其他都跳过。目前猜测的方向应该是有可能会减少一点触发我之前发现的那种崩溃的概率。

就差开机不能启动的用户来试了

这个版本后,我用虚拟机,多屏再也没有崩过了