▌Вступление
Мне неудобно было нажимать 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