├── .gitignore ├── README.md ├── init.lua ├── lower.sh ├── raise.sh └── toggle.sh /.gitignore: -------------------------------------------------------------------------------- 1 | tags 2 | *.bak 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # hammerspoon-config 2 | 3 | https://github.com/Hammerspoon/hammerspoon 4 | -------------------------------------------------------------------------------- /init.lua: -------------------------------------------------------------------------------- 1 | -- hs.hotkey.bind({'cmd', 'alt', 'ctrl'}, 'W', function() 2 | -- hs.alert.show('Hello World!') 3 | -- end) 4 | 5 | -- local grid = require "hs.grid" 6 | -- 7 | -- require "keyboard_grid" 8 | 9 | configFileWatcher = nil 10 | 11 | function move_left() 12 | local win = hs.window.focusedWindow() 13 | if win == nil then 14 | return 15 | end 16 | local f = win:frame() 17 | local screen = win:screen() 18 | local max = screen:frame() 19 | 20 | f.x = max.x 21 | f.y = max.y 22 | f.w = max.w / 2 23 | f.h = max.h 24 | win:setFrame(f) 25 | redrawBorder() 26 | end 27 | 28 | function move_right() 29 | local win = hs.window.focusedWindow() 30 | if win == nil then 31 | return 32 | end 33 | local f = win:frame() 34 | local screen = win:screen() 35 | local max = screen:frame() 36 | 37 | f.x = max.x + (max.w / 2) 38 | f.y = max.y 39 | f.w = max.w / 2 40 | f.h = max.h 41 | win:setFrame(f) 42 | redrawBorder() 43 | end 44 | 45 | function move_topleft() 46 | local win = hs.window.focusedWindow() 47 | if win == nil then 48 | return 49 | end 50 | local f = win:frame() 51 | local screen = win:screen() 52 | local max = screen:frame() 53 | 54 | f.x = max.x 55 | f.y = max.y 56 | f.w = max.w / 2 57 | f.h = max.h / 2 58 | win:setFrame(f) 59 | redrawBorder() 60 | end 61 | 62 | function move_topright() 63 | local win = hs.window.focusedWindow() 64 | if win == nil then 65 | return 66 | end 67 | local f = win:frame() 68 | local screen = win:screen() 69 | local max = screen:frame() 70 | 71 | f.x = max.x + (max.w / 2) 72 | f.y = max.y 73 | f.w = max.w / 2 74 | f.h = max.h / 2 75 | win:setFrame(f) 76 | redrawBorder() 77 | end 78 | 79 | function move_botleft() 80 | local win = hs.window.focusedWindow() 81 | if win == nil then 82 | return 83 | end 84 | local f = win:frame() 85 | local screen = win:screen() 86 | local max = screen:frame() 87 | 88 | f.x = max.x 89 | f.y = max.y + (max.h / 2) 90 | f.w = max.w / 2 91 | f.h = max.h / 2 92 | win:setFrame(f) 93 | redrawBorder() 94 | end 95 | 96 | function move_botright() 97 | local win = hs.window.focusedWindow() 98 | if win == nil then 99 | return 100 | end 101 | local f = win:frame() 102 | local screen = win:screen() 103 | local max = screen:frame() 104 | 105 | f.x = max.x + (max.w / 2) 106 | f.y = max.y + (max.h / 2) 107 | f.w = max.w / 2 108 | f.h = max.h / 2 109 | win:setFrame(f) 110 | redrawBorder() 111 | end 112 | 113 | function maximize_window() 114 | local win = hs.window.focusedWindow() 115 | if win == nil then 116 | return 117 | end 118 | local f = win:frame() 119 | local screen = win:screen() 120 | local max = screen:frame() 121 | 122 | f.x = max.x 123 | f.y = max.y 124 | f.w = max.w 125 | f.h = max.h 126 | win:setFrame(f) 127 | redrawBorder() 128 | end 129 | 130 | function focus_left() 131 | local win = hs.window.filter.new():setCurrentSpace(true) 132 | -- local win = hs.window.focusedWindow() 133 | if win == nil then 134 | return 135 | end 136 | win:focusWindowWest(nil, false, true) 137 | -- win:focusWindowWest(nil, nil, True) 138 | end 139 | 140 | function focus_right() 141 | local win = hs.window.filter.new():setCurrentSpace(true) 142 | if win == nil then 143 | return 144 | end 145 | win:focusWindowEast(nil, false, true) 146 | end 147 | 148 | function focus_north() 149 | local win = hs.window.filter.new():setCurrentSpace(true) 150 | if win == nil then 151 | return 152 | end 153 | win:focusWindowNorth(nil, false, true) 154 | end 155 | 156 | function focus_south() 157 | local win = hs.window.filter.new():setCurrentSpace(true) 158 | if win == nil then 159 | return 160 | end 161 | win:focusWindowSouth(nil, false, true) 162 | end 163 | 164 | -- I always end up losing my mouse pointer, particularly if it's on a monitor full of terminals. 165 | -- This draws a bright red circle around the pointer for a few seconds 166 | function mouseHighlight() 167 | if mouseCircle then 168 | mouseCircle:delete() 169 | if mouseCircleTimer then 170 | mouseCircleTimer:stop() 171 | end 172 | end 173 | mousepoint = hs.mouse.get() 174 | mouseCircle = hs.drawing.circle(hs.geometry.rect(mousepoint.x-40, mousepoint.y-40, 80, 80)) 175 | mouseCircle:setStrokeColor({["red"]=1,["blue"]=0,["green"]=0,["alpha"]=1}) 176 | mouseCircle:setFill(false) 177 | mouseCircle:setStrokeWidth(5) 178 | mouseCircle:show() 179 | 180 | mouseCircleTimer = hs.timer.doAfter(3, function() mouseCircle:delete() end) 181 | end 182 | 183 | global_border = nil 184 | 185 | function redrawBorder() 186 | win = hs.window.focusedWindow() 187 | if win ~= nil then 188 | top_left = win:topLeft() 189 | size = win:size() 190 | if global_border ~= nil then 191 | global_border:delete() 192 | end 193 | global_border = hs.drawing.rectangle(hs.geometry.rect(top_left['x'], top_left['y'], size['w'], size['h'])) 194 | global_border:setStrokeColor({["red"]=1,["blue"]=0,["green"]=0,["alpha"]=0.8}) 195 | global_border:setFill(false) 196 | global_border:setStrokeWidth(8) 197 | global_border:show() 198 | end 199 | end 200 | 201 | redrawBorder() 202 | 203 | allwindows = hs.window.filter.new(nil) 204 | allwindows:subscribe(hs.window.filter.windowCreated, function () redrawBorder() end) 205 | allwindows:subscribe(hs.window.filter.windowFocused, function () redrawBorder() end) 206 | allwindows:subscribe(hs.window.filter.windowMoved, function () redrawBorder() end) 207 | allwindows:subscribe(hs.window.filter.windowUnfocused, function () redrawBorder() end) 208 | 209 | hs.hotkey.bind({'cmd', 'alt'}, 'i', function() 210 | -- hs.alert.show('launching iterm') 211 | ret = hs.application.launchOrFocus('iTerm') 212 | -- ret = hs.application.launchOrFocus('iTerm2') 213 | -- ret = hs.application.launchOrFocus('iTerm 2') 214 | -- hs.alert.show(ret) 215 | -- local iterm = hs.appfinder.appFromName('Safari') 216 | -- local iterm = hs.appfinder.appFromName('iTerm') 217 | -- hs.application.launchOrFocus(iterm) 218 | -- hs.alert.show(iterm:title()) 219 | end) 220 | 221 | -- hs.hotkey.bind({'cmd', 'ctrl'}, 'h', move_focused_window_left) 222 | -- hs.hotkey.bind({'cmd', 'ctrl'}, 'l', move_focused_window_right) 223 | -- hs.hotkey.bind({'cmd', 'alt'}, 'h', move_left) 224 | -- hs.hotkey.bind({'cmd', 'alt'}, 'l', move_right) 225 | 226 | -- hs.hotkey.bind({}, 'F12', function() hs.osascript.applescript('set curVolume to get volume settings if output muted of curVolume is false then set volume with output muted else set volume without output muted end if') end) 227 | -- hs.hotkey.bind({}, 'F11', function() hs.osascript.applescript("set volume output volume (output volume of (get volume settings) + 1) --100%") end) 228 | -- 229 | -- hs.hotkey.bind({}, 'F12', function() hs.osascript.applescript('set curVolume to get volume settings if output muted of curVolume is false then set volume with output muted else set volume without output muted end if') end) 230 | -- hs.hotkey.bind({}, 'F11', function() hs.osascript.applescript("set volume output volume (output volume of (get volume settings) + 1) --100%") end) 231 | -- hs.hotkey.bind({}, 'F10', function() hs.osascript.applescript("set volume output volume (output volume of (get volume settings) + 1) --100%") end) 232 | -- 233 | -- -- hs.hotkey.bind({'cmd', 'alt'}, 'Left', function() hs.osascript.applescript("set volume output volume (output volume of (get volume settings) + 1) --100%") end) 234 | -- hs.hotkey.bind({}, 'f10', function() hs.osascript.applescript("set volume output volume (output volume of (get volume settings) + 1) --100%") end) 235 | -- hs.hotkey.bind({}, 'F12', move_left) 236 | 237 | toggle_vol = function() os.execute('./toggle.sh') end 238 | lower_vol = function() os.execute('./lower.sh') end 239 | raise_vol = function() os.execute('./raise.sh') end 240 | 241 | hs.hotkey.bind({}, 'f12', toggle_vol, function() end, toggle_vol) 242 | hs.hotkey.bind({}, 'f10', raise_vol, function() end, raise_vol) 243 | hs.hotkey.bind({}, 'f9', lower_vol, function() end, lower_vol) 244 | 245 | hs.hotkey.bind({'cmd', 'alt'}, 'Left', move_left) 246 | hs.hotkey.bind({'cmd', 'alt'}, 'Right', move_right) 247 | hs.hotkey.bind({'cmd', 'ctrl'}, 'h', move_left) 248 | hs.hotkey.bind({'cmd', 'ctrl'}, 'l', move_right) 249 | hs.hotkey.bind({'cmd', 'ctrl'}, 'k', maximize_window) 250 | hs.hotkey.bind({'cmd', 'alt'}, 'Up', maximize_window) 251 | 252 | 253 | hs.hotkey.bind({'cmd', 'shift', 'alt'}, 'h', function() hs.window.focusedWindow():moveOneScreenWest() end) 254 | hs.hotkey.bind({'cmd', 'shift', 'alt'}, 'l', function() hs.window.focusedWindow():moveOneScreenEast() end) 255 | hs.hotkey.bind({'cmd', 'shift', 'ctrl'}, 'h', function() hs.window.focusedWindow():moveOneScreenWest() end) 256 | hs.hotkey.bind({'cmd', 'shift', 'ctrl'}, 'l', function() hs.window.focusedWindow():moveOneScreenEast() end) 257 | hs.hotkey.bind({'cmd', 'shift', 'alt'}, 'Left', function() hs.window.focusedWindow():moveOneScreenWest() end) 258 | hs.hotkey.bind({'cmd', 'shift', 'alt'}, 'Right', function() hs.window.focusedWindow():moveOneScreenEast() end) 259 | 260 | hs.hotkey.bind({'cmd', 'alt'}, 'y', move_topleft) 261 | hs.hotkey.bind({'cmd', 'alt'}, 'o', move_topright) 262 | hs.hotkey.bind({'cmd', 'alt'}, 'n', move_botleft) 263 | hs.hotkey.bind({'cmd', 'alt'}, '.', move_botright) 264 | 265 | hs.hotkey.bind({'cmd', 'ctrl'}, 'y', move_topleft) 266 | hs.hotkey.bind({'cmd', 'ctrl'}, 'o', move_topright) 267 | hs.hotkey.bind({'cmd', 'ctrl'}, 'n', move_botleft) 268 | hs.hotkey.bind({'cmd', 'ctrl'}, '.', move_botright) 269 | 270 | hs.hotkey.bind({'cmd', 'alt'}, 'h', focus_left) 271 | hs.hotkey.bind({'cmd', 'alt'}, 'l', focus_right) 272 | 273 | hs.hotkey.bind({'cmd', 'shift'}, 'h', focus_left) 274 | hs.hotkey.bind({'cmd', 'shift'}, 'l', focus_right) 275 | hs.hotkey.bind({'cmd', 'shift'}, 'j', focus_south) 276 | hs.hotkey.bind({'cmd', 'shift'}, 'k', focus_north) 277 | 278 | -- hs.hints.style = 'vimperator' 279 | hs.hints.showTitleThresh = 10 280 | -- hs.hotkey.bind({'cmd', 'alt'}, 'p', hs.hints.windowHints) 281 | hs.hotkey.bind({'cmd'}, 'p', hs.hints.windowHints) 282 | 283 | ------------------------------------------------ 284 | -- Expose 285 | ------------------------------------------------ 286 | -- set up your instance(s) 287 | expose = hs.expose.new(nil,{showThumbnails=false}) -- default windowfilter, no thumbnails 288 | expose_app = hs.expose.new(nil,{onlyActiveApplication=true}) -- show windows for the current application 289 | expose_space = hs.expose.new(nil,{includeOtherSpaces=false}) -- only windows in the current Mission Control Space 290 | expose_browsers = hs.expose.new{'Safari','Google Chrome'} -- specialized expose using a custom windowfilter 291 | -- for your dozens of browser windows :) 292 | 293 | -- then bind to a hotkey 294 | hs.expose.ui.maxHintLetters = 1 295 | hs.hotkey.bind('ctrl-cmd','e','Expose',function()expose:toggleShow()end) 296 | hs.hotkey.bind('ctrl-cmd-shift','e','App Expose',function()expose_app:toggleShow()end) 297 | hs.hotkey.bind('cmd','e','Expose',function()expose:toggleShow()end) 298 | 299 | 300 | ------------------------------------------------ 301 | -- TILE WINDOWS ON CURRENT SCREEN 302 | ------------------------------------------------ 303 | hs.hotkey.bind({'cmd', 'ctrl'}, 't', function() 304 | local wins = hs.window.filter.new():setCurrentSpace(true):getWindows() 305 | local screen = hs.screen.mainScreen():currentMode() 306 | local rect = hs.geometry(0, 0, screen['w'], screen['h']) 307 | hs.window.tiling.tileWindows(wins, rect) 308 | end) 309 | 310 | ------------------------------------------------ 311 | -- GRID 312 | ------------------------------------------------ 313 | hs.grid.setGrid('6x6') 314 | hs.grid.setMargins('0x0') 315 | hs.hotkey.bind({'cmd'}, 'g', hs.grid.show) 316 | hs.hotkey.bind({'cmd'}, 'h', hs.grid.show) 317 | ------------------------------------------------ 318 | -- GRID 319 | ------------------------------------------------ 320 | 321 | 322 | hs.window.animationDuration = 0 323 | hs.alert.show('Config loaded!') 324 | 325 | function reloadConfig() 326 | if configFileWatcher ~= nil then 327 | configFileWatcher:stop() 328 | configFileWatcher = nil 329 | end 330 | 331 | hs.reload() 332 | end 333 | 334 | configFileWatcher = hs.pathwatcher.new(os.getenv("HOME") .. "/.hammerspoon/init.lua", reloadConfig) 335 | configFileWatcher:start() 336 | -- hs.alert('reloaded') 337 | -------------------------------------------------------------------------------- /lower.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | osascript -e "set volume output volume (output volume of (get volume settings) - 2)" 4 | -------------------------------------------------------------------------------- /raise.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | osascript -e "set volume output volume (output volume of (get volume settings) + 2)" 4 | -------------------------------------------------------------------------------- /toggle.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | osascript -e "set curVolume to get volume settings 4 | if output muted of curVolume is false then 5 | set volume with output muted 6 | else 7 | set volume without output muted 8 | end if" 9 | --------------------------------------------------------------------------------