Xswitcher ืื•ื™ืกืœื™ื™ื’ ืงืึธืจืจืขืงื˜ืึธืจ ืคึฟืึทืจ ืœื™ื ื•ืงืก: ืฉืจื™ื˜ ืฆื•ื•ื™ื™

ื•ื•ื™ ืคืจื™ื™ึทืขืจื“ื™ืง ื•ื™ืกื’ืึทื‘ืข (ืงืกืกื•ื•ื™ื˜ืฉืขืจ ืื™ืŸ ื“ืขืจ "ื“ืขืจื•ื•ื™ื™ึทื– ืคื•ืŸ ื‘ืึทื’ืจื™ืฃ" ื‘ื™ื ืข) ื‘ืืงื•ืžืขืŸ ื’ืึทื ืฅ ืึท ืคึผืœืึทืฅ ืคื•ืŸ ืงืึทื ืกื˜ืจืึทืงื˜ื™ื•ื• ื‘ืึทืžืขืจืงื•ื ื’ืขืŸ (ื•ื•ืึธืก ืื™ื– ืฉื™ื™ืŸ), ืื™ืš ื’ืขืฆื•ื™ื’ืŸ ืฆื• ืคืึทืจื‘ืจืขื ื’ืขืŸ ืžื™ื™ืŸ ืคืจื™ื™ ืฆื™ื™ื˜ ืฆื• ืึทื ื˜ื•ื•ื™ืงืœืขืŸ ื“ื™ ืคึผืจื•ื™ืขืงื˜. ืื™ืฆื˜ ืื™ืš ื•ื•ื™ืœืŸ ืฆื• ืคืึทืจื‘ืจืขื ื’ืขืŸ ืึท ื‘ื™ืกืœ ืคื•ืŸ ื“ื™ื™ืŸ ... ื“ื™ ืจื’ืข ืฉืจื™ื˜ ื•ื•ืขื˜ ื ื™ืฉื˜ ื–ื™ื™ืŸ ื’ืึทื ืฅ ื’ืขื•ื•ื™ื™ื ื˜ืœืขืš: ืคืึธืจืฉืœืึธื’ / ื“ื™ืกืงื•ืกื™ืข ืคื•ืŸ โ€‹โ€‹ืงืึทื ืคื™ื’ื™ืขืจื™ื™ืฉืึทืŸ ืคึผืœืึทืŸ.

Xswitcher ืื•ื™ืกืœื™ื™ื’ ืงืึธืจืจืขืงื˜ืึธืจ ืคึฟืึทืจ ืœื™ื ื•ืงืก: ืฉืจื™ื˜ ืฆื•ื•ื™ื™

ืขืคืขืก ืขืก ื˜ื•ืจื ืก ืื•ื™ืก ืึทื– ื ืึธืจืžืึทืœ ืคึผืจืึธื•ื’ืจืึทืžืขืจื– ื’ืขืคึฟื™ื ืขืŸ ืขืก ื™ื ืงืจืขื“ืึทื‘ืœื™ ื ื•ื“ื ืข ืฆื• ืฉื˜ืขืœืŸ ืึทืœืข ื“ื™ ืงืึธื ื˜ืจืึธืœืก.

ื›ื“ื™ ื ื™ืฉื˜ ืฆื• ื–ื™ื™ืŸ ืื•ืžื’ืจื•ื ื“, ืื™ื– ืื™ื ืขื•ื•ื™ื™ื ื™ื’ ื ื‘ื™ื™ืฉืคื™ืœ ืคื•ืŸ ื•ื•ืืก ืื™ืš ื”ืืฃ ืžื™ื˜.
ืงื•ื™ืœืขืœื“ื™ืง ื•ื™ืกื’ืขืฆื™ื™ื›ื ื˜ ืงืึทื ืกื™ื•ื•ื“ (ืื•ืŸ ื’ืขื–ื•ื ื˜ ื™ืžืคึผืœืึทืžืขื ืึทื“) Apache Kafka & ZooKeeper.
- ืงืึธื ืคื™ื’ื•ืจืึทื˜ื™ืึธืŸ? ืื‘ืขืจ ืก'ืื™ื– ื ื•ื“ื ืข! ืฉื˜ื•ื ืงืกืžืœ (ื•ื•ื™ื™ึทืœ ืขืก ืื™ื– "ืื•ื™ืก ืคื•ืŸ ื“ื™ ืงืขืกื˜ืœ").
โ€• ืึทืš, ืฐื™ืœื˜ ืื™ืจ ืืฑืš ืึทืŸ ืึทืงืœ? ืื‘ืขืจ ืขืก ืื™ื– ืึทื–ื•ื™ ื ื•ื“ื ืข! ื˜ืึทืคึผึพื‘ืœื•ืคึผืขืจ... ืขืคึผืขืก ืึทื–ืฑ.

ืึธื‘ืขืจ ืื™ืŸ ืžื™ื™ืŸ ืึทืจื‘ืขื˜ ืขืก ืื™ื– ืคึผื•ื ืงื˜ ื“ื™ ืคืึทืจืงืขืจื˜. ืจืขื›ื˜ (ื•ื•ื™ื™, ื›ึผืžืขื˜ ืงื™ื™ื ืžืึธืœ ื“ืขืจ ืขืจืฉื˜ืขืจ ืžืึธืœ) ื“ื™ ืงืึทื ืกื˜ืจืึทืงื˜ืึทื“ ืžืึธื“ืขืœ ืึทืœืึทื•ื– ืื™ืจ ืฆื• ืคืึธืจื–ืขืฆืŸ ื•ื•ื™ื™ึทื˜ืขืจ ืœื™ื™ื›ื˜ ืื•ืŸ ื’ืขื•ื•ื™ื™ื ื˜ืœืขืš (ื›ึผืžืขื˜) ืึทืกืขืžื‘ืึทืœ ืึท ื“ื™ืึทื’ืจืึทืžืข.

ืื™ืš ื”ืึธื‘ ืœืขืฆื˜ื ืก ื’ืขืคึฟื•ื ืขืŸ ืึทืŸ ืึทืจื˜ื™ืงืœ ืื•ื™ืฃ Habrรฉ ื•ื•ืขื’ืŸ ื“ื™ ืฉื•ื•ืขืจ ืึทืจื‘ืขื˜ ืคื•ืŸ ื“ืึทื˜ืŸ ืกื™ื™ืึทื ื˜ื™ืก ...
ืขืก ื˜ื•ืจื ืก ืื•ื™ืก ืึทื– ื“ืขืจ ืžืึธืžืขื ื˜ ืื™ื– ื’ืึธืจ ืื™ื™ื ื’ืขื–ืขืŸ ืคึฟืึทืจ ื–ื™ื™. ืื•ืŸ ืื™ืŸ ืžื™ื™ืŸ ืคื™ืจ, ื•ื•ื™ ื–ื™ื™ ื–ืึธื’ืŸ, "ืœื™ื›ื˜ ื•ื•ืขืจืกื™ืข". ืžื•ืœื˜ื™-ื‘ืึทื ื“ ืžืึธื“ืขืœืก, ืกื™ื–ืึทื ื“ ืคึผืจืึธื•ื’ืจืึทืžืขืจื– ืžื™ื˜ OOP ืื™ืŸ ื“ื™ ื’ืจื™ื™ื˜, ืขื˜ืง. - ื“ืึธืก ืึทืœืฅ ื•ื•ืขื˜ ื“ืขืจืฉื™ื™ึทื ืขืŸ ืฉืคึผืขื˜ืขืจ ื•ื•ืขืŸ / ืื•ื™ื‘ ืขืก ื ืขืžื˜ ืึทื•ื•ืขืง. ืื‘ืขืจ ื“ืขืจ ื“ื™ื–ื™ื™ื ืขืจ ื“ืึทืจืฃ ืฆื• ืึธื ื”ื™ื™ื‘ืŸ ืขืจื’ืขืฅ ื“ืึธ ืื•ืŸ ืื™ืฆื˜.

ื‘ืึทืงื•ืžืขืŸ ืฆื• ื“ื™ ืคื•ื ื˜. ืื™ืš ื’ืขื ื•ืžืขืŸ TOML ื•ื•ื™ ืึท ืกื™ื ื˜ืึทืงื˜ื™ืง ื™ืงืขืจ ืคื•ืŸ ื“ืขื ื‘ื™ืจื’ืขืจ.

ื•ื•ื™ื™ืœ ืขืจ (TOML) ืคื•ืŸ ืื™ื™ืŸ ื–ื™ื™ื˜, ืžืขื ื˜ืฉ-ืขื“ื™ื˜ืึทื‘ืœืข. ืื•ื™ืฃ ื“ื™ ืื ื“ืขืจืข ื”ืึทื ื˜, ืขืก ืื™ื– ืื™ื‘ืขืจื’ืขื–ืขืฆื˜ 1: 1 ืื™ืŸ ืงื™ื™ืŸ ืคื•ืŸ ื“ื™ ืžืขืจืกื˜ ืคึผืจืึธืกื˜ ืกื™ื ื˜ืึทืงืกื™ื–: XML, JSON, YAML.
ื“ืขืจืฆื•, ื“ื™ ื™ืžืคึผืœืึทืžืขื ื˜ื™ื™ืฉืึทืŸ ืื™ืš ื’ืขื•ื•ื™ื™ื ื˜ ืคึฟื•ืŸ "github.com/BurntSushi/toml", ื›ืึธื˜ืฉ ื ื™ื˜ ื“ื™ ืžืขืจืกื˜ ืžืึธื“ืขืจืŸ (ื ืึธืš 1.4 ืกื™ื ื˜ืึทืงืก), ืื™ื– ืกื™ื ื˜ืึทืงื˜ื™ืงืึทืœ ืงืึทืžืคึผืึทื˜ืึทื‘ืึทืœ ืžื™ื˜ ื“ื™ ื–ืขืœื‘ืข ("ื’ืขื‘ื•ื™ื˜-ืื™ืŸ") JSON.

ืึทื– ืื™ื–, ืื•ื™ื‘ ืื™ืจ ื•ื•ื™ืœื˜, ืื™ืจ ืงืขื ืขืŸ ืคืฉื•ื˜ ื–ืึธื’ืŸ "ื’ื™ื™ืŸ ื“ื•ืจืš ื“ื™ ื•ื•ืึทืœื“ ืžื™ื˜ ื“ืขื TOML ืคื•ืŸ ื“ื™ื™ึทืŸ, ืื™ืš ื•ื•ื™ืœืŸ XXX" ืื•ืŸ "ืœืึทื˜ืข" ื“ื™ ืงืึธื“ ืžื™ื˜ ื‘ืœื•ื™ื– ืื™ื™ืŸ ืฉื•ืจื”.

ืื–ื•ื™, ืื•ื™ื‘ ืื™ืจ ื•ื•ื™ืœืŸ ืฆื• ืฉืจื™ื™ึทื‘ืŸ ืขื˜ืœืขื›ืข ืคึฟืขื ืฆื˜ืขืจ ืฆื• ืงืึทื ืคื™ื’ื™ืขืจ xswitcher (ืื™ืš ื‘ื™ืŸ ื ื™ืฉื˜ ื–ื™ื›ืขืจ) ืงื™ื™ืŸ ืคืจืื‘ืœืขืžืขืŸ ื–ืขื ืขืŸ ื“ืขืจื•ื•ืึทืจื˜ "ืžื™ื˜ ื“ืขื ืคืึทืจืฉื™ืœื˜ืŸ ืงืึทื ืคื™ื’ื™ืขืจื™ื™ืฉืึทืŸ ืคื•ืŸ ื“ื™ื™ืŸ."

ืคึฟืึทืจ ืึทืœืข ืื ื“ืขืจืข, ื“ื™ ืกื™ื ื˜ืึทืงืก ืื™ื– ื‘ืื–ื™ืจื˜ ืื•ื™ืฃ "ืฉืœื™ืกืœ = ื•ื•ืขืจื˜" (ืื•ืŸ ืžืžืฉ ืึท ืคึผืึธืจ ืคื•ืŸ ืžืขืจ ืงืึธืžืคึผืœื™ืฆื™ืจื˜ ืึธืคึผืฆื™ืขืก, ื•ื•ื™ = [ืขื˜ืœืขื›ืข, ืึทื–, ืžืขื ื’ืข]) ืื™ืš ื˜ืจืื›ื˜
ื™ื ื˜ื•ืึทื˜ื™ื•ื•ืœื™ ื‘ืึทืงื•ื•ืขื.
ื•ื•ืึธืก ืก ื˜ืฉื™ืงืึทื•ื•ืข ืื™ื– ืึทื– "ื’ืขื‘ืจืขื ื˜" ืึทืจื•ื ื“ืขืจ ื–ืขืœื‘ื™ืงืขืจ ืฆื™ื™ื˜ (ืึทืจื•ื 2013). ื ืึธืจ, ื ื™ื˜ ืขื ืœืขืš ืžื™ืจ, ืื™ื– ื“ืขืจ ืžื—ื‘ืจ ืคึฟื•ืŸ ืชื•ืžืœ ืึทืจืฒึทืŸ ืื™ืŸ ืึท ื’ืขื”ืขืจื™ืงืŸ ืฉืึทืคึฟื•ื ื’.

ื“ืขืจื™ื‘ืขืจ, ืื™ืฆื˜ ืขืก ืื™ื– ื’ืจื™ื ื’ืขืจ ืคึฟืึทืจ ืžื™ืจ ืฆื• ืกื˜ืจื•ื™ืขืจืŸ ื“ื™ ื™ืžืคึผืœืึทืžืขื ื˜ื™ื™ืฉืึทืŸ ืฆื• ื–ื™ืš, ืื•ืŸ ื ื™ืฉื˜ ื•ื•ื™ืฆืข ื•ื•ืขืจืกืึท.

ืื™ืŸ ืึทืœื’ืขืžื™ื™ืŸ, ืžื™ืจ ื ืขืžืขืŸ TOML (ื–ื™ื™ืขืจ ืขื ืœืขืš ืฆื• ื“ื™ ืึทืœื˜ Windows INI). ืื•ืŸ ืžื™ืจ ื”ืึธื‘ืŸ ืึท ืงืึทื ืคื™ื’ื™ืขืจื™ื™ืฉืึทืŸ ืื™ืŸ ื•ื•ืึธืก ืžื™ืจ ื‘ืึทืฉืจื™ื™ึทื‘ืŸ ื•ื•ื™ ืฆื• ืฆื•ื˜ืฉืขืคึผืขืŸ ืึท ืกืขืจื™ืข ืคื•ืŸ โ€‹โ€‹ื›ื•ืงืก ื“ื™ืคึผืขื ื“ื™ื ื’ ืื•ื™ืฃ ื“ื™ ื’ืึทื ื’ ืคื•ืŸ ื“ื™ ืœืขืฆื˜ืข ื™ื‘ืขืจืงื•ืงืŸ ืงืึธื•ื“ื– ืคื•ืŸ ื“ื™ ืงืœืึทื•ื•ื™ืึทื˜ื•ืจ. ื•ื ื˜ืขืจ, ืฉื˜ื™ืง ืคึฟืึทืจ ืฉื˜ื™ืง, ืื™ื– ื•ื•ืึธืก ืื™ื– ื’ืขืฉืขืŸ ื‘ื™ื– ืื™ืฆื˜. ืื•ืŸ ืึท ื“ืขืจืงืœืขืจื•ื ื’ ืคื•ืŸ ื•ื•ืึธืก ืื™ืš ื‘ืึทืฉืœืึธืกืŸ ืึทื–ื•ื™.

0. ื™ืงืขืจื“ื™ืง ืึทื‘ืกื˜ืจืึทืงืฆื™ืขืก

  • ื™ื‘ืขืจืงื•ืงืŸ ืงืึธื“ ื“ืขื–ื™ื’ื ื™ื™ืฉืึทื ื–. ืขืก ืื™ื– ื‘ืืฉื˜ื™ืžื˜ ื ื™ื™ื˜ื™ืง ืฆื• ื˜ืึธืŸ ืขืคึผืขืก ื•ื•ืขื’ืŸ ื“ืขื, ื•ื•ื™ื™ึทืœ ืคืฉื•ื˜ ื“ื™ื’ื™ื˜ืึทืœ ืงืึธื•ื“ื– ื–ืขื ืขืŸ ืœืขื’ืึทืžืจืข ื ื™ืฉื˜ ืœื™ื™ื ืขื•ื•ื“ื™ืง ืคึฟืึทืจ ืžืขื ื˜ืฉ (ื“ืึธืก ืื™ื– ื ืึธืจ ืžื™ืจ ืœืึธืœืึธืกื•ื•ื™ื˜ื˜ืฉืขืจ).
    ืื™ืš ื”ืึธื‘ ืึทืจื•ื™ืกื’ืขืฉืึธืงืœื˜ "ecodes.go" ืคึฟื•ืŸ "ื’ืึธืœืึทื ื’-ืขื•ื•ื“ืขื•ื•" (ืื™ืš ื‘ื™ืŸ ื’ืขื•ื•ืขืŸ ืฆื• ืคื•ื™ืœ ืฆื• ืงื•ืงืŸ ืื•ื™ืฃ ื“ืขืจ ืึธืจื™ื’ื™ื ืขืœืขืจ ืžืงื•ืจ, ื›ืึธื˜ืฉ ื“ืขืจ ืžื—ื‘ืจ ื”ืึธื˜ ืขืก ืึธื ื’ืขื•ื•ื™ื–ืŸ ื’ืึทื ืฅ ืงื•ืœื˜ื•ืจืขืœ). ืื™ืš ืงืขืจืขืงื˜ืึทื“ ืึท ื‘ื™ืกืœ (ื“ืขืจื•ื•ื™ื™ึทืœ) ืขืคึผืขืก ื•ื•ืึธืก ืื™ื– ื’ืขื•ื•ืขืŸ ื’ืึทื ืฅ ืฉืจืขืงืœืขืš. ื•ื•ื™ "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"

ื“ื™ ืจืึธื•ื– ืคื•ืŸ ื“ื™ ื˜ื™ืฉ ื–ืขื ืขืŸ ืื™ืŸ ื˜ืึธืคึผืœ ืงื•ื•ืึทื“ืจืึทื˜ ื‘ืจืึทืงืึทืฅ ืžื™ื˜ ื–ื™ื™ึทืŸ ื ืึธืžืขืŸ. ืขืก ืงืขืŸ ื ื™ืฉื˜ ื”ืึธื‘ืŸ ื’ืขื•ื•ืขืŸ ื’ืจื™ื ื’ืขืจ ืจืขื›ื˜ ืึทื•ื•ืขืง ื“ื™ ืคืœืขื“ืขืจืžื•ื™ื–. ื“ืขืคึผืขื ื“ื™ื ื’ ืื•ื™ืฃ ื“ื™ ืื™ืฆื˜ ืึทืงื˜ื™ื•ื• ืคึฟืขื ืฆื˜ืขืจ, ืื™ืจ ืงืขื ืขืŸ ืื•ื™ืกืงืœื™ื™ึทื‘ืŸ ื“ื™ ืคืืœื’ืขื ื“ืข ืึธืคึผืฆื™ืขืก:

  • ื“ื™ื™ืŸ ืื™ื™ื’ืขื ืข ื’ืึทื ื’ ืคื•ืŸ "ื”ื™ื™ืก ืงื™ื–" "ืึทืงืฉืึทื ื– = ...". ืื•ื™ื‘ ื ื™ืฉื˜ / ืœื™ื™ื“ื™ืง, ื˜ืึธืŸ ื’ืึธืจื ื™ืฉื˜.
  • ื‘ืึทืฉื˜ื™ืžืขืŸ "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, ื ืขืžืขืŸ ืžื™ื˜ ื“ืึทืฅ ืžื•ื–ืŸ ื–ื™ื™ืŸ ืื™ืŸ ืฆื™ื˜ื™ืจื˜.

ื™ืขื“ืขืจ ืึธืคึผื˜ื™ื™ืœื•ื ื’ ื–ืึธืœ ื–ื™ื™ืŸ ื“ื™ืกืงืจื™ื™ื‘ื“ ืื•ื ื˜ืŸ ืžื™ื˜ ื“ื™ ื–ืขืœื‘ืข ื ืึธืžืขืŸ.

ืื™ืŸ ืกื“ืจ ื ื™ืฉื˜ ืฆื• ื‘ืœืึธื–ืŸ ืžืขื ื˜ืฉืŸ ืก ืžื—ืฉื‘ื•ืช ืžื™ื˜ "ื ืึทืงืขื˜" ืจืขื’ื™ืึทืœืขืจื– (ืคื•ืŸ ื“ืขืจืคืึทืจื•ื ื’, ื–ื™ื™ืขืจ ืฆื• ืฉืจื™ื™ึทื‘ืŸืืคึฟืฉืจ ืื™ื™ื ืขืจ ืคึฟื•ืŸ ืฆืขืŸ ืคึผืจืึธืคืขืกืกื™ืึธื ืึทืœืก), ืื™ืš ืžื™ื“ ื™ื ืกื˜ืจื•ืžืขื ื˜ ื ืึธืš ืกื™ื ื˜ืึทืงืก.

  • "ืึทื•ื•ืขืง:" (ืึธื“ืขืจ "ืื•ื™ืฃ:") ืื™ื™ื“ืขืจ ืจืขื’ืขืงืกืคึผ (ืจืขื’ื•ืœืขืจ ืื•ื™ืกื“ืจื•ืง) ื“ืึทืจืคืŸ ืึทื– ื“ื™ ืคืืœื’ืขื ื“ืข ืงื ืขืคึผืœืขืš ื–ืึธืœ ื–ื™ื™ืŸ ืคืจื™ื™ (ืึธื“ืขืจ ื’ืขื“ืจื™ืงื˜).
    ื•ื•ื™ื™ึทื˜ืขืจ ืื™ืš ื‘ื™ืŸ ื’ืขื’ืื ื’ืขืŸ ืฆื• ืžืึทื›ืŸ ืึท "ื•ืžื™ื•ื™ืฉืขืจื“ื™ืง" ืจืขื’ื•ืœืขืจ ืื•ื™ืกื“ืจื•ืง. ืžื™ื˜ ื‘ืึทื–ื•ื ื“ืขืจ ืงืึธื ื˜ืจืึธืœื™ืจื•ื ื’ ืคื•ืŸ ื‘ืจืขืงืœืขืš ืฆื•ื•ื™ืฉืŸ ืคึผื™ื™ืคึผืก "|". ืื™ืŸ ืกื“ืจ ืฆื• ืจืขื“ื•ืฆื™ืจืŸ ื“ื™ ื ื•ืžืขืจ ืคื•ืŸ ืจืขืงืึธืจื“ืก ื•ื•ื™ "[ืœืจ]_SHIFT" (ื•ื•ื• ื“ืึธืก ืื™ื– ืงืœืืจ ื ื™ื˜ ื ื™ื™ื˜ื™ืง).
  • "SEQ:" ืื•ื™ื‘ ื“ื™ ืคืจื™ืขืจื“ื™ืงืข ืฆื•ืฉื˜ืึทื ื“ ืื™ื– ื‘ืื’ืขื’ื ื˜ (ืึธื“ืขืจ ื ื™ื˜ืึธ), ืžื™ืจ ืงืึธื ื˜ืจืึธืœื™ืจืŸ ืงืขื’ืŸ ืึท "ื ืึธืจืžืึทืœ" ืจืขื’ื•ืœืขืจ ืื•ื™ืกื“ืจื•ืง. ืคืืจ ืคึผืจื˜ื™ื ืฉื™ืง ืื™ืš ื’ืœื™ื™ืš ืฆื• ^W ื“ื™ โ€œืจืขื’ืขืงืกืคึผโ€ ื‘ื™ื‘ืœื™ืึธื˜ืขืง. ื•ื•ื™ื™ึทืœ ืื™ืš ื ืึธืš ื”ืึธื‘ืŸ ื ื™ืฉื˜ ื‘ืึทื“ืขืจื“ ืฆื• ื’ืขืคึฟื™ื ืขืŸ ื“ื™ ื’ืจืึทื“ ืคื•ืŸ ืงืึทืžืคึผืึทื˜ืึทื‘ื™ืœืึทื˜ื™ ืžื™ื˜ ืžื™ื™ืŸ ื‘ืึทืœื™ื‘ืกื˜ืข ืคึผืงืจืข ("ืคึผืขืจืœ ืงืึทืžืคึผืึทื˜ืึทื‘ืึทืœ").
  • ื“ืขืจ ืื•ื™ืกื“ืจื•ืง ืื™ื– ื’ืขืฉืจื™ื‘ืŸ ืื™ืŸ ื“ืขืจ ืคืึธืจืขื "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" ืื•ืŸ ื˜ืึธืŸ ื ื™ื˜ ื–ื™ื™ืŸ ืฆื• ืคื•ื™ืœ ืฆื• ืฉืจื™ื™ึทื‘ืŸ ืืŸ ืื ื“ืขืจ ืึธืคึผื˜ื™ื™ืœื•ื ื’ ืคึฟืึทืจ ืื™ื).
ืื™ืŸ ื‘ืึทื–ื•ื ื“ืขืจ, ื“ื™ ืจื™ื˜ื™ืคึผื™ื ื’ ืคื•ืŸ ืึท ื•ื•ืึธืจื˜ ืื™ืŸ ื“ื™ ืงืขืจืขืงื˜ืึทื“ ืื•ื™ืกืœื™ื™ื’ ืื™ื– ืฆืขื˜ื™ื™ืœื˜ ืื™ืŸ ืฆื•ื•ื™ื™ ื˜ื™ื™ืœืŸ: "ื˜ื•ื™ืฉืŸ ื“ื™ ืื•ื™ืกืœื™ื™ื’ ื•ื•ื™ ืกืคึผืขืกื™ืคื™ืขื“ ื“ืึธืจื˜" ะธ "ืจื™ื˜ื™ืคึผืข" ("RetypeWord").

ื“ื™ ืจื•ืขืŸ ืคึผืึทืจืึทืžืขื˜ืขืจืก ื–ืขื ืขืŸ ื’ืขืฉืจื™ื‘ืŸ ืฆื• ื“ื™ "ื•ื•ืขืจื˜ืขืจื‘ื•ืš" ("ืžืึทืคึผืข" ืื™ืŸ ื’ืึธืœืึทื ื’) ืคึฟืึทืจ ืึท ื’ืขื’ืขื‘ืŸ ืงืึทืžืฃ, ื–ื™ื™ืขืจ ืจืฉื™ืžื” ื“ืขืคึผืขื ื“ืก ืื•ื™ืฃ ื•ื•ืึธืก ืื™ื– ื’ืขืฉืจื™ื‘ืŸ ืื™ืŸ "ืึทืงืฉืึทืŸ".

ืขื˜ืœืขื›ืข ืคืึทืจืฉื™ื“ืขื ืข ืึทืงืฉืึทื ื– ืงืขื ืขืŸ ื–ื™ื™ืŸ ื“ื™ืกืงืจื™ื™ื‘ื“ ืื™ืŸ ืื™ื™ืŸ ืงื•ืคึผืข (ืกืขืงืฉืึทื ื–). ืึธื“ืขืจ ืื™ืจ ืงืขื ืขืŸ ืฆื™ืขืŸ ืขืก ื‘ืึทื–ื•ื ื“ืขืจ. ื•ื•ื™ ืื™ืš ื’ืขื•ื•ื™ื–ืŸ ืื•ื™ื‘ืŸ.

ืื™ืš ืžื™ื“ ืฉื˜ืขืœืŸ ื“ื™ "ืขืงืกืขืง" ืงืึทืžืฃ ืฆื• ื•ื™ืกืคื™ืจืŸ ื“ื™ ืคื•ื ื“ืจื•ื™ืกื ื“ื™ืง ืฉืจื™ืคื˜. ืžื™ื˜ ื“ื™ ืึธืคึผืฆื™ืข ืฆื• ืฉื˜ื•ืคึผืŸ ื“ื™ ืจืขืงืึธืจื“ืขื“ ื‘ืึทืคืขืจ ืื™ืŸ ืกื˜ื“ื™ืŸ.

  • "ื•ื•ืึทืจื˜ืŸ = 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

ืœื™ื™ื’ืŸ ืึท ื‘ืึทืžืขืจืงื•ื ื’