Skip to content

Instantly share code, notes, and snippets.

@johntdyer
Created April 24, 2015 18:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save johntdyer/53e5130cfb9c99bea21d to your computer and use it in GitHub Desktop.
Save johntdyer/53e5130cfb9c99bea21d to your computer and use it in GitHub Desktop.
--[[
# Install dependencies.
brew update
brew install lua
brew install luarocks
brew install blueutil
wget https://github.com/sdegutis/mjolnir/releases/download/0.4.3/Mjolnir-0.4.3.tgz
mkdir -p ~/.luarocks
mkdir -p ~/.mjolnir/
echo 'rocks_servers = { "http://rocks.moonscript.org" }' > ~/.luarocks/config.lua
luarocks install mjolnir.application
luarocks install mjolnir.hotkey
luarocks install mjolnir.screen
luarocks install mjolnir.sk.transform
luarocks install mjolnir._asm.timer
luarocks install mjolnir._asm.ui.notification
#Move this file to ~/.mjolnir/init.lua
]]--
local hotkey = require "mjolnir.hotkey"
local window = require "mjolnir.window"
local fnutils = require "mjolnir.fnutils"
local application = require "mjolnir.application"
local timer = require "mjolnir._asm.timer"
local notification = require "mjolnir._asm.ui.notification"
local transform = require "mjolnir.sk.transform"
local alert = require "mjolnir.alert"
-- extensions
local ext = {
frame = {},
win = {},
app = {},
utils = {}
}
-- window extension settings
ext.win.margin = 2
ext.win.movespeed = 30
ext.win.positions = {}
ext.win.animate = false
ext.win.fixenabled = false
-- returns frame pushed to screen edge
function ext.frame.push(screen, direction)
-- Define heights, widths, positions.
local short = screen.h / 2 - ext.win.margin
local tall = screen.h - ext.win.margin * (2 - 1 / 4)
local wide = screen.w - ext.win.margin * 2
local narrow = screen.w / 2 - ext.win.margin * (2 - 1 / 4)
local screenx = ext.win.margin + screen.x
local screenmiddlex = ext.win.margin / 2 + screen.w / 2 + screen.x
local screeny = ext.win.margin + screen.y
local screenmiddley = ext.win.margin * 3 / 4 + screen.h / 2 + screen.y
local frames = {
[ "full"] = function() return { x = screenx, y = screeny, w = wide, h = tall } end,
[ "upleft"] = function() return { x = screenx, y = screeny, w = narrow, h = short } end,
[ "upright"] = function() return { x = screenmiddlex, y = screeny, w = narrow, h = short } end,
[ "downright"] = function() return { x = screenmiddlex, y = screenmiddley, w = narrow, h = short } end,
[ "downleft"] = function() return { x = screenx, y = screenmiddley, w = narrow, h = short } end,
[ "up" ] = function() return { x = screenx, y = screeny, w = wide, h = short } end,
[ "down" ] = function() return { x = screenx, y = screenmiddley, w = wide, h = short } end,
[ "left" ] = function() return { x = screenx, y = screeny, w = narrow, h = tall } end,
[ "right" ] = function() return { x = screenmiddlex, y = screeny, w = narrow, h = tall } end
}
return frames[direction]()
end
-- throw to next screen, center and fit
function ext.win.next(win,direction)
local screen = win:screen():next():frame()
local frame = win:frame()
frame.x = screen.x
frame.y = screen.y
frame = ext.frame.fit(frame, screen)
frame = ext.frame.center(frame, screen)
ext.win.fix(win)
ext.win.set(win, frame)
win:focus()
ext.win.center(win)
end
-- throw to next screen, center and fit
function ext.win.previous(win)
local screen = win:screen():previous():frame()
local frame = win:frame()
frame.x = screen.x
frame.y = screen.y
frame = ext.frame.fit(frame, screen)
frame = ext.frame.center(frame, screen)
ext.win.fix(win)
ext.win.set(win, frame)
win:focus()
ext.win.center(win)
end
-- centers window
function ext.win.center(win)
local screen = win:screen():frame()
local frame = win:frame()
frame = ext.frame.center(frame, screen)
ext.win.set(win, frame)
end
-- returns frame centered inside screen
function ext.frame.center(frame, screen)
frame.x = screen.w / 2 - frame.w / 2 + screen.x
frame.y = screen.h / 2 - frame.h / 2 + screen.y
return frame
end
-- returns frame moved by ext.win.margin
function ext.frame.nudge(frame, screen, direction)
local modifyframe = {
[ "up" ] = function(frame)
frame.y = math.max(screen.y + ext.win.movespeed, frame.y - ext.win.movespeed)
return frame
end,
[ "down" ] = function(frame)
frame.y = math.min(screen.y + screen.h - frame.h - ext.win.movespeed * 3 / 4, frame.y + ext.win.movespeed)
return frame
end,
[ "left" ] = function(frame)
frame.x = math.max(screen.x + ext.win.movespeed, frame.x - ext.win.movespeed)
return frame
end,
[ "right" ] = function(frame)
frame.x = math.min(screen.x + screen.w - frame.w - ext.win.movespeed, frame.x + ext.win.movespeed)
return frame
end
}
return modifyframe[direction](frame)
end
-- returns frame sent to screen edge
function ext.frame.send(frame, screen, direction)
local modifyframe = {
[ "up" ] = function(frame)
frame.y = screen.y + ext.win.margin
return frame
end,
[ "down" ] = function(frame)
frame.y = screen.y + screen.h - frame.h - ext.win.margin * 3 / 4
return frame
end,
[ "left" ] = function(frame)
frame.x = screen.x + ext.win.margin
return frame
end,
[ "right" ] = function(frame)
frame.x = screen.x + screen.w - frame.w - ext.win.margin
return frame
end
}
return modifyframe[direction](frame)
end
-- returns frame fited inside screen
function ext.frame.fit(frame, screen)
frame.w = math.min(frame.w, screen.w - ext.win.margin * 2)
frame.h = math.min(frame.h, screen.h - ext.win.margin * (2 - 1 / 4))
return frame
end
-- set frame
function ext.win.set(win, frame, time)
time = time or 0.15
if ext.win.animate then
transform:setframe(win, frame, time)
else
win:setframe(frame)
end
end
-- ugly fix for problem with window height when it's as big as screen
function ext.win.fix(win)
if ext.win.fixenabled then
local screen = win:screen():frame()
local frame = win:frame()
if (frame.h > (screen.h - ext.win.margin * (2 - 1 / 4))) then
frame.h = screen.h - ext.win.margin * 10
ext.win.set(win, frame)
end
end
end
-- pushes window in direction
function ext.win.push(win, direction)
local screen = win:screen():frame()
local frame
frame = ext.frame.push(screen, direction)
ext.win.fix(win)
ext.win.set(win, frame)
end
-- nudges window in direction
function ext.win.nudge(win, direction)
local screen = win:screen():frame()
local frame = win:frame()
frame = ext.frame.nudge(frame, screen, direction)
ext.win.set(win, frame, 0.05)
end
-- push and nudge window in direction
function ext.win.pushandnudge(win, direction)
ext.win.push(win, direction)
ext.win.nudge(win, direction)
end
-- sends window in direction
function ext.win.send(win, direction)
local screen = win:screen():frame()
local frame = win:frame()
frame = ext.frame.send(frame, screen, direction)
ext.win.fix(win)
ext.win.set(win, frame)
end
-- set window size and center
function ext.win.size(win, size)
local screen = win:screen():frame()
local frame = win:frame()
frame.w = size.w
frame.h = size.h
frame = ext.frame.fit(frame, screen)
frame = ext.frame.center(frame, screen)
ext.win.set(win, frame)
end
-- save and restore window positions
function ext.win.pos(win, option)
local id = win:application():bundleid()
local frame = win:frame()
-- saves window position if not saved before
if option == "save" and not ext.win.positions[id] then
ext.win.positions[id] = frame
end
-- force update saved window position
if option == "update" then
ext.win.positions[id] = frame
end
-- restores window position
if option == "load" and ext.win.positions[id] then
ext.win.set(win, ext.win.positions[id])
end
end
-- END Window manager.
-- smart browser launch or focus
ext.app.browser = function()
local browsers = { "Google Chrome", "Safari"}
local runningapps = application.runningapplications()
local runningbrowsers = fnutils.map(browsers, function(browser)
return fnutils.find(runningapps, function(app)
return app:title() == browser
end)
end)
if #runningbrowsers > 0 then
runningbrowsers[1]:activate()
else
application.launchorfocus(browsers[1])
end
end
-- exec command
ext.utils.exec = function(command)
local handle = io.popen(os.getenv("SHELL") .. " -l -i -c \"" .. command .. "\"", "r")
local output = handle:read("*all")
handle:close()
return output
end
-- toggle bluetooth
ext.utils.togglebluetooth = function()
local status = string.len(ext.utils.exec("blueutil | grep 'Power: 1'")) > 0
local command = "blueutil power " .. (status and "0" or "1")
notification.show("Bluetooth", "Turned " .. (status and "off" or "on"))
ext.utils.exec(command)
end
-- toggle wifi
ext.utils.togglewifi = function()
local status = string.len(ext.utils.exec("networksetup -getairportpower en0 | grep On")) > 0
local command = "networksetup -setairportpower en0 " .. (status and "off" or "on")
notification.show("Wireless", "Turned " .. (status and "off" or "on"))
ext.utils.exec(command)
end
-- apply function to a window with optional params, saving it's position for restore
function dowin(fn, param)
local win = window.focusedwindow()
if win and not win:isfullscreen() then
ext.win.pos(win, "save")
fn(win, param)
end
end
-- for simple hotkey binding
function bindwin(fn, param)
return function()
dowin(fn, param)
end
end
-- apply function to a window with a timer
function timewin(fn, param)
return timer.new(0.02, function()
dowin(fn, param)
end)
end
-- keyboard modifier for bindings
local ctrlaltcmd = {"cmd", "alt", "ctrl"}
local shiftctrlalt = {"shift", "ctrl", "alt"}
local ctrlalt = {"ctrl", "alt"}
local cmd = {"cmd"}
local cmdalt = {"cmd", "alt"}
local cmdctrl = { "cmd", "ctrl" }
local cmdaltshift = { "cmd", "alt", "shift" }
-- basic bindings
hotkey.bind(ctrlaltcmd, "c", bindwin(ext.win.center))
hotkey.bind(ctrlaltcmd, "m", bindwin(ext.win.push, "full"))
-- hotkey.bind(ctrlaltcmd, "s", bindwin(ext.win.pos, "update"))
-- hotkey.bind(ctrlaltcmd, "r", bindwin(ext.win.pos, "load"))
hotkey.bind(ctrlaltcmd, "r", function()
mjolnir.reload();
alert.show("Config loaded")
end)
-- Send to left/right Screen .
hotkey.bind(ctrlalt, "right", bindwin(ext.win.next))
hotkey.bind(ctrlalt, "left", bindwin(ext.win.previous))
-- Send to screen corners
hotkey.bind(shiftctrlalt,"left",bindwin(ext.win.push, "upleft"))
hotkey.bind(shiftctrlalt,"up",bindwin(ext.win.push, "upright"))
hotkey.bind(shiftctrlalt,"down",bindwin(ext.win.push, "downleft"))
hotkey.bind(shiftctrlalt,"right",bindwin(ext.win.push, "downright"))
-- push to edges
fnutils.each({ "up", "down", "left", "right" }, function(direction)
local nudge = timewin(ext.win.nudge, direction)
hotkey.bind(ctrlaltcmd, direction, bindwin(ext.win.push, direction))
end)
-- set window sizes
fnutils.each({
{ key = "1", w = 1920, h = 1500 },
{ key = "2", w = 1400, h = 940 },
{ key = "3", w = 980, h = 920 },
{ key = "4", w = 800, h = 880 },
{ key = "5", w = 800, h = 740 }
}, function(object)
hotkey.bind(cmdctrl, object.key, bindwin(ext.win.size, { w = object.w, h = object.h }))
end)
-- launch or focus browser in a smart way
hotkey.bind(ctrlaltcmd, "b", function() ext.app.browser() end)
-- toggle bluetooth and wifi
hotkey.bind(shiftctrlalt, "b", function() ext.utils.togglebluetooth() end)
hotkey.bind(shiftctrlalt, "w", function() ext.utils.togglewifi() end)
-- launch and focus applications
fnutils.each({
{ key = "c", app = "Calendar" },
{ key = "f", app = "Finder" },
{ key = "t", app = "iterm" }
}, function(object)
hotkey.bind(shiftctrlalt, object.key, function() application.launchorfocus(object.app) end)
end)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment