Switch fcitx theme based on system color

With the next version fcitx will be able to switch to an alternative dark theme when system dark/light theme changes.

The feature relies on xdg desktop portal implementation that supports this value.

Hopefully accent-color in xdg portal can be merged soon so we could also support that.

Demo:

Posted in fcitx development | Tagged , , | Leave a comment

对“fcitx5 依赖 boost 和 KDE, 探讨继续使用 fcitx4 的可行性“的回应

原贴:https://forum.suse.org.cn/t/topic/15817/7

首先,要对其中的几个所谓的错误说法进行驳斥。

1、Fcitx 5 依赖 KDE 和 Boost?

这是错误的,作为高度模块化的项目,核心库和服务器,输入法引擎,配置界面都是分离的代码库。

核心部分,反而比以前要精简得多,因为 gtk 和 qt im module 都变成了独立的项目,事实上,如果你乐意,可以编译出一个和图形库无关的 fcitx,这也是 fcitx5 能被移植到 android 上的基础。

输入法引擎部分,现在新的拼音引擎使用了极少的一部分 boost,大部分都是 header only 的,只有几个少量和 io 相关的库需要 boost 的共享库。如果你的发行版拆包精细,将只是引入约 500k 左右的依赖。

而配置界面的部分,则可能是有疑问的了,事实上它本身是在同一个代码库内分解成了两个实现,一个是只依赖于 Qt 和少量 KF5 的库,另一个则是和 KDE 系统设置集成的,也就是和 fcitx4 的 kcm-fcitx 的等价物。对于基于 QtWidgets 的实现,在 Qt 之外只引入了 1.6M 的依赖。和它所依赖的 Qt 库总计 19M 相比可以说是九牛一毛了。

而且时常大家会对 Gtk 和 Qt 在磁盘容量上有一些错觉,认为 Gtk 是 C 所以就「light」,编译出来的代码量就要小得多。而事实上如果你将相关的图形,字体,io,dbus 相关的库统统加起来,才能等价于那几个 Qt 的库。仅做一个不严谨的比较的话,Gtk 需要的依赖在我的系统上

992K    /usr/lib/libgdk-3.so.0.2405.32
7.8M    /usr/lib/libgtk-3.so.0.2405.32
1.3M    /usr/lib/libglib-2.0.so.0.7600.1
1.2M    /usr/lib/libcairo.so.2.11708.0
416K    /usr/lib/libpango-1.0.so.0.5000.14
64K     /usr/lib/libpangocairo-1.0.so.0.5000.14
96K     /usr/lib/libpangoft2-1.0.so.0.5000.14
1.9M    /usr/lib/libgio-2.0.so.0.7600.1
14M     总计
228K    /usr/lib/libKF5ItemViews.so.5.104.0
1.4M    /usr/lib/libKF5WidgetsAddons.so.5.104.0
5.2M    /usr/lib/libQt5Core.so.5.15.8
508K    /usr/lib/libQt5DBus.so.5.15.8
6.4M    /usr/lib/libQt5Gui.so.5.15.8
6.8M    /usr/lib/libQt5Widgets.so.5.15.8
24K     /usr/lib/libQt5X11Extras.so.5.15.8
21M     总计

在 Fcitx 5 中移除了 fcitx-config-gtk 之后减少了我本人大量的维护工作,因为 fcitx5-config-qt 和 kcm-fcitx5 有许多代码是可以共享的。如果有些基础常识的话,也知道用纯 C 写代码会有多么痛苦。如果你想说 Gtk 有 js binding / python binding,那是否又徒增其他语言的依赖了呢?

现在,只要你乐意,写一个基于 ncurses 的 fcitx 配置界面也没什么不可以,或者你想复活 fcitx-config-gtk 我也没有意见,但是请不要指望我去写,因为没有那个时间精力。

2、Fcitx 5 的拼音支持变少了?

表面上来说,是这样的,曾经有自带拼音,libgooglepinyin,sunpinyin,libpinyin,看起来仿佛百花齐放非常热闹,但实际上如果你对他们有所了解的话:

自带的拼音的基于最大前向匹配的算法不会比20年前的智能 ABC 好到哪去,最多就是沾了一个云拼音和词库更大的光。

libgooglepinyin 是移植自一个古老 android 版本的拼音库而且有已知的问题在特定输入会崩溃,算法本身是 unigram,也是没有上下文预测能力的。

libpinyin 是 bigram 的模型,但在几年间我对它的使用经验就是:多次随意 break ABI,早期的低质量数据输入提示有大量错字作为默认选择。

sunpinyin 是 fcitx5 当中采用的 libime 的设计精神来源,也就是trigram 的拼音输入法。如果你稍微了解一些关于 ngram 的知识,当 order 越高的时候,对上下文整句的预测也会更加准确。但在更早一篇博客我已经写过和 sunpinyin 的对比。Sunpinyin 的输入历史会被快速遗忘,没有多词库功能。而 libime 采用和 sunpinyin 相同的算法原理但是采用了存储方面更加优秀的 kenlm 作为语言模型的二进制格式,在同样的效果上占用的内存相比 sunpinyin 更小。在早期的 libime 版本中,语言模型和词库就是采用和 sunpinyin 相同的 open-gram,同样的数据同样的算法事实上计算出的结果也会是完全一样的。所以当初这样考虑之后,根本没必要再去实现 sunpinyin 的支持了。因为在库本身的易用性上,libime 是可以方便支持外挂任意数量的词库文件,拼音解析及继承自 fcitx 4 的拼音但是有额外的改进,也支持了内模糊切分(xi’an 和 xian)等等其他一系列原本 fcitx 4 没有的新功能

事实上在一个新的 code base 的基础上我们可以自由引入许多新的功能,例如语言模型和词库也已经不是最早的 open-gram 而是用更新的数据重新训练的。词库也增加了许多新的词汇,双拼也支持了完全自定义音节。sunpinyin 在近几年已经完全没有更新过,即使不考虑功能上的更新,数据也完全没有更新过。而由 fcitx 项目掌控的 libime 可以更自由的更新数据,增加新的功能。直接用拼音输入颜文字,拆字等等功能也都是在这个新拼音下才有的。

另外这些都是开源项目,只要你乐意,代码就在那,你可以随时把 fcitx4 的拼音移植过来,但我不会去捡起那个过时的代码库的。

3、fcitx 的初心是什么?

我不能替 yuking 代言,事实上代码库在我主要接手之后的十几年早就经过了长久的演化。早在十几年前因为 GNOME 的某些行为,我是真切对这些东西(opensource,是否要继续fcitx)感到失望而迷茫,当时也写过很多篇博客讨论这些事情。

事实上在我看来 fcitx 的优势就在于它的模块化架构。有很多功能如果你拿到别的框架去实现,你会发现是没法简单扩展的。例如快速输入/unicode在独立于输入法之外而存在的功能,不同输入法之间共享的拼写检查功能(键盘 / 拼音的英文输入是又另一个模块提供的功能),剪贴板的访问等等。

另外从头到尾的对代码库的掌控也提供了更好的对于新功能新平台的支持。ibus 的 qt5 输入法模块至今有许多已知的 bug,也无法在 wayland 下完全正常的使用。fcitx 5 是事实上唯一一个在 wayland 下对 compositor 支持最多最全面最可用的输入法。高度的模块化也让 fcitx 5 现在甚至可以运行在 flatpak 沙盒内。

我并不认为有谁忘记了初心,甚至坚持的很好,不要自己臆想一个初心强加给别人了。

Posted in fcitx development | Tagged , , | 3 Comments

第四面墙破还是不破,eva,崩2,龙背,尼尔,和崩3

(叠甲,崩2和龙背3的剧情是云的,尼尔机械纪元是玩过的两轮(指从头清零玩),以下内容含有上面这些作品的剧透)

啰嗦的视频版:https://b23.tv/GC8VvS5

众所周知,崩坏系列一直就有着大型eva同人作品的称号。虽然精神内核上崩坏的故事可以说和eva没有那么多的相似之处,但在表达方式上制作组往往也按不住自己想要致敬的手。

从老剧场版一转实景拍摄观众席,到新剧场版父子打架打出摄影棚到最后车站又实景,乃至贯穿最后一部的“再见了,所有的福音战士”,创作者们有时候是想要通过更直接的方式,仿佛喊话一般把自己的感情传递给观众。尽管在所谓的“严谨性”方面有时会有相应的欠缺。

而崩坏系列也不是第一次在主线中这样直接和玩家交流了。早在崩坏2逐火之蛾DLC的结局,就进行过一次由玩家意志上线代打的剧情。所以在崩坏3中再见到一次直接的对话,倒也并没有让我非常惊讶。

而在表现形式上,崩坏3这次结局的部分则有些类似尼尔机械纪元和龙背3结合。龙背3的观测者有着和大爱酱类似的身份,而她们都在观测的同时对这段“故事”产生了感情。而核心密钥留言和集合玩家之力破盾,则又颇有机械纪元e结局给其他玩家寄语,“借用”其他玩家存档来代替生命损耗。

当然,表现形式的类似并不代表感情的底色是一样的。龙背和尼尔的监督横尾太郎自己也是eva的粉丝。(这下万物起源eva了,笑)在我看来他的剧情倒是很有eva那种闹别扭的扭曲感情一面甚至还要更多,但崩坏系列从来就是一个充满希望的故事。如果说尼尔机械纪元和龙背3的最终结局的解锁方式都有一种外力“扭转绝望”的意味(观测者/pod觉醒),但表现出来都是含有很强的和玩家对话的趋势。龙背可能还不那么直接,尼尔e结局中的连续选项则可以说是直接向玩家提问了。

如果说龙背的“观测者”还有那么一些“机械”觉醒人性的意味安排存在的话,扭转尼尔e结局的和制作组名单打飞机就是完全跳脱于设定之外的演出了。

在一次横尾太郎访谈中,关于打破第四面墙的问题横尾太郎是这样回答的。

——在《尼尔机械军团》中有不少Meta要素,游戏中的角色会“打破第四面墙”,好像是制作者通过角色直接和玩家做交流,请问横尾先生是出于什么想法做了这样的设计?

横尾:我想电子游戏有很多的可能性,不过现在的很多游戏都有公式化的倾向。而脱离这种“公式化”所做出的游戏,会给人一种新鲜激动的感觉,就像我小时候所感受的那样。所以我会不断去探索电子游戏新的可能性。

崩坏3的制作组可能也有类似的想法。

回到崩坏3的结局上来,崩坏3的核心密钥设置的部分竟然是之前的“活动剧情”独占的。这意味着这段演出实质上是针对崩坏3老玩家的礼物。毕竟崩坏3也是一个已经运营了超过6年的“高龄”游戏了。我愿意相信制作组就是真的想在这段演出中和玩家“舰长”直白的感谢。

在核心密钥之后立刻就是开休伯利安的剧情,不如说正式让我们当了名副其实的舰长。

玩家集合帮忙破盾,除了给玩家参与感之外,也暗合了集合全人类之力的主题。

诚然,这种演出方式从另一个角度讲,也有“破坏故事”,或者所谓的“机械降神”。即使尼尔的e结局,也有评价为“机械降神”的看法。

但这其中也有我认为游戏作为第九艺术的魅力所在。游戏是一种交互的艺术,是有别于其他艺术形式的重要特征。在某些剧情和游玩割裂的地方,例如玩家实际打趴了敌人cg却一转被吊打,往往会有这样的吐槽“别拿游戏性和设定混为一谈啦”。

而崩坏3这次的演出,在我看来反而不是这种割裂的例子。如果你一路游玩下来,只要你真心喜欢这个游戏,认同它一直一来的内核,相信不难注意到玩家在结局希望看到一个“更美好”的世界的愿望和游戏中的人物是同一的。这时我们参与进去,看到当初的寄语,然后借由“核心密钥”操作休伯利安正是对这种感情的回应。

总结下来,我对于这段演出的情绪是认同的。而且制作组也努力掌握好了不让大爱酱承担关键转折的点避免真的变成“机械降神”。应当把它当作对玩家的彩蛋礼物,而不是剧情核心。

最后用我自己的寄语结束这么多的唠叨吧。

我永远喜欢崩坏3!

Posted in 日志 | Tagged | 1 Comment

A comprehensive guide about using Fcitx 5 on wayland

The term fragmentation is IMHO the best word to describe wayland. And that’s the reason why this page exists. While traditional “three lines” (XMODIFIERS, GTK_IM_MODULE, QT_IM_MODULE) also mostly work under wayland, but there is some other way to setup and special care needed to make all application works.

Hopefully I covered most of things in this wiki page: https://fcitx-im.org/wiki/Using_Fcitx_5_on_Wayland

Also, you are always welcome to contribute to fcitx’s wiki. https://fcitx-im.org/wiki/Contribute_to_this_Wiki

Posted in fcitx development | Leave a comment

How to make your application support Input method under Linux

As an Linux application developer, one might not aware that there could be certain effort required to support Input Method (or Input Method Editor, usually referred as IME) under Linux.

What is input method and why should I care about it?

Even if you are not aware, you are probably already using it in daily life. For example, the virtual keyboard on your smart phone is a form of input method. You may noticed that the virtual keyboard allows you to type something, and gives you a list of words based on what you already partially typed. That is a very simple use case of input method. But for CJKV (Chinese, Japanese, Korean, Vietnamese) users, Input method is necessary for them to type their own language properly. Basically imagine this: you only have 26 English key on the keyboard, how could you type thousands of different Chinese characters by a physical keyboard with only limited keys? The answers, using a mapping that maps a sequence of key into certain characters. In order to make it easy to memorize, usually such mapping is similar to what is called Transliteration , or directly use an existing Romanization system.

For example, the most popular way for typing Chinese is Hanyu Pinyin.

In the screenshot above, user just type “d e s h i j i e”, and the input method gives a list of candidates. Modern Input method always tries to be smarter to predict the most possible word that the user wants to type. And then, user may use digit key to select the candidate either fully or partially.

What do I need to do to support Input method?

The state of art of input method on Linux are all server-client based frameworks. The client is your application, and the server is the input method server. Usually, there is also a third daemon process that works as a broker to transfer the message between the application and the input method server.

1. Which GUI toolkit to use?

Gtk & Qt

If you are using Gtk, Qt, there is a good news for you. There is usually nothing you need to do to support input method. Those Gtk toolkit provides a generic abstraction and sometimes even an extensible plugin system (Gtk/Qt case) behind to hide all the complexity for the communication between input method server and application.

The built-in widget provided by Gtk or Qt already handles everything need for input method. Unless you are implementing your own fully custom widget, you do not need to use any input method API. If you need your custom widget, which sometimes happens, you can also use the API provided by the toolkit to implement it.

Here are some pointers to the toolkit API:

Gtk: gtk_im_multicontext_new GtkIMContext

Qt: https://doc.qt.io/qt-6/qinputmethod.html https://doc.qt.io/qt-6/qinputmethodevent.html

The best documentation about how to use those API is the built-in widget implementation.

SDL & winit

If you are using SDL, or rust’s winit, which does have some sort of input method support, but lack of built-in widget (There might be third-party library based on them, which I have no knowledge of), you will need to refer to their IME API to do some manual work, or their demos.

Refer to their offical documentation and examples for the reference:

https://wiki.libsdl.org/SDL2/Tutorials-TextInput

https://github.com/libsdl-org/SDL/blob/main/test/testime.c

https://github.com/rust-windowing/winit/blob/master/examples/ime.rs

Xlib & XCB

Xlib has built-in XIM protocol support, which you may access via Xlib APIs. I found a good article about how to add input method support with Xlib at:

https://tedyin.com/posts/a-brief-intro-to-linux-input-method-framework/

As for XCB, you will need to use a third-party library. I wrote one for XCB for both server and client side XIM. If you need a demo of it, you can find one at:

https://github.com/fcitx/xcb-imdkit/blob/master/test/client_demo.c

Someone also wrote a rust binding for it, which is used by wezterm in real world project. Some demo code can be found at:

https://github.com/H-M-H/xcb-imdkit-rs/tree/master/examples

wayland-client

As for writing a native wayland application from scratch with wayland-client, then you will want to pick the client side input method protocol first. The only common well supported (GNOME, KWin, wlroots, etc, but not weston, just FYI) one is:

https://wayland.app/protocols/text-input-unstable-v3

2. How to write one with the APIs above?

If you use a toolkit with widget that can already support input method well, you can skip this and call it a day. But if you need to use low level interaction with input method, or just interested in how this works, you may continue to read. Usually it involves following steps:

  1. Create a connection to input method service.
  2. Tell input method, you want to communicate with it.
  3. Keyboard event being forwarded to input method
  4. input method decide how key event is handled.
  5. Receives input method event that carries text that you need to show, or commit to the application.
  6. Tell input method you are done with text input
  7. Close the connection when your application ends, or the relevant widget destructs.

The 1st step sometimes contains two steps, a. create connection. b. create a server side object that represent a micro focus of your application. Usually, this is referred as “Input Context”. The toolkit may hide the these complexity with their own API.

Take Xlib case as an example:

  1. Create the connection: XOpenIM
  2. Create the input context: XCreateIC
  3. Tell input method your application wants to use text input with input method: XSetICFocus
  4. Forward keyevent to input method: XFilterEvent
  5. Get committed text with XLookupString
  6. When your widget/window lost focus, XUnsetICFocus
  7. Clean up: XDestroyIC, XCloseIM.

Take wayland-client + text-input-v3 as an example

  1. Get global singleton object from registry: zwp_text_input_manager_v3
  2. Call zwp_text_input_manager_v3.get_text_input
  3. Call zwp_text_input_v3.enable
  4. Key event is forward to input method by compositor, nothing related to keyboard event need to be done on client side.
  5. Get committed text zwp_text_input_v3.commit_string
  6. Call zwp_text_input_v3.disable
  7. Destroy relevant wayland proxy object.

And always, read the example provided by the toolkit to get a better idea.

3. Some other concepts except commit the text

Support input method is not only about forwarding key event and get text from input method. There are some more interaction required between application and input method that is important to give better user experience.

Preedit

Preedit is a piece of text that is display by application that represents the composing state. See the screenshot at the beginning of this article, the “underline” text is the “preedit”. Preedit contains the text and optionally some formatting information to show some rich information.

Surrounding Text

Surrounding text is an optional information that application can provide to input method. It contains text around the cursor, where the cursor and user selection is. Input method may use those information to provide better prediction. For example, if your text box has “I love |” ( | is the cursor). With surrounding text, input method will know that there is already “I love ” in the box and may predict your next word as “you” so you don’t need to type “y-o-u” but just select from the prediciton.

Surrounding text is not supported by XIM. Also, not all application can provide valid surrounding text information, for example terminal app.

Reporting cursor position on the window

Many input method engine needs to show a popup window to display some information. In order to allow input method place the window just at the position of the cursor (blinking one), application will need to let input method know where the cursor is.

Notify the state change that happens on the application side

For example, even if user is in the middle of composing something, they may still choose to use mouse click another place in the text box, or the text content is changed programmatically by app’s own logic. When such things happens, application may need to notify that the state need a “reset”. Usually this is also called “reset” in the relevant API.

Posted in Linux | Tagged , , , , | 3 Comments