3 Revize 81e225f937 ... 8bc93f27ee

Autor SHA1 Zpráva Datum
  secext2022 8bc93f27ee 更新(版本): v0.1.0-a7 před 5 měsíci
  secext2022 a11caee0e4 更改(ibus): update comment před 5 měsíci
  Guillaume Girol fe62eaa717 Ergonomics (#3) před 5 měsíci

+ 4 - 4
.github/workflows/ci.yml

@@ -89,8 +89,8 @@ jobs:
 
     - uses: actions/upload-artifact@v4
       with:
-        name: ibrus-0.1.0a5-1.fc40.x86_64.rpm
-        path: ~/rpmbuild/RPMS/x86_64/ibrus-0.1.0a5-1.fc40.x86_64.rpm
+        name: ibrus-0.1.0a7-1.fc40.x86_64.rpm
+        path: ~/rpmbuild/RPMS/x86_64/ibrus-0.1.0a7-1.fc40.x86_64.rpm
 
     # Fedora 39
     - run: toolbox create -y -d fedora -r 39
@@ -109,5 +109,5 @@ jobs:
 
     - uses: actions/upload-artifact@v4
       with:
-        name: ibrus-0.1.0a5-1.fc39.x86_64.rpm
-        path: ~/rpmbuild/RPMS/x86_64/ibrus-0.1.0a5-1.fc39.x86_64.rpm
+        name: ibrus-0.1.0a7-1.fc39.x86_64.rpm
+        path: ~/rpmbuild/RPMS/x86_64/ibrus-0.1.0a7-1.fc39.x86_64.rpm

+ 6 - 4
Cargo.toml

@@ -1,6 +1,6 @@
 [package]
 name = "librush"
-version = "0.1.0-a6"
+version = "0.1.0-a7"
 edition = "2021"
 license = "LGPL-2.1-or-later OR GPL-3.0-or-later"
 
@@ -16,13 +16,15 @@ path="src/bin.rs"
 
 [dependencies]
 log = "^0.4.21"
-serde = "^1.0.202"
+serde = "^1.0.203"
 serde_json = "^1.0.117"
 zbus = { version = "^4.2.2", default-features = false }
+arbitrary-int = "1.2.7"
+bitbybit = "1.3.2"
+xkeysym = "0.2.0"
 
 env_logger = "^0.11.3"
-
-tokio = { version = "^1.37.0", features = ["full"], optional = true }
+tokio = { version = "^1.38.0", features = ["full"], optional = true }
 
 [build-dependencies]
 built = { version = "^0.7.3" }

+ 1 - 1
rpm/ibrus.spec

@@ -1,5 +1,5 @@
 Name:       ibrus
-Version:    0.1.0a5
+Version:    0.1.0a7
 Release:    1%{?dist}
 Summary:    ibus module for pmim (a Chinese pinyin input method)
 License:    LGPL-2.1-or-later OR GPL-3.0-or-later

+ 132 - 11
src/ibus/engine.rs

@@ -4,17 +4,34 @@ use std::future::Future;
 use std::marker::Send;
 
 use log::info;
+use xkeysym::{KeyCode, Keysym};
 use zbus::{fdo, interface, zvariant::Value, Connection, SignalContext};
 
-/// Implement this trait to implement a input method
+use super::{ibus_serde::make_ibus_text, IBusModifierState, LookupTable};
+
+/// Implement this trait to implement an input method.
+///
+/// Your implementation can use the methods of the [`IBusEngineBackend`]
+/// to display text to the user.
 pub trait IBusEngine: Send + Sync {
-    /// 键盘按键消息
+    /// A key was pressed or released.
+    ///
+    /// `keyval` encodes the symbol of the key interpreted according to the current keyboard layout.
+    ///
+    /// `keycode` encodes the position of the key on the keyboard, which is independent of the
+    /// keyboard layout.
+    ///
+    /// state encodes wether the key was pressed or released, and modifiers (shift, control...).
+    ///
+    /// Note that when `shift+a` is pressed, `keyval` will be `Keysym::A` (instead of `Keysym::a`).
+    /// `state.shift()` will still be `true`. Same applies for `AltGr` in keyboard layouts which
+    /// have it.
     fn process_key_event(
         &mut self,
         _sc: SignalContext<'_>,
-        _keyval: u32,
-        _keycode: u32,
-        _state: u32,
+        _keyval: Keysym,
+        _keycode: KeyCode,
+        _state: IBusModifierState,
     ) -> impl Future<Output = fdo::Result<bool>> + Send {
         async { Ok(false) }
     }
@@ -103,11 +120,106 @@ pub trait IBusEngine: Send + Sync {
     }
 }
 
+#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Ord, Eq)]
+pub enum IBusPreeditFocusMode {
+    Clear,
+    Commit,
+}
+
+impl From<IBusPreeditFocusMode> for u32 {
+    fn from(value: IBusPreeditFocusMode) -> Self {
+        match value {
+            IBusPreeditFocusMode::Clear => 0,
+            IBusPreeditFocusMode::Commit => 1,
+        }
+    }
+}
+
+/// Methods that the IBus daemon provides for inputs methods to use
+pub trait IBusEngineBackend: IBusEngine + 'static {
+    /// Type this text on behalf of the user
+    fn commit_text(
+        sc: &SignalContext<'_>,
+        text: String,
+    ) -> impl std::future::Future<Output = zbus::Result<()>> + Send;
+
+    /// (UI) Show of hide this lookup table
+    fn update_lookup_table(
+        sc: &SignalContext<'_>,
+        table: &LookupTable,
+        visible: bool,
+    ) -> impl std::future::Future<Output = zbus::Result<()>> + Send;
+
+    /// (UI) Sets the preedit text.
+    ///
+    /// The preedit text is a piece of text displayed in the place where text is to be written, but
+    /// not written yet, in the sense that the underlying application is not aware of it.
+    ///
+    /// cursor_pos is a value from 0 to `text.len()` indicating where the cursor should be
+    /// displayed.
+    fn update_preedit_text(
+        sc: &SignalContext<'_>,
+        text: String,
+        cursor_pos: u32,
+        visible: bool,
+        mode: IBusPreeditFocusMode,
+    ) -> impl std::future::Future<Output = zbus::Result<()>> + Send;
+
+    /// (UI) Sets the auxiliary text
+    ///
+    /// The auxiliary text is a text shown in a floating textbox besides the place where text is
+    /// to be written.
+    fn update_auxiliary_text(
+        sc: &SignalContext<'_>,
+        text: String,
+        visible: bool,
+    ) -> impl std::future::Future<Output = zbus::Result<()>> + Send;
+}
+
+impl<T: IBusEngine + 'static> IBusEngineBackend for T {
+    async fn commit_text(sc: &SignalContext<'_>, text: String) -> zbus::Result<()> {
+        Engine::<Self>::commit_text(sc, make_ibus_text(text)).await
+    }
+
+    async fn update_lookup_table(
+        sc: &SignalContext<'_>,
+        table: &LookupTable,
+        visible: bool,
+    ) -> zbus::Result<()> {
+        Engine::<Self>::update_lookup_table(sc, table.serialize(), visible).await
+    }
+
+    async fn update_preedit_text(
+        sc: &SignalContext<'_>,
+        text: String,
+        cursor_pos: u32,
+        visible: bool,
+        mode: IBusPreeditFocusMode,
+    ) -> zbus::Result<()> {
+        Engine::<Self>::update_preedit_text(
+            sc,
+            make_ibus_text(text),
+            cursor_pos,
+            visible,
+            mode.into(),
+        )
+        .await
+    }
+
+    async fn update_auxiliary_text(
+        sc: &SignalContext<'_>,
+        text: String,
+        visible: bool,
+    ) -> zbus::Result<()> {
+        Engine::<Self>::update_auxiliary_text(sc, make_ibus_text(text), visible).await
+    }
+}
+
 /// D-Bus interface: `org.freedesktop.IBus.Engine`
 ///
 /// <https://ibus.github.io/docs/ibus-1.5/IBusEngine.html>
 #[derive(Debug, Clone)]
-pub struct Engine<T: IBusEngine + 'static> {
+pub(crate) struct Engine<T: IBusEngine + 'static> {
     e: T,
 
     _op: String,
@@ -229,7 +341,16 @@ impl<T: IBusEngine + 'static> Engine<T> {
         keycode: u32,
         state: u32,
     ) -> fdo::Result<bool> {
-        self.e.process_key_event(sc, keyval, keycode, state).await
+        // Note: ibuskeysyms-update.pl indicates that IBUS_KEY_* constants are the same as XK_
+        // constants provided by xkeysym
+        self.e
+            .process_key_event(
+                sc,
+                keyval.into(),
+                keycode.into(),
+                IBusModifierState::new_with_raw_value(state),
+            )
+            .await
     }
 
     async fn set_cursor_location(
@@ -367,9 +488,9 @@ impl<T: IBusEngine + 'static> Engine<T> {
     }
 
     #[zbus(signal)]
-    pub async fn commit_text(sc: &SignalContext<'_>, text: Value<'_>) -> zbus::Result<()>;
+    async fn commit_text(sc: &SignalContext<'_>, text: Value<'_>) -> zbus::Result<()>;
 
-    // 忽略 (用户界面相关)
+    // (UI)
     #[zbus(signal)]
     async fn update_preedit_text(
         sc: &SignalContext<'_>,
@@ -379,7 +500,7 @@ impl<T: IBusEngine + 'static> Engine<T> {
         mode: u32,
     ) -> zbus::Result<()>;
 
-    // 忽略 (用户界面相关)
+    // (UI)
     #[zbus(signal)]
     async fn update_auxiliary_text(
         sc: &SignalContext<'_>,
@@ -389,7 +510,7 @@ impl<T: IBusEngine + 'static> Engine<T> {
 
     // (UI)
     #[zbus(signal)]
-    pub async fn update_lookup_table(
+    async fn update_lookup_table(
         sc: &SignalContext<'_>,
         table: Value<'_>,
         visible: bool,

+ 88 - 61
src/ibus/ibus_serde.rs

@@ -1,3 +1,5 @@
+use arbitrary_int::u11;
+use bitbybit::bitfield;
 use zbus::zvariant::{Array, Signature, Structure, Value};
 
 // 源文件: `ibus/src/ibustext.c`
@@ -49,70 +51,95 @@ pub fn make_ibus_text(text: String) -> Value<'static> {
     Value::new(st2)
 }
 
-// ibus 按键定义
-
 // 源文件: `ibus/src/ibustypes.h`
-
-/// 这个标志位表示按键释放 (松开) 消息
-pub const IBUS_RELEASE_MASK: u32 = 1 << 30;
-/// shift 键
-pub const IBUS_SHIFT_MASK: u32 = 1 << 0;
-/// ctrl 键
-pub const IBUS_CONTROL_MASK: u32 = 1 << 2;
-/// Alt 键, Meta_L 键
-pub const IBUS_MOD1_MASK: u32 = 1 << 3;
-/// Super_L 键, Hyper_L 键
-pub const IBUS_MOD4_MASK: u32 = 1 << 6;
-/// super (win) 键
-pub const IBUS_SUPER_MASK: u32 = 1 << 26;
-/// hyper 键
-pub const IBUS_HYPER_MASK: u32 = 1 << 27;
-/// meta 键
-pub const IBUS_META_MASK: u32 = 1 << 28;
-
-// 源文件: `ibus/src/ibuskeysyms.h`
-
-/// 退格键
-pub const IBUS_KEY_BACKSPACE: u32 = 0xff08;
-/// 回车键
-pub const IBUS_KEY_RETURN: u32 = 0xff0d;
-/// ESC
-pub const IBUS_KEY_ESCAPE: u32 = 0xff1b;
-/// 删除键
-pub const IBUS_KEY_DELETE: u32 = 0xffff;
-/// 方向键: 左
-pub const IBUS_KEY_LEFT: u32 = 0xff51;
-/// 方向键: 上
-pub const IBUS_KEY_UP: u32 = 0xff52;
-/// 方向键: 右
-pub const IBUS_KEY_RIGHT: u32 = 0xff53;
-/// 方向键: 下
-pub const IBUS_KEY_DOWN: u32 = 0xff54;
-
-/// 检查按键消息: 是否为按下按键
-pub fn is_keydown(state: u32) -> bool {
-    !is_keyup(state)
+#[bitfield(u32)]
+pub struct IBusModifierState {
+    /// `IBUS_SHIFT_MASK`: Shift  is activated.
+    #[bit(0, rw)]
+    shift: bool,
+    /// `IBUS_LOCK_MASK`: Cap Lock is locked.
+    #[bit(1, rw)]
+    lock: bool,
+    /// `IBUS_CONTROL_MASK`: Control key is activated.
+    #[bit(2, rw)]
+    control: bool,
+    /// `IBUS_MOD1_MASK`: Modifier 1 (Usually Alt_L (0x40),  Alt_R (0x6c),  Meta_L (0xcd)) activated.
+    #[bit(3, rw)]
+    mod1: bool,
+    /// `IBUS_MOD2_MASK`: Modifier 2 (Usually Num_Lock (0x4d)) activated.
+    #[bit(4, rw)]
+    mod2: bool,
+    /// `IBUS_MOD3_MASK`: Modifier 3 activated.
+    #[bit(5, rw)]
+    mod3: bool,
+    /// `IBUS_MOD4_MASK`: Modifier 4 (Usually Super_L (0xce),  Hyper_L (0xcf)) activated.
+    #[bit(6, rw)]
+    mod4: bool,
+    /// `IBUS_MOD5_MASK`: Modifier 5 (ISO_Level3_Shift (0x5c),  Mode_switch (0xcb)) activated.
+    #[bit(7, rw)]
+    mod5: bool,
+    /// `IBUS_BUTTON1_MASK`: Mouse button 1 (left) is activated.
+    #[bit(8, rw)]
+    button1: bool,
+    /// `IBUS_BUTTON2_MASK`: Mouse button 2 (middle) is activated.
+    #[bit(9, rw)]
+    button2: bool,
+    /// `IBUS_BUTTON3_MASK`: Mouse button 3 (right) is activated.
+    #[bit(10, rw)]
+    button3: bool,
+    /// `IBUS_BUTTON4_MASK`: Mouse button 4 (scroll up) is activated.
+    #[bit(11, rw)]
+    button4: bool,
+    /// `IBUS_BUTTON5_MASK`: Mouse button 5 (scroll down) is activated.
+    #[bit(12, rw)]
+    button5: bool,
+    #[bits(13..=23, rw)]
+    unused1: u11,
+    /// `IBUS_HANDLED_MASK`: Handled mask indicates the event has been handled by ibus.
+    #[bit(24, rw)]
+    handled: bool,
+    /// `IBUS_FORWARD_MASK`: Forward mask indicates the event has been forward from ibus.
+    #[bit(25, rw)]
+    forward: bool,
+    /// `IBUS_SUPER_MASK`: Super (Usually Win) key is activated.
+    #[bit(26, rw)]
+    super_: bool,
+    /// `IBUS_HYPER_MASK`: Hyper key is activated.
+    #[bit(27, rw)]
+    hyper: bool,
+    /// `IBUS_META_MASK`: Meta key is activated.
+    #[bit(28, rw)]
+    meta: bool,
+    #[bit(29, rw)]
+    unused2: bool,
+    /// `IBUS_RELEASE_MASK`: Key is released.
+    #[bit(30, rw)]
+    release: bool,
+    #[bit(31, rw)]
+    unused3: bool,
 }
 
-/// 检查按键消息: 是否为松开按键
-pub fn is_keyup(state: u32) -> bool {
-    (state & IBUS_RELEASE_MASK) != 0
-}
+impl IBusModifierState {
+    /// True when modifiers are pressed which indicate that this keypress is keybinding and would
+    /// typically not be used to type text.
+    ///
+    /// Modifiers considered:
+    /// - control
+    /// - mod1
+    /// - mod4
+    /// - super
+    /// - hyper
+    pub fn has_special_modifiers(self) -> bool {
+        self.control() || self.mod1() || self.mod4() || self.super_() || self.meta() || self.hyper()
+    }
+
+    // True when this keyboard event is caused by releasing a key, not pressing it
+    pub fn is_keyup(self) -> bool {
+        self.release()
+    }
 
-/// 检查按键消息: 特殊组合键是否被按下
-///
-/// 包括: Shift, Ctrl, Alt, Super 等
-pub fn is_special_mask(state: u32) -> bool {
-    if ((state & IBUS_SHIFT_MASK) != 0)
-        || ((state & IBUS_CONTROL_MASK) != 0)
-        || ((state & IBUS_MOD1_MASK) != 0)
-        || ((state & IBUS_MOD4_MASK) != 0)
-        || ((state & IBUS_SUPER_MASK) != 0)
-        || ((state & IBUS_HYPER_MASK) != 0)
-        || ((state & IBUS_META_MASK) != 0)
-    {
-        true
-    } else {
-        false
+    // True when this keyboard event is caused by pressing a key, not releasing it
+    pub fn is_keydown(self) -> bool {
+        !self.is_keyup()
     }
 }

+ 2 - 2
src/ibus/lookup_table.rs

@@ -1,6 +1,6 @@
 use zbus::zvariant::{Array, Signature, Structure, Value};
 
-use super::make_ibus_text;
+use super::ibus_serde::make_ibus_text;
 
 #[derive(Debug, Copy, Clone)]
 pub enum IBusOrientation {
@@ -65,7 +65,7 @@ impl LookupTable {
         })
     }
 
-    pub fn serialize(&self) -> Value<'static> {
+    pub(crate) fn serialize(&self) -> Value<'static> {
         let special = Array::new(Signature::from_str_unchecked("{sv}"));
         let candidates: Array = self
             .candidates

+ 3 - 6
src/ibus/mod.rs

@@ -12,12 +12,9 @@ mod lookup_table;
 
 pub use addr::get_ibus_addr;
 pub use bus::IBus;
-pub use engine::{Engine, IBusEngine};
+pub use engine::{IBusEngine, IBusEngineBackend, IBusPreeditFocusMode};
 pub use error::IBusErr;
 pub use factory::IBusFactory;
-pub use ibus_serde::{
-    is_keydown, is_keyup, is_special_mask, make_ibus_text, IBUS_KEY_BACKSPACE, IBUS_KEY_DELETE,
-    IBUS_KEY_DOWN, IBUS_KEY_ESCAPE, IBUS_KEY_LEFT, IBUS_KEY_RETURN, IBUS_KEY_RIGHT, IBUS_KEY_UP,
-    IBUS_RELEASE_MASK,
-};
+pub use ibus_serde::IBusModifierState;
 pub use lookup_table::LookupTable;
+pub use xkeysym;

+ 8 - 5
src/pmim/engine.rs

@@ -1,7 +1,8 @@
+use xkeysym::{KeyCode, Keysym};
 use zbus::{fdo, SignalContext};
 
 use super::server::Pmims;
-use crate::ibus::{IBusEngine, IBusFactory};
+use crate::ibus::{IBusEngine, IBusFactory, IBusModifierState};
 
 #[derive(Debug, Clone)]
 pub struct PmimEngine {
@@ -18,11 +19,13 @@ impl IBusEngine for PmimEngine {
     async fn process_key_event(
         &mut self,
         sc: SignalContext<'_>,
-        keyval: u32,
-        keycode: u32,
-        state: u32,
+        keyval: Keysym,
+        keycode: KeyCode,
+        state: IBusModifierState,
     ) -> fdo::Result<bool> {
-        self.s.process_key_event(sc, keyval, keycode, state).await
+        self.s
+            .process_key_event(sc, keyval.into(), keycode.into(), state.raw_value())
+            .await
     }
 
     async fn set_cursor_location(

+ 14 - 21
src/pmim/server/at/km.rs

@@ -1,11 +1,10 @@
 //! 按键管理器
 use log::debug;
+use xkeysym::key;
+
+use crate::ibus::IBusModifierState;
 
 use super::super::m::{MSender, Ms, MsT};
-use crate::ibus::{
-    is_keydown, is_special_mask, IBUS_KEY_BACKSPACE, IBUS_KEY_DELETE, IBUS_KEY_DOWN,
-    IBUS_KEY_ESCAPE, IBUS_KEY_LEFT, IBUS_KEY_RETURN, IBUS_KEY_RIGHT, IBUS_KEY_UP,
-};
 
 #[derive(Debug, Clone, Copy, PartialEq)]
 enum 输入状态 {
@@ -33,13 +32,6 @@ pub struct Km {
 }
 
 // 按键 b'' as u32 定义
-const K_A: u32 = b'a' as u32;
-const K_Z: u32 = b'z' as u32;
-const K_A1: u32 = b'A' as u32;
-const K_Z1: u32 = b'Z' as u32;
-const K_0: u32 = b'0' as u32;
-const K_9: u32 = b'9' as u32;
-const KS: u32 = b' ' as u32;
 const KC1: u32 = b'`' as u32;
 const KC2: u32 = b'~' as u32;
 const KC3: u32 = b'!' as u32;
@@ -140,13 +132,14 @@ impl Km {
     }
 
     pub async fn process_key_event(&mut self, keyval: u32, _keycode: u32, state: u32) -> bool {
+        let state = IBusModifierState::new_with_raw_value(state);
         // 禁用按键捕捉
         if self.禁用 {
             return false;
         }
 
         let mut 捕捉 = false;
-        let 按下 = is_keydown(state);
+        let 按下 = state.is_keydown();
 
         match self.状态 {
             输入状态::默认 => {
@@ -154,10 +147,10 @@ impl Km {
                 if 按下 {
                     match keyval {
                         // `a` ~ `z`
-                        K_A..=K_Z => {
+                        key::a..=key::z => {
                             // 如果特殊按键同时按下 (Shift, Ctrl, Alt, Super 等)
                             // 忽略按键
-                            if !is_special_mask(state) {
+                            if !(state.has_special_modifiers() || state.shift()) {
                                 捕捉 = true;
                                 // 进入拼音状态
                                 self.状态 = 输入状态::拼音;
@@ -173,7 +166,7 @@ impl Km {
             输入状态::拼音 => match keyval {
                 // 捕捉所有相关按键
                 // `a` ~ `z`
-                K_A..=K_Z => {
+                key::a..=key::z => {
                     捕捉 = true;
                     if 按下 {
                         // 禁用退格的同时, 也禁止输入新的拼音
@@ -184,14 +177,14 @@ impl Km {
                     }
                 }
                 // ESC: 强制退出
-                IBUS_KEY_ESCAPE => {
+                key::Escape => {
                     捕捉 = true;
                     if 按下 {
                         self.清理(false).await;
                     }
                 }
                 // `0` ~ `9`, 空格, Enter
-                K_0..=K_9 | KS | IBUS_KEY_RETURN => {
+                key::_0..=key::_9 | key::space | key::Return => {
                     捕捉 = true;
                     // 如果启用了输入反馈, 忽略按键
                     if 按下 && (!self.ef) {
@@ -202,7 +195,7 @@ impl Km {
                     }
                 }
                 // 退格 (backspace)
-                IBUS_KEY_BACKSPACE => {
+                key::BackSpace => {
                     捕捉 = true;
                     // 如果禁用了退格键, 忽略按键
                     if 按下 && (!self.禁用退格) {
@@ -218,7 +211,7 @@ impl Km {
                     }
                 }
                 // `A` ~ `Z`
-                K_A1..=K_Z1 => {
+                key::A..=key::Z => {
                     捕捉 = true;
                     // 忽略按键
                 }
@@ -230,12 +223,12 @@ impl Km {
                     // 忽略按键
                 }
                 // 删除键
-                IBUS_KEY_DELETE => {
+                key::Delete => {
                     捕捉 = true;
                     // 忽略按键
                 }
                 // 光标按键: 上下左右
-                IBUS_KEY_LEFT | IBUS_KEY_RIGHT | IBUS_KEY_UP | IBUS_KEY_DOWN => {
+                key::Left | key::Right | key::Up | key::Down => {
                     捕捉 = true;
                     // 忽略按键
                     debug!("光标: 上下左右");

+ 0 - 0
src/pmim/server/at/r.rs


Některé soubory nejsou zobrazeny, neboť je v těchto rozdílových datech změněno mnoho souborů