اصلاح کننده طرح بندی Xswitcher برای لینوکس: مرحله دوم

مانند انتشار قبلی (xswitcher در مرحله "اثبات مفهوم") بازخورد سازنده زیادی دریافت کرد (که خوبه)، من همچنان اوقات فراغت خود را صرف توسعه پروژه کردم. حالا می خوام کمی از تو خرج کنم... مرحله دوم کاملاً معمول نخواهد بود: پیشنهاد/بحث طراحی پیکربندی.

اصلاح کننده طرح بندی Xswitcher برای لینوکس: مرحله دوم

به نوعی معلوم می شود که برنامه نویسان عادی تنظیم همه این کنترل ها را بسیار خسته کننده می دانند.

برای اینکه بی اساس نباشم، داخل نمونه ای است که من با آن سروکار دارم.
به طور کلی Apache Kafka & ZooKeeper بسیار عالی تصور شده (و به خوبی اجرا شده است).
- پیکربندی؟ اما خسته کننده است! xml گنگ (چون "خارج از جعبه" است).
- اوه، آیا شما هم ACL می خواهید؟ اما خیلی خسته کننده است! Tap-blooper... یه همچین چیزی.

اما در کار من دقیقا برعکس است. درست (افسوس، تقریباً هرگز اولین بار) مدل ساخته شده به شما اجازه می دهد تا به راحتی و به طور طبیعی ادامه دهید (تقریبا) یک نمودار جمع کنید

من اخیراً به مقاله ای در Habré برخورد کردم که در مورد کار سخت دانشمندان داده بود ...
معلوم می شود که این لحظه برای آنها کاملاً تحقق یافته است. و در عمل من، همانطور که می گویند، "نسخه سبک". مدل های چند جلدی، برنامه نویسان کارکشته با OOP آماده و غیره. - همه اینها بعداً هنگامی که/اگر خاموش شود ظاهر می شود. اما طراح باید از اینجا و اکنون از جایی شروع کند.

برو سر اصل مطلب. من TOML را به عنوان مبنای نحوی در نظر گرفتم از این شهروند.

چون او (TOML) از یک طرف، قابل ویرایش توسط انسان. از سوی دیگر، 1:1 به هر یک از نحوهای رایج تر ترجمه می شود: XML، JSON، YAML.
علاوه بر این، پیاده‌سازی که من از "github.com/BurntSushi/toml" استفاده کردم، اگرچه شیک‌ترین (هنوز نحو 1.4) نیست، از نظر نحوی با همان JSON ("ساخت‌شده") سازگار است.

یعنی در صورت تمایل می توانید به سادگی بگویید «با آن TOML خودت از جنگل عبور کن، من XXX می خواهم» و تنها با یک خط کد را «پچ کن».

بنابراین، اگر می خواهید چند پنجره برای پیکربندی xswitcher بنویسید (مطمئن نیستم) "با این پیکربندی لعنتی شما هیچ مشکلی پیش بینی نمی شود."

برای بقیه، نحو بر اساس "کلید = ارزش" است. (و به معنای واقعی کلمه چند گزینه پیچیده تر، مانند = [بعضی، آن، آرایه]) حدس می زنم
به طور مستقیم راحت
جالب اینجاست که "سوخته" تقریباً در همان زمان (حدود سال 2013). فقط، برخلاف من، نویسنده TOML در مقیاس مناسب وارد شد.

بنابراین ، اکنون برای من آسان تر است که اجرای آن را مطابق با خودم تنظیم کنم و نه برعکس.

به طور کلی، ما TOML (بسیار شبیه به Windows INI قدیمی) را می گیریم. و ما یک پیکربندی داریم که در آن نحوه اتصال یک سری قلاب بسته به مجموعه آخرین کدهای اسکن از صفحه کلید را شرح می دهیم. در زیر، قطعه قطعه، آنچه تاکنون اتفاق افتاده است. و توضیحی در مورد اینکه چرا اینطور تصمیم گرفتم.

0. انتزاعات اساسی

  • اسکن کدها قطعاً باید کاری در مورد این موضوع انجام شود، زیرا کدهای دیجیتال به سادگی قابل خواندن توسط انسان نیستند (این فقط من هستم لولوسویچر).
    من "ecodes.go" را از "golang-evdev" تکان دادم (من خیلی تنبل بودم که منبع اصلی را نگاه کنم، اگرچه نویسنده آن را کاملاً فرهنگی نشان داده است). من کمی (فعلا) چیزی را اصلاح کردم که کاملاً ترسناک بود. مانند "LEFTBRACE" → "L_BRACE".
  • علاوه بر این، او مفهوم "کلیدهای حالت" را معرفی کرد. از آنجایی که گرامر منظم استفاده شده اجازه عبور طولانی را نمی دهد. (اما این امکان را به شما می دهد که با حداقل هزینه سربار بررسی کنید. اگر فقط از ضبط مستقیم استفاده می کنید.)
  • یک "حذف کننده" داخلی از آنچه فشرده می شود وجود خواهد داشت. بدین ترتیب حالت "repeat"=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"

ردیف های جدول با نام آن در دو کروشه مربع قرار دارند. از همان ابتدا نمی توانست آسان تر باشد. بسته به پنجره فعال فعلی، می توانید گزینه های زیر را انتخاب کنید:

  • مجموعه ای از "کلیدهای داغ" "اقدامات = ..." خودتان. اگر نه/خالی است، کاری انجام ندهید.
  • "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 نسخه 1.4، نام های دارای نقطه باید در گیومه باشند.

هر بخش باید در زیر توضیح داده شود با همین نام.

برای اینکه اذهان مردم را با افراد عادی «برهنه» منفجر نکنیم (از تجربه، آنها برای نوشتنشاید از هر ده یکی حرفه ای ها، من بلافاصله دستور اضافی را پیاده سازی می کنم.

  • "OFF:" (یا "روشن:") قبل از اینکه regexp (عبارت منظم) نیاز به رها کردن (یا فشار دادن) دکمه های زیر داشته باشد.
    بعد می خواهم یک عبارت منظم "غیر منصفانه" بسازم. با بررسی جداگانه قطعات بین لوله ها "|". به منظور کاهش تعداد رکوردهایی مانند "[LR]_SHIFT" (که به وضوح لازم نیست).
  • "SEQ:" اگر شرط قبلی برآورده شود (یا وجود نداشته باشد)، آنگاه یک عبارت منظم "عادی" را بررسی می کنیم. برای جزئیات، من بلافاصله کتابخانه "regexp" را به ^W ارسال می کنم. زیرا من هنوز به خود زحمت نداده ام تا درجه سازگاری با pcre مورد علاقه خود ("perl compatible") را بفهمم.
  • عبارت به شکل نوشته شده است "BUTTON_1: CODE1، BUTTON_2: CODE2" و غیره، به ترتیب دریافت کدهای اسکن.
  • چک همیشه به انتهای دنباله "نقل داده می شود".، بنابراین نیازی به اضافه کردن "$" به دم نیست.
  • تمام بررسی ها در یک خط یکی پس از دیگری انجام می شود و با "من" ترکیب می شوند. اما از آنجایی که مقدار به عنوان یک آرایه توصیف می شود، می توانید یک چک جایگزین بعد از کاما بنویسید. اگر به دلایلی این مورد نیاز است.
  • ارزش "SeqLength = 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» را بنویسید و برای نوشتن بخش دیگری برای آن تنبل نباشید).
به طور خاص، تایپ مجدد یک کلمه در طرح اصلاح شده به دو بخش تقسیم می شود: "طرح بندی را همانطور که در آنجا مشخص شده تغییر دهید" и "Retype" ("RetypeWord").

پارامترهای باقیمانده در "لغت نامه" نوشته می شوند ("نقشه" به زبان گلانگ) برای یک عمل معین، لیست آنها به آنچه در "عمل" نوشته شده است بستگی دارد.

چندین عمل مختلف را می توان در یک پشته توصیف کرد (بخش ها). یا می توانید آن را جدا کنید. همانطور که در بالا نشان دادم.

من بلافاصله عمل Exec را برای اجرای اسکریپت خارجی تنظیم کردم. با گزینه فشار دادن بافر ضبط شده به stdin.

  • "Wait = 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

اضافه کردن نظر