Альтернативне керування вікнами в Linux

Я з тих, хто ставить на Caps Lock перемикання розкладки тому, що ліньки натискати 2 клавіші, коли можна натискати одну. Я б навіть хотів дві непотрібні клавіші: одну б я використав для включення англійської розкладки, а другу для російської. Але друга непотрібна клавіша — виклик контекстного меню, яка настільки непотрібна, що випилюється багатьма виробниками ноутбуків. Тож доводиться задовольнятися тим, що є.

А ще я не хочу при перемиканні вікон шукати їх іконки на панелі завдань, ловити поглядом назви при перегортанні через Alt + Tab, гортати робочі столи і т. д. Я хочу натиснути комбінацію клавіш (в ідеалі взагалі одну, але вільних непотрібних клавіш вже немає) і одразу потрапити у потрібне вікно. Наприклад так:

  • Alt+F: Firefox
  • Alt+D: Firefox (Private Browsing)
  • Alt+T: Terminal
  • Alt+M: Калькулятор
  • Alt+E: IntelliJ Idea
  • і т.д.

Причому, за натисканням, наприклад, на Alt+M я хочу бачити калькулятор незалежно від того, чи запущена зараз ця програма. Якщо запущена, її вікно треба передати фокус, а якщо ні — запустити потрібну програму і передати фокус коли вона завантажиться.

На випадки, які не покриваються попереднім сценарієм, хочу мати універсальні комбінації клавіш, на які можна легко призначити будь-які з відкритих вікон. Наприклад, у мене призначено 10 комбінацій від Alt + 1 до Alt + 0, які не прив'язані до жодних програм. Я можу просто натиснути Alt + 1 і вікно, яке зараз у фокусі, отримуватиме фокус при натисканні Alt + 1.

Під катом опис ще пари фічі та відповідь на те, як можна це зробити. Але відразу попереджу, що подібна кастомізація під себе може викликати сильну залежність і навіть ломку при необхідності використовувати Windows, Mac OS або навіть чужий комп'ютер з Linux.

Насправді, якщо подумати, ми не так багато програм використовуємо повсякденно. Браузер, термінал, IDE, якийсь месенджер, файловий менеджер, калькулятор і це, мабуть, практично все. Потрібно не так багато комбінацій клавіш, щоб покрити 95% повсякденних завдань.

Для програм, у яких відкрито кілька вікон, одне з них можна призначити основним. Наприклад, відкрито кілька вікон IntelliJ Idea, призначених на Alt + E. У звичайних умовах при натисканні на Alt + E буде відкриватися якесь вікно цієї програми, швидше за все те, що було відкрито першим. Однак, якщо натиснути на Alt + E коли одне з вікон даної програми вже у фокусі, то саме це вікно буде призначено головним і саме йому передаватиметься фокус при наступних натисканнях комбінації.

Головне вікно можна перепризначити. Для цього комбінацію потрібно спочатку скинути, а потім призначити на неї головне інше вікно. Для скидання комбінації потрібно натиснути саму комбінацію, а потім спеціальну комбінацію скидання, у мене вона призначена на Alt+Backspace. Це скрипт, який скасує призначення головного вікна для попередньої комбінації. А далі можна призначити нове головне вікно, як це було описано в попередньому абзаці. Скидання прив'язаного вікна до універсальних комбінацій відбувається аналогічно.

Вступ вийшов довгим, але хотілося спочатку розповісти що робитимемо, а потім пояснити як робити.

Для тих, кому набридло читати

Якщо коротко, то посилання на скрипти наприкінці статті.

Але все одно відразу встановити та користуватися не вийде. Прийде спочатку розібратися як скрипт знаходить потрібне вікно. Без цього не вдасться вказати скрипту, куди саме потрібно передати фокус. І треба зрозуміти що робити, якщо раптом відповідного вікна не знайшлося.

А ще я не загострюватиму увагу на тому, як налаштовувати виконання скриптів по натисканню комбінацій клавіш. Наприклад, у KDE це System Settings → Shortcuts → Custom Shortcuts. В інших віконних менеджерах таке теж має бути.

Знайомство з wmctrl

Wmctrl - Консольна утиліта для взаємодії з X Window Manager. Це є ключова програма для скрипту. Давайте швидко глянемо на те, як їй можна скористатися.

Для початку виведемо список відкритих вікон:

$ wmctrl -lx
0x01e0000e -1 plasmashell.plasmashell             N/A Desktop — Plasma
0x01e0001e -1 plasmashell.plasmashell             N/A Plasma
0x03a00001  0 skype.Skype                         N/A Skype
0x04400003  0 Navigator.Firefox                   N/A Google Переводчик - Mozilla Firefox
0x04400218  0 Navigator.Firefox                   N/A Лучшие публикации за сутки / Хабр - Mozilla Firefox (Private Browsing)
...

Опція -l виводить список усіх відкритих вікон, а додає висновку назву класу (skype.Skype, Navigator.Firefox і т.д). Нам тут знадобиться id вікна (колонка 1), ім'я класу (колонка 3) та назва вікна (остання колонка).

Можна спробувати активувати якесь вікно за допомогою опції -a:

$ wmctrl -a skype.Skype -x

Якщо все пішло за планом, то на екрані з'явиться вікно Skype. Якщо замість опції -x використовувати опцію -i, замість імені класу можна буде вказати id вікна. З id проблема в тому, що id вікна змінюється при кожному запуску програми, і ми не можемо знати його заздалегідь. З іншого боку, цей атрибут однозначно вказує на вікно, що може бути важливо, коли програма відкриває більше одного вікна. Про це трохи далі.

На цьому етапі нам слід запам'ятати, що ми шукатимемо потрібне вікно за допомогою regex з висновку wmctrl -lx. Але це не означає, що нам обов'язково використати щось складне. Зазвичай достатньо імені класу чи назви вікна.

У принципі, основна ідея вже має бути зрозумілою. У налаштуваннях глобальних hotkeys/shortcuts для вашого віконного менеджера налаштовуємо потрібну комбінацію на виконання скрипту.

Як користуватись скриптами

Для початку треба встановити консольні утиліти wmctrl и xdotool:

$ sudo apt-get install wmctrl xdotool

Далі треба завантажити скрипти та додати їх у $ PATH. Я зазвичай кладу їх у ~/bin:

$ cd ~/bin
$ git clone https://github.com/masyamandev/Showwin-script.git
$ ln -s ./Showwin-script/showwin showwin
$ ln -s ./Showwin-script/showwinDetach showwinDetach

Якщо каталогу ~/bin не було, то його треба створити і перезавантажитися (або перелогінитися), інакше ~/bin не потрапить у $ PATH. Якщо все зроблено правильно, то скрипти повинні бути доступні з консолі і має працювати автодоповнення Tab.

Основний скрипт showwin приймає 2 параметри: перший це regex, за яким ми шукатимемо потрібне вікно, а другий параметр це команда, яку потрібно виконати, якщо потрібного вікна не знайшлося.

Можна спробувати виконати скрипт, наприклад:

$ showwin "Mozilla Firefox$" firefox

Якщо Firefox встановлено, його вікно має бути переданий фокус. Навіть якщо Firefox не запущено, він повинен був запуститися.

Якщо вийшло, можна спробувати налаштувати виконання команд на комбінації. У налаштуваннях глобальних hotkeys/shortcuts додаємо:

  • Alt+F: showwin "Mozilla Firefox$" firefox
  • Alt+D: showwin "Mozilla Firefox (Private Browsing)$" "firefox -private-window"
  • Alt+C: showwin "chromium-browser. Chromium-browser N*" chromium-browser
  • Alt+X: showwin "chromium-browser. Chromium-browser I *" "chromium-browser -incognito"
  • Alt+S: showwin "skype.Skype" skypeforlinux
  • Alt+E: showwin "jetbrains-idea" idea.sh

І т. д. Комбінації клавіш та софту кожен може налаштувати як йому зручно.
Якщо все вийшло правильно, то за наведеними вище комбінаціями ми зможемо перемикатися між вікнами простим натисканням клавіш.

Розчарую любителів хрому: звичайне вікно він інкогніто відрізнити. wmctrl не можна, у них однакові назви класів та заголовки вікна. У запропонованих regex символи N* і I* необхідні лише у тому, щоб ці регулярки відрізнялися друг від друга і можна було призначити головними різні вікна.

Для скидання головного вікна попередньої комбінації (за фактом для regex, яким showwin викликався востаннє) потрібно викликати скрипт showwinDetach. У мене цей скрипт призначений для комбінації клавіш. Alt+Backspace.

У скрипту showwin Існує ще одна функція. Коли він викликається з одним параметром (у даному випадку параметр є просто ідентифікатором), він взагалі не перевіряє regex, а всі вікна вважає підходящими. Саме по собі це здається марним, однак таким чином ми можемо призначити будь-яке вікно головним і швидко перемикатися саме до цього вікна.

У мене налаштовані такі комбінації:

  • Alt+1: showwin "CustomKey1"
  • Alt+2: showwin "CustomKey2"
  • ...
  • Alt+0: showwin "CustomKey0"
  • Alt+Backspace: showwinDetach

Таким чином я можу прив'язати будь-які вікна до комбінацій Alt + 1...Alt + 0. Просто натиснувши Alt + 1 я прив'язую поточне вікно до цієї комбінації. Скасувати прив'язку можу натиснувши Alt + 1, а потім Alt+Backspace. Або закрити вікно, то теж працює.

Далі я розповім трохи технічних деталей. Їх можна не читати, а просто спробувати налаштувати та подивитися. Але я б рекомендував розібратися в чужих скриптах перш, ніж запускати їх у себе на комп'ютері :).

Як розрізняти різні вікна однієї програми

В принципі, перший приклад "wmctrl -a skype.Skype -x" був робочий і його можна використовувати. Але давайте ще раз подивимося на приклад з Firefox, в якому відкриті 2 вікна:

0x04400003  0 Navigator.Firefox                   N/A Google Переводчик - Mozilla Firefox
0x04400218  0 Navigator.Firefox                   N/A Лучшие публикации за сутки / Хабр - Mozilla Firefox (Private Browsing)

Перше вікно – звичайний режим, а друге – Private Browsing. Ці вікна хотілося б вважати різними програмами та перемикатися в них за різними комбінаціями клавіш.

Потрібно ускладнити скрипт, що перемикає вікна. Я використав таке рішення: вивести список усіх вікон, зробити GREP по regex, взяти перший рядок за допомогою голова, дістати першу колонку (це буде id вікна) за допомогою вирізати, переключити на вікно id.

Тут має бути жарт про регулярні висловлювання та дві проблеми, але за фактом я не використовую нічого складного. Регулярки мені потрібні для того, щоб можна було вказати кінець рядка (символ $) і відрізняти Mozilla Firefox$ від Mozilla Firefox (Private Browsing)$.

Команда виглядає приблизно так:

$ wmctrl -i -a `wmctrl -lx | grep -i "Mozilla Firefox$" | head -1 | cut -d" " -f1`

Тут вже можна здогадатися про другу особливість скрипту: якщо grep нічого не видав, значить потрібна програма не відкрита і її потрібно запустити, виконавши команду з другого параметра. А потім періодично перевіряти, чи не відкрилося потрібне вікно, щоб передати йому фокус. Не загострюватиму на цьому увагу, кому треба, той подивиться вихідники.

Коли вікна програми не помітні

Отже, ми навчилися передавати фокус вікну потрібної програми. Але що, якщо відкрито більше одного вікна у програми? До якого їх передавати фокус? Скрипт вище передасть, швидше за все, першому відкритому вікну. Однак ми б хотіли більшої гнучкості. Хотілося б мати можливість запам'ятати яке вікно нам потрібно і перемикатися саме до цього вікна.

Ідея була така: Якщо ми хочемо запам'ятати для комбінації клавіш якесь певне вікно, потрібно натиснути цю комбінацію тоді, коли потрібне вікно у фокусі. Надалі при натисканні цієї комбінації фокус віддаватиметься саме цьому вікну. Поки що вікно не закриється або ми не зробимо скидання для цієї комбінації скрипту showwinDetach.

Алгоритм скрипту showwin приблизно такий:

  • Перевірити, чи ми не запам'ятали раніше id вікна, якому треба передати фокус.
    Якщо запам'ятали і таке вікно все ще існує, передаємо фокус йому і виходимо.
  • Дивимося якесь вікно зараз у фокусі, і якщо воно підходить під наш запит, то запам'ятаємо його id для переходу до нього надалі і виходимо.
  • Переходимо хоч до якогось відповідного вікна, якщо воно існує або відкриваємо потрібну програму.

Дізнатися яке вікно зараз у фокусі можна за допомогою консольної утиліти xdotool, перетворивши її виведення на шістнадцятковий формат:

$ printf "0x%08x" `xdotool getwindowfocus`

Щось запам'ятовувати в bash найпростіше створюючи файли у віртуальній файловій системі, що у пам'яті. У Ubuntu така підключена за замовчуванням /dev/shm/. Про інші дистрибутиви нічого не можу сказати, сподіваюся, що таке теж є. Можна подивитися командою:

$ mount -l | grep tmpfs

Скрипт створюватиме порожні директорії в цій папці, ось такі: /dev/shm/$USER/showwin/$SEARCH_REGEX/$WINDOW_ID. Додатково при кожному дзвінку він буде створювати symlink /dev/shm/$USER/showwin/showwin_last на /dev/shm/$USER/showwin/$SEARCH_REGEX. Це знадобиться для того, щоб при необхідності видалити id вікна для певної комбінації за допомогою скрипту showwinDetach.

Що можна покращити

По-перше, скрипти треба налаштовувати руками. Напевно, через необхідність вникати і робити багато руками, багато хто з вас навіть не спробує налаштувати систему. Якби була можливість просто поставити пакет і налаштувати все простіше, то, можливо, це набуло б деякої популярності. А там дивись і в стандартні дистрибутиви запилили б додаток.

І можливо, простіше можна зробити. Якщо з id вікна можна дізнатися id процесу, що його створив, а з id процесу дізнатися яка команда його створила, можна було б автоматизувати налаштування. Насправді я не розбирався, чи можливе те, що я написав у цьому абзаці. Справа в тому, що особисто для мене влаштовує те, як вона працює зараз. Але якщо комусь крім мене весь підхід видасться зручним і хтось його покращить, то я з радістю використовуватиму краще рішення.

Інша проблема, як я вже писав, у тому, що в деяких випадках вікна не можна відрізнити одне від одного. Я поки що спостерігав таке тільки з incognito у chrome/chromium, але, можливо, десь ще є подібне. У крайньому випадку завжди є варіант універсальних комбінацій Alt + 1...Alt + 0. Знову ж таки, я використовую Firefox і особисто для мене ця проблема не суттєва.

А ось суттєва для мене проблема в тому, що по роботі я використовую Mac OS і там нічого подібного налаштувати не зміг. Утиліту wmctrl поставити начебто зміг, але вона на Mac OS до ладу не працює. Щось можна зробити з додатком Automatorале воно так гальмує, що користуватися ним не зручно навіть коли воно працює. Налаштувати комбінації клавіш так, щоб вони працювали у всіх програмах, я теж не зміг. Якщо раптом хтось придумає рішення — радий їм користуватися.

Замість висновку

Вийшло несподівано багато слів для такої, начебто, простої функціональності. Хотілося донести ідею і не перевантажувати текст, але я поки що не придумав, як розповісти простіше. Можливо, у форматі відео було б краще, але так не люблять.

Я трохи розповів про те, що під капотом скрипта та як його налаштувати. Подробиці самого скрипта не вдавався, але він лише 50 рядків, розібратися не складно.

Сподіваюся, що хтось ще цю ідею випробуватиме і, можливо, навіть оцінить. Про себе можу сказати, що скрипт був написаний роки 3 тому і мені це дуже зручно. Настільки зручно, що викликає серйозний дискомфорт під час роботи з чужими комп'ютерами. І з робочим макбуком.

Посилання на скрипти

Джерело: habr.com

Додати коментар або відгук