DUAL-FUNCTION-KEYS(1) User Manuals DUAL-FUNCTION-KEYS(1)

interception - کلیدهای دوکاره (dual function keys)

ضربه زدن برای یک کلید، نگه داشتن برای کلیدی دیگر.

بسیار کاربردی برای کلیدهای تبدیل‌گر (modifier) مانند: نگه داشتن برای ctrl، ضربه زدن برای delete.

نجات‌دهنده‌ی دست برای افرادی با محدودیت حرکتی انگشتان.

پلاگینی برای ابزارهای interception (https://gitlab.com/interception/linux).

1.
چند نگاشت در /etc/interception/dual-function-keys/my-mappings.yaml ایجاد کنید. نمونه‌های زیادی در (https://gitlab.com/interception/linux/plugins/dual-function-keys/-/tree/master/doc/examples.md) وجود دارد.
2.
پیکربندی udevmon مربوط به ابزارهای interception (https://gitlab.com/interception/linux) خود را بسازید: /etc/interception/udevmon.d/my-keyboards.yaml. برای شروع می‌توانید از پیکربندی من استفاده کنید.
3.
فعال‌سازی udevmon: sudo systemctl enable udevmon
4.
راه‌اندازی (مجدد) udevmon: sudo systemctl restart udevmon
5.
بررسی مشکلات: journalctl -u udevmon. بی‌خبری خوش‌خبری است (عدم نمایش پیام نشان‌دهنده عملکرد صحیح است). می‌توانید با خیال راحت از پیام‌های ignoring /etc/interception/udevmon.yaml, reason: bad file: /etc/interception/udevmon.yaml صرف‌نظر کنید.

در این مثال‌ها از کلید شیفت چپ (LS) استفاده خواهیم کرد.

این کلید به‌گونه‌ای پیکربندی شده است که ضربه زدن به آن عمل delete (DE) و نگه داشتن آن عمل LS را انجام می‌دهد.

- KEY: KEY_LEFTSHIFT
  TAP: KEY_DELETE
  HOLD: KEY_LEFTSHIFT

فشردن و رها کردن کلید LS در بازه زمانی TAP_MILLIS (پیش‌فرض ۲۰۰ میلی‌ثانیه) برای عمل DE.

به‌طور پیش‌فرض، تا زمان کامل شدن ضربه، LS دریافت می‌شود. برای مشاهده سایر گزینه‌ها به بخش‌های زیر مراجعه کنید.

                <---------200ms--------->     <---------200ms--------->
keyboard:       LS↓      LS↑                  LS↓                          LS↑
computer sees:  LS↓      LS↑ DE↓ DE↑          LS↓                          LS↑

ضربه بزنید و سپس مجدداً در بازه DOUBLE_TAP_MILLIS (پیش‌فرض ۱۵۰ میلی‌ثانیه) فشار دهید تا DE نگه داشته شود.

                             <-------150ms------->
                <---------200ms--------->
keyboard:       LS↓         LS↑             LS↓               LS↑
computer sees:  LS↓         LS↑ DE↓ DE↑     DE↓ ..(repeats).. DE↑

تا زمانی که در بازه زمانی DOUBLE_TAP_MILLIS باشید، می‌توانید به زدن ضربه‌های دوتایی ادامه دهید.

اگر کلید دیگری را در بازه زمانی TAP_MILLIS فشار داده یا رها کنید، عمل ضربه زدن (tap) انجام نخواهد شد.

این قابلیت به‌ویژه برای کلیدهای تبدیل‌گر بسیار مفید است، برای مثال اجرای سریع ctrl-C. در این مثال، کلید a را در طول این بازه زمانی فشار می‌دهیم.

ضربه دوتایی پس از مصرف شدن عمل نمی‌کند؛ ابتدا باید یک ضربه ساده بزنید.

رویدادهای ماوس و تاچ‌پد (EV_REL و EV_ABS) نیز می‌توانند ضربه‌ها را مصرف کنند، با این حال باید از یک پیکربندی چنددستگاهی (Multiple Devices) استفاده کنید.

                                                               <-------150ms------->
                                                 <---------200ms--------->
                                 <-------150ms------->
                <---------200ms--------->
keyboard:       LS↓      a↓  a↑  LS↑             LS↓          LS↑           LS↓
computer sees:  LS↓      a↓  a↑  LS↑             LS↓          LS↑ DE↓ DE↑   DE↓ ..(repeats)..

[تصویر: وضعیت بسته‌بندی (https://repology.org/badge/vertical-allrepos/interception-dual-function-keys.svg)] (https://repology.org/project/interception-dual-function-keys/versions)

وابستگی‌های زمان اجرا را ببینید (https://gitlab.com/interception/linux/tools#runtime-dependencies).

ابتدا ابزارهای Interception Tools (https://gitlab.com/interception/linux/tools) را نصب کنید.

git clone https://gitlab.com/interception/linux/plugins/dual-function-keys.git
cd dual-function-keys
make && sudo make install

پیشوند نصب به‌طور پیش‌فرض /usr/local است. این مقدار را می‌توان در config.mk بازنویسی کرد..SH "پیکربندی (CONFIGURATION)"

دو بخش برای پیکربندی وجود دارد: dual-function-keys و udevmon، که dual-function-keys را اجرا می‌کند.

به مثال‌ها (https://gitlab.com/interception/linux/plugins/dual-function-keys/-/tree/master/doc/examples.md) که شامل پیکربندی‌های dual-function-keys و udevmon.yaml است مراجعه کنید.

این فایل yaml به طور معمول در /etc/interception/dual-function-keys قرار دارد.

می‌توانید از کدهای کلید خام (عدد صحیح) استفاده کنید، با این حال استفاده از رشته‌های #define شده از input-event-codes.h (https://github.com/torvalds/linux/blob/master/include/uapi/linux/input-event-codes.h) آسان‌تر است.

# optional
TIMING:
  TAP_MILLISEC: <integer>
  DOUBLE_TAP_MILLISEC: <integer>
  SYNTHETIC_KEYS_PAUSE_MILLISEC: <integer>
# necessary
MAPPINGS:
  - KEY: <integer | string>
    TAP: [ <integer | string>, ... ]
    HOLD: [ <integer | string>, ... ]
    # optional
    HOLD_START: [ AFTER_PRESS | AFTER_RELEASE | BEFORE_CONSUME | BEFORE_CONSUME_OR_RELEASE ]
  - KEY: ...

مثال ما از بخش پیشین به این صورت است:

TIMING:
  TAP_MILLISEC: 200
  DOUBLE_TAP_MILLISEC: 150
MAPPINGS:
  - KEY: KEY_LEFTSHIFT
    TAP: KEY_DELETE
    HOLD: KEY_LEFTSHIFT

می‌توانید TAP را به عنوان یک “ترکیب (combo)” پیکربندی کنید، که چندین کلید را به ترتیب فشرده و سپس رها می‌کند، برای نمونه space cadet (:

MAPPINGS:
  - KEY: KEY_LEFTSHIFT
    TAP: [ KEY_LEFTSHIFT, KEY_9, ]
    HOLD: KEY_LEFTSHIFT

می‌توانید HOLD را به عنوان یک “ترکیب (combo)” پیکربندی کنید، که چندین کلید را به ترتیب فشرده و سپس رها می‌کند، برای نمونه کلید تبدیل‌کننده هایپر (hyper modifier):

MAPPINGS:
  - KEY: KEY_TAB
    TAP: KEY_TAB
    HOLD: [ KEY_LEFTCTRL, KEY_LEFTMETA, KEY_LEFTALT, ]

به‌طور پیش‌فرض، یک مکث ۲۰ میلی‌ثانیه‌ای بین کلیدها در “ترکیب” وجود خواهد داشت. این مقدار قابل تغییر است:

TIMING:
    SYNTHETIC_KEYS_PAUSE_MILLISEC: 10

می‌توانید به صورت اختیاری از HOLD_START برای پیکربندی رفتار کلیدهای HOLD استفاده کنید.

اگر HOLD_START مشخص نشده باشد، برابر AFTER_PRESS یا یک مقدار ناشناخته باشد، رفتار پیش‌فرض اعمال خواهد شد.

کلیدهای HOLD پیش از مصرف شدن KEY فشرده می‌شوند، و هنگامی که KEY رها شود، رها می‌شوند. بنابراین هنگامی که روی KEY ضربه زده (tap) می‌شود، هیچ کلید اضافه‌ای به جز کلیدهای TAP ارسال نمی‌شود، در حالی که کلیدهای HOLD همچنان می‌توانند به عنوان کلیدهای تبدیل‌کننده (modifiers) استفاده شوند.

MAPPINGS:
  - KEY: KEY_LEFTSHIFT
    TAP: KEY_DELETE
    HOLD: KEY_LEFTSHIFT
    HOLD_START: BEFORE_CONSUME
                <---------200ms--------->     <---------200ms--------->
keyboard:       LS↓      LS↑                  LS↓                          LS↑
computer sees:           DE↓ DE↑
                                                               <-------150ms------->
                                                 <---------200ms--------->
                                 <-------150ms------->
                <---------200ms--------->
keyboard:       LS↓      a↓  a↑   LS↑             LS↓          LS↑           LS↓
computer sees:       LS↓ a↓  a↑   LS↑                          DE↓ DE↑       DE↓ ..(repeats)..

رفتار مشابه BEFORE_CONSUME است، با این تفاوت که وقتی KEY رها می‌شود و پیش از آن نه ضربه خورده و نه مصرف شده است، کلیدهای HOLD به ترتیب فشرده شده و سپس به ترتیب رها می‌شوند.

MAPPINGS:
  - KEY: KEY_LEFTSHIFT
    TAP: KEY_DELETE
    HOLD: KEY_LEFTSHIFT
    HOLD_START: BEFORE_CONSUME_OR_RELEASE
                <---------200ms--------->     <---------200ms--------->
keyboard:       LS↓      LS↑                  LS↓                          LS↑
computer sees:           DE↓ DE↑                                           LS↓ LS↑
حالت نگه‌داشتن (Hold) تنها پس از رها شدن کلید آغاز می‌شود، به شرطی که زمان TAP_MILLISEC سپری شده باشد.
این نوع شروع نگه‌داشتن تحت تأثیر هیچ نوع مصرف‌شدنی (consumption) قرار نمی‌گیرد.
MAPPINGS:
  - KEY: KEY_A
    TAP: KEY_A
    HOLD: [KEY_LEFTSHIFT, KEY_A]
    HOLD_START: AFTER_RELEASE
                <---------200ms--------->     <---------200ms--------->
keyboard:       a↓       a↑                   a↓                           a↑
computer sees:           a↓  a↑                                            A↓ A↑

یک کلید تبدیل‌گر (modifier) یکسان را به دو کلیدی که قصد دارید هم‌زمان فشار دهید اختصاص ندهید، چرا که با یکدیگر تداخل خواهند داشت. از نسخه‌های چپ و راست تبدیل‌گرها استفاده کنید؛ به عنوان مثال alt-tab با space-caps:

MAPPINGS:
  - KEY: KEY_CAPSLOCK
    TAP: KEY_TAB
    HOLD: KEY_LEFTALT
  - KEY: KEY_SPACE
    TAP: KEY_SPACE
    HOLD: KEY_RIGHTALT

در غیر این صورت، می‌توانید از HOLD_START: BEFORE_CONSUME یا HOLD_START: BEFORE_CONSUME_OR_RELEASE استفاده کنید و سپس اختصاص تبدیل‌گر یکسان بلامانع خواهد بود:

MAPPINGS:
  - KEY: KEY_CAPSLOCK
    TAP: KEY_TAB
    HOLD: KEY_LEFTALT
    HOLD_START: BEFORE_CONSUME_OR_RELEASE
  - KEY: KEY_SPACE
    TAP: KEY_SPACE
    HOLD: KEY_LEFTALT
    HOLD_START: BEFORE_CONSUME_OR_RELEASE

باید به udevmon اطلاع داده شود که می‌خواهیم از کلیدهای دوکاره (Dual Function Keys) استفاده کنیم. برای شرح کامل، بخش نحوه کار (https://gitlab.com/interception/linux/tools#how-it-works) را ببینید.

- JOB: "intercept -g $DEVNODE | dual-function-keys -c </path/to/dual-function-keys.yaml> | uinput -d $DEVNODE"
  DEVICE:
    NAME: <keyboard name>

نام دستگاه را می‌توان با اجرای دستور زیر مشخص کرد:

sudo uinput -p -d /dev/input/by-id/X

که در آن X دستگاهی است که نامی شبیه به صفحه‌کلید شما دارد. اطمینان حاصل کنید که تمام EV_KEYها در زیر بخش EVENTS وجود داشته باشند. اگر نمی‌توانید صفحه‌کلید خود را در /dev/input/by-id بیابید، دستگاه‌های موجود در /dev/input را مستقیماً بررسی کنید.

برای اطلاعات بیشتر دربارهٔ uinput -p، به Interception Tools: How It Works (https://gitlab.com/interception/linux/tools#how-it-works) مراجعه کنید.

معمولاً این نام برای شناسایی یکتای صفحه‌کلید کافی است، با این حال برخی صفحه‌کلیدها چندین دستگاه مانند یک ماوس مجازی را ثبت می‌کنند. می‌توانید dual-function-keys را برای همهٔ دستگاه‌ها اجرا کنید، اما ترجیح من اجرای آن صرفاً برای صفحه‌کلید واقعی است.

فایل /etc/interception/udevmon.d/my-keyboards.yaml من:

- JOB: "intercept -g $DEVNODE | dual-function-keys -c /etc/interception/dual-function-keys/home-row-modifiers.yaml | uinput -d $DEVNODE"
  DEVICE:
    NAME: "Minimalist Keyboard ABC"
    EVENTS:
      EV_KEY: [ KEY_LEFTSHIFT ]
- JOB: "intercept -g $DEVNODE | dual-function-keys -c /etc/interception/dual-function-keys/thumb-cluster.yaml | uinput -d $DEVNODE"
  DEVICE:
    NAME: "Split Keyboard XYZ"
    EVENTS:
      EV_KEY: [ KEY_LEFTSHIFT ]

هنگام استفاده از ورودی‌های چند دستگاه مختلف، مثلاً ctrl-scroll، ممکن است برای اینکه dual-function-keys بین این دستگاه‌ها کار کند (مثلاً اسکرول، ctrl را مصرف کند)، تسهیم کردن (mux) دستگاه‌ها با یکدیگر (https://gitlab.com/interception/linux/tools#mux) لازم باشد.

پیکربندی نمونه udevmon برای ماوس و صفحه‌کلید:

- CMD: mux -c dfk -c my-keyboard -c my-mouse
- JOB:
  - mux -i dfk | dual-function-keys -c /etc/interception/dual-function-keys/my-cfg.yaml | mux -o my-keyboard -o my-mouse
  - mux -i my-keyboard | uinput -c /etc/interception/udevmon.d/my-keyboard.yaml
  - mux -i my-mouse | uinput -c /etc/interception/udevmon.d/my-mouse.yaml
- JOB: intercept -g $DEVNODE | mux -o dfk
  DEVICE:
    NAME: AT Translated Set 2 keyboard
    EVENTS:
      EV_KEY: [ KEY_LEFTCTRL ]
- JOB: intercept -g $DEVNODE | mux -o dfk
  DEVICE:
    NAME: Razer Razer Naga Trinity
    EVENTS:
      EV_REL: [REL_WHEEL]
      EV_KEY: [BTN_LEFT]

در مثال بالا، my-keyboard.yaml و my-mouse.yaml نمایانگر دستگاه‌های مجازی هستند که udevmon برای خروجی دادن رویدادها ایجاد خواهد کرد. آن‌ها یک‌بار از خود دستگاه تولید می‌شوند؛ به عنوان مثال:

sudo uinput -p -d /dev/input/by-id/usb-my-keyboard-kbd > my-keyboard.yaml

یک راهکار جایگزین، در صورتی که ریسک آن را می‌پذیرید (https://gitlab.com/interception/linux/plugins/dual-function-keys/-/issues/31#note_725722450)، این است که پیکربندی دستگاه مجازی را بی‌درنگ و در حین کار (on the fly) تولید کنید؛ به عنوان مثال:

- CMD: mux -c dfk -c my-keyboard -c my-mouse
- JOB:
  - mux -i dfk | dual-function-keys -c /etc/interception/dual-function-keys/my-cfg.yaml | mux -o my-keyboard -o my-mouse
  - mux -i my-keyboard | uinput -d /dev/input/by-path/my-keyboard-event-kbd
  - mux -i my-mouse | uinput -d /dev/input/by-id/usb-my-mouse-event-mouse
- JOB: intercept -g $DEVNODE | mux -o dfk
  DEVICE:
    LINK: /dev/input/by-path/my-keyboard-event-kbd
- JOB: intercept -g $DEVNODE | mux -o dfk
  DEVICE:
    LINK: /dev/input/by-id/usb-my-mouse-event-mouse
همانند همیشه یک نکته وجود دارد: dual-function-keys بر روی keycodes خام عمل می‌کند، نه keysyms، آن‌گونه که توسط X11 یا Wayland دیده می‌شوند.
اگر هر چیزی دارید که نگاشت keycode->keysym را دستکاری می‌کند، مانند XKB (https://www.x.org/wiki/XKB) یا xmodmap (https://wiki.archlinux.org/index.php/Xmodmap)، توجه داشته باشید که dual-function-keys پیش از آن‌ها اعمال می‌شود.
برخی از کاربردهای رایج XKB که ممکن است در پیکربندی X11 شما یافت شوند:
Option "XkbModel" "pc105"
Option "XKbLayout" "us"
Option "XkbVariant" "dvp"
Option "XkbOptions" "caps:escape"

لطفاً یک issue ثبت کنید.

dual-function-keys برای برطرف‌کردن نیازهای من ساخته شده است. بسیار علاقه‌مندم ایده‌های شما را بشنوم و به عملی‌شدن آن‌ها کمک کنم.

مثل همیشه، از ارسال PR به‌گرمی استقبال می‌شود.

؟'>https://qmk.fm) بهره می‌برد. چرا مستقیماً از Tap-Hold (https://docs.qmk.fm/#/tap_hold) استفاده نمی‌کنید؟

نکته‌سنجی خوبی بود! آن قابلیت در واقع عملکرد مشابهی با dual-function-keys ارائه می‌دهد. متأسفانه معایبی وجود دارد:

1.
صفحه‌کلیدهای اندکی از QMK Firmware استفاده می‌کنند.
2.
مشکلاتی در آن قابلیت وجود دارد، همان‌طور که در مستندات Tap-Hold (https://docs.qmk.fm) ذکر شده است.
3.
این قابلیت نیازمند پردازنده‌ای سریع در صفحه‌کلید است. آزمایش‌های غیرعلمی من با یک Ergodox (~800 اسکن در ثانیه) و HHKB (~140) نشان داد که صفحه‌کلید کندتر، اسفنجی شده و دقت آن تا حد بلااستفاده بودن کاهش می‌یابد.

؟'>https://github.com/alols/xcape) استفاده نمی‌کنید؟

برنامه xcape تنها قابلیت ساده tap/hold را ارائه می‌دهد. به نظر می‌رسد افزودن باقی قابلیت‌ها با استفاده از سازوکارهای XTestFakeKeyEvent آن بسیار دشوار (شاید غیرممکن؟) باشد.

اطمینان حاصل کنید که مدیر پنجره (window manager) شما این ترکیب کلید را رهگیری و مسدود نمی‌کند.

مقدار DOUBLE_TAP_MILLISEC را برابر با 0 قرار دهید. بخش Key Combinations, No Double Tap (https://gitlab.com/interception/linux/plugins/dual-function-keys/-/blob/master/doc/examples.md#key-combinations-no-double-tap) را ببینید.

لطفاً این مخزن را فورک کرده و یک PR ارسال کنید.

اگر در حال ایجاد تغییرات در مستندات هستید، لطفاً فایل pandoc با نام dual-function-keys.md را ویرایش کرده و دستور make doc را اجرا کنید.

لطفاً مطمئن شوید که این README.md و صفحه راهنما dual-function-keys.1 دارای تغییرات شما هستند و هر سه را کامیت کنید.

می‌توانید صفحه راهنمای تولیدشده را با دستور man -l dual-function-keys.1 آزمایش کنید.

همانند همیشه، لطفاً قواعد .editorconfig را رعایت فرمایید.

کپی‌رایت © 2020 Alexander Courtis

Alexander Courtis.

2020/12/25 Dual Function Keys