Yup, this is the first release with a pre-built Android application. Note: it's flaky. Consider the Android port an early beta. About 2/3rds of the savers are included, and not all of them are working properly. But more on that in a minute.
This is a pretty long post. I guess I don't usually talk much about the process that goes into xscreensaver, so, why not. I'm pretty proud of this release so there's kind of a lot to say. It's a big one, because xscreensaver tends to get a lot of attention when my business is doing badly. So my misery is your gain.
First, the new screenhacks:
RaverHoop: It may surprise you to learn that I spend a lot of time at DNA Lounge. Over the last few years we've had quite a few more "rave"-like parties, and those always attract the fuzzy-boots crowd with their light-up hula hoops. When I'm hanging out at the bar watching people goofing around with toys that make blinky persistence-of-vision vapor trails, one of the things my brain does is try to figure out the motion model necessary to simulate it. "I can replace you with a screen saver" is something nobody wants to hear but it's where the brainmeats go. Well that one percolated for a couple of years, and then I finally got it mentally worked out, and wrote it in about half a day. I cracked it when I stopped thinking about inverse kinematics and just came up with a set of simple chained oscillators that kinda matched the physical reality (the term of art for this is "cheating").
That's one of my longest ratios between those two ends of the process, I think. The cool insight I had in this one was the way I manage the decaying after-images as a particle system. I use the traditional GL matrix manipulations to rotate and translate the disk, driven by those randomized oscillators, but then I don't actually render anything while I'm down that matrix stack! Instead I just reverse the matrix to convert the resultant points back to world coordinates and put them on a stack where they decay away. The actual rendering takes place back at the top level. So basically I'm using GL as a matrix library, but then not actually drawing with it. I thought that was neat.
DymaxionMap came together pretty easily too; matching up the triangles with the texture coordinates was the hard part. I admit that there are some magic constants in the unfolding code. I cheated and eyeballed a few things instead of doing it in pure trig. I really enjoy how it stellates out into an object closer to a sphere. The one thing that I'm disappointed in, though, is that I had to use a Dymaxion-shaped texture map for it, instead of starting with a Mercator projection. I'd like to change it so generate the Dymaxion texture on the fly from the same texture that GLPlanet uses, because then (by regenerating the texture a few times a second) I could combine the day and night maps and show where the terminator would be, and show its progression. That would look incredibly freaky when it was folded out flat. But the function F(x,y) that converts Dymaxion Cartesian coordinates to Mercator latitude and longitude... that's a really complicated function, that is.
So I kept having this conversation. "What's up?" "Well, I wrote a thing. Look." And I show them my phone running DymaxionMap, and they say, "Wow, that's freaky!" And I ask, "Do you recognize it?", and they say "No." And I ask, "Do you know who Buckminster Fuller was?" "No." (They always say no. Nobody knows who he was.) "Well, he was this brilliant and kooky mathematician and engineer who invented all of these amazing things, with this very sincere social agenda behind them all, like, he invented the geodesic dome and tensegrity structures and it was with the explicit goal of making inexpensive prefab housing to make homelessness impossible. And this map -- which distorts the relative size of the land masses less than most other projections -- whose political goal was to make everyone realize that all the contintents are more or less connected, and that north and south are political constructs, and it's really one big island that we're all stuck on together. But he was a huge proto-hippie and it wasn't convenient or familiar and nobody cared. So he had these huge ideas, invented all of these incredible things, almost none of which ever actually became relevant or important. He died a fairly tragic figure like, I dunno, Tesla, except that we actually use AC current, and nobody uses a geodesic dome outside of god damned Burning Man, and everything he devoted his life to kind of came to nothing. And then it occurs to me that maybe I'm not talking about Buckminster Fuller at all any more and I get another drink. So. I hope you like the screen saver.
About that Android port: Let me remind you of the layers of software archaeology going on here:
I started writing XScreenSaver in 1991, using the then-relevant vintage-1985 X11 API. A couple years later, I added the first 3D hacks, targetting the vintage-1992 OpenGL API.
When porting XScreenSaver to other platforms, my goal is to keep a single code base that compiles for multiple platforms. That is, I don't want to end up with two different files that implement "Decayscreen" using different APIs or different languages.
So when I ported it to MacOS, I did that by implementing most of X11 in terms of Cocoa.
That was, you know, kind of a lot of work.
Then I wanted to port it to iOS. Well, iOS supports something kind of like Cocoa, so I got to re-use a lot of that work, but it doesn't have OpenGL, it has OpenGLES instead -- which, despite the similar name, doesn't have much in common with its namesake. Which is incredibly stupid. I talked about that at length here.
So I implemented most of OpenGL in terms of OpenGLES.
That was, you know, kind of a lot of work.
So then Dennis made a first pass at porting XScreenSaver to Android. Now, Android really, really wants you to write your programs in Java. But, there is a "native interface" where Java code can call out to C code. It's a hairy, finicky pain in the ass. So he got a chunk of the xscreensaver C code to compile on Android (cross-compiling for like 16 different architectures, since the Android ecosystem is so amazingly fragmented). He was able to use my OpenGLES implementation of OpenGL to get some of the hacks up and running. (The interface between C and Java is completely maniacal. Transferring objects between the two languages is incredibly verbose, and extremely persnickety about object lifetimes, what's running in what thread, and exceptions. I recommend avoiding JNI if at all possible.)
So then Dave took a crack at getting the X11 hacks working. Instead of doing what I had done the first time and implementing X11 in terms of android.graphics.Canvas or something, he decided to start implementing X11 in terms of OpenGL... Which is altogether a more portable and reasonable idea, and some day we might end up replacing the Cocoa implementation with the OpenGL implementation. But it's not quite complete or up to speed yet.
This is so many layers it's almost like we've built a stack of emulators to run these old programs.
For me this is yet another exercise in "writing programs for hardware you don't own", since I don't have an Android device. That's because I think the Android mobile UI is just monumentally shitty, and I would never inflict that kind of pain on myself in my daily life. I need an appliance that works, not one whose UI feels like it was designed by Linux kernel hackers -- which, in fact, it was. Plus it has all of the security you've come to expect from desktop Linux -- "Oh, I'm sorry Sir, I didn't realize you were root." Pre-jailborken for your botnet and surveillance convenience! I have a lot of issues with Apple and their lock-everything-down gatekeeper business practices, but... "Say what you want about the tenets of National Socialism, at least it's an ethos."
Anyway. It's been a long-assed time since I've written any Java code, and man, it was kind of like slipping into a warm bath. Just so mellow and pleasant. I said this in conversation the other day and my friend said, "What, even after your Java rant?" And I said, "Remember that that thing began with, 'It's is the best language going out of the set bagbiting loser languages that we have to work with!'" He said, "How do you remember the exact words you wrote two decades ago?" That's what I do, I drink and I remember things. Anyway, probably all of those complaints are still true, and I still think it's dumb that they force you to wed your class hierarchy to your directory hierarchy, but it's all just so easy in Java. I think I enjoy Objective C more, though. Objective C just feels more honest about what it is. It's C with a pretty decent Smalltalk sheen on top (but shitty memory safety), whereas Java is really trying to be a Lisp but it is overall a swing and a miss.
I do think it's kind of awesome, though, that my team totally won and these days basically all languages have the Lambda Nature. Remember when I used to have to argue with people about how garbage collection was a good idea, bounds checking was a good idea, intrinsic types were a good idea, and everyone disagreed, I mean, you had to actually argue with people about that, but now every language has garbage collection, bounds checking and intrinsic types? Ha ha, screw you guys.
Hey, pop quiz! Which of these languages is the best Lisp dialect?
- Objective C
The answer, of course, is Perl, because it's the only one of those languages that has both lexical closures and first class anonymous functions. And how sad is that?
And -- yes -- Dear Debian, please stop shipping xscreensaver.
As for the rest of you, I hope you enjoy them!