Last active
August 29, 2015 14:09
-
-
Save devnore/858c7fe438310a66f623 to your computer and use it in GitHub Desktop.
Mjolnir - init.lua
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--[[ | |
# 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" | |
-- 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(); mjolnir.openconsole() 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 = 1400, h = 940 }, | |
{ key = "2", w = 980, h = 920 }, | |
{ key = "3", w = 800, h = 880 }, | |
{ key = "4", w = 800, h = 740 }, | |
{ key = "5", w = 700, h = 740 }, | |
{ key = "6", w = 850, h = 620 }, | |
{ key = "7", w = 770, h = 470 } | |
}, 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 = "r", app = "Reminders" }, | |
-- { 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