375 lines
13 KiB
Lua
375 lines
13 KiB
Lua
----------------------------------------------------------------------------------------------------
|
|
-- Settings
|
|
----------------------------------------------------------------------------------------------------
|
|
hs.autoLaunch(true)
|
|
hs.automaticallyCheckForUpdates(true)
|
|
hs.consoleOnTop(true)
|
|
hs.dockIcon(false)
|
|
hs.menuIcon(false)
|
|
hs.uploadCrashData(false)
|
|
|
|
hs.window.animationDuration = 0
|
|
|
|
configWatcher = hs.pathwatcher.new(hs.configdir, hs.reload)
|
|
configWatcher:start()
|
|
|
|
local moonlanderMode = false
|
|
local maximizeMode = false
|
|
|
|
----------------------------------------------------------------------------------------------------
|
|
-- Utilities
|
|
----------------------------------------------------------------------------------------------------
|
|
|
|
local modifier = {
|
|
cmd = "cmd",
|
|
shift = "shift",
|
|
ctrl = "ctrl",
|
|
option = "alt",
|
|
}
|
|
|
|
local modifiers = {
|
|
hyper = { modifier.cmd, modifier.shift, modifier.ctrl, modifier.option },
|
|
window = { modifier.ctrl, modifier.option },
|
|
clipboard = { modifier.ctrl, modifier.cmd }
|
|
}
|
|
|
|
local bundleID = {
|
|
activityMonitor = "com.apple.ActivityMonitor",
|
|
finder = "com.apple.finder",
|
|
firefox = "org.mozilla.firefox",
|
|
emacs = "org.gnu.emacs",
|
|
iterm = "com.googlecode.iterm2",
|
|
safari = "com.apple.Safari",
|
|
safariTechnologyPreview = "com.apple.SafariTechnologyPreview",
|
|
spotify = "com.spotify.client",
|
|
bitwarden = "com.bitwarden.desktop",
|
|
teams = "com.microsoft.teams",
|
|
faclieThings = "com.electron.nativefier.facilethings-nativefier-cf88de",
|
|
timeular = "com.timeular.zei",
|
|
logseq = "com.electron.logseq"
|
|
}
|
|
|
|
local usbDevice = {
|
|
moonlander = "Moonlander Mark I"
|
|
}
|
|
|
|
local function languageIsGerman() return hs.host.locale.preferredLanguages()[1]:sub(0, 2) == "de" end
|
|
|
|
-- local function maximizeCurrentWindow() hs.window.focusedWindow():maximize() end
|
|
--
|
|
local function centerCurrentWindow() hs.window.focusedWindow():centerOnScreen() end
|
|
--
|
|
-- local function moveCurrentWindowToLeftHalf()
|
|
-- local win = hs.window.focusedWindow()
|
|
-- local screenFrame = win:screen():frame()
|
|
-- local newFrame = hs.geometry.rect(screenFrame.x, screenFrame.y, screenFrame.w / 2, screenFrame.h)
|
|
-- win:setFrame(newFrame)
|
|
-- end
|
|
--
|
|
-- local function moveCurrentWindowToRightHalf()
|
|
-- local win = hs.window.focusedWindow()
|
|
-- local screenFrame = win:screen():frame()
|
|
-- local newFrame = hs.geometry.rect(screenFrame.x + screenFrame.w / 2, screenFrame.y, screenFrame.w / 2, screenFrame.h)
|
|
-- win:setFrame(newFrame)
|
|
-- end
|
|
--
|
|
-- local function moveCurentWindowToNextScreen()
|
|
-- local win = hs.window.focusedWindow()
|
|
-- win:moveToScreen(win:screen():next())
|
|
-- end
|
|
|
|
local function moveMouseToWindowCenter()
|
|
local windowCenter = hs.window.frontmostWindow():frame().center
|
|
hs.mouse.absolutePosition(windowCenter)
|
|
end
|
|
|
|
local function moveMouseToUpperLeft()
|
|
local screenFrame = (hs.window.focusedWindow():screen() or hs.screen.primaryScreen()):frame()
|
|
local newPoint = hs.geometry.point(screenFrame.x + screenFrame.w / 4, screenFrame.y + screenFrame.h / 4)
|
|
hs.mouse.absolutePosition(newPoint)
|
|
end
|
|
|
|
local function moveMouseToUpperRight()
|
|
local screenFrame = (hs.window.focusedWindow():screen() or hs.screen.primaryScreen()):frame()
|
|
local newPoint = hs.geometry.point(screenFrame.x + screenFrame.w * 3 / 4, screenFrame.y + screenFrame.h / 4)
|
|
hs.mouse.absolutePosition(newPoint)
|
|
end
|
|
|
|
local function moveMouseToLowerLeft()
|
|
local screenFrame = (hs.window.focusedWindow():screen() or hs.screen.primaryScreen()):frame()
|
|
local newPoint = hs.geometry.point(screenFrame.x + screenFrame.w / 4, screenFrame.y + screenFrame.h * 3 / 4)
|
|
hs.mouse.absolutePosition(newPoint)
|
|
end
|
|
|
|
local function moveMouseToLowerRight()
|
|
local screenFrame = (hs.window.focusedWindow():screen() or hs.screen.primaryScreen()):frame()
|
|
local newPoint = hs.geometry.point(screenFrame.x + screenFrame.w * 3 / 4, screenFrame.y + screenFrame.h * 3 / 4)
|
|
hs.mouse.absolutePosition(newPoint)
|
|
end
|
|
|
|
----------------------------------------------------------------------------------------------------
|
|
-- Menu
|
|
----------------------------------------------------------------------------------------------------
|
|
|
|
local function menuItems()
|
|
return {
|
|
{
|
|
title = "Hammerspoon " .. hs.processInfo.version,
|
|
disabled = true
|
|
},
|
|
{ title = "-" },
|
|
{
|
|
title = "Moonlander Mode",
|
|
checked = moonlanderMode,
|
|
fn = function() moonlanderDetected(not moonlanderMode) end
|
|
},
|
|
{
|
|
title = "Maximize Mode",
|
|
checked = maximizeMode,
|
|
fn = function() maximizeMode = not maximizeMode end
|
|
},
|
|
{ title = "-" },
|
|
{
|
|
title = "Reload",
|
|
fn = hs.reload
|
|
},
|
|
{
|
|
title = "Console...",
|
|
fn = hs.openConsole
|
|
},
|
|
{ title = "-" },
|
|
{
|
|
title = "Quit",
|
|
fn = function() hs.application.get(hs.processInfo.processID):kill() end
|
|
}
|
|
}
|
|
end
|
|
|
|
menu = hs.menubar.new()
|
|
menu:setMenu(menuItems)
|
|
|
|
----------------------------------------------------------------------------------------------------
|
|
-- Moonlander Detection
|
|
----------------------------------------------------------------------------------------------------
|
|
|
|
local moonlanderModeConfig = {
|
|
[false] = {
|
|
keyboardLayout = "Colemak DH ISO copy",
|
|
icon = hs.configdir .. "/assets/statusicon_off.tiff"
|
|
},
|
|
[true] = {
|
|
keyboardLayout = "EurKEY v1.2",
|
|
icon = hs.configdir .. "/assets/statusicon_on.tiff"
|
|
}
|
|
}
|
|
|
|
local function isDeviceMoonlander(device) return device.productName == usbDevice.moonlander end
|
|
|
|
function moonlanderDetected(connected)
|
|
moonlanderMode = connected
|
|
hs.keycodes.setLayout(moonlanderModeConfig[connected].keyboardLayout)
|
|
menu:setIcon(moonlanderModeConfig[connected].icon)
|
|
end
|
|
|
|
local function searchMoonlander()
|
|
local usbDevices = hs.usb.attachedDevices()
|
|
local moonlanderConnected = hs.fnutils.find(usbDevices, isDeviceMoonlander) ~= nil
|
|
|
|
moonlanderDetected(moonlanderConnected)
|
|
end
|
|
|
|
searchMoonlander()
|
|
|
|
usbWatcher = hs.usb.watcher.new(function(event)
|
|
if event.productName == usbDevice.moonlander then
|
|
moonlanderDetected(event.eventType == "added")
|
|
end
|
|
end)
|
|
usbWatcher:start()
|
|
|
|
caffeinateWatcher = hs.caffeinate.watcher.new(function(event)
|
|
if event == hs.caffeinate.watcher.systemDidWake then
|
|
searchMoonlander()
|
|
end
|
|
end)
|
|
caffeinateWatcher:start()
|
|
|
|
----------------------------------------------------------------------------------------------------
|
|
-- Window Management
|
|
----------------------------------------------------------------------------------------------------
|
|
|
|
hs.window.filter.ignoreAlways = {
|
|
["Mail Web Content"] = true,
|
|
["Mail-Webinhalt"] = true,
|
|
["QLPreviewGenerationExtension (Finder)"] = true,
|
|
["Reeder Web Content"] = true,
|
|
["Reeder-Webinhalt"] = true,
|
|
["Safari Web Content (Cached)"] = true,
|
|
["Safari Web Content (Prewarmed)"] = true,
|
|
["Safari Web Content"] = true,
|
|
["Safari Technology Preview Web Content (Cached)"] = true,
|
|
["Safari Technology Preview Web Content (Prewarmed)"] = true,
|
|
["Safari Technology Preview Web Content"] = true,
|
|
["Safari-Webinhalt (im Cache)"] = true,
|
|
["Safari-Webinhalt (vorgeladen)"] = true,
|
|
["Safari-Webinhalt"] = true,
|
|
["Strongbox (Safari)"] = true,
|
|
}
|
|
windowFilter = hs.window.filter.new({
|
|
"App Store",
|
|
"Code",
|
|
"DataGrip",
|
|
"Firefox",
|
|
"Fork",
|
|
"Fotos",
|
|
"Google Chrome",
|
|
"Vivaldi",
|
|
"IntelliJ IDEA",
|
|
"Mail",
|
|
"Emacs",
|
|
"Microsoft Outlook",
|
|
"Microsoft Teams",
|
|
"Music",
|
|
"Musik",
|
|
"Photos",
|
|
"Postman",
|
|
"Reeder",
|
|
"Safari",
|
|
"Safari Technology Preview",
|
|
"Spotify",
|
|
"Strongbox",
|
|
"BitWarden",
|
|
"Logseq",
|
|
"Timeular",
|
|
"Tower",
|
|
})
|
|
windowFilter:subscribe({ hs.window.filter.windowCreated, hs.window.filter.windowFocused }, function(window)
|
|
if maximizeMode and window ~= nil and window:isStandard() and window:frame().h > 500 then
|
|
window:maximize()
|
|
end
|
|
end)
|
|
|
|
----------------------------------------------------------------------------------------------------
|
|
-- Keyboard Shortcuts
|
|
----------------------------------------------------------------------------------------------------
|
|
|
|
function showHideBundleId(bundleId)
|
|
local focusedWindow = hs.window.focusedWindow()
|
|
if focusedWindow ~= nil and focusedWindow:application():bundleID() == bundleId then -- window is focused
|
|
focusedWindow:close() -- hide
|
|
else
|
|
hs.application.launchOrFocusByBundleID(bundleId)
|
|
hs.window.focusedWindow():centerOnScreen(hs.mouse.getCurrentScreen())
|
|
end
|
|
end
|
|
|
|
-- hs.hotkey.bind(modifiers.window, hs.keycodes.map.left, moveCurrentWindowToLeftHalf)
|
|
-- hs.hotkey.bind(modifiers.window, hs.keycodes.map.right, moveCurrentWindowToRightHalf)
|
|
-- hs.hotkey.bind(modifiers.window, hs.keycodes.map.down, moveCurentWindowToNextScreen)
|
|
-- hs.hotkey.bind(modifiers.window, hs.keycodes.map["return"], maximizeCurrentWindow)
|
|
hs.hotkey.bind(modifiers.window, "c", centerCurrentWindow)
|
|
|
|
hs.loadSpoon("MiroWindowsManager")
|
|
hs.window.animationDuration = 0
|
|
spoon.MiroWindowsManager:bindHotkeys({
|
|
up = {modifiers.window, "up"},
|
|
right = {modifiers.window, "right"},
|
|
down = {modifiers.window, "down"},
|
|
left = {modifiers.window, "left"},
|
|
fullscreen = {modifiers.window, "return"},
|
|
nextscreen = {modifiers.hyper, "right"}
|
|
})
|
|
|
|
|
|
hs.hotkey.bind(modifiers.hyper, "[", moveMouseToWindowCenter)
|
|
hs.hotkey.bind(modifiers.hyper, "m", moveMouseToUpperLeft)
|
|
hs.hotkey.bind(modifiers.hyper, "o", moveMouseToUpperRight)
|
|
hs.hotkey.bind(modifiers.hyper, hs.keycodes.map.up, moveMouseToLowerLeft)
|
|
hs.hotkey.bind(modifiers.hyper, hs.keycodes.map.down, moveMouseToLowerRight)
|
|
hs.hotkey.bind(modifiers.hyper, hs.keycodes.map.delete, function() hs.caffeinate.lockScreen() end)
|
|
hs.hotkey.bind(modifiers.hyper, "a", function() showHideBundleId(bundleID.activityMonitor) end)
|
|
hs.hotkey.bind(modifiers.hyper, "c", function() showHideBundleId(bundleID.safari) end)
|
|
hs.hotkey.bind(modifiers.hyper, "f", function() showHideBundleId(bundleID.faclieThings) end)
|
|
hs.hotkey.bind(modifiers.hyper, "p", function() showHideBundleId(bundleID.timeular) end)
|
|
hs.hotkey.bind(modifiers.hyper, "b", function() showHideBundleId(bundleID.bitwarden) end)
|
|
hs.hotkey.bind(modifiers.hyper, "t", function() showHideBundleId(bundleID.iterm) end)
|
|
hs.hotkey.bind({ modifier.cmd }, "\\", function()
|
|
local application = hs.application.frontmostApplication()
|
|
|
|
if application:bundleID() == bundleID.bitwarden then
|
|
application:hide()
|
|
else
|
|
hs.application.launchOrFocusByBundleID(bundleID.bitwarden)
|
|
end
|
|
end)
|
|
|
|
----------------------------------------------------------------------------------------------------
|
|
-- Mouse Shortcuts
|
|
----------------------------------------------------------------------------------------------------
|
|
|
|
local function handleMouse4()
|
|
hs.eventtap.keyStroke({ modifier.cmd }, "left")
|
|
end
|
|
|
|
local function handleMouse5()
|
|
hs.eventtap.keyStroke({ modifier.cmd }, "right")
|
|
end
|
|
|
|
-- bind mouse3/4 to back and forward
|
|
mouseTap = hs.eventtap.new({ hs.eventtap.event.types.otherMouseDown }, function(event)
|
|
if event:getButtonState(3) then
|
|
handleMouse4()
|
|
return true
|
|
elseif event:getButtonState(4) then
|
|
handleMouse5()
|
|
return true
|
|
end
|
|
return false
|
|
end)
|
|
mouseTap:start()
|
|
|
|
----------------------------------------------------------------------------------------------------
|
|
-- Clipboard Manager
|
|
----------------------------------------------------------------------------------------------------
|
|
|
|
-- clipboard = require("clipboard")
|
|
-- clipboard:start()
|
|
--
|
|
-- hs.hotkey.bind(modifiers.clipboard, "v", function() clipboard:toggleClipboard() end)
|
|
-- hs.hotkey.bind(modifiers.clipboard, hs.keycodes.map.delete, function() clipboard:clearAll() end)
|
|
|
|
----------------------------------------------------------------------------------------------------
|
|
-- notmuch indicator
|
|
----------------------------------------------------------------------------------------------------
|
|
|
|
local notmuchTaskRunning = false
|
|
local function refreshNotmuchMenubar(currentlyRunning)
|
|
hs.task.new("@notmuchMails@", function(exitCode, stdout, stderr)
|
|
if currentlyRunning then
|
|
stdout = "R: " .. stdout
|
|
end
|
|
print(stdout)
|
|
notmuchMenubar:setTitle(hs.styledtext.new(stdout))
|
|
end):start()
|
|
end
|
|
|
|
local function notmuchTimerFunction()
|
|
if not notmuchTaskRunning then
|
|
refreshNotmuchMenubar(true)
|
|
notmuchTaskRunning = true
|
|
hs.task.new("/etc/profiles/per-user/ragon/bin/zsh",
|
|
function() notmuchTaskRunning = false; refreshNotmuchMenubar(false) end,
|
|
function() return false end, { "-c", "syncmail" }):start()
|
|
end
|
|
end
|
|
|
|
notmuchMenubar = hs.menubar.new()
|
|
-- notmuchMenubar:setClickCallback(function(options)
|
|
-- if options.shift then
|
|
-- notmuchTimerFunction()
|
|
-- else
|
|
-- hs.task.new("@myEmacs@/bin/emacsclient", nil, function() return false end,
|
|
-- { "-c", "-a", "", "--eval", "(=notmuch)" }):start()
|
|
-- end
|
|
-- end)
|
|
notmuchTimer = hs.timer.doEvery(300, notmuchTimerFunction)
|