مصحح تخطيط Xswitcher لنظام التشغيل Linux: الخطوة الثانية

كما المنشور السابق (xswitcher في مرحلة "إثبات المفهوم") تلقى الكثير من التعليقات البناءة (وهو لطيف)واصلت قضاء وقت فراغي في تطوير المشروع. والآن أريد أن أقضي القليل من وقتك... لن تكون الخطوة الثانية معتادة تمامًا: اقتراح/مناقشة تصميم التكوين.

مصحح تخطيط Xswitcher لنظام التشغيل Linux: الخطوة الثانية

بطريقة ما اتضح أن المبرمجين العاديين يجدون أنه من الممل للغاية إعداد كل هذه الضوابط.

وحتى لا يكون لا أساس له من الصحة، في الداخل مثال على ما أتعامل معه.
بشكل عام، تم تصميم Apache Kafka & ZooKeeper بشكل ممتاز (وتنفيذهما بشكل جيد).
- إعدادات؟ لكنها مملة! غبي XML (لأنه "خارج الصندوق").
- أوه، هل تريد أيضًا الرباط الصليبي الأمامي؟ لكنها مملة جدا! خطأ في الصنبور... شيء من هذا القبيل.

لكن في عملي الأمر عكس ذلك تماماً. يمين (للأسف، لم تكن هذه هي المرة الأولى تقريبًا) يتيح لك النموذج المصمم الاستمرار بشكل أكبر بسهولة وبشكل طبيعي (بالكاد) تجميع رسم تخطيطي.

لقد عثرت مؤخرًا على مقالة عن حبري حول العمل الشاق الذي يقوم به علماء البيانات...
اتضح أن هذه اللحظة قد تحققت بالكامل بالنسبة لهم. وفي ممارستي، كما يقولون، "نسخة خفيفة". نماذج متعددة المجلدات، ومبرمجون متمرسون مع OOP جاهزون، وما إلى ذلك. - سيظهر كل هذا لاحقًا عندما/إذا أقلع. لكن المصمم يحتاج أن يبدأ من مكان ما هنا والآن.

أوضح ماذا تقصد. لقد أخذت TOML كأساس نحوي من هذا المواطن.

لأنه (تومل) من ناحية، قابلة للتحرير البشري. ومن ناحية أخرى، تتم ترجمته 1:1 إلى أي من تركيبات الجملة الأكثر شيوعًا: XML، JSON، YAML.
علاوة على ذلك، فإن التطبيق الذي استخدمته من "github.com/BurntSushi/toml"، على الرغم من أنه ليس الأكثر شيوعًا (بناء الجملة 1.4)، إلا أنه متوافق من الناحية النحوية مع نفس JSON ("المدمج").

وهذا يعني، إذا كنت ترغب في ذلك، يمكنك ببساطة أن تقول "اذهب عبر الغابة باستخدام TOML الخاص بك، أريد XXX" و"تصحيح" الكود بسطر واحد فقط.

وبالتالي، إذا كنت تريد كتابة بعض النوافذ لتكوين xswitcher (لست متأكد) لا يُتوقع حدوث مشكلات "مع هذا التكوين اللعين الخاص بك."

بالنسبة لجميع الآخرين، يعتمد بناء الجملة على "مفتاح = قيمة" (وحرفيًا بضعة خيارات أكثر تعقيدًا، مثل = [some, that, array]) اعتقد
مريحة بشكل حدسي.
ما هو مثير للاهتمام هو ذلك "أحرق" في نفس الوقت تقريبًا (حوالي عام 2013). فقط، على عكسي، دخل مؤلف TOML على نطاق مناسب.

لذلك، أصبح من الأسهل بالنسبة لي الآن تعديل تنفيذه بما يناسبني، وليس العكس.

بشكل عام، نحن نأخذ TOML (يشبه إلى حد كبير Windows INI القديم). ولدينا تكوين نصف فيه كيفية إرفاق سلسلة من الخطافات اعتمادًا على مجموعة أحدث رموز المسح من لوحة المفاتيح. وفيما يلي، قطعة قطعة، ما حدث حتى الآن. وشرح لماذا قررت بهذه الطريقة.

0 التجريدات الأساسية

  • مسح تسميات التعليمات البرمجية. من المؤكد أنه يجب القيام بشيء حيال ذلك، نظرًا لأن الرموز الرقمية ليست قابلة للقراءة على الإطلاق من قبل الإنسان (هذا أنا فقط com.loloswitcher).
    لقد استخرجت "ecodes.go" من "golang-evdev" (كنت كسولًا جدًا بحيث لم أتمكن من النظر إلى المصدر الأصلي، على الرغم من أن المؤلف أشار إليه ثقافيًا تمامًا). لقد صححت قليلاً (في الوقت الحالي) شيئًا كان مخيفًا للغاية. مثل "LEFTBRACE" → "L_BRACE".
  • بالإضافة إلى ذلك، قدم مفهوم "مفاتيح الحالة". نظرًا لأن القواعد النحوية المستخدمة لا تسمح بمقاطع طويلة. (لكنه يسمح لك بالتحقق بأقل قدر من الحمل. إذا كنت تستخدم التسجيل "المباشر" فقط.)
  • سيكون هناك "أداة إلغاء النسخ" مدمجة لما يتم الضغط عليه. وبالتالي، سيتم كتابة الحالة "تكرار"=2 واحد الوقت.

1. قسم القوالب

[Templates] # "@name@" to simplify expressions
 # Words can consist of these chars (regex)
 "WORD" = "([0-9A-Z`;']|[LR]_BRACE|COMMA|DOT|SLASH|KP[0-9])"

مما تتكون كلمة لغة بشرية ذات تدوين صوتي؟ (إما أن تكون مسألة رسوم بيانية والمعروفة أيضًا باسم "الهيروغليفية")؟ نوع من "الورقة" الرهيبة. لذلك، سأقدم على الفور مفهوم "القالب".

2. ماذا تفعل عند النقر على شيء ما (وصول رمز مسح آخر)

[ActionKeys]
 # Collect key and do the test for command sequence
 # !!! Repeat codes (code=2) must be collected once per key!
 Add = ["1..0", "=", "BS", "Q..]", "L_CTRL..CAPS", "N_LOCK", "S_LOCK",
        "KP7..KPDOT", "R_CTRL", "KPSLASH", "R_ALT", "KPEQUAL..PAUSE",
        "KPCOMMA", "L_META..COMPOSE", "KPLEFTPAREN", "KPRIGHTPAREN"]

 # Drop all collected keys, including this.  This is default action.
 Drop = ["ESC", "-", "TAB", "ENTER", "KPENTER", "LINEFEED..POWER"]
 # Store extra map for these keys, when any is in "down" state.
 # State is checked via "OFF:"|"ON:" conditions in action.
 # (Also, state of these keys must persist between buffer drops.)
 # ??? How to deal with CAPS and "LOCK"-keys ???
 StateKeys = ["L_CTRL", "L_SHIFT", "L_ALT", "L_META", "CAPS", "N_LOCK", "S_LOCK",
              "R_CTRL", "R_SHIFT", "R_ALT", "R_META"]

 # Test only, but don't collect.
 # E.g., I use F12 instead of BREAK on dumb laptops whith shitty keyboards (new ThinkPads)
 Test = ["F1..F10", "ZENKAKUHANKAKU", "102ND", "F11", "F12",
          "RO..KPJPCOMMA", "SYSRQ", "SCALE", "HANGEUL..YEN",
          "STOP..SCROLLDOWN", "NEW..MAX"]

هناك 768 رموز في المجموع. (ولكن "فقط في حالة" قمت بإدخال "المفاجآت" في كود xswitcher).
لقد وصفت في الداخل ملء المصفوفة بروابط للوظائف "ما يجب فعله". في جولانج هذا هو (فجأة) اتضح أنها مريحة وواضحة.

  • أخطط لتقليل "الإسقاط" إلى الحد الأدنى في هذا المكان. لصالح معالجة أكثر مرونة (سأعرضها أدناه).

3. الجدول مع فئات النافذة

# Some behaviour can depend on application currently doing the input.
[[WindowClasses]]
 # VNC, VirtualBox, qemu etc. emulates there input independently, so never intercept.
 # With the exception of some stupid VNC clients, which does high-level (layout-based) keyboard input.
 Regex = "^VirtualBox"
 Actions = "" # Do nothing while focus stays in VirtualBox

[[WindowClasses]]
 Regex = "^konsole"
 # In general, mouse clicks leads to unpredictable (at the low-level where xswitcher resides) cursor jumps.
 # So, it's good choise to drop all buffers after click.
 # But some windows, e.g. terminals, can stay out of this problem.
 MouseClickDrops = 0
 Actions = "Actions"

[[WindowClasses]] # Default behaviour: no Regex (or wildcard like ".")
 MouseClickDrops = 1
 Actions = "Actions"

صفوف الجدول موجودة بين قوسين مربعين مع اسمها. لم يكن من الممكن أن يكون الأمر أسهل على الفور. اعتمادًا على النافذة النشطة حاليًا، يمكنك تحديد الخيارات التالية:

  • مجموعتك الخاصة من "المفاتيح السريعة" "Actions =...". إذا لم يكن/فارغًا، فلا تفعل شيئًا.
  • قم بتبديل "MouseClickDrops" - ما يجب فعله عند اكتشاف نقرة بالماوس. نظرًا لأنه عند النقطة التي يتم فيها تشغيل xswitcher لا توجد تفاصيل حول "مكان النقر"، فإننا نقوم بإعادة تعيين المخزن المؤقت افتراضيًا. لكن في المحطات الطرفية (على سبيل المثال) ليس عليك القيام بذلك (عادة).

4. يؤدي تسلسل واحد (أو عدة) من النقرات إلى تشغيل خطاف أو آخر

# action = [ regex1, regex2, ... ]
# "CLEAN" state: all keys are released
[Actions]
# Inverse regex is hard to understand, so extract negation to external condition.
# Expresions will be checked in direct order, one-by-one. Condition succceds when ALL results are True.
 # Maximum key sequence length, extra keys will be dropped. More length - more CPU.
 SeqLength = 8
 # Drop word buffer and start collecting new one
 NewWord = [ "OFF:(CTRL|ALT|META)  SEQ:(((BACK)?SPACE|[LR]_SHIFT):[01],)*(@WORD@:1)", # "@WORD@:0" then collects the char
             "SEQ:(@WORD@:2,@WORD@:0)", # Drop repeated char at all: unlikely it needs correction
             "SEQ:((KP)?MINUS|(KP)?ENTER|ESC|TAB)" ] # Be more flexible: chars line "-" can start new word, but must not completelly invalidate buffer!
 # Drop all buffers
 NewSentence = [ "SEQ:(ENTER:0)" ]

 # Single char must be deleted by single BS, so there is need in compose sequence detector.
 Compose = [ "OFF:(CTRL|L_ALT|META|SHIFT)  SEQ:(R_ALT:1,(R_ALT:2,)?(,@WORD@:1,@WORD@:0){2},R_ALT:0)" ]

 "Action.RetypeWord" = [ "OFF:(CTRL|ALT|META|SHIFT)  SEQ:(PAUSE:0)" ]
 "Action.CyclicSwitch" = [ "OFF:(R_CTRL|ALT|META|SHIFT)  SEQ:(L_CTRL:1,L_CTRL:0)" ] # Single short LEFT CONTROL
 "Action.Respawn" = [ "OFF:(CTRL|ALT|META|SHIFT)  SEQ:(S_LOCK:2,S_LOCK:0)" ] # Long-pressed SCROLL LOCK

 "Action.Layout0" = [ "OFF:(CTRL|ALT|META|R_SHIFT)  SEQ:(L_SHIFT:1,L_SHIFT:0)" ] # Single short LEFT SHIFT
 "Action.Layout1" = [ "OFF:(CTRL|ALT|META|L_SHIFT)  SEQ:(R_SHIFT:1,R_SHIFT:0)" ] # Single short RIGHT SHIFT

 "Action.Hook1" = [ "OFF:(CTRL|R_ALT|META|SHIFT)  SEQ:(L_ALT:1,L_ALT:0)" ]

تنقسم السنانير إلى نوعين. مدمج، مع أسماء "ناطقة" (NewWord، NewSentence، Compose) وقابل للبرمجة.

تبدأ الأسماء القابلة للبرمجة بـ "الإجراء". لأن TOML v1.4، يجب أن تكون الأسماء التي تحتوي على نقاط بين علامتي اقتباس.

وينبغي وصف كل قسم أدناه بنفس الاسم.

لكي لا تفجر عقول الناس بالنظاميين "العراة" (من الخبرة، الكتابةربما واحد من أصل عشرة المهنيين)، أقوم على الفور بتنفيذ بناء جملة إضافي.

  • "إيقاف:" (أو "تشغيل:") قبل أن يتطلب regexp (التعبير العادي) تحرير الأزرار التالية (أو الضغط عليها).
    بعد ذلك، سأقوم بعمل تعبير عادي "غير عادل". مع فحص منفصل للقطع بين الأنابيب "|". من أجل تقليل عدد السجلات مثل "[LR]_SHIFT" (حيث من الواضح أن هذا ليس ضروريًا).
  • "التسلسل:" إذا تم استيفاء الشرط السابق (أو غيابه)، فإننا نتحقق من التعبير العادي "العادي". للحصول على التفاصيل، أرسل فورًا إلى ^W مكتبة "regexp". لأنني ما زلت لم أزعج نفسي بمعرفة درجة التوافق مع جهاز الكمبيوتر المفضل لدي ("متوافق مع Perl").
  • يتم كتابة التعبير في النموذج "BUTTON_1: CODE1، BUTTON_2: CODE2" وما إلى ذلك، بالترتيب الذي يتم به استلام رموز المسح الضوئي.
  • يتم دائمًا "تثبيت" الشيك حتى نهاية التسلسل، لذلك ليست هناك حاجة لإضافة "$" إلى الذيل.
  • يتم تنفيذ جميع عمليات التحقق في سطر واحد واحدة تلو الأخرى ويتم دمجها بواسطة "أنا". ولكن بما أن القيمة موصوفة على أنها مصفوفة، فيمكنك كتابة فحص بديل بعد الفاصلة. إذا كان هذا مطلوبًا لسبب ما.
  • قيمة "الطول التسلسلي = 8" يحد من حجم المخزن المؤقت الذي يتم تنفيذ كافة الاختبارات عليه. لأن لم أواجه (حتى الآن) موارد لا نهاية لها في حياتي.

5. ضبط الخطافات الموضحة في القسم السابق

# Action is the array, so actions could be chained (m.b., infinitely... Have I to check this?).
# For each action type, extra named parameters could be collected. Invalid parameters will be ignored(?).
[Action.RetypeWord] # Switch layout, drop last word and type it again
 Action = [ "Action.CyclicSwitch", "RetypeWord" ] # Call Switch() between layouts tuned below, then RetypeWord()

[Action.CyclicSwitch] # Cyclic layout switching
 Action = [ "Switch" ] # Internal layout switcher func
 Layouts = [0, 1]

[Action.Layout0] # Direct layout selection
 Action = [ "Layout" ] # Internal layout selection func
 Layout = 0

[Action.Layout1] # Direct layout selection
 Action = [ "Layout" ] # Internal layout selection func
 Layout = 1

[Action.Respawn] # Completely respawn xswitcher. Reload config as well
 Action = [ "Respawn" ]

[Action.Hook1] # Run external commands
  Action = [ "Exec" ]
  Exec = "/path/to/exec -a -b --key_x"
  Wait = 1
  SendBuffer = "Word" # External hook can process collected buffer by it's own means.

الشيء الرئيسي هنا هو "الإجراء = [الصفيف]". كما هو الحال في القسم السابق، هناك مجموعة محدودة من الإجراءات المضمنة. وإمكانية الالتحام ليست محدودة من حيث المبدأ (اكتب "Action.XXX" ولا تتكاسل في كتابة قسم آخر له).
وعلى وجه الخصوص، تنقسم إعادة كتابة الكلمة في التخطيط المصحح إلى قسمين: "قم بتغيير التخطيط كما هو محدد هناك" и "إعادة الكتابة" ("إعادة كتابة الكلمة").

تتم كتابة المعلمات المتبقية إلى "القاموس" ("الخريطة" في جولانج) بالنسبة لإجراء معين، تعتمد قائمتهم على ما هو مكتوب في "الإجراء".

يمكن وصف عدة إجراءات مختلفة في كومة واحدة (أقسام). أو يمكنك تفكيكها. كما أظهرت أعلاه.

قمت على الفور بتعيين الإجراء "Exec" لتنفيذ البرنامج النصي الخارجي. مع خيار دفع المخزن المؤقت المسجل إلى stdin.

  • "انتظر = 1" - انتظر حتى تكتمل عملية التشغيل.
  • ربما، "في الكومة" سوف ترغب في وضع أشخاص إضافيين في البيئة. معلومات مثل اسم فئة النافذة التي تم اعتراضها منها.
    "هل تريد توصيل المعالج الخاص بك؟ هذا هو المكان الذي يجب أن تذهب إليه."

أوه (الزفير). يبدو أنني لم أنس أي شيء.

أُووبس! نعم لم أنسى...
أين هو تكوين الإطلاق؟ في الكود الصعب؟ مثل هذا:

[ScanDevices]
 # Must exist on start. Self-respawn in case it is younger then 30s
 Test = "/dev/input/event0"
 Respawn = 30
 # Search mask
 Search = "/dev/input/event*"
 # In my thinkPads there are such a pseudo-keyboards whith tons of unnecessary events
 Bypass = "(?i)Video|Camera" # "(?i)" obviously differs from "classic" pcre's.

أين نسيت/أخطأت؟ (لا سبيل دون هذا)، آمل حقًا ألا يكون القراء اليقظون كسالى جدًا لدرجة أنهم لا يستطيعون دس أنوفهم.

حظا سعيدا!

المصدر: www.habr.com

إضافة تعليق