Spifftastic,

 

Ascension 2 Development

I released the Ascension 2 Live Wallpaper on Google Play last month, so it’s probably high time I wrote about it. I’ll just run through a few topics and see where that goes.

For those not sure what Ascension is, you can first click the link to the store page, above, and that should give you a good idea of what it looks like. If you don’t know what a live wallpaper does: it displays a wallpaper on the Android home screen (the launcher), and typically the wallpaper animates or provides information or some other feature. It might render a model, maybe a tree, maybe an aquarium. Ascension displays bars that change color over time and react to touch. I mean for Ascension to stay subtle, more so than most live wallpapers, but it still allows users to customize it enough to make it their own.

I originally built Ascension for myself, but I’ll try to explain why I built Ascension 2. I still made it for myself, but its purpose changed a little this time around.

Motivation

Ascension’s actually pretty old now. I released it back in 2010 — when live wallpapers first appeared and various spam-developers released tons of them. Some featured sphere-mapped Android robot models floating in front of a background, every national flag, and other garbage.1 I still used an Android phone at the time and wanted a live wallpaper, so I made the first version of Ascension to appease my desire for a good live wallpaper.

After a while and some updates, I decided I no longer wanted to develop for Android. Android grew fragmented between various OS versions and hardware specs, and to an extent it remains that way today. Not so much OEM skins anymore, but they’re there. I ditched my Android phone for an iPhone and left Android behind while it went through its growing pains with tablets (recall the Motorola Xoom and how it failed to deliver on its features). Google later recognized that performance on Android sucked, so they tried to fix most of that with Android 4.0. Anyway, Android sucked at the time.

Come 2013, the Android platform had settled, tablets became usable (though they still lack good apps), and the fragmentation issue slowly began to sort itself out when Google stopped making OS upgrades necessary to benefit from app improvements when they shoved a lot of Android into Play and moved their apps to the store.2

Background aside, Android sucked less, I had a Nexus 7, and I figured I should try to re-learn Android development and forget most of what I learned. Ascension sat in its little corner of the Play Store, horribly out of date and ugly and generally unpleasant to use.3 That in mind, I figured I should bring Ascension back up to speed with the current Android. So, Sometime early in December 2012, I started to write Ascension 2. I initially wrote this reboot in Java. Most people will pause and think, “Well, obviously, you use Java to write Android apps.” I think “But wait, there’s more!” sounds appropriate here.

I wrote the first version of Ascension 2 in Java. I never released this version, neither to testers nor the Play store. It worked very well, but I stopped writing it when finals started up. I still had my degree to finish at the time, so I put it on hold. When I returned to the code afterward, I decided I didn’t want to use Java, so I killed that version of Ascension 2, went back to my iPad, and ignored my Nexus 7 except as a curiosity until September of this year. Its battery ran down to nothing several times during that period.

In early September, I still hadn’t found a job, and needed something to keep me busy. I couldn’t let myself just tinker with code and various projects, so I looked at the broken build of Ascension 2 I had on my Nexus 7 and decided I’d finish it. Except not in Java. I’d made my decision there, and I would find another language that didn’t piss me off. I only considered two options for alternative languages: Mirah and Scala.

Mirah looked like Ruby, which as far as I know is intentional. It’s not like JRuby, where Ruby runs on the JVM. Instead, Mirah borrows some of the syntax and compiles to JVM bytecode. I needed to find a language that compiled to JVM bytecode, otherwise it’d be difficult to compile to dex, so Mirah met that requirement. The problem with Mirah is that it’s still a bit iffy right now, and although I want it to become a popular JVM language, it doesn’t feel stable. So, I dropped it as a possible choice soon after I looked at it.

Enter Scala, which for a long time bore the title of “the language that confused me.” In retrospect, I don’t know why I found it confusing, but I’ll assume I just didn’t give it a good look. Scala is also like Ruby in that it tends to result in expressive code without the verbosity of Java, though the similarity ends there. It’s also both imperative and functional, and though nobody should listen to me when it comes to functional programming, I think having it leads to generally better code, assuming you avoid side-effects and otherwise write deterministic functions.

After I toyed with Scala for a while, I decided it had everything I wanted and, at the very least, I could use it as a less-verbose Java.4 In the best case, it would help ensure I wasn’t doing anything horrible. I still do horrible things, of course, but Scala let me get away with writing a lot of code that would cause me pain in Java.5

Scala has its ups and downs, particularly when it comes to Android development. Scala’s major upsides, those that let you write better code, are that it avoids verbosity, it provides pattern matching, it allows you to write both functional and imperative code, it supports anonymous functions, traits that allow mixin-like composition, and plenty of other features. This all leads to code that tends to be more expressive and less error-prone. As a short example, it doesn’t take too much work to get to a position where you can write code like this:

import android.app.Fragment
import android.view.{View, ViewGroup, LayoutInflater}
import android.os.Bundle
import android.widget.Toast
import scala.language.implicitConversions
import net.spifftastic.view.util.implicits._

class MainMenuFragment extends Fragment {
  override def onCreateView(inflater: LayoutInflater, root: ViewGroup, state: Bundle): View =
    inflater.inflate(R.layout.main, root, false)

  override def onViewCreated(root: View, state: Bundle): Unit = {
    super.onViewCreated(root, state)

    (root withView R.id.new_game) {
      _ onClick Toast.makeText(getActivity, "Poppy", Toast.LENGTH_LONG).show()
    }
  }
}

One small downside: I encountered trouble when I tried to use Scala’s standard library with Android. In particular, if you include the entire standard library, you will easily exceed the Dalvik VM’s 16-bit method limit per dexfile.6 ProGuard solves this but, as usual, it takes some work to configure ProGuard. Rather than wrestle with ProGuard yourself, though, I’d recommend anyone interested use pfn’s Android SDK Plugin for SBT. It’s hard to adjust to SBT, but it’s great when things start to work, and pfn’s plugin will handle most of the heavy lifting for projects. If you decide to go with Scala, though, you’ll more or less need to use Scala anyway, so just get comfortable with it.

After that, I set some goals for Ascension 2. All of the goals were set with the idea to take something that exists and learn to use new APIs to implement it. The goals:

  • Aim for almost complete feature parity with Ascension 1.

    This sounds obvious, but the “almost” there is important. There are features in Ascension 2 that still haven’t made their way into the release build. I’ve delayed the first update for one particular feature, as well. I really want what I’ve planned to make it in early on, but also wanted to get it into users’ hands sooner. As a result, Ascension 2 does not have one setting that Ascension 1 had: you cannot specify a custom bar color. That feature’s coming back in a different form, but more on that when I finish the update. It’s not a small addition.

  • Design the app for tablets first.

    Ascension 2 has no phone-specific UIs, so you see the the tablet layout on all devices. There are minor differences that depend on the screen’s width in DIPs, but otherwise Ascension uses the same UI everywhere. All devices and tablets get both a settings pane and a preview pane to view their changes. Only a few settings require you to open a dialog to select something, and otherwise the settings app shows every setting as you change it.

  • Allow users to save their configurations.

    Users of Ascension 1 requested this often enough, but I designed Ascension 1 in such a way that it would’ve been difficult to implement (meaning Ascension 1 had bad code). Ascension 2 has this, and although I doubt it sees much use, I’m sure someone is grateful that they can save a configuration and load it again later.

  • Implement the renderer with OpenGL ES 2 as a minimum.

    GL ES 2 just lets me move more work off to the GPU and in doing so I’m made less dependent on hacks to implement certain features. For example, I implemented brightness in the shader, whereas previously I had to account for it when the renderer generated bar colors. The code gets smaller and easier to maintain as a result.

  • Target Android 4.x and up.

    I decided to only target Android 4.x because I just don’t want to support older devices. It’s not fun, it leaves me hamstringed if I want to adopt new APIs since I then have to try to maintain compatibility with older APIs, and it overall just limits what I can do. If I had continued to target Android 2.x, I would no longer have access to PreferenceFragment, for example, a class crucial to Ascension 2’s design. Rather than limit myself and target older devices, I decided to target what let me make the app I wanted.

None of this should surprise anyone, or at least not developers. Put short, I wanted a way to get back into Android development, I wanted to use a language that didn’t suck, and I wanted to build an app that felt like it fit in on Android 4.x. As such, it seemed right to take an app that I made for myself and build it again, improve it, and do it right this time. The first problem I had came down to how to design something for a tablet.

Tablet UI Layouts

As I mentioned above, when I designed Ascension 2’s user interface, I made it for tablets first. Also, I have no Android phones.7 When I test on a phone, I either borrow family members’ phones or ask my testers to run something on their phones. So it makes sense that I design the UI for what I’ve got on hand.

First off, because Ascension is a live wallpaper, it has only one main user interface, aside from the wallpaper itself, to worry about: the settings activity. Ascension 1.x’s settings had no preview and required a lot of taps to change things, and you couldn’t preview your changes. Users needed to poke around in the dark and see how things looked for each change. I decided early on, then, that I needed to fix this.

Ascension 2 displayed on a Nexus 7 in landscape orientation.
Ascension 2 displayed on a Nexus 7 in landscape orientation.

Above is a picture of Ascension on a Nexus 7, in landscape. This is the UI you get on all devices. The only difference is portrait orientation, which sees the preview pane placed at the top of the screen.8 The preview pane, in landscape, is always on the left. This is to put it out of the way of your right hand, which — since most people are right handed — means you can scroll, swipe, and otherwise interact with the settings without using your left hand, or even having to do anything other than move your thumb.9

The largest change to the settings UI simplified how users interacted with the settings pane. Early on in development, the activity started off with a list of the settings pages and the choice to save or load a config. In short, you had a list:

  • Bars10
  • Colors
  • Save Config
  • Load Config

You could tap any of these to get to the actual settings or work with configs. This sucked. It meant that, in order to get to another settings page, you had to tap the back button then tap the entry for the other page. This persisted for a while, since I had to get the settings hooked up before I could find a better way to display them. Once I’d had a few things hooked up to see how I wanted them to work (I’ll go into that in a moment), I pulled out the basic list and replaced it with a ViewPager and ActionBar tabs.

This was much simpler than I’d expected and made the settings app easier to navigate. The downside is that there are two ways to get between settings pages, but they’re both easy to use: swipe the page or tap on a tab. No need to press the back button. The only problem I had is that the normal FragmentPagerAdapter generates names for the fragments it provides. This makes it difficult to communicate with specific fragments, so I reimplemented it in Scala. In reality, this isn’t a huge deal and implementing a PagerAdapter shouldn’t take too long, as with most basic adapters.

The preview pane itself doesn’t need too much explanation: it allocates its own renderer and displays everything the same as the live wallpaper normally does. The only difference is that you cannot change its offset by swiping, since it doesn’t have multiple pages of content.

The important part of the new settings panes, however, involved showing changes to settings as they happened. Rather than go the easy route and embed preferences’ layouts in a dialog (using DialogPreference — also known as the lazy coder’s preference), you just place them in the preferences’ layouts. It’s not hard to implement this and it makes it a lot more fun to customize Ascension’s settings. There’s no tapping back and forth between views to see what’s changed, you just change it, and you can see from the preview pane what’s changed.11 For me, that proved that the preview pane worked.

Configurations

As I mentioned above, one of the main goals for Ascension 2 was to let users save their configurations. Each saved configuration is written to a JSON file. They look like this:

{
  "use_touch_color": false,
  "bar_ping_lifespan": 15,
  "shimmer_speed": 0.20000000298023224,
  "use_uniform_height": true,
  "use_bar_pings": true,
  … and so on …
  "bar_count": 100,
  "flip_bar_mode": "even"
}

I had no plans to necessarily make them human-readable, and I wouldn’t say the JSON makes them accessible in that sense, but it does allow you to tweak the files in a text editor if you really wanted to. This also allows you to share configurations, though I don’t expect any users to do this. At any rate, it’s easy to save a config. The challenges all involve how Ascension 2 loads configurations, displays their previews, and deletes configurations.

It’s simple to load a config, apply its properties, and persist the values, but it’s complicated to refresh all the preferences’ views once done. You have to set every preference’s current value to the newly persisted value. Preferences do not automatically refresh their views, which is sensible, there’s no reason for most to ever check their values except when initialized or changes are persisted. That said, sensible or not, you’re required to do it for them, similar to how one must stimulate an abandoned kitten’s bowel movements.

As such, I have to notify all PreferenceFragments in the settings activity to refresh their values. In Scala, this just meant I had to write a new trait and include it with each PreferenceFragment subclass:12

trait RefreshablePreferenceFragment extends PreferenceFragment {
  import RefreshablePreferenceFragment.TAG

  def refreshPreferences(): Unit = {
    implicit val sharedPreferences = getPreferenceManager.getSharedPreferences

    Setting.values foreach { key =>
      val keyString = key.toString

      findPreference(keyString) match {
        case tsp: TwoStatePreference =>
          tsp.setChecked(sharedPreferences.getBoolean(keyString, tsp.isChecked))
        case sbp: SeekPreference =>
          sbp.setProgress(sharedPreferences.getInt(keyString, sbp.getProgress))
        case lp: ListPreference =>
          lp.setValue(sharedPreferences.getString(keyString, lp.getValue))
        case csp: ColorSelectorPreference =>
          csp.setColor(ColorPreference.getColor(keyString, csp.getColor))
        case _ =>
          Log.d(TAG, s"Unhandled preference change: $key")
      }
    }
  }
}

With this, it’s possible to then call refreshPreferences on each PreferenceFragment that supports it. In the case of Ascension, this means all of them. This excludes preference types not used by Ascension, but it takes little effort to add more as needed. Only the ColorSelectorPreference type requires special handling due to how I’ve encoded colors preferences’ values, but it’s still not as difficult as it would be otherwise.

Displaying a configuration preview required building a little offscreen renderer that simply took a GLSurfaceView#Renderer, let it draw a frame, and then returned a Bitmap from the renderer. That’s all easy to do, but doing it asynchronously isn’t as simple, seeing as the settings app generates configuration previews and caches them asynchronously.13 Ordinarily, nothing happens to cause an issue — things only fall apart when the activity dies while there are tasks running to generate the previews, or when the configuration changes. In either case, though, the activity is destroyed, so the fragment is as well.

When the config picker fragment dies, it tries to pull down the offscreen renderer and destroys its context. Normally, this should be pretty easy — synchronize on the renderer, wait for it to be free, and then destroy it. The problem here is that you end up with a deadlock, mainly due to how I’d originally designed the renderer and config preview generation code. One bit would grab a resource and wait on the renderer, while the fragment would grab the renderer and wait on the resource. This meant that if previews were still being generated when the fragment went down, the entire app froze.

In the end, I solved this just by never locking the renderer and instead atomically setting a flag. If the renderer isn’t in use, it’s locked and brought down immediately, which is what usually happens. If it’s in use — still rendering — then the flag is set and when the next render is complete, the renderer is torn down and all further use of it raises an exception (which is caught). By doing that, the renderer can continue to do its thing just long enough to get torn down without the need to throw a wrench in the cogs.

Aside from that, rendering the config previews is fairly straightforward. The only thing that might sound odd is they’re 16-bit BGR bitmaps, but that’s to conserve memory. The previews are small enough that it makes sense to limit their depth.

The last issue with configs gave me a headache, because it involved Android’s media scanner. A quick overview for anyone who hasn’t suffered it: when you plug in an Android device and grab files off of it, the files you see are those on the external storage that Android already scanned. If an application writes a file to external storage, the user may not immediately see the file in external storage — possibly not for a long time, depending on when the scanner runs next. You can usually force it by rebooting, but you don’t really want to ask a user to reboot to pull a file off the storage. So, to make it visible right away, the app tells the media scanner to go ahead and scan the file.

When Ascension creates a config file, it does this. When Ascension deletes a config file, it also does this, and then it tells Android to remove the file once it’s scanned. I do it this way because, if the app deletes a file, it’s still visible in the storage afterward because Android hasn’t scanned and noticed that it’s gone. To tell Android to remove a file, you need the file’s URI in the media content provider. To get the URI, however, you have to scan the file. So when Ascension deletes a file, it performs these steps:

  1. Scan the file and listen for when the scan completes.
  2. Get the URI once notified that the scan has completed.
  3. Delete the file.
  4. Tell the content provider to remove the URI from its database.

This sounds simple, because it is, but it took me a surprisingly long time to do things this way. I figured there has to be a better way, and I assume there still is, but I haven’t found one. As such, the current method to delete one or more config files uses this odd route through the media scanner. It works, I just wish I didn’t need to go through the media scanner for it to work. Still, it keeps things clean on the storage side.

Rendition

At least one person wanted me to discuss rendition in Ascension, but most of it bores me to death because there’s nothing particularly unique about it. Everything is drawn using OpenGL ES 2, but for the most part, the data used by OpenGL rarely changes. The only exception is the colors, but otherwise nothing unique.

Ascension’s renderer uses two fixed-size buffer objects: a vertex buffer and an index buffer. Both allocate the maximum size possible for their buffers. The index buffer is initialized once and never touched again,14 since there’s no need to ever modify it. The indices for bars never change, only the number of bars and their vertices. The vertex components are stored in different segments of the vertex buffer (i.e., not interleaved), so that way only components that need updating will be updated — in normal usage, this means the renderer only updates the color component.

Continuous rendition to the wallpaper engine’s surface is handled by a GLSurfaceView instance because it works well and allows you to queue up events on the thread that handles the EGL context, renderer, and so on. I tried to avoid this, originally, in favor of used ZeroMQ via JeroMQ for sending messages to the renderer, but the problem there is that I’m not directly in control of the GL thread the view manages, so I end up with issues where one socket lives on, which makes me unable to kill the ZeroMQ context. This causes the service to lock up, and it’s just kind of a mess.

Aside from that, Google forbids the use of sockets on the main thread in Android 3.x and up, so you now have to deal with two problems: how you synchronize socket access and how to kill both the context and sockets with AsyncTasks, but in order. You can use a serial executor, but the problem is that one socket is being closed from the GL thread. To ensure order, you have to push the work off to the executor. Meanwhile the service might have already tried to kill the context, causing it to block the executor’s thread. So, the socket is never closed while the context waits for you to close it. Ultimately, I ditched ZeroMQ and used GLSurfaceView’s queueEvent method:

override def onTouchEvent(event: MotionEvent): Unit =
  /* Copy and dispatch before the event gets recycled. */
  if (event != null) {
    /* For some reason, I've received null events, so check for null events. */
    val eventRunnable: TouchEvent = TouchEvent.Cache.allocate()
    eventRunnable.renderer = _renderer
    eventRunnable.event = MotionEvent.obtainNoHistory(event)
    _surfaceView queueEvent eventRunnable
    super.onTouchEvent(event)
  }

Another point to note here: events are cached (see: TouchEvent.Cache.allocate(). This avoids triggering the GC for most situations, since the cache eventually saturates (usually at around 12 to 16 objects — the cache in use above has no upper limit, though one can be set) and no more TouchEvent objects are allocated. This might be overkill when it comes to avoiding the garbage collector, especially given that the GC on Android has improved quite a bit (it’s difficult now to get it to cause frame skips). Still, if possible, I prefer to avoid the GC at all costs.

The above example applies as well to all other events: Ascension configuration changes15, offset changes, touch events, pause and resume events, and so on. Most events are handled as they’re received by the renderer, the only exception is the config event. When a config event is received, it includes the preference key that changes. Ascension takes the key and keeps a set of all changed keys to avoid pulling data out of preferences before it needs to. As such, all config changes are coalesced into a single config delta that is applied at the start of the next frame.16

There’s not that much else to say about rendition. Inside the renderer is a single fixed-step logic loop to keep the bar state updated and a check to delay the frame if not enough time has elapsed since the last frame.17 When the frame is actually drawn, the renderer loads any resources it needs to. Then, the bar state is passed to a color generator which the renderer uses to write the colors for visible bars (plus a few to account for bar overlap) to the vertex buffer. Then, only the visible bars are drawn. Repeat until the engine or service dies.

An Abrupt End

Since I continue to work on Ascension 2, I’ll have more problems to write about. I’ve probably forgotten a few problems that gave me trouble just because I had to block them out of my memory. The difficulty I had when I started to develop Ascension 2 mostly stemmed from using new tools, a new language, and unfamiliar APIs (for example, prior to this, I had never touched fragments). Still, I’ve had a lot of fun so far, and am happy with how far Android has come. It lacks good apps, but I feel well-equipped enough that I can make the apps I want.

Anyhow, Ascension 2 is on the Play Store now. That’s it for now — I’ve droned on for nearly 5,000 words about it and avoided even one mention of centipedes. I’ve got an update to finish and another app to write, so back to work.18

  1. This situation hasn’t improved much. Ascension 2’s competition today includes “twerk” wallpapers, boob-jiggle wallpapers, the many tons of seasonal- and holiday-themed wallpapers that get crapped out on a regular basis, and so on. I believe there is also at least one almost-furry-porn live wallpaper out there as well. Make of that what you will.

  2. The latter happened fairly early on, though. I remember Gmail getting updates via what was then the Android Market (which I still think is the better name — seriously, who the hell came up with “Google Play”?).

  3. To change the bar count and see what it looked like, you had to tap a preference to open a dialog to tweak a number or SeekBar, then close the settings, see if it was what you wanted, and go back to tweaking settings. Repeat this for most settings and you had a ton of things you could customize — Ascension’s main draw — and a lot of taps to go back and forth between settings and a preview.

  4. At least one Scala user will cringe at that.

  5. For example: tail recursion works in Scala. In fact, you can slap an @tailrec annotation on a function and the compiler will emit an error if you fail to write a tail-recursive function. If that doens’t make you immediately happy, you’re a horrible person.

  6. Android Issue 7147

  7. The apps I use and enjoy all live on the iPhone and iPad, so it makes little sense for me to use an Android phone. Much as they’ve improved the OS and devices, I still enjoy iOS more. There’s also the problem that all Android phones try to top the human head in size. Where apps are concerned, I figure I’ll get what I want on Android as soon as I make them myself, so until then, I’ve stuck with my iPhone and iPad for day to day use, and my Nexus 7 — the only Android device I own — sticks around as that thing I grab when I want to see if Android has any “killer apps” yet. It doesn’t.

  8. Since I usually have my thumbs near the bottom of the screen on a tablet, I decided the part that sees more interaction would go on the bottom. In earlier layouts, I’d placed the preview pane at the bottom of the screen to keep the tabs and the settings connected, but this annoyed me after long enough.

  9. I may add a setting specifically to enable a left-handed layout later, though it would only swap the preview and settings panes.

  10. Now the “Appearance” tab.

  11. The exception to this is the multiply blend mode, which unfortunately is both difficult to explain and might confuse people who don’t already know how multiplying two colors works. So, someone might enable multiply blend mode, see everything go black, and just assume it’s broken. In reality, multiply only works with light colors precisely because it multiplies the background and bar colors together. So, it also assumes you know basic arithmetic.

  12. Had I used Java, this would likely be a subclass of PreferenceFragment that sat between PreferenceFragment and each of its subclasses in Ascension. The code itself probably wouldn’t be too complicated, but it would be less pleasant than writing with RefreshablePreferenceFragment.

  13. Using an LruCache, the config picker fragment only stores so many configs in memory before it dumps them (8MB of bitmaps, specifically). They’re never written to storage, so the previews are just re-drawn as needed. I’d change this behavior, but it’s not likely to be an issue, and I’d rather not pollute even temporary storage with a ton of bitmaps.

  14. Except when the app loses the EGL context, either because the configuration or underlying surface changed.

  15. An Ascension configuration change is different from a regular configuration change in that it only affects the current Ascension settings. It’s not the sort that causes an activity to restart.

  16. The config delta is just a bitset, since there’s little need to store actual strings or symbols or objects. This keeps the delta relatively inexpensive and avoids unnecessary allocations on the GL thread. Beforehand, it was just a regular set of Scala Enumeration values, which was fast but resulted in allocations to put objects into the set. A bitset stood out as a good alternative to avoid allocations, since each Enumeration value gets an integer ID, which can be poked into the bitset.

  17. It’s important to not simply return early when doing this, however, since GLSurfaceView will swap buffers even if your renderer does nothing. Instead, sleep for a short amount of time. This is a bad way to do things because it blocks the view’s GL thread, but you will usually only have one active GL view at a time, meaning only one renderer will block the thread. If you have multiple views, you should consider a different way to handle frame limiting.

  18. If anyone wants me to go into more detail about something, shoot me an email and I’ll either reply directly or shove another post up on here. My address is over on the Contact page (see: menu bar).

Spifftastic Design Change

A staple of Spifftastic is that I can change the design periodically. A staple of blogs is that you’re obligated to write a blog post about it, because otherwise how will anyone know? Aside from the visual changes.

Anyway, I rebuild Spifftastic, so the new theme is called Tracey. I figure that continues my tradition of giving projects terrible names. The main point of Tracey was mobile support — specifically to see if I could use one CSS file for phones, tablets, and desktops. So far, I’d say it worked out. You can see the mobile design by shrinking your browser window’s width to 600 pixels or less.

It’s about as simple as Oak, just colorful. And whereas there was always a bar at the top listing other pages, that’s now a sidebar on desktops that’ll scroll with you. The sidebar scrolling is disabled on iOS and Android tablets because I haven’t found a browser that handles fixed position elements well.

I borrowed the color palette from Ascension 2, which I’ll write something about later.1 The title’s additional text remains and all the other little things I liked about Oak. Also, everything is tinted purple. I find it readable that way.

Anyhow, that’s it for now. I’ll have a post up about Ascension 2 sometime later. Until then, not much new.

  1. Would like to finish the next update first, as I’ve aimed to put in a feature that I wanted for a long time. Said feature should also explain the removal of one particular setting between the two major versions of Ascension.

Ruby OpenGL Experiments

One of my complaints in working with Ruby is its lack of anything for doing graphical work, be it UI toolkits – most of which are now unmaintained – or simply graphics in general, like OpenGL. The title of this post should tell you which direction this is going. I could complain that Ruby’s image is taken over by the Rails folks or that web developers ruin the fun for everyone else, but neither of these complaints have any objective basis and are just for fueling my personal biases against web developers. So, let’s talk about Ruby, OpenGL, and what I did to make these two unsafe things work together.

OpenGL

The current state of OpenGL in Ruby is decent-ish. There are a handful of gems for Ruby support, some for Ruby 1.9 and at least two for Ruby 2.0. It’s a good situation. Your options are, for the most part, as follows:

  • ruby-opengl2, ruby-opengl, and opengl — Fundamentally the same gems, though for different Ruby versions. They all provide the GL API versions 1.0 through 2.1. Not the best choice, but there are folks out there trying to make sure it still works, and if nothing else, it’s maintained in that respect. If you’re still using 1.8 or 1.9, it might be up your alley.
  • opengl3 — A GL 2.1 - 4.2 gem implemented using FFI, which makes it pretty useful across interpreters (it should work with JRuby, for example). The downside is that it is large and overly complicated, which is strange. Looks like it might’ve been written by someone whose only other language is Java. It’s still being worked on though, so I’m sure it’s not going anywhere for a while.
  • opengl-core — This is my gem. It only supports the OpenGL core profile and extensions, so it’s purely 3.2 and up. Like ffi-opengl (a defunct 3 year-old gem that’s dead) and opengl3, this uses FFI to load GL. Unlike opengl3, it doesn’t have that much code in it except under the opengl-core/aux features, which aim to wrap some GL functionality in slightly more Ruby-friendly code. By default, it is straight OpenGL with none of the difficult-to-work-with-in-Ruby aspects hidden. The downside to opengl-core is that I’ve only built it for Ruby 2.x, so for those of you stuck on 1.9 for whatever reason, it may not work.

So bindings for OpenGL are available and usable now, which is a far sight better than things were a year ago. For the purpose of this blog post onward, I’ll only talk about opengl-core because I wrote it and therefore I use it. In other words, if you want to use one of the other gems, you’ll have to consult someone else. If you’re not using Ruby 2.x, you might not find this very informative (so go install Ruby 2.x and join the future).

opengl-core is, put simply, a very small amount of handwritten code and a very large amount of generated code. Almost all of opengl-core is defined through its gl_commands.rb and gl_enums.rb scripts, since both are used to sort of statically define all GL commands at runtime. That way there’s not too much need for eval or define_method or what have you, as the method stubs are already there and forward calls to the appropriate function (and, if not loaded, they make sure that happens too). So when you call a function for the first time, it’s loaded then and there, unless you told the gem to load everything ahead of time (Gl.load_all_gl_commands! is a thing).

So why write my own OpenGL gem when opengl3 and ruby-opengl2 et al. exist? Well, I didn’t like the former’s code. Again, looks like it was written by someone who knows only Java — it’s honestly a really strange piece of code to look at when you consider how simple it is to load a GL function. Very over-engineered. But anyway, its developer likely had much different needs than I, so it’s hard to say why it is the way it is without asking ‘em. Either way, opengl3 doesn’t suit my needs. ruby-opengl2 et al., on the other hand, only works up to GL 2.1, which I don’t code for. Period. Easy decision.

So, if you want opengl-core, you just install it via

$ gem install opengl-core

GLFW 3

Another problem I encountered was that there were no GLFW 3 bindings for Ruby. This won’t surprise anyone — Camilla Berglund (aka elmindreda) recently released GLFW 3. It seems obvious that nobody would get around to it in such little time.1 So, there were two options: use another library or write the gem myself.

There were a few other options, like SDL, GLUT, and GLFW 2.7. Using SDL is an alright idea, but there were no SDL 2 bindings for Ruby. The other bindings for 1.2 are plentiful but not really what I was looking for. GLUT is the spawn of Satan and the less we have to deal with it, the better. That it somehow perpetuates itself is mysterious, but I suppose there will always be the old OpenGL tutorials telling people they should use it.

GLFW 2.7 also has bindings, except that its gem, ruby-glfw, doesn’t work on Ruby 2.x, so that made it much less appealing. I’d been asked, previously, by Tom Black about whether I would be willing to help write GLFW bindings for Ruby 2.x, but at the time I was mostly concerned about the time needed to do the GLFW and OpenGL gems and so on, mostly because the work needed to do anything in Ruby would be a bit extensive. I think he might have contacted me because I hold a fork of it on GitHub and once tried to update it. Either way, I didn’t start on either until quite some time after, and I believe he’d found his own solutions.

The «do it yourself» option was then the only one that didn’t involve writing my own window-handling code. That’s something I could’ve done, at least for OS X (it’s remarkably simple), but it’s not a great idea. So, I sat down and wrote the GLFW 3 bindings for Ruby, developing it as a C extension rather than using FFI because that simplified a lot of the Ruby/C compatibility issues. Turns out Ruby’s C API is very nice, but very undocumented and very heavy on the abbreviations. You get used to seeing things like rb_str_new2 and SIZET2NUM (size_t → Numerical2).

For the most part, I tried to make sure the Ruby API for GLFW 3 was similar to its C API without becoming unbearable to use in Ruby. So, Windows and Monitors are their own objects, and GLFW is one big module encompassing them and the constants needed. It’s nice, since you can now create a window and main loop by just writing something like this:

#!/usr/bin/env ruby
require 'glfw3'

Glfw::init

main_window = Glfw::Window.new(800, 600, "Window Title")
raise "Unable to create window" if main_window.nil?

main_window.close_callback = lambda { |window| window.should_close = true }
main_window.make_context_current

until main_window.should_close?
  Glfw::wait_events

  # Do drawing stuff here. Also clear the backbuffer because otherwise
  # you're going to see a lot of junk when swapping buffers.

  main_window.swap_buffers
end

main_window.destroy
Glfw::terminate

So that’s about twelve to fourteen (depending on how you space out your lambda) lines. I think that’s pretty good. I actually fixed a small bug in Glfw::terminate that I hadn’t noticed before as a result of writing out this post, but otherwise there’s not too much to say there. The bindings are easy to use, they’re Ruby-friendly, and they’re out there in the glfw3 gem. Contrary to the repo names I use, which don’t correspond to gem names, the gem is just called glfw3 and you can install it via gem using the following:

$ gem install glfw3

The 3D Math Problem

A problem I greatly enjoy complaining about and solving several times over is that, because OpenGL’s meager math functions were deprecated, we’re always in need of code to handle the 3D maths side of things for us.

At first I wondered how it’d work if 3D maths were implemented in Ruby, so I tried doing a simple 4x4 matrix multiplication — work you’ll need to do reasonably often — in both Ruby and via a C extension and ran it through a profiler. For folks who aren’t doing very much, a Ruby implementation of 3D maths is probably sufficient, but it’s also very, very slow even when you try to reduce allocations, method calls, etc., for that operation.

On average, with my system, 4x4 matrix multiplication is 7x slower than a C extension. Both take a small amount of time, though it’s not as negligible if you’re doing a lot of it and have only 16 milliseconds to do it in. Keep in mind that, for the C extension, the Ruby values have to be converted to C values, Ruby has to forward all the arguments to the C function, and so on, and it’s still 7 times faster. So, if you need fast math code, always write it in C. If you need accurate but slow maths code, you can probably safely write it in Ruby (in which case, you’ll probably use a class like BigDecimal).

So, that led me to take my old Snow-Palm 3D maths code, add some bits to it, and write bindings around it for Ruby and call the result snow-math. Now it’s possible, for the most part, to get somewhat fast 3D maths. Plus it all works with GL since the 3D math types are all stored as contiguous blocks of memory, meaning you don’t have to pack them as strings before sending them off to GL (e.g., [1, 2, 3].pack('fff') to convert an array to three floats).

So, to continue the trend of telling you how to install something, you can install snow-math via gem using the following:

$ gem install snow-math

I’d also recommend checking the README on GitHub because there are some options to customize how the extension gets compiled, like whether to use floats instead of doubles.3 Most common types are covered by the library, namely two-, three-, and four-component vectors, quaternions, and 3x3 and 4x4 matrices. Planes and rays are not covered, unfortunately, though they should be easy to implement using the types provided.


That about sums up all but one odd experiment I did, snow-data — a library for defining a layout for blocks of memory and so on, similar to various C-struct libraries for Ruby (snow-data being one). I won’t write about that here though, as it’s not entirely relevant to OpenGL in Ruby (though you could use it to describe vertices and so on).

At any rate, OpenGL in Ruby is better than it’s been in a long time, and it’s now pretty reasonable to write applications, games, demos, tools, and so on using OpenGL in Ruby. Considering the flexibility of the language and what it offers for anyone who wants to worry more about their programs’ functionality than the boilerplate details, it’s a decent language for the job.

The Gems

If you’d like a list of the gems I made again, here you go:

  1. Especially not when most Ruby people are hideous web developers with no care for doing awesome things with Ruby.

  2. This conversion is missing from JRuby’s C API, but then JRuby deprecated it and is still stuck on 1.9, so screw them. In general, JRuby’s C API is a barren wasteland of misery, so I don’t recommend bothering with it. If you need bindings that are JRuby-compatible, always go with FFI. It’ll probably be faster by going through the JVM as well.

  3. snow-math uses doubles by default because Ruby also uses doubles internally. So, it results in the least amount of casting at the cost of allocating twice as much memory per type.

So I Learned To Code

I taught myself to code. Sort of. I taught myself to code after reading a book on Perl about 11 years ago, part of a book on Perl, and not understanding it. I wanted to make games and Perl was only useful, in my eyes, for text adventures. I’m still somewhat of the opinion that Perl is only useful for text-based stuff, but not for lack of the ability to do graphical things. At any rate, after I tried to learn Perl using a book, I taught myself to code.

Well, no, I read other people’s code and learned how to code from that. I read books that dissected good code, but mostly I read books on things that weren’t specific to code (e.g., 3D maths used in games). I taught myself to read code and write code like the code I read. I read the Quake 1 source code,1 I read the Quake 3 SDK’s source code, I read Ruby scripts, I read Lua scripts while I worked on a game that used Lua,2 and so on.

I asked myself how others did it and I read how others did it to answer the question. I was curious what code written by people who really knew how to code looked like. So I learned to read code, but I still wrote bad code probably for a long time, and maybe still do, but I leave that up to others to decide. After that, I taught myself to code.

Except really, it’s more like I taught myself to use a debugger before I learned to really code. I couldn’t write code if I didn’t know what it did. So, I wrote code or took other folks’ code and I stepped through it and saw how it worked. I read code while it executed, and this let me know how things worked even if I didn’t and sometimes still don’t know how everything works at the lowest level. I know how conditionals work, for example, in assembly, but I couldn’t tell you why the assembly worked with the hardware.3

I was curious about what code did and had to know how it worked before I could really code. So, I learned how it worked and what it did, and after that, I taught myself to code.

When I taught myself to code, I never really started small, I just started esoteric or weird, whatever prodded me just enough to raise questions that seemed out there. My idea of fun wasn’t writing blackjack for a terminal, though I wrote blackjack programs, it was always more along the lines of modifying virtual method tables at runtime or building code to automate other tasks in programming, like writing code to generate Lua bindings.4 I was curious about how I could handle any given situation and what different ways I could approach it.

The whole key here — to how5 I learned to code — is curiosity. Curiosity killed the cat, sure, but the cat had nine lives, and before it touched the capacitor in the CRT, it got a pretty good look at how things worked. Because of my curiosity, I want and need to solve problems. I see puzzles in the crevices between ideas. Programming is not a job, but a strange search for new problems, one part learning to learn, one part problem solving, and one part borderline-obsessively-curious tinkering. A self-taught programmer must necessarily have all three of those parts to survive as a programmer.

Without those parts, that programmer will never seek knowledge, never fix anything, and never try anything new. He or she will be the programmer you don’t want to know, self-taught or not.

But with them? With them, that programmer will never feel limited, never stay bored, and never grow tired. With these things, that programmer will always enjoy programming. With these things, you’ll be a great programmer.

  1. For those who have wanted to read the Quake series’ source code but haven’t had the time to sit down and do it, I do recommend reading Fabien Sanglard’s fantastic code reviews. As of this writing, he’s done Another World, Quake 1, Quake 2, Quake 3, Doom, Doom 3, Duke Nukem 3D, and others.

  2. Lua’s a fantastic language for programming fun little experiments in. When I first ended up learning it, I was working as an artist on a small game called Bioscythe. As far as I know, it was never released, and I went my own way after the project seemed to more or less die as everyone on the team went off to do their own things. I learned a lot from seeing how we used Lua, however, and I’ve since remained convinced that it’s the best scripting language for game development.

  3. That is, I know the assembly but not how the mainboard works, and given the complexity of modern computer hardware, I’m prepared to forgive myself for this for a while even if I’ll have to figure it out sometime. On the upside, knowing what a compiler might produce is pretty handy at times. I say “might” because the compiler might optimize things away and so on, so there’s no guarantee that I’d always see the input in the output.

  4. Which actually ended up landing me a small contract to do exactly. I wrote some experimental code to do at-runtime bindings of Lua code, someone liked it, and gave me a contract to do it again but without the speed hit of the original experiment.

  5. Why I learned to code is another sort of thing altogether, though from the code I read, you can probably guess what set me on the road to code, as it were. Basically, though, I started out as a game artist, and still make game art, but wanted to make a game. Mods were one avenue but it’s difficult to find a good team making one (and the mod scene is mostly dead now, at least for modern titles, due to the amount of work involved). Finding a programmer willing to follow your lead is even more difficult. So, the solution was to write code myself.

    This altered my career path significantly and made me an anomaly in some respects since I’m both a relatively competent artist and a programmer (debatable on the competency part there as I’m a lousy judge of my programming skills), but it made life a whole lot more fun.

Review: Sublime Text Starter

Full disclosure: I was asked to review this book by the publishing company, Packt Publishing, and given a free copy of the book to do the review. You can read the email sent to me by the publisher if you like.

Sublime Text Starter, by Eric Haughee, probably isn’t what you’re looking for to get started with the now-rather-popular programmer’s text editor, Sublime Text 2. As the title suggests, this book intends to get you started with the editor and for three quarters of the book, it really will start you at the very beginning: installation and saving a document.

The book’s downside is that it doesn’t seem to know quite who its audience is. It begins with basic installation instructions – the sort we take for granted and gloss over in all software manuals, especially the ones programmers ignore on a day-to-day basis because we think we know better. After installation, you’re given five pages on how to create, or open and modify, a file, and how to save the results.

This shows how basic the book starts at, which is fair enough for anyone not too used to these tools. However, given Sublime Text 2’s core audience is programmers, it may come across as odd at best and possibly demeaning at worst. Either way, the first 28 pages are likely not intended for programmers. So, we’ll skip past those 28 pages as redundant and get to the parts that don’t pertain to every piece of software intended for modifying some kind of file on the planet.1

We go on to learn about the minimap, multiple cursors, distraction-free mode, vintage mode, the goto panels and the command palette. Except for the explanation of vintage mode, these are all perfectly acceptable and include helpful screenshots and relevant key-bindings. My only complaint with these is that they are more or less covered by Sublime’s front page, as well as likely comprising the subjects of all Sublime Text 2 tutorials. For vintage mode, the book’s explanation only scratches the surface of its use and suggests only Vim users should use it, but I’m not sure a Vim user would read this book, so I’m again unsure who the book is for.

There is minor one downside to vintage mode’s explanation though, a bit of misinformation on the part of the book: it tells users to modify Sublime’s default preferences. This is a no-no, you never modify the default preferences. If you suggest the user modify preferences, you tell them to modify the user preferences, pictured to the right.

For most purposes, this won’t produce a noticeable difference, but it does result in a user unable to easily copy his or her settings over to new installations or versions of Sublime. This might not seem like an issue now, but it will present an issue with the coming release of Sublime Text 3. So, there is a small risk of future frustration involved.

Starter also goes on to mention Package Control, including some perhaps frustrating installation instructions fraught with potential bit-rot. This isn’t likely to be an issue for most people, but typing seven lines of Python into the Sublime Text 2 console is not the most pleasant experience. Thankfully, the author includes a link to the Package Control site as I have above.

The explanation of snippets is mostly sufficient, though the formatting of the book results in some unfortunate line wrapping in the snippet that produces less than eye-catching imagery. In addition, the snippet example provided by the book is invalid and will cause Sublime to insert nothing when using it. This sort of error is par the course for tech books, so I’m inclined to forgive it even if it highlights the sorry state of tech books.

Finally, the book finishes up with macros (never once mentioning how to create your own plugins, but this book is a “starter” and not likely to cover anything advanced despite expecting users to know how to work with XML and JSON). Custom key-bindings are shoehorned in as a part of macros rather than their own rather expressive feature, which is disappointing and perhaps not showing users just how much they can customize their key-bindings to their liking. Either way, the discussion of macros is mostly sufficient, though likely to frustrate users when they discover the limitations of Sublime macros (like certain commands not being recorded or working in macros).

Ultimately, the book seems to expect some level of experience with technology but simultaneously assumes none from the start, so the first chunk of the book might be well-suited for those who don’t know how to install software, but they would be lost in later sections. For those experienced enough to breeze past installation and those who know how to save a document, the rest of the book might work for them, but its brevity leaves something to be desired — again, a byproduct of the book’s attempt to get readers started with Sublime, but never seeming to try to imbue them with the skills needed for further use.

Rather than purchasing the book, either $4.24 for a digital copy or the much more expensive print copy at $19.99 (effectively $20, which is far beyond what anyone ought to pay for 46 pages, 28 of which cover installing an application), I’d recommend waiting for another edition — if you still want to consider this book. Wait for one that cleans up the few errors in the book and goes into enough depth to leave you with the feeling of knowledge gained.

As it is, though the book might be of use to someone, I’m not sure either I or the book know where to find that someone. The audience for the book would have to be someone who doesn’t program, doesn’t know how to install software, and possibly uses Internet Explorer 6. Instead, do as the end of the book suggests and take a gander at Jeffrey Way’s Sublime Text videos, read over the unofficial documentation and for anything else, join the Sublime forums.2

  1. Except for Emacs, which routinely forgoes the use of conventional key-bindings in favor of more esoteric ones, such as ⌃x⌃s (C-x C-s) for save.

  2. For those who practice the necromantic art of IRC, you can also find many Sublime Text users, which includes myself, in #sublimetext on Freenode.

Nexus 7: First Impressions

First impressions of the Nexus 7 after a week using it. This won’t include a lot of references to iOS and such except where there’s an odd discrepancy, like with email, or things seem about equivalent between platforms (like Flipboard sucking). Cue bullet-points because I’m lazy:

  • Android still has an obsession with wasting large amounts of home screen real estate with enormous clocks. Why all the large clocks? You already have a clock in the corner of your screen, it’s in the status bar. The analog clock sucks as well since it’s very lousy at telling time.
  • Scrolling is mostly smooth for the first time in Android’s history. It’s still not great, but the input lag is mostly gone, though it regularly seems to skip rendering stuff just to keep up with the input, which looks ugly. Still, it’s an improvement.
  • Scrolling inertia feels very strange. I flick and it doesn’t seem to do a whole lot, but I flick again and it’s like I’ve broken the speed of light. Very inconsistent and very weird.
  • Screen resolution is acceptable but the way the UI is designed makes it a rather poor decision. You’ve got two mandatory bars on the top and bottom of the screen at all times, so in landscape orientation, your vertical space is limited by a lot. Further, if you try to use the keyboard in landscape, you’re given a tiny sliver of an area to work with — even less if the field pops up the predictive text bar too. This obsession with widescreen aspect ratios really needs to end if tablet manufacturers want to continue making tablets designed for both orientations.
  • The onscreen keyboard in portrait is excellent, especially with Android 4.2’s addition of swipe-typing (basically ripping off whoever came up with it first, doesn’t really matter, though I expect Swype is slightly miffed). In landscape, typing out things with my thumbs is easy enough. Typing any other way, including swiping, is tedious and better off done in portrait.
  • Google Currents, which was pre-installed, is excellent. Beats the everloving hell out of Flipboard, which really, really sucks (both on iOS and Android these days).
  • App selection is still garbage. There are no good apps for writing aside from — amazingly — Google Docs, which is actually completely usable via the Google Drive app now. It’s very good, though limited (which is fine for me — I don’t want a full-featured word processor, just something to write in).
  • The bezel is too small in portrait. I’m frankly not sure how you’re suppose to hold the thing like this without accidentally tapping the screen.
  • The Gmail app is worse than I remember it. No unified inbox and no pull to refresh and so on. Seems unusually limited. I seem to remember the Gmail app being better, but I guess iOS just improved a lot over time.
  • Also, really, we’re still using two separate apps for Gmail and regular mail? Give me one app and let me have my inboxes all in the same damn place.
  • The official Twitter app is shit. End o’ story there.
  • The Google+ app is far worse than its iOS equivalent. It really sucks in comparison, which is strange considering one would expect Google to make Google+ more compelling (I hate that word) on Android.
  • People call this thing “pocketable,” and while I can fit it in a pocket, I think the sheer goofiness of having a tablet in your pocket and the shame that comes from that is enough to make it un-pocketable. Also, the power button is too easily pressed, so putting it in your pocket will probably result in it being frequently woken from sleep.
  • Holding this thing in the palm of your hand, with a thumb on one side and your remaining fingers on the other, is stupid and uncomfortable too. I don’t know why this attribute is considered positive.
  • Notifications as usual are best in class.
  • AndChat is still free and it’s still a great IRC client. Also, thanks to the way Android handles multitasking, I never lose my connection to a server.
  • Chrome is an acceptable improvement over Android’s original browser, though it pales in comparison to every other mobile browser and Google should be ashamed of themselves for shipping such a lackluster piece of crap. Hell, it’s on a tablet and it doesn’t even have a bookmarks bar, so I can’t use any of my nice Javascript bookmarks.
  • On the topic of bookmarks, there is no good way to access them. I either have to replace my current tab with Chrome’s bookmarks UI or go back to the home screen and put a widget down to see my bookmarks. Why can’t I just browse them via a popover? Also, small icons with tiny captions are not the best format for bookmarks. Give me a list so I can actually see the titles of my bookmarks.
  • The Kindle app works fine. Google Books is still garbage and a usability nightmare.
  • Google Maps beats Apple Maps, but you knew that already. They’re about on par around here, though, so it makes little difference.
  • The 1Password app on Android sucks. It’s basically an attempt to clone the iOS UI, and you can guess how well that went.
  • Google Music is very, very strange. I try to upload a playlist and it kills random songs from it even though the songs have been uploaded. The app itself is a confusing mess. For some reason I can only see how many songs a playlist has in landscape. Overall, it sucks, but at least it’s not Winamp.
  • Google Music’s icon is the ugliest thing on this OS.
  • Google Calendar has the most schizophrenic and useless user interface ever. I like that I can scale the week view, though.
  • Kevin Coppock’s Holoku is still the best sudoku app for Android.
  • I shouldn’t have to tap a button seven times to enable the developer settings. Really, that’s stupid.
  • Voice search is about the same as Siri — good for setting alarms and reminders. Unfortunately, Google just creates a calendar entry for a reminder, so it’s a bit annoying that there’s no difference in priority between a calendar entry and a reminder.
  • Dropbox sucks on here.
  • Viewing PDFs in either the PDF viewer that’s built in or Acrobat is slow as all heck and I don’t know why.
  • Updating one or more apps causes the OS to become rather unstable and not register touches. The Play Store app is nigh unusable while any apps are updating as it will constantly attempt to refresh its UI and generally ignore whatever you’re doing.
  • The speaker can get very loud but sounds very shitty no matter what volume you use. Use headphones.
  • Setting the wallpaper is much easier now and the UI for doing so is very well designed. That said, why when pressing the back button after I’ve selected an image am I taken back to the home screen and not the view for selecting an image? What if I just decided a different image was better? I don’t want to have to long-press somewhere on the home screen just to get back to my pictures and select a wallpaper image again. This is retarded.
  • Speaking of retarded, the back button’s fairly inconsistent about where it’ll take you. This is especially obvious if you get taken to the browser, whereupon the back button seems to just be the browser’s back button.

Spifftastic on OakTree

As of today, Spifftastic now uses OakTree instead of WordPress. This post is fairly short because there’s not a lot to say, but I’ll try to talk about the transition a bit.

Most of the WordPress theme ported over cleanly. The stylesheet is the same, save for a few changes to accomodate the Google +1 button and to remove any vestigial traces of WordPress. The theme previously made use of a sidebar, which meant bits of the default WordPress widgets were themed. All of that’s gone now, so it’s surprisingly clean. The theme’s overall structure is still something of a mess from accumulated cruft over the years, but otherwise it works as expected.

The feed was my main concern. First, I’d previously never looked into generating RSS feeds, so I expected a great deal of pain and suffering to get it working. Turns out I was wrong: RSS feeds are dead simple. I suppose it lives up to its name in that regard – it’s a very, very pleasant format to work with.

Second, the RSS feed location changed. As you can see in the previous post, I created a FeedBurner URL for the feed, but this turned out to be more or less unnecessary. mod_rewrite took care of most of the URL-changing pains (previous permalinks were year/month/post-slug, whereas they now have a post/ prefix). As a result, the feed issue is mostly gone. The FeedBurner URL will remain active because I’m a jackass and didn’t consider the options at the time, but it won’t harm anyone to switch.

So, Spifftastic now uses OakTree. About damn time.

Spifftastic RSS Feed

Hey, turns out the RSS feed changery is unnecessary. The FeedBurner URL will continue to work, of course, but mod_rewrite appears to have taken care of most issues with the change-over (all permalinks still work and whatnot).

If you’re currently reading this via an RSS reader, you should probably update your feed URL to the new FeedBurner URL at http://feeds.feedburner.com/Spifftastic. Ordinarily, I would like to avoid changing the feed location, but I’ll be moving Spifftastic over to OakTree shortly, and given my infrequent updates, would at least like to give folks a chance to switch over before I pull the plug on the old feed (which will be replaced with a new feed that FeedBurner can automatically move you to).

There will be a few more changes to Spifftastic, including the addition of a Google +1 button (until I decide I hate them) and the removal of the site’s search function (because it’ll be static HTML). I hope these changes won’t affect anything drastically, as the button is fairly small and part of the post meta and the search function never really got any use anyway.

So, update your feeds and you’ll be good to go when I finally plant OakTree.

bork: Shell to Ruby

bork is my tiny file-tagging utility that essentially maps files to hashes and hashes to tags and so on using the filesystem. It could also probably use a regular db like sqlite, but it doesn’t. This post is mostly discussing one of the important changes that happened while building bork: the transition from shell script to Ruby.

bork initially started out as a series of shell scripts with a fairly simple structure inspired by git (whether git is actually structured this way, I do not know, I’m simply going off appearances): a main jumppad sort of script that calls into other scripts that actually do what I want. This ultimately led to a structure that basically looks like this:

|++ bin
    |- bork
    |++ bork-exec
        |- bork-add
        |- bork-relative-path-to
        |- bork-rm
        |- bork-station
        |- bork-update
        |- ... and so on

This sort of structure actually lends itself fairly well to the particular idea I was going for, where you can work with bork in two different ways:

  1. By using the jumppad script: bork add ...
  2. By using the scripts directly: bork-add ...

The reason this worked well is that each bork command depended on other bork commands. Most bork commands depended on bork-station to get an absolute path to the nearest bork station, the directory containing all hashed files and tags and so on that allow bork to work. It works in a fashion similar to git as well (and likely Subversion and other tools like them) in that it searches the current working directory (CWD) and all directories above the CWD for a station. Almost all commands required a bork station, so they’d run bork-station to get a path (if there was one) and do their thing. Some others depended on bork-update, such as bork-rm and bork-find, both of which modified the station and therefore depended on it being up-to-date.

A second upside is that this potentially allows me to replace various commands down the road with new implementations. Ideally, I should be able to pull out an old shell script, replace it with something written in C, and call it good. Shell script isn’t particularly fast, so I was expecting this to become an issue fairly quickly, and I was right.

What I was wrong about was the ease with which I can replace commands with new implementations. To do a new implementation that doesn’t simply go out, run a new process to do something, and return some results back to the calling command means rewriting most of bork in the new language. Having two separate implementations of a particular aspect of bork isn’t really the best idea, so the way I saw it was fairly simple: rewrite the whole thing and ditch the original structure or put up with the less than pleasant idea of dealing with reading/writing from pipes to other processes. Not too difficult, but also not fun. The latter is unpleasant enough to do a complete rewrite, so rewrite I did.1

I was more or less settled on using Ruby for the project, so that was the target language. bork’s predecessor, a series of Ruby scripts to do roughly the same thing,2 had proven that Ruby was good for the job, so it wasn’t about to change. Next step: gemification (this was the branch name). bork was originally a tool to generate Makefiles, so I had a gemspec sitting around for it and recycled that. The directory structure was mostly there already, though at the time you wouldn’t have known it by looking at the git repo.

I kept the jumppad script, which had always been written in Ruby for convenience (its Kernel#exec provided an easy way to jump from bork to the command script). It was heavily refactored, but for the most part you can see a lot of the same code in the same places. I moved command scripts into individual Ruby files under a Commands module, each registering itself with the default Bork::Hub instance, which collected command classes for the jumppad and allowed commands to run other commands (provided they’d been loaded).

The Bork::Station class ended up becoming the most important class, containing all methods for accessing data contained in any given station and manipulation those stations. I’d ordinarily feel bad about this, but stations are so small in their functionality that the class never becomes too big. If anything, some of the methods could use refactoring into smaller methods, but otherwise the conversion went fairly well. The important point of this, however, is that the Bork::Station class is effectively bork – everything that makes bork bork resides in it. The hub is only there for the jumppad’s sake and the command classes simply get to a station and do stuff with it.

So this means that there is an opportunity to build other projects around bork’s stations. It’s entirely possible that one could subclass a station and add new functionality to it, new metadata, and so on. So, there’s a lot of potential there, provided I refactor the hell out of some of the functions. Eventually, it would be nice to split up the station into the interface and a backend. Either that or simply define a base class for stations to implement and let them go from there. Who knows what maddening things I could do there.

So, that’s bork’s shell to Ruby conversion in short. I have to say, it was a lot of fun writing bork, so I’m glad Ruby makes this stuff so pleasant.

  1. There were probably other options I didn’t consider or ignored, but these were the biggest ones on the table. I treat the shell script version of bork as a prototype that got the idea working and the station structure set up, while the Ruby implementation is the current end goal (until Ruby is too slow).

  2. Long lost to accidental deletion. This was when I didn’t use version control (git wasn’t terribly accessible yet and Subversion was a huge pain in the ass to set up) and keep all my neat toys in some project directory. These days, I’m a little more careful, though I admit to sometimes using rm -rf to wipe out entire directories without thinking.

I Left Facebook

I expected I would close my Facebook account for a long time now, but I finally dropped it. Last night I deactivated my account. Once I’ve determined I have all the data I want out of it, I will permanently delete the account.

Why leave Facebook now? Mainly because they moved everyone’s email address over to an @facebook.com address.1 This by itself is a fairly benign change at face value, but it also creeps me out and violates my one rule of social networking: never modify the information a user provides.

By changing everyone’s visible e-mail address, Facebook likely hopes to route all communications through its servers. Access to user emails gains them further access to personal information in e-mails and such, and that information furthers their advertising goals. Google more or less does this with Gmail already – it’s not as though Facebook has committed themselves to some heretofore unseen evil. However, I trust Google more than Facebook. This level of trust is likely arbitrary, but it’s still there. As such, the one rule comes into play.

The assumption I make with a social network is that the data I give them is immutable on their end. They are then free to connect it to other data, do whatever creepy things they see fit to do in the background, and generally do the usual slimy things with my data. But for the profile I give them, I expect the data to remain as I set it. If the network changes my data, it has then violated the idea that I control my information. Facebook disagrees with this rule and believes they control my information. I’m obligated to condemn their actions, so I danced my worthless protest and closed my Facebook account.

For now I’ll reside mainly on Google+ and Twitter. Google still retains more of my trust than it perhaps deserves. That said, it’s not entirely misplaced: they’ve stored my emails in relative safety for years. Twitter seems benign enough that the information they have seems useful only in a union of all other social network data. Therefore both retain my membership for the time being.

Adiós, Facebook. I won’t miss you.

  1. See Ars Technica’s article for more on the subject.

The End: Ascension

Over a year ago, I programmed, designed, and shipped Ascension. It made a small headway as a live wallpaper but never did particularly well. Live wallpapers occupy a niche of the Android Market, now Google Play, and serve no utility. They provide mild entertainment and pleasure, but developers give you little reason to purchase one. The best we can do is show a picture of it that looks pretty cool. Ascension looks pretty cool. I still think it looks pretty cool and represents a sort of higher-end in live wallpapers. Users customize it to suit their tastes, and customization sits as the most important feature of Ascension. Folks still wanted more, but I didn’t.

I privately declared Ascension a finished project some time ago, mostly around when I finally decided Android development depressed me. A few users have since contacted me and learned this as well. I’d never declared a project finished before, at least not a user-facing one. Sure, I’ve made libraries and they’re finished, but nothing sold directly to consumers. The decision came easy, though: I wanted nothing else out of Ascension, wanted nothing to do with Android, so I stopped. I prefer to work on what makes me happy. Working on Ascension brought only the opposite feeling. So I stopped.

Ascension became feature complete the day I released it, with bug fixes and small additions afterward as a sort of bonus while it continued to sell well enough to cover a cheap meal a day. But sales slowed down despite updates and I decided then that only bugs would get me to update it. Then I decided that only bugs that I could test would get me to update it. Turns out Ascension lacks many bugs, if any. I’d received a few bug reports of unusual foreign handsets failing to run it, but I honestly didn’t care. They appeared to fill the cheap phone role, and I had no interest in supporting those users. They were all Lite version users anyway.

The Lite version may have been my one big mistake. I released a feature-stripped version of Ascension for users to try, mostly as an experiment. If it resulted in increased sales, the free version would stay. If it hurt things, it would go. Unfortunately, results were mixed, and I let the Lite version be. Ultimately, it seems more likely that only people interested in getting a free app downloaded the Lite version. I pulled it from Google Play earlier this year.

In the end, Ascension pleased a good number of people. The e-mails users sent me seem to prove that. The comments on the app seem to prove that. The consistently high rating on the Market seems to prove that. I made something I wanted for my phone, sold it, and easily made back the cost of development. It was a worthwhile project. But it’s done, so I suppose I should get back to my current project. Back to the project that makes me happy.

Sublime Text 2 Theme — Nil

Today I released my Sublime Text 21 theme, Nil. It’s comprised mainly of solid colors and tries to communicate mostly through what I hope is a quick and obvious use of color rather than an attempt to be too flashy. Though it probably is slightly flashy just for being a dark theme. Have a screenshot:

The Nil Theme

Regarding colors, blue is the active buffer/selection, orange is a dirty buffer/selection in tabs and open files, and subdued orange is inactive and dirty in tabs and open files. Aside from that, everything is a shade of grey and keeps my eyes in a state of relative happiness. In particular, the tab colors are intended to communicate state better than something that might be easy to overlook, like a little dot.

Installing The Theme

The theme can currently be downloaded on Github. I’ve submitted it for inclusion in Sublime Package Control, though if and when that happens seems indeterminable right now. Installation is fairly simply, and the README covers it all. However, for the sake of simplicity, or in the event that you don’t want to visit Github, the instructions are as follows:

You can download or clone the repository into your Sublime Text 2 Packages directory. To do this, simply navigate to ~/Library/Application Support/Sublime Text 2/Packages (or wherever it is on your particular operating system) and run the following command:

$ git clone git://github.com/nilium/st2-nil-theme.git 'Theme - Nil'

It’s very important you clone the repository into the Theme - Nil directory otherwise the theme won’t locate its assets and will take on an eldritch appearance. You don’t want Shub-Niggurath crawling out of your screen, so remember, put it in the right directory.

So there you have it. If the theme appeals to you in any way, go right ahead and download it.

Acknowledgments

The theme itself is a fork of Raik Ilves’s Pseudo OSX theme, which is in turn a fork of Ian Hill’s Soda theme. So, we’ve got a bit of a chain going now. Additionally, I would not have done this at all had I overlooked Liam Cain’s Refresh theme, which is another nice alternative. Basically, if you don’t like the Nil theme, these three are all excellent alternatives. I wouldn’t have done this without the three of them. Check ‘em out.