▌Вступление
Мне неудобно было нажимать 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:\Windows\explorer.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:\Windows\explorer.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-а как привычный для многих вариант.
- Сохранена непосредственная функция капса, так как иногда она, все-таки, бывает полезна.
- Если вам идеологически не нравится такой подход — вы можете им просто не пользоваться. А если им пользуется кто-то другой, а вы вынуждены поработать за его рабочим местом, вы в один клик останавливаете выполнение скрипта и продолжаете работать в комфортной обстановке.
- Если все будут пользоваться этим методом, а не переназначать стандартные горячие клавиши, то, садясь за чужое рабочее место, вы отключаете запущенный вариант скрипта и получаете стандартные и привычные горячие клавиши как в варианте а), или запускаете свою версию скрипта и работаете в совсем уж оптимизированной под вас обстановке.
