diff --git a/devices/surface/default.nix b/devices/surface/default.nix index 2e1f1811..db03b9bc 100644 --- a/devices/surface/default.nix +++ b/devices/surface/default.nix @@ -27,7 +27,7 @@ inputs: nix = { substituters = [ "https://cache.nixos.org/" "https://nix-store.chn.moe" ]; githubToken.enable = true; }; kernel = { variant = "xanmod-lts"; patches = [ "surface" "hibernate-progress" ]; }; networking.hostname = "surface"; - gui.enable = true; + gui = { enable = true; touchscreen = true; }; initrd.unl0kr = {}; }; hardware = { cpus = [ "intel" ]; gpu.type = "intel"; }; diff --git a/flake.lock b/flake.lock index a7307417..7de7bf00 100644 --- a/flake.lock +++ b/flake.lock @@ -238,6 +238,22 @@ "type": "github" } }, + "fcitx5-virtualkeyboard-ui": { + "flake": false, + "locked": { + "lastModified": 1724249565, + "narHash": "sha256-flMCrq4HEhugiwKXCd4xZkgZVF+XUo0C+L0OhRXCfIE=", + "owner": "CHN-beta", + "repo": "fcitx5-virtualkeyboard-ui", + "rev": "695bf005da908c79f1d461c3b0801ce651f34ade", + "type": "github" + }, + "original": { + "owner": "CHN-beta", + "repo": "fcitx5-virtualkeyboard-ui", + "type": "github" + } + }, "fenix": { "inputs": { "nixpkgs": [ @@ -1251,6 +1267,7 @@ "date": "date", "eigen": "eigen", "envfs": "envfs", + "fcitx5-virtualkeyboard-ui": "fcitx5-virtualkeyboard-ui", "git-lfs-transfer": "git-lfs-transfer", "gricad": "gricad", "home-manager": "home-manager", diff --git a/flake.nix b/flake.nix index 7be4a2c3..b828d2bb 100644 --- a/flake.nix +++ b/flake.nix @@ -66,6 +66,7 @@ sockpp = { url = "github:fpagliughi/sockpp"; flake = false; }; git-lfs-transfer = { url = "github:charmbracelet/git-lfs-transfer"; flake = false; }; nc4nix = { url = "github:helsinki-systems/nc4nix"; flake = false; }; + fcitx5-virtualkeyboard-ui = { url = "github:CHN-beta/fcitx5-virtualkeyboard-ui"; flake = false; }; # does not support lfs yet # nixos-wallpaper = { url = "git+https://git.chn.moe/chn/nixos-wallpaper.git"; flake = false; }; diff --git a/modules/system/gui.nix b/modules/system/gui/default.nix similarity index 79% rename from modules/system/gui.nix rename to modules/system/gui/default.nix index 0aba6446..41659d81 100644 --- a/modules/system/gui.nix +++ b/modules/system/gui/default.nix @@ -5,6 +5,7 @@ inputs: enable = mkOption { type = types.bool; default = false; }; preferred = mkOption { type = types.bool; default = inputs.config.nixos.system.gui.enable; }; autoStart = mkOption { type = types.bool; default = inputs.config.nixos.system.gui.preferred; }; + touchscreen = mkOption { type = types.bool; default = false; }; }; config = let inherit (inputs.config.nixos.system) gui; in inputs.lib.mkIf gui.enable { @@ -38,9 +39,13 @@ inputs: { enable = true; type = "fcitx5"; - fcitx5.addons = builtins.map (p: inputs.pkgs."fcitx5-${p}") - [ "rime" "chinese-addons" "mozc" "nord" "material-color" ]; + fcitx5.addons = (builtins.map (p: inputs.pkgs."fcitx5-${p}") + [ "rime" "chinese-addons" "mozc" "nord" "material-color" ]) + ++ inputs.lib.mkIf gui.touchscreen (inputs.pkgs.localPackages.fcitx5-virtualkeyboard-ui); }; programs.dconf.enable = true; + nixpkgs.overlays = inputs.lib.mkIf gui.touchscreen + [(final: prev: { fcitx5 = prev.fcitx5.overrideAttrs (prev: + { patches = prev.patches or [] ++ [ ./fcitx5.patch ]; });})]; }; } diff --git a/modules/system/gui/fcitx5.patch b/modules/system/gui/fcitx5.patch new file mode 100644 index 00000000..242652a2 --- /dev/null +++ b/modules/system/gui/fcitx5.patch @@ -0,0 +1,248 @@ +From 5662218e904bc245c47ce4b33f04c2e3a74c7d92 Mon Sep 17 00:00:00 2001 +From: Daijiro Fukuda +Date: Mon, 28 Feb 2022 15:08:40 +0900 +Subject: [PATCH 1/3] Add simple group enumerating function for addons (#452) + +--- + src/lib/fcitx/inputmethodmanager.cpp | 22 ++++++++++++++++++++++ + src/lib/fcitx/inputmethodmanager.h | 3 +++ + 2 files changed, 25 insertions(+) + +diff --git a/src/lib/fcitx/inputmethodmanager.cpp b/src/lib/fcitx/inputmethodmanager.cpp +index 6e842ee07..e2e0d6184 100644 +--- a/src/lib/fcitx/inputmethodmanager.cpp ++++ b/src/lib/fcitx/inputmethodmanager.cpp +@@ -276,6 +276,28 @@ const InputMethodGroup &InputMethodManager::currentGroup() const { + return d->groups_.find(d->groupOrder_.front())->second; + } + ++void InputMethodManager::enumerateGroup(bool forward) { ++ FCITX_D(); ++ if (groupCount() < 2) { ++ return; ++ } ++ emit(d->groupOrder_.front()); ++ if (forward) { ++ d->groupOrder_.splice( ++ d->groupOrder_.end(), ++ d->groupOrder_, ++ d->groupOrder_.begin() ++ ); ++ } else { ++ d->groupOrder_.splice( ++ d->groupOrder_.begin(), ++ d->groupOrder_, ++ std::prev(d->groupOrder_.end()) ++ ); ++ } ++ emit(d->groupOrder_.front()); ++} ++ + void InputMethodManager::setDefaultInputMethod(const std::string &name) { + FCITX_D(); + auto ¤tGroup = d->groups_.find(d->groupOrder_.front())->second; +diff --git a/src/lib/fcitx/inputmethodmanager.h b/src/lib/fcitx/inputmethodmanager.h +index 0069c4108..0be42630d 100644 +--- a/src/lib/fcitx/inputmethodmanager.h ++++ b/src/lib/fcitx/inputmethodmanager.h +@@ -84,6 +84,9 @@ class FCITXCORE_EXPORT InputMethodManager : public ConnectableObject { + /// Return the current group. + const InputMethodGroup ¤tGroup() const; + ++ /// Simply enumerate input method groups. ++ void enumerateGroup(bool forward); ++ + /** + * Set default input method for current group. + * + +From 3891b04380a5dd36b48af753b85c81472e6c03a4 Mon Sep 17 00:00:00 2001 +From: Takuro Ashie +Date: Mon, 7 Mar 2022 12:09:34 +0900 +Subject: [PATCH 2/3] Make sure to show UI on activate + +--- + src/frontend/waylandim/waylandimserver.cpp | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/src/frontend/waylandim/waylandimserver.cpp b/src/frontend/waylandim/waylandimserver.cpp +index 58a4b713f..abbe3d8a4 100644 +--- a/src/frontend/waylandim/waylandimserver.cpp ++++ b/src/frontend/waylandim/waylandimserver.cpp +@@ -168,6 +168,7 @@ void WaylandIMInputContextV1::activate(wayland::ZwpInputMethodContextV1 *ic) { + ic_->modifiersMap(&array); + wl_array_release(&array); + focusIn(); ++ updateUserInterface(UserInterfaceComponent::InputPanel); + } + + void WaylandIMInputContextV1::deactivate(wayland::ZwpInputMethodContextV1 *ic) { + +From 861f59bd40916936daeac796fc74844a43f11aaa Mon Sep 17 00:00:00 2001 +From: daipom +Date: Fri, 14 Jan 2022 11:24:42 +0900 +Subject: [PATCH 3/3] Add function to handle virtual key events to InputContext + +We can use `InputContext::virtualKeyEvent` to handle KeyEvents the same +way as physical key events. +--- + src/frontend/waylandim/waylandimserver.cpp | 41 +++++++++++++++++++--- + src/frontend/waylandim/waylandimserver.h | 2 ++ + src/lib/fcitx/inputcontext.cpp | 18 ++++++++++ + src/lib/fcitx/inputcontext.h | 13 +++++++ + 4 files changed, 70 insertions(+), 4 deletions(-) + +diff --git a/src/frontend/waylandim/waylandimserver.cpp b/src/frontend/waylandim/waylandimserver.cpp +index abbe3d8a4..87bd3da33 100644 +--- a/src/frontend/waylandim/waylandimserver.cpp ++++ b/src/frontend/waylandim/waylandimserver.cpp +@@ -413,13 +413,44 @@ void WaylandIMInputContextV1::keyCallback(uint32_t serial, uint32_t time, + server_->modifiers_, code), + state == WL_KEYBOARD_KEY_STATE_RELEASED, time); + +- if (state == WL_KEYBOARD_KEY_STATE_RELEASED && key == repeatKey_) { ++ processKeyEvent(event, false, serial); ++} ++ ++void WaylandIMInputContextV1::virtualKeyEventImpl(KeyEvent &event) { ++ // `mods_locked` value seems to be 16 usually. ++ // It will be 18 with the capslocked state, ++ // but we don't have to consider about it in virtual key process. ++ if (event.rawKey().hasModifier()) { ++ if (event.rawKey().states().test(KeyState::Shift)) { ++ modifiersCallback(serial_, (uint32_t)KeyState::Shift, 0, 16, 0); ++ } ++ } else { ++ modifiersCallback(serial_, 0, 0, 16, 0); ++ } ++ ++ processKeyEvent(event, true, serial_); ++} ++ ++void WaylandIMInputContextV1::processKeyEvent(KeyEvent &event, ++ bool isVirtualKey, ++ uint32_t serial) { ++ const uint32_t key = event.rawKey().code() > 8 ? event.rawKey().code() - 8 ++ : 0; ++ const auto state = event.isRelease() ? WL_KEYBOARD_KEY_STATE_RELEASED ++ : WL_KEYBOARD_KEY_STATE_PRESSED; ++ ++ const auto cancelRepeat = isVirtualKey ++ ? (state == WL_KEYBOARD_KEY_STATE_RELEASED) ++ // Strict condition for physical keys. ++ : (state == WL_KEYBOARD_KEY_STATE_RELEASED && key == repeatKey_); ++ ++ if (cancelRepeat) { + timeEvent_->setEnabled(false); + } else if (state == WL_KEYBOARD_KEY_STATE_PRESSED && +- xkb_keymap_key_repeats(server_->keymap_.get(), code)) { ++ xkb_keymap_key_repeats(server_->keymap_.get(), event.rawKey().code())) { + if (repeatRate_) { + repeatKey_ = key; +- repeatTime_ = time; ++ repeatTime_ = event.time(); + repeatSym_ = event.rawKey().sym(); + // Let's trick the key event system by fake our first. + // Remove 100 from the initial interval. +@@ -430,11 +461,13 @@ void WaylandIMInputContextV1::keyCallback(uint32_t serial, uint32_t time, + + WAYLANDIM_DEBUG() << event.key().toString() + << " IsRelease=" << event.isRelease(); ++ + if (!keyEvent(event)) { +- ic_->key(serial, time, key, state); ++ ic_->key(serial, event.time(), key, state); + } + server_->display_->flush(); + } ++ + void WaylandIMInputContextV1::modifiersCallback(uint32_t serial, + uint32_t mods_depressed, + uint32_t mods_latched, +diff --git a/src/frontend/waylandim/waylandimserver.h b/src/frontend/waylandim/waylandimserver.h +index 1ffed14db..3b7871a2f 100644 +--- a/src/frontend/waylandim/waylandimserver.h ++++ b/src/frontend/waylandim/waylandimserver.h +@@ -84,6 +84,7 @@ class WaylandIMInputContextV1 : public InputContext { + void deactivate(wayland::ZwpInputMethodContextV1 *id); + + protected: ++ void virtualKeyEventImpl(KeyEvent &event) override; + void commitStringImpl(const std::string &text) override { + if (!ic_) { + return; +@@ -120,6 +121,7 @@ class WaylandIMInputContextV1 : public InputContext { + void keymapCallback(uint32_t format, int32_t fd, uint32_t size); + void keyCallback(uint32_t serial, uint32_t time, uint32_t key, + uint32_t state); ++ void processKeyEvent(KeyEvent &event, bool isVirtualKey, uint32_t serial); + void modifiersCallback(uint32_t serial, uint32_t mods_depressed, + uint32_t mods_latched, uint32_t mods_locked, + uint32_t group); +diff --git a/src/lib/fcitx/inputcontext.cpp b/src/lib/fcitx/inputcontext.cpp +index e6138036f..1fdc16358 100644 +--- a/src/lib/fcitx/inputcontext.cpp ++++ b/src/lib/fcitx/inputcontext.cpp +@@ -243,6 +243,19 @@ bool InputContext::keyEvent(KeyEvent &event) { + return result; + } + ++void InputContext::virtualKeyEvent(KeyEvent &event) { ++ decltype(std::chrono::steady_clock::now()) start; ++ // Don't query time if we don't want log. ++ if (::keyTrace().checkLogLevel(LogLevel::Debug)) { ++ start = std::chrono::steady_clock::now(); ++ } ++ virtualKeyEventImpl(event); ++ FCITX_KEYTRACE() << "KeyEvent handling time: " ++ << std::chrono::duration_cast( ++ std::chrono::steady_clock::now() - start) ++ .count(); ++} ++ + void InputContext::invokeAction(InvokeActionEvent &event) { + FCITX_D(); + RETURN_IF_HAS_NO_FOCUS(); +@@ -333,6 +346,11 @@ StatusArea &InputContext::statusArea() { + return d->statusArea_; + } + ++void InputContext::virtualKeyEventImpl(KeyEvent &event) { ++ FCITX_D(); ++ d->postEvent(event); ++} ++ + void InputContext::updateClientSideUIImpl() {} + + InputContextEventBlocker::InputContextEventBlocker(InputContext *inputContext) +diff --git a/src/lib/fcitx/inputcontext.h b/src/lib/fcitx/inputcontext.h +index 58363b506..8e56f7628 100644 +--- a/src/lib/fcitx/inputcontext.h ++++ b/src/lib/fcitx/inputcontext.h +@@ -133,6 +133,10 @@ class FCITXCORE_EXPORT InputContext : public TrackableObject { + /// Send a key event to current input context. + bool keyEvent(KeyEvent &event); + ++ /// Send a virtual key event to current input context. ++ /// This handles the event the same way as physical key events. ++ void virtualKeyEvent(KeyEvent &event); ++ + /// Returns whether the input context holds the input focus. Input context + /// need to have focus. + bool hasFocus() const; +@@ -253,6 +257,15 @@ class FCITXCORE_EXPORT InputContext : public TrackableObject { + void updateProperty(const InputContextPropertyFactory *factory); + + protected: ++ /** ++ * Process the virtual key event. ++ * ++ * @param event KeyEvent ++ * ++ * @see virtualKeyEvent ++ */ ++ virtual void virtualKeyEventImpl(KeyEvent &event); ++ + /** + * Send the committed string to client + * diff --git a/packages/default.nix b/packages/default.nix index 09527553..42b6d82b 100644 --- a/packages/default.nix +++ b/packages/default.nix @@ -72,6 +72,8 @@ inputs: rec winjob = inputs.pkgs.callPackage ./winjob { stdenv = inputs.pkgs.gcc14Stdenv; }; sockpp = inputs.pkgs.callPackage ./sockpp.nix { src = inputs.topInputs.sockpp; }; git-lfs-transfer = inputs.pkgs.callPackage ./git-lfs-transfer.nix { src = inputs.topInputs.git-lfs-transfer; }; + fcitx5-virtualkeyboard-ui = inputs.pkgs.callPackage ./fcitx5-virtualkeyboard-ui.nix + { src = inputs.topInputs.fcitx5-virtualkeyboard-ui; }; fromYaml = content: builtins.fromJSON (builtins.readFile (inputs.pkgs.runCommand "toJSON" {} diff --git a/packages/fcitx5-virtualkeyboard-ui.nix b/packages/fcitx5-virtualkeyboard-ui.nix new file mode 100644 index 00000000..65ed7033 --- /dev/null +++ b/packages/fcitx5-virtualkeyboard-ui.nix @@ -0,0 +1,18 @@ +{ + lib, stdenv, src, substituteAll, + cmake, extra-cmake-modules, fcitx5, gettext, fmt, cairo, expat, libXdmcp, pango, pcre2, util-linux, libselinux, + libsepol, fribidi, libthai, libdatrie, xorg, kdePackages, egl-wayland, eglexternalplatform, gdk-pixbuf, lerc +}: stdenv.mkDerivation +{ + name = "fcitx5-virtualkeyboard-ui"; + inherit src; + cmakeFlags = [ "-DWAYLAND_PROTOCOLS_PKGDATADIR=${kdePackages.wayland-protocols}/share/wayland-protocols" ]; + + nativeBuildInputs = [ cmake extra-cmake-modules ]; + buildInputs = + [ + fcitx5 gettext fmt cairo expat libXdmcp pango pcre2 util-linux libselinux libsepol fribidi libthai libdatrie + xorg.xcbutil xorg.xcbutilwm xorg.xcbutilkeysyms kdePackages.wayland kdePackages.wayland-protocols egl-wayland + eglexternalplatform gdk-pixbuf lerc + ]; +}