Sleeping through a decade of Cocoa: Retrospective from modernizing an old Mac app

A few years ago since I started using Macs more often, one annoying thing I dealt with was using my local music library. My usual solution was to just drag files from the file manager to a music player, but this wasn’t as nice on macOS (due to i.e. SMB latency). However, I did have a Subsonic server, which provides a nice music streaming server, complete with an API for clients to use for things like phones. Why not use this on my laptop too?

Of course, if I bought a Mac, I’m not going to put up with bad cross-platform solutions that suck everywhere, when I can instead run bad native software that sucks uniquely for my platform of choice. However, there weren’t too many clients available on Mac. Mostly all of them were unmaintained and had been abandoned in the Snow Leopard era. One of them was Submariner, and it was open-source after the developer (RafaĆ«l Warnault) had stopped working on it. Writing my own seemed a bit daunting with no background, but what if I used the Submariner codebase, and started from there?

Now I’ve been maintaining Submariner for almost two years at this point (it even has a minimal website), adding features and mostly just focusing on modernizing the codebase. It’s been an interesting experience as my first Objective-C/Mac project. A lot of the lessons of modernizing legacy code are universally applicable, but I’ve learned a lot about the specifics of Apple platforms and how they compare. This article aims to be both a retrospective on what I had to learn, what I had to do, and the lessons I took from it, including a comparison of what the development culture is like between platforms.

Continue reading

Opening System Settings/Preferences to a specific app’s notification settings

If you need to open System Settings (formerly System Preferences) to a specific preference pane, there’s a URL scheme for that. However, some panes seem to take options for opening specific things. In my case, I wanted open the notifications pane to a specific app. The system Maps application can already do this, so I popped it open in a disassembler and learned the secret – ?id=(app bundle ID). For example, opening the following URL will open the notifications settings for Firefox:

x-apple.systempreferences:com.apple.preference.notifications?id=org.mozilla.firefox

Debugging an x86 application in Rosetta for Linux

Rosetta on Linux has been great for me, as someone who switched to an ARM Mac and sometimes develops in a VM. This is because my dayjob often involves debugging a proprietary database driver that only has x86 and PowerPC versions available for Linux, not 64-bit ARM. However, while Rosetta can make it easy to run x86 binaries, it’s not as obvious how to debug them. If you naively try running GDB on your x86 program, you get errors like:

warning: `/lib64/ld-linux-x86-64.so.2': Shared library architecture unknown is not compatible with target architecture aarch64.
warning: `/lib64/ld-linux-x86-64.so.2': Shared library architecture unknown is not compatible with target architecture aarch64.
Warning:
Cannot insert breakpoint -1.
Cannot access memory at address 0x66ec48

This is because you’re attaching to the Rosetta binary, which causes all sorts of confusion for GDB. Turns out, Rosetta does actually have a way to handle this.

Continue reading

Building a static GraphViz for macOS

Note: This is a very old post from 2020 I had sitting in my drafts. I figured I would publish it in case it was useful for someone else. Notably, this predates Apple Silicon, so yo would want to build another version that’s ARM and use lipo to make a universal binary. This is left as an exercise to the reader.

I recently had the need for building GraphViz without any external dependencies other than what comes with macOS, as part of an application calling into it. While on Windows, I used their provided binaries, I was on my own for macOS since I wasn’t willing to ship half of Homebrew with my application. Instead, I took a simpler way.

Continue reading

Dealing with key-based polymorphic JSON in Swift Codables

I’ve been trying to use Swift’s Codable protocol with some data I wanted to decode over the wire. Codable makes it easy to serialize things in Swift. Unfortunately, the schema of the protocol I was using doesn’t cleanly map to something easily represented in Swift. It consists of a single object, with a single key, and the key’s name determining its value and type. For example, various JSON blobs:

{"Chat": {"message": "Hello world!"}}
{"Error": {"message": "Invalid request"}}
{"Hello": {"username": "alice", "version": "1.0"}}
/* there are more, but we'll stick with this set for the example */

How do we decode this cleanly?

Continue reading

Scaling images with alpha blending properly with PHP gd

I had some code trying to scale an image in gd using the imagescale function, that looked something like this:

/* makes a copy of the image, instead of modifying in place */
$target = imagescale($image, $width, $height);

However, the images it created were heavily distorted when using bilinear filtering, and didn’t quite look right with neared neighbour either. For example, with bilinear filtering…

Continue reading

Portable software is more complex than you think

I’m someone who cares about making software portable. In fact, I actually have a job basically doing so. For most Unix-shaped things (better known as things, since Unix destroyed all competition), the POSIX standard exists to codify common attributes and provide a common ground. Unfortunately, this is made far more complicated by systems both doing many things outside of POSIX’s lowest common denominator, and systems just not implementing POSIX correctly. People tend to think “portability” is whatever operating systems they use, and assuming the lowest common denominator is that. While many guides recommend writing software in a disciplined (or tortured, if you disagree) manner with separate compilation units for platform differences when possible, the reality is your codebase will have #ifdefs and a configure script if it does anything useful. Not to mention the increasing irrelevance of the standard itself.

Continue reading

Quick and dirty subclassing in Win32

Context: So I wanted to implement drag and drop with files the quick and dirty way; specifically, WM_DROPFILES. (You can also do this with COM, but it’s a bit more involved, especially from raw C. I haven’t written about drag and drop yet, so the comparison is covered elsewhere for now.) My use case is simple, so I didn’t need the benefits or complexity of COM drag and drop. Turns out it’s just marking it as having the extended style in the resource editor or calling DragAcceptFiles, or so I thought. While I could get my dialog to accept the files, I couldn’t get the list view to do so. It turns out that the list view doesn’t handle these messages, nor does it send a notification back to the parent.

One strategy to deal with this is subclassing, although we aren’t going through the formal channels to do. That’s trickier, because it involves using things like CreateWindowEx, and might be a pain with dialog resources that already have a list view. Instead, we’ll actually replace the window procedure out from under the already materialized dialog control – basically monkey patching. In fact, Microsoft’s own documentation mentions this (and a better way with caveats). However, it doesn’t provide a concrete example. This article will quickly show you how, and provides an interesting, if brief example of Windows API principles.

Continue reading

Notes on the system image list

If you need the icons for files in a Windows program, the easiest way to do so is with the system image list. This is an image list (a resource containing icons, mapped by index) that caches those system icons. The advantage of being an image list is you can easily associate it with a control (like a ListView) and pick out the images by their index, or draw out of it.

Continue reading

UTF-8 conversion issues on legacy Windows

Short post: On Windows, UTF-16 was the dominant locale, and UTF-8 was something only to convert to and from. (Microsoft jumped the gun before Unicode expanded the address space.) While it got better (Windows 10 can use UTF-8 as an MBCS locale with ANSI APIs), it was historically a lot worse.

For converting, you’d use the MultiByteToWideChar and its opposite WideCharToMultiByte. On legacy Windows, they have slightly confusing semantics. Specifically, with flags. While Vista on introduced many flags that can be used with the UTF-8 codepage (to deal with the quirks of conversion, like invalid characters), previously only MB_ERR_INVALID_CHARS was allowed, and only if you were running XP or 2000 SP4. Before that, you can’t have any flags if you’re converting to or from UTF-16 and UTF-8. It’s unfortunately a little dangerous, but that’s the rub.