Choosing a Window Level in Red Stripe and Sim Daltonism
In OS X, windows are grouped by level. Windows within a particular level can usually be reordered by clicking on one to bring it to the front, but that will only bring the window on top of all other windows of the level it belongs to. For instance, clicking on a document window won’t make it come on top of a floating palette, or the dock, because the floating palette and the dock belong to a higher level.
When writing code, you can choose the level a window belongs to by setting the windowLevel
property on the window. The OS has many predefined window levels you can access using CGWindowLevelForKey
with one of the keys defined in the CGWindowLevelKey
enum. Calling that function returns an integer representing the level: windows with a higher level stay on top of those with a lower one.
Here is some code to check the predefined levels in a Swift 3 playground, with the result for OS X 10.11:
CGWindowLevelForKey(.baseWindow) // -2147483648
CGWindowLevelForKey(.minimumWindow) // -2147483643
CGWindowLevelForKey(.desktopWindow) // -2147483623
CGWindowLevelForKey(.desktopIconWindow) // -2147483603
CGWindowLevelForKey(.backstopMenu) // -20
CGWindowLevelForKey(.normalWindow) // 0
CGWindowLevelForKey(.floatingWindow) // 3
CGWindowLevelForKey(.tornOffMenuWindow) // 3
CGWindowLevelForKey(.modalPanelWindow) // 8
CGWindowLevelForKey(.utilityWindow) // 19
CGWindowLevelForKey(.dockWindow) // 20
CGWindowLevelForKey(.mainMenuWindow) // 24
CGWindowLevelForKey(.statusWindow) // 25
CGWindowLevelForKey(.popUpMenuWindow) // 101
CGWindowLevelForKey(.helpWindow) // 200
CGWindowLevelForKey(.draggingWindow) // 500
CGWindowLevelForKey(.screenSaverWindow) // 1000
CGWindowLevelForKey(.assistiveTechHighWindow) // 1500
CGWindowLevelForKey(.cursorWindow) // 2147483630
CGWindowLevelForKey(.maximumWindow) // 2147483631
When I wrote Red Stripe, I used the assistiveTechHighWindow
level because the goal is to filter the screen to help differentiate colors, and to that end the window must sit on top of everything. Even the name is appropriate, since Red Stripe is an assistive technology.
Then I rewrote Sim Daltonism this January based on the code from Red Stripe. Sim Daltonism is basically the reverse of an assistive technology: it simulates color blindness. Unsurprisingly, there is no predefined window level for that. Like Red Stripe, it needs to sit on top of everything so it can filter the content behind the window, so at first I was using the same window level.
But at some point I tested the two apps together. It’s great that you can use Red Stripe to add a pattern to certain colors and then put Sim Daltonism on top to check as a not-color-blind person if you can make sense of the resulting image. Except that Red Stripe only works well when the Sim Daltonism window is above it, otherwise it gets an image which already lost its useful color information and adding stripes does not make sense.
So now Sim Daltonism uses the following non-standard window level to always stay on top of Red Stripe:
CGWindowLevelForKey(.assistiveTechHighWindow) + 1
This is basically a made-up window level, and it turns out the window server will happily accept that. Note that I’m keeping the level relative to the assistive tech window level, so if that level changes in a future version of OS X, the window will still be above it. If I hard-coded the value 1501 instead, it wouldn’t be as future proof.