December Adventure Day 08
Normal [OPL] Service Has Been Resumed
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:
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 RevoCreate the destination directory if it doesn’t existBuild 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 🥲
-
Thoughts Lite? Thoughts, EPOC Edition? I really don’t know what I’m calling this. ↩
-
If you enjoy creating retro graphics and would like to join me on this little adventure, I’d love your help! ↩