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
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.
December 9, 2006
programming style and process
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
I added a super simple thing to prevent spam on these comments, and it has worked perfectly.
We'll see how long that lasts...