How I traded my first software project 15 years ago
And rewrote it in rust today
June 26, 2019
Gmail recently celebrated its 15 years anniversary, which led me down the path of how I got my first gmail invite. Back then, the private Gmail beta was all the rage. In the first few months, gmail invites were scarce. I got one by doing the most popular thing at the time: sending a postcard to a stranger. He was nice enough to send it right away, but the postcard took so long to arrive (2 weeks+) that he initially thought I had scammed him :-) That's how I was initially part of that name rush.
To get my second invite, I decided walking to a post office and spending ~2€ was a bit too much effort, so instead of a postcard, I went back to the gmail-invite exchanges and proposed something only I had: a small software project I had been writing on my free time: a binary clock.
I don't remember how I learned about binary clocks, but at the time, various existed in watch, alarm clock, or software form. As a student, I decided to put to use my freshly-learned C and SDL skills to rewrite one for my desktop.
I had been using it for a few weeks when I decided to trade this software in a gmail invite marketplace. I sent the windows build to a random stranger for a gmail invite. Fun fact: I remember it not working on the first try because it depended on the standard C runtime DLL (msvcrt.dll) which I didn't ship with it. I didn't know what a runtime or C library was back then, I just knew I had to ship SDL.dll and that it would work. I got my gmail invite and completely forgot about this binary clock after a few months.
I rediscovered this bit of code in my backups. It survived despite my backup strategy not being as safe as I'd like: there was only one copy for a long time. It's now multi-site and multi-copy, but I wouldn't mind one more of each.
Needless to say, the code was ugly, despite being a very simple project (less than 200 lines of C). But it still built and worked (mostly) as expected. SDL is truly a work of art, and with sdl12-compat, the projects based on it should continue to live on for a long time.
I fixed all the warnings given by modern gcc, passed it through automatic indenting, rewrote the makefiles, changed the newlines to unix ones, and am releasing it today. The stranger who received the windows build had a long enough exclusivity period :-).
As I am learning the Rust programming language with the Rust book, I thought this small project would be good idea for a rewrite, since it's simple enough. You can have a look at the code here as well.
There is an sdl2 crate, which allows calling the SDL2 library in safe Rust. The crate doc is good enough, and the crate itself has enough examples to start just by modifying some code. It took me some time to work out the image loading. The examples load pngs, which requires SDL_Image in C, or the "image" feature of the crate in Rust, which means I had to add that. This is clearly documented in the README, which I unfortunately managed to skip. But since I was loading bmps in the original code, it took me a while to realize that the load_bmp method from sdl2::surface::Surface would do the job, from which I could create a Texture for use in a Canvas.
While I expected this after reading so many examples from the Rust book, it's still surprising how much Rust ownership constraints forces you to structure your code in a different way in order to match the safety constraints. Luckily, I've found the rustc errors to be (mostly) explanatory, although the suggested solution wasn't always what I needed. Maybe this little program lacks depth, but it seems to me that the Rust team has taken to heart the initial criticisms that rustc errors were hard to grok.
Since this is a clock, I was surprised to find date/time package in the rust standard library. But the rust cookbook recommends using chrono, which looks like the de-facto crate for this job. It looks quite good and is well documented, but it's hard to discover such a crucial missing part when you're doing offline work and can't go search for crates.
The rewrite differs from the original a bit since it uses SDL2 instead of SDL 1.2. It also uses an event pump instead to build an event loop, instead of a manually built one. Unlike games, this program does not need to update at 60 frames per second, but at best only once per second. The original program had two updates per second as an heuristic not to miss updates. This time I wanted to be a bit more clever with the event loop, and only update on window events (window just reappeared) or every second. Unfortunately, there's no timer event in SDL, so my solution is a bit hackish: the program just checks if the second changed to do the refresh.
In the end, the new program seems to consume more CPU resources than the original, but I haven't looked in depth at the cause (rust-c ffi? SDL2 event loop?); this will be an area of improvement for future work.
Update June 14, 2020: I have now fixed the higher CPU usage has been fixed, and it was due to misunderstanding how events work in SDL2.This article →