Cantabile for OS X — Progress Update 2

In my last post I hinted that I’d been working on the Cantabile for OS X port. Today I thought I’d share a little on what I’ve been up to. This post is a little technical but gives a glimpse behind the scenes that I hope you find interesting.

GuiKit2 Functionally Complete

GuiKit is the custom user-interface library that Cantabile’s entire user-interface is built upon. The previous version of GuiKit only worked on Windows so the main job in porting Cantabile to OS X was to get GuiKit working. It’s taken a while, but…

GuiKit is now fully functional on both Windows an OS X.

This was a huge job but well worth the effort as I now have a very clean way to build cross platform UI in C#.

Unfortunately, things changed…

The original idea behind all this was to port GuiKit and “presto” — Cantabile would magically just run on OS X. In practice I’ve had to make quite a few changes to better deal with the way some things work on OS X so it’s not as transparent as I originally hoped.

Also, I’ve made some non-trivial improvements along the way so there’s going to be more work in this area than originally planned.

Without going into too much technical detail, here’s some highlights of what’s new and improved in GuiKit2…

Cross Platform

As mentioned GuiKit2 runs seamlessly on both OS X and Windows meaning Cantabile itself will be almost completely oblivious to which operating system it’s running on.

GuiKit2 also includes cross-platform abstractions for some non-UI related services like power suspend/resume notifications, clipboard, drag-drop and other platform specific services.

Support for True Non-Rectangular Windows

In the current version, the popup windows used for selecting route targets have a little pointer arrow making the window “non-rectangular”.

The way this was previously implemented was a bit of a hack but GuiKit2 now supports true alpha blended edges and shadows and works beautifully on both platforms.

New Rendering and Compositing Model

GuiKit2 introduces a new rendering and composition model that caches parts of the user interface in off-screen buffers, reducing the amount of work required to redraw the screen. It’s also more aggressive in culling regions that don’t need to be redrawn.

In particular the performance of redrawing small frequently updating elements like level meters and MIDI activity indicators should be improved

The new rendering model was also designed with OpenGL in mind so it should be possible to introduce hardware accelerated rendering in the future.

Interestingly, the trickiest part here was figuring out how to make all this work with sub-pixel text rendering but it’s working now and text looks crisp even on standard resolution monitors.

I’ve also put a lot of effort into pixel alignment — making sure that everything is aligned to screen pixels where possible to ensure graphics are crisp and that theirs no weird rendering artifacts — even at unusual scaling factors.

The new rendering model works great on Windows. On OS X it’s not yet as fast as I’d like but I’ve tracked it down to a bottle neck at the very last stage where the final composited image is copied to the screen. OS X’s support for this seems to be at least 5 or 6 times slower than the equivalent on Windows. I’m ignoring this problem for the moment because it’ll go away if I switch to OpenGL — so I’ll revisit then.

List View Improvements

The work-horse of Cantabile’s main window user interface is GuiKit’s ListView control. It’s used for the main routing panels, the bindings panel, the side panel lists, popup lists and more. It’s an important component that has to work well.

The existing version has a couple of long standing issues relating to focus and selection management. They were minor issues but something I wanted to address as part of the port and they’ve all been resolved and the new List View is working great.

Also the grid view (as used by the set list grid) which used to be a separate component has now been integrated as a “layout engine” for the main list view control. It’s a simplification that lets me throw out a bunch of code that’s no longer needed and ensures more consistent behaviour between these two UI elements.

Drag/Drop Support

Although I’m not a big fan of it (I prefer using the keyboard), many users have asked for the ability to use drag/drop to re-order lists so I’ve enhanced the list view control to include support for it. All those lists (including the grid list) should soon support dragging to re-order things.

Dynamic Scaling

Cantabile 3 has always supported UI re-scaling however it was somewhat limited in that it required a restart and you could only re-scale the entire window.

GuiKit2 introduces dynamic, on-the-fly re-scaling and different elements can be re-scaled independently. This means it should be possible to scale up the set list panel while leaving the routing and bindings panel at their standard size (for example).

Improved Scroll Animations

GuiKit uses scroll animations on touch devices for flick scrolling and over-scroll bouncing (the bounce back when you hit the end of the scroll range). GuiKit1 had a passable implementation of this but to me something always felt a bit unnatural about it.

So I’ve spent some time experimenting with different approaches and came up with something nicer — the deceleration is smoother, the bounce back now uses Hooke’s law physics and the transition between decelerating and bounce back is smoother.

Check It Out

This little video shows some of the things described above. It looks a little bland at the moment because for testing reasons I’m using highly contrasting solid color fills for most elements. The tearing effects are caused by the screen recorder — there’s none of that in real life.

Here’s something similar running on OS X, but also showing the .NET Core build process…

Skia Drawing Engine

Early versions of GuiKit2 used a simple drawing engine that was basically a wrapper around GDI+ on Windows and CoreGraphics on OS X. While this kind of worked, it wasn’t great because the drawing capabilities were somewhat limited and the differences between the two platforms were starting to become a problem.

So I’ve to switched to Skia for all graphics and text drawing operations.

Skia is the graphics library used by Chrome and several other web browsers and I’ve been hugely impressed by it. It’s fast, cross platform, very capable and there’s a nice C# wrapper for it from Xamarin.

The only downside… it’ll add about 5Mb to the download package, but it’s worth it for the extra capabilities and the problems it solves.

NetCore 2 — Just In Time (hopefully)

Up until recently all of the work on OS X was using the 32-bit Mono run-time (the open source version of the .NET platform). Recently however Microsoft released .NET Core — their cross-platform version of .NET.

Unfortunately .NET Core 1 had a few missing features and although I could have worked around them .NET Core 2 is due for release soon and includes everything I need for Cantabile on OS X.

This is fantastic because even though I’m a fan of Mono there something reassuring about a .NET run-time for OS X from Microsoft. I’ve now moved all the OS X code to .NET Core and it’s working incredibly well.

.NET Core also has a self contained deployment option which means I can bundle Cantabile, the .NET run-time and all the dependencies into a single (albeit large ~50Mb) OS X app bundle and users won’t need to install anything else to make it work. The only prerequisite will be the operating system (which looks like it will be v10.12, aka “macOS Sierra”).

The biggest downside of .NET Core is that on OS X they only support x64 so Cantabile for OS X won’t be available in a 32-bit edition.

For Windows, I’ll be sticking to the official Windows .NET Framework but I might move from 4.0 to 4.6 because it’s more compatible with NetCore2 and it also lets me start using some newer C# language features. This means Cantabile will no longer run on Windows XP or Vista, but I think it’s time to move on!

Fully Managed Implementation

Back in November Mac Month all the work I did for the form controls was written in Objective-C with the C# code interacting with it via P/Invoke.

This worked acceptably but had some down sides:

  • I’m not a fan of the Objective-C language
  • Having to work in two IDE’s was a pain
  • The interop code between the two was kind of messy
  • It introduced an extra run-time dependency with an additional dylib that would need to shipped
  • It complicated the build process by having to invoke XCode to build parts of GuiKit
  • It doesn’t offer any advantages over a purely C# implementation — if I could figure out how to do it.

None of these problems are detrimental but I thought I’d spend some time investigating how hard it would be to move all the Objective-C code to C#. Turns out it’s not trivial, but it is possible and I’ve just finished writing a helper library that lets me implement Objective-C classes in C#.

I’ve started porting all that Objective-C code to C# but since it’s not blocking other development I’ll continue this work on this on the side. All that’s to say — I see no reason why GuiKit2 can’t be implemented in pure C# on both platforms.

Is GuiKit Available for Use in Other Projects?

I get asked this from time to time if GuiKit is available for use in other projects. Unfortunately not. I simply don’t have the time that would be required to document and support it properly. Also, it’s fairly specific to Cantabile in that it includes just what Cantabile needs and nothing else — and I like it that way.

So What’s Next?

The next step is to switch Cantabile over to GuiKit2. As mentioned this won’t be as seamless as originally planned but it’s mostly a matter of fixing things up so it builds — it’s certainly not a re-write or anything as drastic as that.

I guess that’ll take a week or two by which stage I expect to have Cantabile’s user-interface fully functional on both platforms, leaving just the audio engine to be ported (although much of that is already done too).

Stay tuned!


Do you like these more technical posts? If so, let me know — either in the comments or via email, Twitter or Facebook.