I started day 08 a little behind typing up Saturday’s December Adventure unexpected side-quest. And then bumped into a frustrating crash in InContext, my site builder, that slowed everything up. With that in mind, I decided to return to my more mellow OPL explorations for the rest of the day. (But not before I shipped a proof-of-concept update to my website’s image handling which will hopefully mean images work in the feed.)

Thoughts

Returning to Thoughts for EPOC1—there are a lot of little bugs to fix and tiny improvements I’d like to make:

  • Editor doesn’t correctly open new notes on the Revo
  • Create the destination directory if it doesn’t exist
  • Build a .app instead of a .opo
  • Adopt the constants in Const.oph for key event handling
  • Render a 90s style splash screen
  • Create an installer
  • Design an app icon
  • Support destination directory customization
  • Add a toolbar for quick actions
  • Add a toolbar for quick actionsDesign toolbar icons
  • Document the dependencies and build process in the project readme

Thankfully, it’s a heck of a lot more relaxing than working on a large Swift codebase like InContext, so I just started working my way through them.

Launching Editor

Remembering back to day 06, I was seeing issues opening the newly created notes in Editor on the Revo—it was just opening the last file I’d been editing. It turns out this was a side effect of using a more recent version of Editor (1.41) that revealed a bug in the way I was calling RUNAPP&:. I was passing the command 2 (run), instead of 0 (open), which was causing Editor to ignore the document parameter and open the most recent document.

I wish there were constants for this (Const.oph contains a constant for KCmdLetterCreate$ which is used by CMD$:, but no equivalent for RUNAPP&:), but at least I have a fix (#3):

k& = RUNAPP&:("Editor", path$, "", 0)

A happy side-effect of using this later version of Editor is that it also comes in color for the Series 7:

Look at those rich 256-color icons!

Create the Destination Directory

Nice and simple. Check if the directory exists and, if it doesn’t, create it (#4):

REM Ensure the destination directory exists.
IF NOT EXIST(directory$)
    MKDIR directory$
ENDIF

I’ll make this configurable later on.

Build an App

OPL programs are compiled to ‘executable’ .opo files by default. These don’t have an icon, and don’t appear in the Extras bar—for this to happen, they need to be built as a .app with a corresponding .aif resource file which contains the app caption, unique identifier, and icon. Fortunately, and much to my surprise as I’ve forgotten most of my 90s-era OPL, achieving this simply a matter of adding a declaration to the top of your OPL file (#7):

APP Thoughts, &100092ca
    CAPTION "Thoughts", 1
 ENDA

There’s a tiny bit of nuance here: OPL apps are identified by globally unique 32-bit integers which were managed centrally by Psion who aren’t about to be issuing new ones anytime soon. Thankfully, they allocated them in blocks of 10 and I was issued a block starting at &100092c8 back in the 90s. I’ve only used two so far, so &100092ca it is!

In case you’re curious (and for my own reference), my current programs are as follows:

UID Program Description
0x100092c8 nEzumi Virtual pet mouse for your Psion.
0x100092c9 Greedy Rebuild of the EPOC16 game Greedy by Frédéric Botton for EPOC32.
0x100092ca Thoughts Thoughts!

The only small wrinkle is that OpoLua’s compile.lua (which I’ve been using for my GitHub Actions CI builds) doesn’t support generating .app and .aif files so, while I can still benefit from the ‘compiler’ checks, I can’t automate the full build process (yet).

Constants for Key Event Handling

As I’m slowly getting back into OPL programming and remembering the various conventions, there are occasional refactorings that need to happen to keep everything clean. One of those is to adopt the constants in Const.oph wherever possible. Starting using KEvAType% makes indexing into the ev& array a little clearer and prepares the event processing for more complex behaviors (#5):

WHILE 1
    GETEVENT32 ev&()
    IF ev&(KEvAType%) = KKeyMenu% OR ev&(KEvAType%) = KKeySidebarMenu%
        REM Menu button.
        menu:
    ELSEIF ev&(KEvAType%) = %t AND ev&(KEvAKMod%) = KKModFn%
        REM Global hotkey (Fn + T)
        new:
    ELSEIF ev&(KEvAType%) < 32
        REM Key codes are modified if control is pressed.
        k& = ev&(KEvAType%) + %a - 1
        IF k& = %n
            new:
        ELSEIF k& = %e AND ev&(KEvAKMod%) = KKModControl%
            close:
        ELSEIF k& = %k AND ev&(KEvAKMod%) = KKModControl%
            clear:
        ELSEIF k& = %a AND ev&(KEvAKMod%) = (KKModControl% OR KKModShift%)
            about:
        ELSE
            PRINT k&
        ENDIF
    ELSEIF (ev&(KEvAType%) AND &400) = 0
        PRINT ev&(1), ev&(2), ev&(3), ev&(4), ev&(5)
    ENDIF
ENDWH

Well, that’s me done for the day and I can update my on-going Thoughts todo list:

  • Editor doesn’t correctly open new notes on the Revo
  • Create the destination directory if it doesn’t exist
  • Build a .app instead of a .opo
  • Adopt the constants in Const.oph for key event handling
  • Render a 90s style splash screen
  • Create an installer
  • Design an app icon
  • Support destination directory customization
  • Add a toolbar for quick actions
  • Add a toolbar for quick actionsDesign toolbar icons
  • Document the dependencies and build process in the project readme

I also added one new task:

  • Automate .app and .aif builds

Still a bunch of things to keep me occupied this month2 🥲


  1. Thoughts Lite? Thoughts, EPOC Edition? I really don’t know what I’m calling this. 

  2. If you enjoy creating retro graphics and would like to join me on this little adventure, I’d love your help