From 262dcebd7938847432b68c80be29bab2431575ea Mon Sep 17 00:00:00 2001 From: jowj Date: Fri, 10 Jan 2020 09:27:59 -0600 Subject: [PATCH 1/3] Moving hammerspoon config around. --- .config/hammerspoon/init.lua | 128 ++++++++++++++++++++++++ .config/hammerspoon/modalHotKey.lua | 148 ++++++++++++++++++++++++++++ 2 files changed, 276 insertions(+) create mode 100644 .config/hammerspoon/init.lua create mode 100644 .config/hammerspoon/modalHotKey.lua diff --git a/.config/hammerspoon/init.lua b/.config/hammerspoon/init.lua new file mode 100644 index 0000000..fbee8d0 --- /dev/null +++ b/.config/hammerspoon/init.lua @@ -0,0 +1,128 @@ +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) + +hs.hotkey.bind({"cmd", "ctrl"}, "f", function() + -- size focused window to size of desktop + 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 + f.h = max.h + win:setFrame(f) +end) + + +hs.hotkey.bind({"cmd", "ctrl"}, "up", function() + -- size focused window to top half of display + 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 + f.h = max.h / 2 + win:setFrame(f) +end) + +hs.hotkey.bind({"cmd", "ctrl"}, "down", function() + -- size focused window to bottom half of display + 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 + (max.h / 2) + f.w = max.w + f.h = max.h / 2 + win:setFrame(f) +end) + +-- quick reload +hs.hotkey.bind({"cmd", "ctrl"}, "R", function() + hs.reload() +end) + +-- stolen from @mrled +appCuts = { + e = 'Emacs', + d = 'Dash', + h = 'Hammerspoon', + f = 'Firefox', + l = 'Slack', + o = 'Microsoft Outlook', + p = '1Password 7', + r = 'Riot', + s = 'Safari', + t = 'iTerm', + c = 'Google Chrome' +} + +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" +) + +-- stolen from stackoverflow, for moving between monitors. +-- Get the focused window, its window frame dimensions, its screen frame dimensions, +-- and the next screen's frame dimensions. +-- https://stackoverflow.com/questions/54151343/how-to-move-an-application-between-monitors-in-hammerspoon +hs.hotkey.bind({"cmd", "ctrl"}, "o", function() + local focusedWindow = hs.window.focusedWindow() + local focusedScreenFrame = focusedWindow:screen():frame() + local nextScreenFrame = focusedWindow:screen():next():frame() + local windowFrame = focusedWindow:frame() + + -- Calculate the coordinates of the window frame in the next screen and retain aspect ratio + windowFrame.x = ((((windowFrame.x - focusedScreenFrame.x) / focusedScreenFrame.w) * nextScreenFrame.w) + nextScreenFrame.x) + windowFrame.y = ((((windowFrame.y - focusedScreenFrame.y) / focusedScreenFrame.h) * nextScreenFrame.h) + nextScreenFrame.y) + windowFrame.h = ((windowFrame.h / focusedScreenFrame.h) * nextScreenFrame.h) + windowFrame.w = ((windowFrame.w / focusedScreenFrame.w) * nextScreenFrame.w) + + -- Set the focused window's new frame dimensions + focusedWindow:setFrame(windowFrame) +end) diff --git a/.config/hammerspoon/modalHotKey.lua b/.config/hammerspoon/modalHotKey.lua new file mode 100644 index 0000000..290be24 --- /dev/null +++ b/.config/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 From 2ba24f870c69367c7b30cb9bc416e1c1c5070222 Mon Sep 17 00:00:00 2001 From: jowj Date: Fri, 10 Jan 2020 09:29:20 -0600 Subject: [PATCH 2/3] Update call to modal hotkey to reflect new location. --- .config/hammerspoon/init.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.config/hammerspoon/init.lua b/.config/hammerspoon/init.lua index fbee8d0..228b31e 100644 --- a/.config/hammerspoon/init.lua +++ b/.config/hammerspoon/init.lua @@ -95,7 +95,7 @@ appCuts = { c = 'Google Chrome' } -modalHotKey = dofile(os.getenv("HOME") .. "/.hammerspoon/modalHotKey.lua") +modalHotKey = dofile(os.getenv("HOME") .. "/.config/hammerspoon/modalHotKey.lua") appActionTable = {} for key, app in pairs(appCuts) do appActionTable[key] = function() hs.application.launchOrFocus(app) end From 7d294069e66e16a69c05083d0d0bf14ffdc31e93 Mon Sep 17 00:00:00 2001 From: jowj Date: Mon, 13 Jan 2020 11:46:23 -0600 Subject: [PATCH 3/3] Make zsh work with history / C-r again. --- .zshrc | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/.zshrc b/.zshrc index 81ba8cc..e27f52c 100644 --- a/.zshrc +++ b/.zshrc @@ -34,12 +34,7 @@ man() { man "$@" } -# history control variables -export HISTCONTROL=ignoreboth -export HISTSIZE="INFINITE" -export HISTFILESIZE=5000 -export HISTCONTROL="ignorespace" -export HISTTIMEFORMAT="%Y-%m-%d %H:%M:%S " +setopt appendhistory # make less more friendly for non-text input files, see lesspipe(1) [ -x /usr/bin/lesspipe ] && eval "$(SHELL=/bin/sh lesspipe)"