From e01cc5490718dbffe8f484597064d52320604128 Mon Sep 17 00:00:00 2001 From: jowj Date: Wed, 31 Jul 2019 15:57:10 -0500 Subject: [PATCH] Create syncing hammerspoon dir. - ugh it is TOO BAD that I can't use this on any lyenux :*( --- .../.hammerspoon/init.lua | 64 ++++++++ .../.hammerspoon/modalHotKey.lua | 148 ++++++++++++++++++ 2 files changed, 212 insertions(+) create mode 100644 applicationConfiguration/.hammerspoon/init.lua create mode 100644 applicationConfiguration/.hammerspoon/modalHotKey.lua diff --git a/applicationConfiguration/.hammerspoon/init.lua b/applicationConfiguration/.hammerspoon/init.lua new file mode 100644 index 0000000..5dfd440 --- /dev/null +++ b/applicationConfiguration/.hammerspoon/init.lua @@ -0,0 +1,64 @@ +hammerSpoonEmoji = "🔨🥄" + +hs.printf("======== config file reloaded ========") +hs.alert.show(hammerSpoonEmoji .. " Config Loaded") + +animationDuration = 0 + +-- window sizing +hs.hotkey.bind({"cmd", "ctrl"}, "Left", function() + local win = hs.window.focusedWindow() + local f = win:frame() + local screen = win:screen() + local max = screen:frame() + + f.x = max.x + f.y = max.y + f.w = max.w / 2 + f.h = max.h + win:setFrame(f) +end) + +hs.hotkey.bind({"cmd","ctrl"}, "Right", function() + local win = hs.window.focusedWindow() + local f = win:frame() + local screen = win:screen() + local max = screen:frame() + + f.x = max.x + (max.w / 2) + f.y = max.y + f.w = max.w / 2 + f.h = max.h + win:setFrame(f) +end) + + +-- quick reload +hs.hotkey.bind({"cmd", "ctrl"}, "R", function() + hs.reload() +end) + +-- stolen from @mrled +appCuts = { + e = 'Emacs', + h = 'Hammerspoon', + f = 'Firefox', + l = 'Slack', + o = 'Microsoft Outlook', + p = '1Password 7', + r = 'Riot', + s = 'Safari', + t = 'iTerm', +} + +modalHotKey = dofile(os.getenv("HOME") .. "/.hammerspoon/modalHotKey.lua") +appActionTable = {} +for key, app in pairs(appCuts) do + appActionTable[key] = function() hs.application.launchOrFocus(app) end +end +appModal = modalHotKey.new( + hs.hotkey.modal.new({"cmd", "ctrl"}, "Space"), + appActionTable, + appCuts, + hammerSpoonEmoji .. "Awful App Switcher" +) diff --git a/applicationConfiguration/.hammerspoon/modalHotKey.lua b/applicationConfiguration/.hammerspoon/modalHotKey.lua new file mode 100644 index 0000000..290be24 --- /dev/null +++ b/applicationConfiguration/.hammerspoon/modalHotKey.lua @@ -0,0 +1,148 @@ +--[[ modalHotKey.lua +Adapted from https://github.com/asmagill/hammerspoon-config/blob/master/_scratch/modalSuppression.lua +see https://github.com/Hammerspoon/hammerspoon/issues/1505 +save file somewhere then type `modalHotKey = dofile("modalHotKey.lua")` to load it +Here's an example based on my usage: +appCuts = { + e = 'Emacs', + f = 'Finder', + j = 'Cisco Jabber', + k = 'Keychain Access', + o = 'Microsoft Outlook', + s = 'Safari', + t = 'Terminal', +} +appActionTable = {} +for key, app in pairs(appCuts) do + appActionTable[key] = function() hs.application.launchOrFocus(app) end +end +modalHotKey = dofile("modalHotKey.lua") +appModal = modalHotKey.new( + hs.hotkey.modal.new({"cmd", "ctrl"}, "t"), + appActionTable, + appCuts, + "Special Application Switcher" +) +*only* the recognized key sequences will be allowed through -- this means you can't even quit hammerspoon with Cmd-Q +without tapping escape first. +--]] + +-- suppressKeysOtherThanOurs(): Create custom eventtap to suppress unwanted keys and pass through the ones we do want +-- modal: An object created from module.new() below (not just a modal hotkey from hs.hotkey.modal.new(); requires our extension methods) +local suppressKeysOtherThanOurs = function(modal) + local passThroughKeys = {} + + -- this is annoying because the event's raw flag bitmasks differ from the bitmasks used by hotkey, so + -- we have to convert here for the lookup + + for i,v in ipairs(modal.keys) do + -- parse for flags, get keycode for each + local kc, mods = tostring(v._hk):match("keycode: (%d+), mods: (0x[^ ]+)") + local hkFlags = tonumber(mods) + local hkOriginal = hkFlags + local flags = 0 + if (hkFlags & 256) == 256 then hkFlags, flags = hkFlags - 256, flags | hs.eventtap.event.rawFlagMasks.command end + if (hkFlags & 512) == 512 then hkFlags, flags = hkFlags - 512, flags | hs.eventtap.event.rawFlagMasks.shift end + if (hkFlags & 2048) == 2048 then hkFlags, flags = hkFlags - 2048, flags | hs.eventtap.event.rawFlagMasks.alternate end + if (hkFlags & 4096) == 4096 then hkFlags, flags = hkFlags - 4096, flags | hs.eventtap.event.rawFlagMasks.control end + if hkFlags ~= 0 then print("unexpected flag pattern detected for " .. tostring(v._hk)) end + passThroughKeys[tonumber(kc)] = flags + end + + return hs.eventtap.new({ + hs.eventtap.event.types.keyDown, + hs.eventtap.event.types.keyUp, + }, function(event) + -- check only the flags we care about and filter the rest + local flags = event:getRawEventData().CGEventData.flags & ( + hs.eventtap.event.rawFlagMasks.command | + hs.eventtap.event.rawFlagMasks.control | + hs.eventtap.event.rawFlagMasks.alternate | + hs.eventtap.event.rawFlagMasks.shift + ) + if passThroughKeys[event:getKeyCode()] == flags then + hs.printf("passing: %3d 0x%08x", event:getKeyCode(), flags) + return false -- pass it through so hotkey can catch it + else + hs.printf("suppressing: %3d 0x%08x", event:getKeyCode(), flags) + modal:exitWithMessage("Invalid modal key " .. event:getKeyCode() .. " exiting mode") + return true -- delete it if we got this far -- it's a key that we want suppressed + end + end) +end + + +local module = {} + +-- new(): Create a new modal hotkey +-- triggerKey: A table created by invoking hs.hotkey.modal.new(), like hs.hotkey.modal.new({"cmd", "ctrl"}, "t") +-- actionTable: A table of {subKey = action} pairs +-- actionDescTable: A table of {subKey = description} pairs +-- modalMessagePrefix: A message prefix to display when communicating to the user about this hot key +module.new = function(triggerKey, actionTable, actionDescTable, modalMessagePrefix) + local modality = {} + modality.activeAlert = nil + modality.alertMessage = modalMessagePrefix .. "\n========\n" + + -- Create a table of index->subKey mappings + -- This lets us alphabetize our hotkeys for display + alphaKeys = {} + for subKey, _ in pairs(actionDescTable) do + table.insert(alphaKeys, subKey) + end + table.sort(alphaKeys) + + -- Show a menu of allowed subKey presses + doubleTab = " " + for idx, subKey in pairs(alphaKeys) do + desc = actionDescTable[subKey] + modality.alertMessage = modality.alertMessage .. "\n" .. subKey .. ":" .. doubleTab .. desc + end + + triggerKey.exitWithMessage = function(self, message) + hs.alert(modalMessagePrefix .. "\n========\n" .. message) + self:exit() + end + + triggerKey.entered = function(self) + self._eventtap = suppressKeysOtherThanOurs(self):start() + modality.activeAlert = hs.alert.show(modality.alertMessage, {}, hs.screen.mainScreen(), "forever") + end + + triggerKey.exited = function(self) + self._eventtap:stop() + self._eventtap = nil + hs.alert.closeSpecific(modality.activeAlert) + end + + -- define an explicit way out + triggerKey:bind({}, "escape", function() triggerKey:exit() end) + + for subKey, action in pairs(actionTable) do + triggerKey:bind({}, subKey, function() + action() + triggerKey:exit() + end) + end + + modality.triggerKey = triggerKey + + modality.start = function(self) + if self.triggerKey._eventtap then + self.triggerKey:enter() + end + end + modality.stop = function(self) + if self.triggerKey._eventtap then + self.triggerKey:exit() + end + end + modality.started = function(self) + return not not self.triggerKey._eventtap + end + + return modality +end + + +return module