December 9, 2006
In August I began cleaning up REAPER's source code, and began separating the platform-specific code (mostly UI) from the mostly-portable code. Ten days ago, after 3 months of doing that while also adding new features and releasing new Windows versions every few days, I began spending a great deal of time actually writing a bunch of Cocoa code so that we could have it run on OS X.
It has gone very well, expect some preview version around Christmas.
I love it when this stuff goes faster than I had expected. I'm going to detail some of my experiences here.
There's a lot to like about programming on OS X, but there's also plenty of idiocy.
CAUTION-Boring programming discussion follows. These are mostly things that I have sort of, but not completely correctly, working.
For example, if you wish to draw text, there are countless ways to do it, and at least for what I wanted to do (emulate DrawText for our GDI emulation layer), I still haven't found a way that works as I want. The first way I tried was using CGContextShowText, which worked, except the only way I could find to measure the text before, or after drawing it didn't seem to work (using CGContextSetTextDrawingMode with kCGTextClip to modify the clip path, then get the bounding rectangle of that). So without any way to measure the rendered text, I had to find another way. I could use NSAttributedString to draw, but the first problem is that I wanted to supprot drawing to an arbitrary CGContext, which may or may not be the "active" context. Then, suggested to me, was to use Carbon's HIThemeDrawTextBox etc, which isn't really documented at all (other than in the header file and some examples). HIThemeDrawTextBox works, except the font I selected using CGContextSelectFont isn't used. It appears I could use some other API to set the font, but I havent spent that much time on that. Why can't there just a be a simple, working way?! At least win32's DrawText just works (though I hear the internals are a nightmare).
OK I won't go too much more into those sort of things. The good things are some things are just easier. Anytime you want to modify the behavior of something, it's WAY easier, since you can do a simple objective C subclass, rather than having to do the tricky hacks we do in Windows. Porting the customizable keyboard code to OS X took an hour or so. Getting it to work originally on Windows took many times that.
Rendering with Quartz just looks nice, too. The plain Windows GDI calls just look harsh and cruddy in comparison.
Here's an interesting challenge: on Windows, REAPER renders its play cursor (the line that moves constantly with playback) by using XOR drawing. It renders it by flipping all of the bits in the line, then when it needs to erase the cursor (in order to draw it in the new position), it can just XOR again. Win32 makes this available by using DrawFocusRect(). Anyway, on OS X this isn't possible, to my knowledge, because the system handles so much of the drawing process. I didn't want to be responsible for updating any of our track view at 30fps+, so I had to come up with another solution. After some experimentation, I found that you could create a new NSWindow, make it a child of the main window, and move it around as the play cursor. And it works, the system handles redrawing the track view, from its cached rendered version, so it's FAST, probably hardware accelerated, and you can do neat things with the alpha channel of the cursor. The verdict: while you can't do oldschool things like XOR drawing, you can do things sexier.
So to aid porting, I've created a new part of WDL (our general reusable code library, pronounced "whittle"), called SWELL (Small Windows Emulation Layer Library or something). So far it emulates (at an API level), portions of the Windows GDI, menu API, MessageBox, ini file (GetPrivateProfileString etc), and a bit more. Eventually we'll probably BSD license SWELL, too.
One interesting thing in SWELL is a set of macros and small functions that let us paste in menu definitions from a .rc into a C++ source file and have it generate a HMENU for it. Fun use of the C preprocessor, if you ask me.
Anyway, this is all challenging, and as a result, mostly fun.
I previously said I was planning on writing an article on coding style etc, and while I haven't
gotten around to finishing it, I'll go ahead and discuss some of it.
1) If you program, and you're working on something that you expect to be working on for more than
a couple hours, use version control. The benefits are countless. Safety, yes, but also, it
gives you the ability to easily see everything you've done (if you're religious about checking
your code in often), and even more importantly (to me), it lets you diff and review your changes
before checking in. I do this all of the time, to make sure everything I did was everything I
wanted, and that I didnt fuck anything up.
2) Don't be afraid to let things get messy. A very common (and valid) sentiment in programming is to
avoid "premature optimization", where the programmer spends too much time too early on some part
which may or may not even need it, introducing complexity for the sake of possible speed
improvements etc. I don't hear people say this much, but I think you should take the same stance
on organization. If you're programming software, and you need to add some new function for some
task, don't be afraid to just toss it in near where you need it, and see what happens. Don't go
creating tons of new files every time you need them. Moving code around for organization later
is easy, and if you don't do it too soon, you'll a) stay focused, and b) save time in the event
that the code you added wasn't used. Which brings us to the next point:
3) Don't be afraid to toss out code that you've written. Sometimes you have some problem and code
a solution for it, and at the end of it, you just don't like it. If you don't like it, and
think you could do it better the second time, do it the second time.
4) Going back to point #2, don't obsess from the beginning about reusability. Write code that you
need, and once you're using it and see how you actually need to reuse it, then go make it reusable.
5) Finally, evolve your code. Getting something to the point of limping is the hardest part, so
you should try to get to it as fast as possible. Once you're there, progress is much faster. It's
like waiting for a minute to pass. If you look at a clock with a seconds hand, it goes quick, but if
you just stare into space, it takes forever. Get to the point where you can see progress, then
enjoy it (and test your work often).
More later, perhaps...
December 9, 2006
December 9, 2006
Posted by Danno on Sun 10 Dec 2006 at 07:12 from 128.113.148.x
Posted by Mauro on Sun 10 Dec 2006 at 11:34 from 203.28.159.x
Posted by hi on Sun 10 Dec 2006 at 14:23 from 24.31.9.x
Posted by Cassidy on Sun 10 Dec 2006 at 15:54 from 209.66.18.x
Posted by Jed on Mon 11 Dec 2006 at 09:44 from 222.153.241.x
Posted by Jersey Jay on Mon 11 Dec 2006 at 10:37 from 137.237.193.x
Posted by Jay on Tue 12 Dec 2006 at 22:16 from 220.130.149.x
Posted by magicchord on Thu 04 Jan 2007 at 13:11 from 204.128.192.x
Posted by VCS on Wed 17 Jan 2007 at 08:38 from 131.161.241.x
Posted by ron on Fri 26 Jan 2007 at 05:44 from 139.149.1.x
Posted by Mike on Thu 08 Feb 2007 at 13:26 from 64.119.103.x
Posted by Jamsmith on Wed 12 Dec 2007 at 21:46 from 69.15.208.x
Add comment: