Add hammerspon + karabiner config
parent
4a7d8e964a
commit
3d783b96b0
@ -0,0 +1,191 @@
|
||||
# hs.tiling
|
||||
|
||||
Add tiling window management powers to your [hammerspoon][hammerspoon]. (Fork of nathankot's [mjolnir.tiling](https://github.com/nathankot/mjolnir.tiling).)
|
||||
|
||||
## Features
|
||||
|
||||
* Different layouts per space ([with this magic][magic])
|
||||
* Multi-monitor supported
|
||||
* Custom layouts
|
||||
|
||||
## Quick start
|
||||
|
||||
First up, install [Hammerspoon][hammerspoon] if you haven't already.
|
||||
|
||||
Then install `hs.tiling` by cloning into your hammerspoon configuration directory:
|
||||
|
||||
```
|
||||
mkdir -p $HOME/.hammerspoon/hs
|
||||
git clone https://github.com/dsanson/hs.tiling $HOME/.hammerspoon/hs/tiling
|
||||
```
|
||||
|
||||
In your `~/.hammerspoon/init.lua`:
|
||||
|
||||
```lua
|
||||
local tiling = require "hs.tiling"
|
||||
local hotkey = require "hs.hotkey"
|
||||
local mash = {"ctrl", "cmd"}
|
||||
|
||||
hotkey.bind(mash, "c", function() tiling.cycleLayout() end)
|
||||
hotkey.bind(mash, "j", function() tiling.cycle(1) end)
|
||||
hotkey.bind(mash, "k", function() tiling.cycle(-1) end)
|
||||
hotkey.bind(mash, "space", function() tiling.promote() end)
|
||||
hotkey.bind(mash, "f", function() tiling.goToLayout("fullscreen") end)
|
||||
|
||||
-- If you want to set the layouts that are enabled
|
||||
tiling.set('layouts', {
|
||||
'fullscreen', 'main-vertical'
|
||||
})
|
||||
```
|
||||
|
||||
## Updating
|
||||
|
||||
To update to the latest `hs.tiling`, pull the latest from git:
|
||||
|
||||
```
|
||||
cd $HOME/.hammerspoon/hs/tiling
|
||||
git pull
|
||||
```
|
||||
|
||||
## Using custom layouts
|
||||
|
||||
You can define your own layouts like so (please see [layouts.lua](/layouts.lua) for definition examples:)
|
||||
|
||||
```lua
|
||||
tiling.addLayout('custom', function(windows)
|
||||
fnutils.each(windows, function(window)
|
||||
window:maximize()
|
||||
end)
|
||||
end)
|
||||
```
|
||||
|
||||
## Floating Windows
|
||||
|
||||
Using `tiling.toggleFloat` you can toggle whether or not a window that is on your desktop will be
|
||||
included in your tiling calculations. You can optionally pass in a function as a callback to process
|
||||
the window if it was tiled.
|
||||
|
||||
```lua
|
||||
-- Push the window into the exact center of the screen
|
||||
local function center(window)
|
||||
frame = window:screen():frame()
|
||||
frame.x = (frame.w / 2) - (frame.w / 4)
|
||||
frame.y = (frame.h / 2) - (frame.h / 4)
|
||||
frame.w = frame.w / 2
|
||||
frame.h = frame.h / 2
|
||||
window:setFrame(frame)
|
||||
end
|
||||
|
||||
hotkey.bind(mash, "f", function() tiling.toggleFloat(center) end)
|
||||
```
|
||||
|
||||
## Layouts
|
||||
|
||||
These are the layouts that come with `hs.tiling`:
|
||||
|
||||
Name | Screenshot
|
||||
------------------------------------------- | ------------------------------------
|
||||
`fullscreen` | ![fullscreen](https://raw.github.com/dsanson/hs.tiling/master/screenshots/fullscreen.png)
|
||||
`main-vertical` | ![main-vertical](https://raw.github.com/dsanson/hs.tiling/master/screenshots/main-vertical.png)
|
||||
`main-horizontal` | ![main-horizontal](https://raw.github.com/dsanson/hs.tiling/master/screenshots/main-horizontal.png)
|
||||
`rows` | ![rows](https://raw.github.com/dsanson/hs.tiling/master/screenshots/rows.png)
|
||||
`columns` | ![columns](https://raw.github.com/dsanson/hs.tiling/master/screenshots/columns.png)
|
||||
`gp-vertical` | ![gp-vertical](https://raw.github.com/dsanson/hs.tiling/master/screenshots/gp-vertical.png)
|
||||
`gp-horizontal` | ![gp-horizontal](https://raw.github.com/dsanson/hs.tiling/master/screenshots/gp-horizontal.png)
|
||||
|
||||
## Variable Layouts
|
||||
|
||||
I am experimenting with adding support for variable width layouts.
|
||||
|
||||
For now, I have added a variable width variant of `main-vertical`, called
|
||||
`main-vertical-variable`. It it just like `main-vertical`, except the
|
||||
proportion of the screen filled by the main window is determined by the value
|
||||
of the global variable, `mainVertical`, which defaults to `0.5`. I've also
|
||||
added two functions, `tiling.setMainVertical(val)`, which takes a value
|
||||
between `0` and `1` (`0.25` means that the main window takes a quarter of the
|
||||
screen, while the other windows take the remaining three quarters; `0.75`
|
||||
means that the main window takes 3/4 of the screen, while the remaining
|
||||
windows take 1/4, and so on) and `tiling.adjustMainVertical(factor)`, which
|
||||
takes a negative or positive factor by which to adjust the value of
|
||||
`mainVertical` (e.g., if `mainVertical` is `0.5`, then
|
||||
`tiling.adjustMainVertical(-0.1)` sets it to `0.4`).
|
||||
|
||||
Here is how I use them in my `init.lua` (as
|
||||
part of a "windows" mode using `hotkeys.modal`:
|
||||
|
||||
```
|
||||
w:bind({},"left", function() tiling.adjustMainVertical(-0.05) end)
|
||||
w:bind({},"right", function() tiling.adjustMainVertical(0.05) end)
|
||||
w:bind({},"=", function() tiling.setMainVertical(0.5) end)
|
||||
```
|
||||
|
||||
Once I have all the kinks worked out, I plan to enable this feature for
|
||||
`main-vertical` and `gp-vertical`, as well as a corresponding feature for
|
||||
`main-horizontal` and `gp-horizontal`.
|
||||
|
||||
## Contributing
|
||||
|
||||
Yes! Please :)
|
||||
|
||||
```sh
|
||||
git clone https://github.com/dsanson/hs.tiling.git
|
||||
cd hs.tiling
|
||||
```
|
||||
|
||||
## Contributors
|
||||
|
||||
This is a port of [nathankot's](https://github.com/nathankot) [mjolnir.tiling][mjolnir.tiling].
|
||||
|
||||
I replaced
|
||||
references to mjolnir extensions (e.g., `mjolnir.window`) with references to
|
||||
the corresponding hammerspoon extensions (e.g., `hs.window`). I rewrote
|
||||
function calls in camelCase where necessary. I renamed `tiling.lua` to
|
||||
`init.lua`. It seems to work.
|
||||
|
||||
Nathan lists, as contributors to mjolnir.tiling,
|
||||
|
||||
* [csaunders](https://github.com/csaunders)
|
||||
* [acmcelwee](https://github.com/acmcelwee)
|
||||
* [iveney](https://github.com/iveney)
|
||||
* [mavant](https://github.com/mavant)
|
||||
* [OrBaruk](https://github.com/OrBaruk)
|
||||
* [peterjcaulfield](https://github.com/peterjcaulfield)
|
||||
|
||||
Thanks to Nathan and all these contributors for making mjolnir.tiling, and making it so easy to port over to hammerspoon!
|
||||
|
||||
## To-do
|
||||
|
||||
* [x] Better documentation
|
||||
* [x] More layouts
|
||||
* [x] Allow globally enabling/disabling layouts
|
||||
* [ ] Functions to move windows across spaces
|
||||
* [ ] Event-based tiling, although requires [sdegutis/mjolnir#72][72]
|
||||
|
||||
[72]: https://github.com/sdegutis/mjolnir/issues/72
|
||||
[magic]: https://github.com/dsanson/hs.tiling/blob/master/init.lua#L95-L124
|
||||
[hammerspoon]: https://github.com/Hammerspoon/hammerspoon
|
||||
[mjolnir.tiling]: https://github.com/nathankot/mjolnir.tiling
|
||||
|
||||
## License
|
||||
|
||||
> The MIT License (MIT)
|
||||
>
|
||||
> Copyright (c) 2015 Nathan Kot
|
||||
>
|
||||
> Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
> of this software and associated documentation files (the "Software"), to deal
|
||||
> in the Software without restriction, including without limitation the rights
|
||||
> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
> copies of the Software, and to permit persons to whom the Software is
|
||||
> furnished to do so, subject to the following conditions:
|
||||
>
|
||||
> The above copyright notice and this permission notice shall be included in
|
||||
> all copies or substantial portions of the Software.
|
||||
>
|
||||
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
> THE SOFTWARE.
|
@ -0,0 +1,195 @@
|
||||
local tiling = {}
|
||||
|
||||
local application = require "hs.application"
|
||||
local window = require "hs.window"
|
||||
local screen = require "hs.screen"
|
||||
local fnutils = require "hs.fnutils"
|
||||
local geometry = require "hs.geometry"
|
||||
local alert = require "hs.alert"
|
||||
local layouts = require "hs.tiling.layouts"
|
||||
local spaces = {}
|
||||
local settings = { layouts = {} }
|
||||
|
||||
local excluded = {}
|
||||
-- navigate to layout by name
|
||||
function tiling.goToLayout(name)
|
||||
local space = getSpace()
|
||||
local i = 0
|
||||
while space.layout ~= name and i < #settings.layouts do
|
||||
space.layout = space.layoutCycle()
|
||||
i = i + 1
|
||||
end
|
||||
if i < #settings.layouts then
|
||||
alert.show(space.layout, 1)
|
||||
apply(space.windows, space.layout)
|
||||
else
|
||||
alert.show('Layout ' .. name .. ' does not exist', 1)
|
||||
end
|
||||
end
|
||||
|
||||
function tiling.toggleFloat(floatfn)
|
||||
local win = window:focusedWindow()
|
||||
local id = win:id()
|
||||
excluded[id] = not excluded[id]
|
||||
|
||||
if excluded[id] then
|
||||
if floatfn then floatfn(win) end
|
||||
alert.show("Excluding " .. win:title() .. " from tiles")
|
||||
else
|
||||
alert.show("Adding " .. win:title() .. " to tiles")
|
||||
end
|
||||
|
||||
local space = getSpace()
|
||||
apply(space.windows, space.layout)
|
||||
end
|
||||
|
||||
function tiling.addLayout(name, layout)
|
||||
layouts[name] = layout
|
||||
setLayouts(layouts)
|
||||
end
|
||||
|
||||
function tiling.set(name, value)
|
||||
settings[name] = value
|
||||
end
|
||||
|
||||
function tiling.retile()
|
||||
local space = getSpace()
|
||||
apply(space.windows, space.layout)
|
||||
end
|
||||
|
||||
function tiling.cycle(direction)
|
||||
local space = getSpace()
|
||||
local windows = space.windows
|
||||
local win = window:focusedWindow() or windows[1]
|
||||
local direction = direction or 1
|
||||
local currentIndex = fnutils.indexOf(windows, win)
|
||||
local layout = space.layout
|
||||
if not currentIndex then return end
|
||||
nextIndex = currentIndex + direction
|
||||
if nextIndex > #windows then
|
||||
nextIndex = 1
|
||||
elseif nextIndex < 1 then
|
||||
nextIndex = #windows
|
||||
end
|
||||
|
||||
windows[nextIndex]:focus()
|
||||
apply(windows, layout)
|
||||
end
|
||||
|
||||
function tiling.cycleLayout()
|
||||
local space = getSpace()
|
||||
space.layout = space.layoutCycle()
|
||||
alert.show(space.layout, 1)
|
||||
apply(space.windows, space.layout)
|
||||
end
|
||||
|
||||
function tiling.promote()
|
||||
local space = getSpace()
|
||||
local windows = space.windows
|
||||
local win = window:focusedWindow() or windows[1]
|
||||
local i = fnutils.indexOf(windows, win)
|
||||
if not i then return end
|
||||
|
||||
local current = table.remove(windows, i)
|
||||
table.insert(windows, 1, current)
|
||||
win:focus()
|
||||
apply(windows, space.layout)
|
||||
end
|
||||
|
||||
function tiling.setMainVertical(val)
|
||||
if val > 0 and val < 1 then
|
||||
local space = getSpace()
|
||||
if space.layout == 'main-vertical-variable' then
|
||||
space.mainVertical = val
|
||||
tiling.retile()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function tiling.adjustMainVertical(factor)
|
||||
local space = getSpace()
|
||||
if space.layout == 'main-vertical-variable' then
|
||||
local mainVertical = space.mainVertical
|
||||
if mainVertical == nil then
|
||||
mainVertical = 0.5
|
||||
end
|
||||
tiling.setMainVertical(mainVertical + factor)
|
||||
end
|
||||
end
|
||||
|
||||
function apply(windows, layout)
|
||||
layouts[layout](windows)
|
||||
end
|
||||
|
||||
function isWindowIncluded(win)
|
||||
onScreen = win:screen() == screen.mainScreen()
|
||||
standard = win:isStandard()
|
||||
hasTitle = #win:title() > 0
|
||||
isTiling = not excluded[win:id()]
|
||||
return onScreen and standard and hasTitle and isTiling
|
||||
end
|
||||
|
||||
-- Infer a 'space' from our existing spaces
|
||||
function getSpace()
|
||||
local windows = fnutils.filter(window.visibleWindows(), isWindowIncluded)
|
||||
|
||||
fnutils.each(spaces, function(space)
|
||||
local matches = 0
|
||||
fnutils.each(space.windows, function(win)
|
||||
if fnutils.contains(windows, win) then matches = matches + 1 end
|
||||
end)
|
||||
space.matches = matches
|
||||
end)
|
||||
|
||||
table.sort(spaces, function(a, b)
|
||||
return a.matches > b.matches
|
||||
end)
|
||||
|
||||
local space = {}
|
||||
|
||||
if #spaces == 0 or spaces[1].matches == 0 then
|
||||
space.windows = windows
|
||||
space.layoutCycle = fnutils.cycle(settings.layouts)
|
||||
space.layout = settings.layouts[1]
|
||||
table.insert(spaces, space)
|
||||
else
|
||||
space = spaces[1]
|
||||
end
|
||||
|
||||
space.windows = syncWindows(space.windows, windows)
|
||||
return space
|
||||
end
|
||||
|
||||
function syncWindows(windows, newWindows)
|
||||
-- Remove any windows no longer around
|
||||
windows = fnutils.filter(windows, function(win)
|
||||
return fnutils.contains(newWindows, win)
|
||||
end)
|
||||
|
||||
-- Add any new windows since
|
||||
fnutils.each(newWindows, function(win)
|
||||
if fnutils.contains(windows, win) == false then
|
||||
table.insert(windows, win)
|
||||
end
|
||||
end)
|
||||
|
||||
-- Remove any bad windows
|
||||
windows = fnutils.filter(windows, function(win)
|
||||
return win:isStandard()
|
||||
end)
|
||||
|
||||
return windows
|
||||
end
|
||||
|
||||
function setLayouts(layouts)
|
||||
local n = 0
|
||||
for k, v in pairs(layouts) do
|
||||
n = n + 1
|
||||
settings.layouts[n] = k
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
setLayouts(layouts)
|
||||
|
||||
return tiling
|
@ -0,0 +1,200 @@
|
||||
local fnutils = require "hs.fnutils"
|
||||
local layouts = {}
|
||||
|
||||
layouts['fullscreen'] = function(windows)
|
||||
fnutils.each(windows, function(window)
|
||||
window:maximize()
|
||||
end)
|
||||
end
|
||||
|
||||
layouts['main-vertical'] = function(windows)
|
||||
local winCount = #windows
|
||||
|
||||
if winCount == 1 then
|
||||
return layouts['fullscreen'](windows)
|
||||
end
|
||||
|
||||
for index, win in pairs(windows) do
|
||||
local frame = win:screen():frame()
|
||||
|
||||
if index == 1 then
|
||||
frame.w = frame.w / 2
|
||||
else
|
||||
frame.x = frame.x + frame.w / 2
|
||||
frame.w = frame.w / 2
|
||||
frame.h = frame.h / (winCount - 1)
|
||||
frame.y = frame.y + frame.h * (index - 2)
|
||||
end
|
||||
|
||||
win:setFrame(frame)
|
||||
end
|
||||
end
|
||||
|
||||
layouts['main-horizontal'] = function(windows)
|
||||
local winCount = #windows
|
||||
|
||||
if winCount == 1 then
|
||||
return layouts['fullscreen'](windows)
|
||||
end
|
||||
|
||||
for index, win in pairs(windows) do
|
||||
local frame = win:screen():frame()
|
||||
|
||||
if index == 1 then
|
||||
frame.h = frame.h / 2
|
||||
else
|
||||
frame.y = frame.y + frame.h / 2
|
||||
frame.h = frame.h / 2
|
||||
frame.w = frame.w / (winCount - 1)
|
||||
frame.x = frame.x + frame.w * (index - 2)
|
||||
end
|
||||
|
||||
win:setFrame(frame)
|
||||
end
|
||||
end
|
||||
|
||||
layouts['columns'] = function(windows)
|
||||
local winCount = #windows
|
||||
|
||||
if winCount == 1 then
|
||||
return layouts['fullscreen'](windows)
|
||||
end
|
||||
|
||||
for index, win in pairs(windows) do
|
||||
local frame = win:screen():frame()
|
||||
|
||||
frame.w = frame.w / winCount
|
||||
frame.x = frame.x + (index - 1) * frame.w
|
||||
frame.y = 0
|
||||
|
||||
win:setFrame(frame)
|
||||
end
|
||||
end
|
||||
|
||||
layouts['rows'] = function(windows)
|
||||
local winCount = #windows
|
||||
|
||||
if winCount == 1 then
|
||||
return layouts['fullscreen'](windows)
|
||||
end
|
||||
|
||||
for index, win in pairs(windows) do
|
||||
local frame = win:screen():frame()
|
||||
|
||||
frame.h = frame.h / winCount
|
||||
frame.y = frame.y + (index - 1) * frame.h
|
||||
frame.x = 0
|
||||
|
||||
win:setFrame(frame)
|
||||
end
|
||||
end
|
||||
|
||||
layouts['gp-vertical'] = function(windows)
|
||||
local winCount = #windows
|
||||
|
||||
if winCount == 1 then
|
||||
return layouts['fullscreen'](windows)
|
||||
end
|
||||
|
||||
local width
|
||||
local height
|
||||
local x = 0
|
||||
local y = 0
|
||||
|
||||
for index, win in pairs(windows) do
|
||||
local frame = win:screen():frame()
|
||||
|
||||
if index == 1 then
|
||||
height = frame.h
|
||||
width = frame.w / 2
|
||||
elseif index % 2 == 0 then
|
||||
if index ~= winCount then
|
||||
height = height / 2
|
||||
end
|
||||
x = x + width
|
||||
else
|
||||
if index ~= winCount then
|
||||
width = width / 2
|
||||
end
|
||||
y = y + height
|
||||
end
|
||||
|
||||
frame.x = frame.x + x
|
||||
frame.y = frame.y + y
|
||||
frame.w = width
|
||||
frame.h = height
|
||||
|
||||
win:setFrame(frame)
|
||||
end
|
||||
end
|
||||
|
||||
layouts['gp-horizontal'] = function(windows)
|
||||
local winCount = #windows
|
||||
|
||||
if winCount == 1 then
|
||||
return layouts['fullscreen'](windows)
|
||||
end
|
||||
|
||||
local width
|
||||
local height
|
||||
local x = 0
|
||||
local y = 0
|
||||
|
||||
for index, win in pairs(windows) do
|
||||
local frame = win:screen():frame()
|
||||
|
||||
if index == 1 then
|
||||
height = frame.h / 2
|
||||
width = frame.w
|
||||
elseif index % 2 == 0 then
|
||||
if index ~= winCount then
|
||||
width = width / 2
|
||||
end
|
||||
y = y + height
|
||||
else
|
||||
if index ~= winCount then
|
||||
height = height / 2
|
||||
end
|
||||
x = x + width
|
||||
end
|
||||
|
||||
frame.x = frame.x + x
|
||||
frame.y = frame.y + y
|
||||
frame.w = width
|
||||
frame.h = height
|
||||
|
||||
win:setFrame(frame)
|
||||
end
|
||||
end
|
||||
|
||||
layouts['main-vertical-variable'] = function(windows)
|
||||
local winCount = #windows
|
||||
|
||||
if winCount == 1 then
|
||||
return layouts['fullscreen'](windows)
|
||||
end
|
||||
|
||||
local space = getSpace()
|
||||
local mainVertical = space.mainVertical
|
||||
|
||||
if mainVertical == nil then
|
||||
mainVertical = 0.5
|
||||
end
|
||||
|
||||
for index, win in pairs(windows) do
|
||||
local frame = win:screen():frame()
|
||||
|
||||
if index == 1 then
|
||||
frame.w = frame.w * mainVertical
|
||||
else
|
||||
frame.x = frame.x + frame.w * mainVertical
|
||||
frame.w = frame.w * (1 - mainVertical)
|
||||
frame.h = frame.h / (winCount - 1)
|
||||
frame.y = frame.y + frame.h * (index - 2)
|
||||
end
|
||||
|
||||
win:setFrame(frame)
|
||||
end
|
||||
end
|
||||
|
||||
return layouts
|
@ -0,0 +1,120 @@
|
||||
local tiling = require 'hs.tiling'
|
||||
local hyper = { 'cmd', 'alt', 'shift', 'ctrl' }
|
||||
local meta = { 'ctrl', 'cmd' }
|
||||
local keys = {
|
||||
'1', '2', '3', '4', '5', '6', '7', '8', '9', '0',
|
||||
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',
|
||||
'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
|
||||
'u', 'v', 'w', 'x', 'y', 'z', '[', ']', 'space'
|
||||
}
|
||||
|
||||
-- hyper mode
|
||||
-------------
|
||||
|
||||
k = hs.hotkey.modal.new({}, 'F17')
|
||||
|
||||
local printIsdown = function(b) return b and 'down' or 'up' end
|
||||
|
||||
-- sends a key event with all modifiers
|
||||
-- bool -> string -> void -> side effect
|
||||
local hyperFunction = function(isdown)
|
||||
return function(key)
|
||||
return function()
|
||||
k.triggered = true
|
||||
local event = hs.eventtap.event.newKeyEvent(hyper, key, isdown)
|
||||
event:post()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local hyperDown = hyperFunction(true)
|
||||
local hyperUp = hyperFunction(false)
|
||||
|
||||
local hyperBind = function(key)
|
||||
k:bind('', key, msg, hyperDown(key), hyperUp(key), nil)
|
||||
end
|
||||
|
||||
-- bind all keys defined above to hyper + key
|
||||
for index, key in pairs(keys) do hyperBind(key) end
|
||||
|
||||
-- Enter Hyper Mode when F18 (Hyper/Capslock) is pressed
|
||||
-- This has to be mapped with karabiner-elements
|
||||
local pressedF18 = function()
|
||||
k.triggered = false
|
||||
k:enter()
|
||||
end
|
||||
|
||||
-- Leave Hyper Mode when F18 (Hyper/Capslock) is pressed,
|
||||
-- send ESCAPE if no other keys are pressed.
|
||||
local releasedF18 = function()
|
||||
k:exit()
|
||||
if not k.triggered then
|
||||
hs.eventtap.keyStroke({}, 'ESCAPE')
|
||||
end
|
||||
end
|
||||
|
||||
-- Bind the Hyper key
|
||||
local f18 = hs.hotkey.bind({}, 'F18', pressedF18, releasedF18)
|
||||
|
||||
-- Ctrl-key sends escape if it is hold for less than 0.2 seconds
|
||||
----------------------------------------------------------------
|
||||
|
||||
local send_escape = false
|
||||
local last_mods = {}
|
||||
local control_key_timer = hs.timer.delayed.new(0.2, function()
|
||||
send_escape = false
|
||||
end)
|
||||
|
||||
hs.eventtap.new({hs.eventtap.event.types.flagsChanged}, function(evt)
|
||||
local new_mods = evt:getFlags()
|
||||
if last_mods['ctrl'] == new_mods['ctrl'] then
|
||||
return false
|
||||
end
|
||||
if not last_mods["ctrl"] then
|
||||
last_mods = new_mods
|
||||
send_escape = true
|
||||
control_key_timer:start()
|
||||
else
|
||||
if send_escape then
|
||||
hs.eventtap.keyStroke({}, 'escape')
|
||||
end
|
||||
last_mods = new_mods
|
||||
control_key_timer:stop()
|
||||
end
|
||||
return false
|
||||
end):start()
|
||||
|
||||
hs.eventtap.new({hs.eventtap.event.types.keyDown}, function(evt)
|
||||
send_escape = false
|
||||
return false
|
||||
end):start()
|
||||
|
||||
|
||||
-- Window management
|
||||
--------------------
|
||||
|
||||
-- Push the window into the center of the screen
|
||||
local function center(window)
|
||||
frame = window:screen():frame()
|
||||
frame.x = frame.w * 0.1
|
||||
frame.y = frame.h * 0.1
|
||||
frame.w = frame.w * 0.8
|
||||
frame.h = frame.h * 0.8
|
||||
window:setFrame(frame)
|
||||
end
|
||||
|
||||
hs.hotkey.bind(hyper, '1', function() tiling.goToLayout('gp-vertical') end)
|
||||
hs.hotkey.bind(hyper, '2', function() tiling.goToLayout('fullscreen') end)
|
||||
hs.hotkey.bind(hyper, 'f', function() tiling.toggleFloat(center) end)
|
||||
hs.hotkey.bind(hyper, 'r', function() tiling.retile() end)
|
||||
hs.hotkey.bind(hyper, 'a', function() tiling.cycle(1) end)
|
||||
hs.hotkey.bind(hyper, 'w', function() tiling.promote() end)
|
||||
hs.hotkey.bind(hyper, 'c', function() tiling.cycleLayout() end)
|
||||
hs.hotkey.bind(hyper, '[', function() hs.window.focusedWindow():moveOneScreenNorth() end)
|
||||
hs.hotkey.bind(hyper, ']', function() hs.window.focusedWindow():moveOneScreenSouth() end)
|
||||
hs.hotkey.bind(hyper, 'h', function() hs.window.focusedWindow():focusWindowWest() end)
|
||||
hs.hotkey.bind(hyper, 'j', function() hs.window.focusedWindow():focusWindowSouth() end)
|
||||
hs.hotkey.bind(hyper, 'k', function() hs.window.focusedWindow():focusWindowNorth() end)
|
||||
hs.hotkey.bind(hyper, 'l', function() hs.window.focusedWindow():focusWindowEast() end)
|
||||
|
||||
tiling.set('layouts', { 'fullscreen', 'gp-vertical', 'gp-horizontal', 'columns' })
|
@ -0,0 +1,47 @@
|
||||
{
|
||||
"global": {
|
||||
"check_for_updates_on_startup": true,
|
||||
"show_in_menu_bar": false,
|
||||
"show_profile_name_in_menu_bar": false
|
||||
},
|
||||
"profiles": [
|
||||
{
|
||||
"devices": [
|
||||
{
|
||||
"disable_built_in_keyboard_if_exists": true,
|
||||
"identifiers": {
|
||||
"is_keyboard": true,
|
||||
"is_pointing_device": false,
|
||||
"product_id": 256,
|
||||
"vendor_id": 2131
|
||||
},
|
||||
"ignore": false
|
||||
}
|
||||
],
|
||||
"fn_function_keys": {
|
||||
"f1": "display_brightness_decrement",
|
||||
"f10": "mute",
|
||||
"f11": "volume_decrement",
|
||||
"f12": "volume_increment",
|
||||
"f2": "display_brightness_increment",
|
||||
"f3": "mission_control",
|
||||
"f4": "launchpad",
|
||||
"f5": "illumination_decrement",
|
||||
"f6": "illumination_increment",
|
||||
"f7": "rewind",
|
||||
"f8": "play_or_pause",
|
||||
"f9": "fastforward"
|
||||
},
|
||||
"name": "Default profile",
|
||||
"selected": true,
|
||||
"simple_modifications": {
|
||||
"caps_lock": "left_control",
|
||||
"right_option": "f18"
|
||||
},
|
||||
"virtual_hid_keyboard": {
|
||||
"caps_lock_delay_milliseconds": 0,
|
||||
"keyboard_type": "ansi"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
Loading…
Reference in New Issue