-
Notifications
You must be signed in to change notification settings - Fork 213
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement Hyper Mode exclusively with Hammerspoon
- Hyper Mode is now activated by simultaneously pressing the 'k' and 'l' keys and holding them down. This is similar to activating (S)uper (D)uper Mode by simultaneously pressing the 's' and 'd' keys and holding them down. - Hyper Mode is no longer activated by pressing the right option key. - The format of the Hyper Mode key-to-app mappings has changed. See hammerspoon/hyper-apps-defaults.lua.
- Loading branch information
1 parent
8dc9812
commit b6e7d24
Showing
4 changed files
with
189 additions
and
44 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,19 +1,40 @@ | ||
local status, hyperModeAppMappings = pcall(require, 'keyboard.hyper-apps') | ||
local eventtap = hs.eventtap | ||
local eventTypes = hs.eventtap.event.types | ||
local simultaneousKeypressModal = require('keyboard.simultaneous-keypress-modal') | ||
|
||
-- Look for custom Hyper Mode app mappings. If there are none, then use the | ||
-- default mappings. | ||
local status, hyperModeAppMappings = pcall(require, 'keyboard.hyper-apps') | ||
if not status then | ||
hyperModeAppMappings = require('keyboard.hyper-apps-defaults') | ||
end | ||
|
||
for i, mapping in ipairs(hyperModeAppMappings) do | ||
local key = mapping[1] | ||
local app = mapping[2] | ||
hs.hotkey.bind({'shift', 'ctrl', 'alt', 'cmd'}, key, function() | ||
-- Create a hotkey that will enter Hyper Mode when 'k' and 'l' are pressed | ||
-- simultaneously. | ||
hyperMode = simultaneousKeypressModal.new('Hyper Mode', 'k', 'l') | ||
|
||
-------------------------------------------------------------------------------- | ||
-- Watch for key-down events in Hyper Mode, and trigger app associated with the | ||
-- given key. | ||
-------------------------------------------------------------------------------- | ||
hyperModeKeyListener = eventtap.new({ eventTypes.keyDown }, function(event) | ||
if not hyperMode:isActive() then | ||
return false | ||
end | ||
|
||
local app = hyperModeAppMappings[event:getCharacters(true):lower()] | ||
|
||
if app then | ||
if (type(app) == 'string') then | ||
hs.application.open(app) | ||
elseif (type(app) == 'function') then | ||
app() | ||
else | ||
hs.logger.new('hyper'):e('Invalid mapping for Hyper +', key) | ||
end | ||
end) | ||
end | ||
return true | ||
end | ||
end):start() | ||
|
||
--- We're all set. Now we just enable Hyper Mode and get to work. 👔 | ||
hyperMode:enable() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,151 @@ | ||
local eventtap = hs.eventtap | ||
local eventTypes = hs.eventtap.event.types | ||
local message = require('keyboard.status-message') | ||
|
||
local modal={} | ||
|
||
modal.new = function(name, key1, key2) | ||
local instance = { | ||
-- The two keys that must be pressed simultaneously to enter the modal state | ||
key1 = key1, | ||
key2 = key2, | ||
|
||
-- If key1 and key2 are *both* pressed within this time period, consider | ||
-- this to mean that they've been pressed simultaneously, and therefore we | ||
-- should enter the modal state. | ||
maxTimeBetweenSimultaneousKeypresses = 0.04, -- 40 milliseconds | ||
|
||
-- The status message to display when the modal state is active | ||
statusMessage = message.new(name), | ||
|
||
-- Resets object to initial state | ||
reset = function(self) | ||
self.active = false | ||
self.isKey1Down = false | ||
self.isKey2Down = false | ||
self.ignoreNextKey1 = false | ||
self.ignoreNextKey2 = false | ||
self.statusMessage:hide() | ||
|
||
return self | ||
end, | ||
|
||
-- Are we in the modal state? | ||
isActive = function(self) | ||
return self.active | ||
end, | ||
|
||
-- Enters the modal state | ||
-- | ||
-- Mimics hs.hotkey.modal:enter() | ||
enter = function(self) | ||
if self.active then return end | ||
|
||
self.statusMessage:show() | ||
self.active = true | ||
self:entered() | ||
end, | ||
|
||
-- Exits the modal state | ||
-- | ||
-- Mimics hs.hotkey.modal:exit() | ||
exit = function(self) | ||
if not self.active then return end | ||
|
||
self:reset() | ||
self:exited() | ||
end, | ||
|
||
-- Optional callback for when a modal state is entered | ||
-- | ||
-- Mimics hs.hotkey.modal:entered() | ||
entered = function(self) end, | ||
|
||
-- Optional callback for when a modal state is exited | ||
-- | ||
-- Mimics hs.hotkey.modal:exited() | ||
exited = function(self) end, | ||
|
||
-- Enable the simultaneous keypress hotkey | ||
-- | ||
-- Mimics hs.hotkey:enable() | ||
enable = function(self) | ||
self.activationListener:start() | ||
self.deactivationListener:start() | ||
end, | ||
|
||
-- Diable the simultaneous keypress hotkey | ||
-- | ||
-- Mimics hs.hotkey:disable() | ||
disable = function(self) | ||
self:exit() | ||
self.activationListener:stop() | ||
self.deactivationListener:stop() | ||
end, | ||
} | ||
|
||
instance.activationListener = eventtap.new({ eventTypes.keyDown }, function(event) | ||
-- If key1 or key2 is pressed in conjuction with any modifier keys | ||
-- (e.g., command + key1), then we're not activating the modal state. | ||
if not (next(event:getFlags()) == nil) then | ||
return false | ||
end | ||
|
||
local characters = event:getCharacters() | ||
|
||
if characters == instance.key1 then | ||
if instance.ignoreNextKey1 then | ||
instance.ignoreNextKey1 = false | ||
return false | ||
end | ||
-- Temporarily suppress this key1 keystroke. At this point, we're not sure | ||
-- if the user intends to type key1, or if the user is attempting to | ||
-- activate the modal state. If key2 is pressed by the time the following | ||
-- function executes, then activate the modal state. Otherwise, trigger an | ||
-- ordinary key1 keystroke. | ||
instance.isKey1Down = true | ||
hs.timer.doAfter(instance.maxTimeBetweenSimultaneousKeypresses, function() | ||
if instance.isKey2Down then | ||
instance:enter() | ||
else | ||
instance.ignoreNextKey1 = true | ||
keyUpDown({}, instance.key1) | ||
return false | ||
end | ||
end) | ||
return true | ||
elseif characters == instance.key2 then | ||
if instance.ignoreNextKey2 then | ||
instance.ignoreNextKey2 = false | ||
return false | ||
end | ||
-- Temporarily suppress this key2 keystroke. At this point, we're not sure | ||
-- if the user intends to type key2, or if the user is attempting to | ||
-- activate the modal state. If key1 is pressed by the time the following | ||
-- function executes, then activate the modal state. Otherwise, trigger an | ||
-- ordinary key2 keystroke. | ||
instance.isKey2Down = true | ||
hs.timer.doAfter(instance.maxTimeBetweenSimultaneousKeypresses, function() | ||
if instance.isKey1Down then | ||
instance:enter() | ||
else | ||
instance.ignoreNextKey2 = true | ||
keyUpDown({}, instance.key2) | ||
return false | ||
end | ||
end) | ||
return true | ||
end | ||
end) | ||
|
||
instance.deactivationListener = eventtap.new({ eventTypes.keyUp }, function(event) | ||
local characters = event:getCharacters() | ||
if characters == instance.key1 or characters == instance.key2 then | ||
instance:reset() | ||
end | ||
end) | ||
|
||
return instance:reset() | ||
end | ||
|
||
return modal |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters