CapsLock как дополнительный модификатор

в 7:58, , рубрики: autoit, Caps Lock, CapsLock, Программирование, метки: , ,

Вступление

Мне неудобно было нажимать Ctrl-F4 для закрытия вкладки в VisualStudio, я попробовал переназначить эту функцию на стандартную для интернет браузеров и некоторых других программ Ctrl-W, но столкнулся с трудностями и решил найти более гибкое решение.

На хабре уже есть статьи, посвященный Caps Lock-у и, наверное, самым каноническим вариантом считается его использование для переключения раскладки. Я хочу показать более гибкий и, при этом, довольно простой подход.

Идея

А чем CapsLock хуже Shift/Ctrl/Alt? Почему бы не использовать его в том же ключе? Итак, идея такова: написать программу, которая будет отслеживать нажатие/отпускание капса и нажатие некоторых других клавиш в промежутке между этими двумя событиями и совершать какое-то действие на основе результата.

AutoIt

AutoIt — свободно распространяемый скриптовый язык для автоматизации выполнения задач в ОС Windows. Для начала работы с ним достаточно скачать AutoIt Full Installation, установить и запустить усеченную версию редактора SciTE, идущую в пакете.

Исходный код

Код одним куском и без комментариев

#include <Misc.au3>
Global $isSingleCaps = True
HotKeySet("{LAUNCH_MAIL}", "HoldModifier")

While 1
   Sleep(60000)
WEnd

Func HoldModifier()
   $isSingleCaps = True
   If _IsPressed("B4") Then
      HotKeySet("w", "CloseTab")
     HotKeySet("t", "RunAllTests")
     HotKeySet("]", "RestartExplorer")
     HotKeySet("a", "Play")
     HotKeySet("{NUMPADSUB}", "VolumeUp")
     HotKeySet("0", "RealCaps")
      While _IsPressed("B4")
         sleep(0)
      WEnd
      Unset()
   EndIf
   
   If $isSingleCaps Then
     Send("!+9")
   EndIf
EndFunc
 
Func Unset()
   HotKeySet("w")
   HotKeySet("t")
   HotKeySet("]")
   HotKeySet("a")
   HotKeySet("{NUMPADSUB}")
   HotKeySet("0")
EndFunc

Func CloseTab()
   $isSingleCaps = False
   If StringInStr(WinGetTitle("[active]"), "Microsoft Visual Studio", 1, -1) Then
     Send("^{F4}")
   Else
     Send("^{w}")
   EndIf
EndFunc

Func RunAllTests()
   $isSingleCaps = False
   If StringInStr(WinGetTitle("[active]"), "Microsoft Visual Studio", 1, -1) Then
     Send("^u")
     Send("^l")
   EndIf
EndFunc

Func RestartExplorer()
   $isSingleCaps = False
   ShellExecute("taskkill", "/im explorer.exe /f")
   ProcessWaitClose("explorer.exe")
   Run("C:Windowsexplorer.exe")
EndFunc

Func VolumeUp()
   $isSingleCaps = False
   Send("{VOLUME_UP 3}")
EndFunc

Func Play()
   Unset()
   $isSingleCaps = False
   If StringInStr(WinGetTitle("[active]"), "Total Commander", 1, -1) Then
     Send("{RIGHT}") 
     Send("+{RIGHT}")
     Send("aimp3.exe /add_play  ")
     Send("{BACKSPACE}")
     Send("^p")
     Send("{ENTER}")
     Send("{ENTER}")
     Send("{LEFT}")
   EndIf   
EndFunc

Func RealCaps()
   Unset()
   $isSingleCaps = False
   $var = InputBox("To UpperCase", "Этот текст будет передан в UpperCase")
   Send(StringUpper($var))
EndFunc

#include <Misc.au3> ; В Misc.au3 лежит функция _IsPressed, которая будет нужна далее
Global $isSingleCaps = True ; Глобальная переменная для определения одиночного нажатия капса
HotKeySet("{LAUNCH_MAIL}", "HoldModifier") ; Связывает горячую клавишу с функцией, которую надо выполнить при ее нажатии

While 1
   Sleep(60000) ; "Полезная нагрузка"
WEnd

Функция HotKeySet может принимать только определенный набор клавиш и особым образом обрабатывает клавиши-переключатели CapsLock/NumLock/ScrollLock, поэтому для упрощения CapsLock переназначен у меня на {LAUNCH_MAIL} через реестр (SharpKeys — бесплатная утилита, которая, по сути, является графическим интерфейсом к переназначению клавиш через реестр). Существуют и более продвинутые версии HotKeySet, но я не хотел усложнять пример.

Бесконечный цикл показывает пример «событийной модели исполнения» для AutoIt: при вызове функции, приписанной к горячей клавише, выполнение «полезной нагрузки» приостанавливается и продолжается после окончания выполнения функции. Позже я покажу другой вариант.

Func HoldModifier()
   $isSingleCaps = True
   If _IsPressed("B4") Then ; _IsPressed определяет зажата ли в данный момент клавиша с указанным виртуальным кодом
      HotKeySet("w", "CloseTab") ; Если был нажат капс, то устанавливаются следующие горячие клавиши
	  HotKeySet("t", "RunAllTests")
	  HotKeySet("]", "RestartExplorer")
	  HotKeySet("a", "Play")
	  HotKeySet("{NUMPADSUB}", "VolumeUp")
	  HotKeySet("0", "RealCaps")
      While _IsPressed("B4") ; ожидание отпускания капса
         sleep(0)
      WEnd
      Unset()
   EndIf
   
   If $isSingleCaps Then ; Если $isSingleCaps не был изменен, значит, капс был нажат не в комбинации с другими клавишами
	  Send("!+9") ; "!" - Alt, "+" - Shift, на Alt+Shift+9 я установил в настройках системы переключение раскладки на английский язык
   EndIf
EndFunc
 
Func Unset()
   HotKeySet("w") ; HotKeySet без второго параметра снимает регистрацию горячей клавиши
   HotKeySet("t")
   HotKeySet("]")
   HotKeySet("a")
   HotKeySet("{NUMPADSUB}")
   HotKeySet("0")
EndFunc

На этом заканчивается фундаментальная логика использования CapsLock-а, как я хотел показать, дальше примеры того, что можно делать с AutoIt.

Закрытие вкладки в VS:

Func CloseTab()
   $isSingleCaps = False
   If StringInStr(WinGetTitle("[active]"), "Microsoft Visual Studio", 1, -1) Then ; Проверка заголовка окна
	  Send("^{F4}")
   Else
	  Send("^{w}") ; В браузере я теперь тоже закрываю вкладки по Caps+W
   EndIf
EndFunc

Запустить все тесты:

Func RunAllTests()
   $isSingleCaps = False
   If StringInStr(WinGetTitle("[active]"), "Microsoft Visual Studio", 1, -1) Then
	  Send("^u")
	  Send("^l")
   EndIf
EndFunc

Перезапустить процесс проводника (помогало, пока не работали серверы активации Win8):

Func RestartExplorer()
   $isSingleCaps = False
   ShellExecute("taskkill", "/im explorer.exe /f")
   ProcessWaitClose("explorer.exe")
   Run("C:Windowsexplorer.exe")
EndFunc

Я не нашел как в восьмой винде изменить шаг изменения громкости с помощью мультимедиа клавиш, поэтому пошел таким путем:

Func VolumeUp()
   $isSingleCaps = False
   Send("{VOLUME_UP 3}") ; Послать {VOLUME_UP} три раза
EndFunc

Отправить на проигрывание в AIMP папку, на которой стоит курсор в TotalCommander-е (Ctrl+P — отправляет в командную строку путь к текущей папке, как отправить путь к папке под курсором я не нашел, поэтому получился такой кривоватый, но рабочий вариант (сборка PowerUser, если это важно)):

Func Play()
   Unset()
   $isSingleCaps = False
   If StringInStr(WinGetTitle("[active]"), "Total Commander", 1, -1) Then
	  Send("{RIGHT}") ; Зайти в папку
	  Send("+{RIGHT}") ; Перейти в командную строку для папки
	  Send("aimp3.exe /add_play  ") ; Два пробела в конце на случай автодополнения
	  Send("{BACKSPACE}") ; Отмена автодополнение/стирание одного пробела
	  Send("^p") ; Передать путь к текущей папке
	  Send("{ENTER}")
	  Send("{ENTER}")
	  Send("{LEFT}") ; Выйти из папки наверх
   EndIf   
EndFunc

Функция CapsLock-а, как она есть.

Func RealCaps()
   Unset()
   $isSingleCaps = False
   $var = InputBox("To UpperCase", "Этот текст будет передан в UpperCase")
   Send(StringUpper($var))
EndFunc

Другая модель исполнения

В общем виде выглядит так:

While 1
	If _IsPressed("B4") Then
    	...
    EndIf
    Sleep(50)
WEnd

Требует немного больше ресурсов, но позволяет использовать любой виртуальный код.

Заключение

Финальным шагом остается скомпилировать программу Ctrl+F7 и поместить ссылку на получившийся экзешник в папку автозагрузки.

Подобный механизм, конечно, может быть реализован и на других языках программирования и для других операционных систем, никакого rocket science тут нет.

У такого метода есть несколько существенных позитивных качеств:

  • Переключение раскладки по одиночному нажатию CapsLock-а как привычный для многих вариант.
  • Сохранена непосредственная функция капса, так как иногда она, все-таки, бывает полезна.
  • Если вам идеологически не нравится такой подход — вы можете им просто не пользоваться. А если им пользуется кто-то другой, а вы вынуждены поработать за его рабочим местом, вы в один клик останавливаете выполнение скрипта и продолжаете работать в комфортной обстановке.
  • Если все будут пользоваться этим методом, а не переназначать стандартные горячие клавиши, то, садясь за чужое рабочее место, вы отключаете запущенный вариант скрипта и получаете стандартные и привычные горячие клавиши как в варианте а), или запускаете свою версию скрипта и работаете в совсем уж оптимизированной под вас обстановке.

Автор: aush

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js