Category Archives: Hammerspoon

how to add a window switcher

If you’re a big fan of the command tab Application switcher, you might enjoy adding a window switcher to your list of keyboard hotkeys. The window switcher allows you to jump between different windows, both those of other apps and the same app with a hotkey like option tab, which sits nicely next to command tab in my muscle memory!

This is particularly useful if you have a couple of windows open in several applications, and it is much faster and neater than first using command tab and then command backtick to cycle through an app’s windows. Another advantage here is that the window switcher will include full screen and non-full screen windows in multiple spaces, which command backtick typically does not handle well, something I find particularly frustrating when using Xcode.

Adding a window switcher is easy and doesn’t require any hacking. It does require Hammerspoon, however. But if you’re a regular reader of this blog, you’ll already have installed Hammerspoon after reading my earlier posts on it here and here and here. 🙂

With Hammerspoon up and running, adding the window switcher is just a case of cutting and pasting some code into your config file, saving it then reloading. You can use the default code in hs.window.switcher docs or use mine below. The default code is a bit ugly for my liking. Instead, I use the code below, which sets up the switcher’s ui as seen in the screenshots here with option tab and option-shift tab for shortcuts, but you can modify the appearance to suit your taste. As ever, the Hammerspoon docs are wonderfully clear and easy to follow (take a lesson, Apple!).





-- set up your windowfilter
switcher = hs.window.switcher.new() -- default windowfilter: only visible windows, all Spaces
switcher.ui.highlightColor = {0.4,0.4,0.5,0.8}
switcher.ui.thumbnailSize = 112
switcher.ui.selectedThumbnailSize = 284
switcher.ui.backgroundColor = {0.3, 0.3, 0.3, 0.5}
switcher.ui.fontName = 'System'
switcher.ui.textSize = 14
switcher.ui.showSelectedTitle = false

-- bind to hotkeys; WARNING: at least one modifier key is required!
hs.hotkey.bind("alt","tab",function()switcher:next()end)
hs.hotkey.bind("alt-shift","tab",function()switcher:previous()end)
--EOF



Adjust values such as shortcut bindings as you please, and that’s it. For two minutes work, you just added a very useful window switcher to macOS!

Enjoy! 😀

how to display wifi name (ssid) in menu bar

If you’re one for visiting coffee shops and other places with public networks, you might have an interest in today’s Hammerspoon tip.

This little code added to your config file will display the name (SSID) of the currently connected Wifi network in your menu bar. Whenever the Wifi network changes, the name will automatically update so you can always see at a glance that you’re connected to the Wifi network that you think you are.

Playing around with that unusually long number (between 2147483644 and …647) will move the SSID name along the icon bar. Set it to the default ‘1000’ if you want macOS to decide where it should be.

wifiWatcher = nil
function ssidChanged()
local wifiName = hs.wifi.currentNetwork()
if wifiName then
wifiMenu:setTitle(wifiName)
else
wifiMenu:setTitle("Wifi OFF")
end
end

wifiMenu = hs.menubar.newWithPriority(2147483645)
ssidChanged()

wifiWatcher = hs.wifi.watcher.new(ssidChanged):start()







Enjoy! 🙂


Further Reading
More fun with Hammerspoon
Get automated with Hammerspoon

more fun with Hammerspoon

For anyone using Hammerspoon and folder ‘watchers’ to alert them to changes to a given folder, I’ve updated the watcher function I wrote about here

The problem with the simple alert I demonstrated last time is that it only hangs around for a second or two (much less than a Folder Action alert, which takes a couple of minutes to time out). In this updated function, it now also writes a list of the file changes to a (by default) file on the Desktop. The write is an append: if the file doesn’t exist it will create it before writing; if it does exist, it will append the latest changes and date to the file. This way, even if you miss the alert, you’ll always have a record of what files have been added, deleted or modified in your watched folder.

In this example, the folder being watched is ~/Library/LaunchAgents since we want to be aware of any adware, malware or other unsavoury processes being surreptitiously added by, for example, apps we download from the internet. Although there are, of course, many legitimate reasons for apps placing items in here, this folder is prime real estate for attackers as it is one of the locations that can launch processes at log in time without the user’s interaction (or knowledge).

Here’s the code, also available from my pastebin here. A code walkthrough follows.

function writeStringToFileWithMode(aString, aPath, aMode)
local this_file
this_file = io.open(aPath, aMode)
this_file:write(aString)
this_file:close()
end

function myFolderWatch(files)
local str = "Launch Agents folder was modified on " .. os.date() .. " :\n\t"
local this_path = os.getenv("HOME") .. "/Desktop/LaunchFolderModified.txt"
local ignore = "DS_Store"
local count = 0

for _,file in pairs (files) do
count = count + 1
i = string.find(file, ignore)
if not i then
str = str .. file .. "\n\t"
else
if count == 1 then
str = "\n"
end
end
end
str = str .. "\n"
writeStringToFileWithMode(str, this_path, "a")

if string.len(str) > 2 then
hs.alert.show("Launch Agents folder was modified.")
end
end

local aWatcher = hs.pathwatcher.new(os.getenv("HOME") .. "/Library/LaunchAgents/", myFolderWatch):start()



Code walkthrough
The first function, ‘writeStringToFileWithMode’ is just a convenience function. Hopefully the clue is in the name. The ‘aMode’ parameter is either “w” for write(over) or “a” for write(append).

The myFolderWatch function starts off by declaring some local variables.

‘str’ includes the initial line that we want to write to our output file and interpolates the time of the change by calling os.date().

‘this_path’ defines the path that we want to write our list of file names too.

The ‘ .. ‘ in both these assignments is a string concatenator (i.e., like ‘&’ in AppleScript or ‘stringByAppendingString’ in Obj-C).

‘ignore’ is a string that will help us to exclude .DS_Store files from triggering the alert or appearing in the list of file changes.

The ‘count’ variable is an internal var we need in order to eliminate .DS_Store changes when it’s the only change. Lua doesn’t have an easy way to count entries in tables, so we bascially iterate a variable each time through the loop to achieve the same effect.

After that, we have the ‘for’ loop. For loops in Lua are weird (at least for me), as you’ll see that they have this structure for a,b in pair (aPair). I won’t go into why, other than to say its a result of Lua’s table data structure. The '_' here is just a dummy variable for the first parameter. The ‘files’ in parentheses are the list of file names (not file specifiers, you AppleScripters!) that were added, deleted, or modified in the watched folder.

The loop begins by incrementing the count, then checks for the ignore file (.DS_Store). If the ignore file is not found, we then append the filename to the str variable.

If it is found, we check the count. If the count is ‘1’ (i.e., the only change was .DS_Store) we discard the entire str var and replace it with new line character. If the count is more than 1 we don’t do anything to ‘str’. We just ignore adding anything to it all.

At the end of the for loop we add another new line to the string just so our outputted text file looks nice and neat.

Then we call the write function mentioned above, passing ‘a’ (append) for the mode.

Finally, we fire the UI alert on the condition that the string has got more than 2 characters in it (if it didn’t it was just the “\n\n” string from ignoring the DS.Store file).

After the function definition, the aWatcher variable sets up the watcher on the path we want to observe and tells the watcher to start monitoring. It tells the watcher to call our myFolderWatch function when anything happens.

Deploying the code
After editing the config file, remember there’s two steps: i. save the file and ii. choose ‘Reload Config’ from the Hammerspoon menu.



Further Reading:
More details are available about Hammerspoon from the official site here.

My beginners guide with the original simple watcher function is here.

%d bloggers like this: