Got an idea for a new feature for Dynamo-Browse. Began working on it this evening, starting with the UI:

    Recorded using VHS.

    Sort of in-between projects at the moment so I’m doing a bit of light stuff on Mainboard Mayhem. I had an idea for a new element: a remote control which, when picked up, will allow the player to toggle walls and tanks using the keyboard, much like the green and blue buttons.

    I used ChatGGT to come up with some artwork, and it produced something that was pretty decent.

    Image generated from DALL-E with the prompt: pixel art of a remote control with a single red button styled like the tiles found in Chips Challange, rotated 45 degrees to the right.
    Prompt: pixel art of a remote control with a single red button styled like the tiles found in Chips Challange, rotated 45 degrees to the right.

    Only issue was that the image was huge — 1024 x 1024 — and the tiles in Mainboard Mayhem were only 32 x 32.

    I tried shrinking it down in Acorn, using various scaling algorithms. The closest that worked was bringing it down slowly to about 128 x 128 using Nearest Neighbour, than trying to go all the way down to 32 x 32 using Lanczos. That worked, but it required true 32 bit colour to be recognisable, and I wanted to preserve the 16 colour palette used by the original Chips Challenge.

    So using the original image as a reference, I bit the bullet and drew my own in Acorn. You can see it here in this test level:

    Example Mainboard Mayhem level showing the green and blue remote controls. The controls have 4 small buttons and one large bulbous button that is either blue or green, with a bit of phong and shadow applied
    They're the elements that look like remote controls.

    It turn out okay. At least it’s recognisable. Anyway, I coded it up and gave it a bit of a try:

    Yeah, it works well. When the player has the appropriate colour remote, they can hit either Z or X to toggle the green walls or blue tanks respectively. I really should add some indicators in the status bar to show which button to press.

    Not sure what I’ll do after this. The fun part was coming up with the element. But I guess I’ll have to come up with a few puzzles that use it.

    More work on Mainboard Mayhem today. Had a bit more success getting the Windows build into a releasable state.

    First thing was the app icon. That blog post I talked about yesterday worked: I was able to set the icon of the executable. I did make a slight adjustment though. The post suggested using ImageMagick to produce the ICO file, but I wasn’t happy with how they looked. There were a lot of artefacts on the smaller icon sizes.

    So I looked around for an alternative, and found this package by Lea Anthony. He’s the maintainer of Wails, a cross-platform toolkit for making browser-based GUI apps in Go, sort of like Electron but without bundling Chrome. In fact, most of the build for Mainboard Mayhem was put together by reading the Wails source code, so I trust he knows what his doing. And sure enough, his package produced a nicely scaled ICO file from a source PNG image. Better yet, it was distributed as a Go package, so I could no need to install and shell-out to run it: I could just integrated it directly into the project’s build tool.

    Using rsrc to generate the SYSO file with the icon worked as expected: Go did pick it up and embed it into the executable. I did have some trouble getting the Go compiler to pick up these files at first. In short, they need to be in the same directory as the main package. So if you’re running go build ./cmd/thing, make sure the SYSO files are in ./cmd/thing. Other than that, no real issues here.

    Screenshot of Windows 10 file browser with mainboard.exe shown with the app icon, plus a few sdl DLLs
    A beautiful site: Mainboard.exe with the embedded app icon

    One last thing I had to deal with was the console window. Running a Go app in Windows shows the console by default. Perfectly fine for command line tools, but less so for games:

    Screenshot of Mainboard Mayhem running with the console window open in the background showing log messages
    Mainboard Mayhem with that annoying console window. Even the log messages are dull (well, unless you're working on the app).

    So I had to find a way to hide the console on launch. Since Mainboard Mayhem is using SDL, I’m actually using MinGW to cross-compile the Windows release on an Ubuntu build runner. The documentation for MinGW suggests adding -mwindows as a linker option to hide the console:

    # What I was doing before, which didn't work
    CGO_ENABLED=1 \
    CC="x86_64-w64-mingw32-gcc" \
    GOOS="windows" \
    CGO_LDFLAGS="-mwindows -L…" \
    go build -o dist/cclm/mainboard.exe ./cmd/cclm'
    

    This didn’t actually work when I tried it: launching the app kept bringing up the console. Turns out what I should’ve done was follow the advice of many Stack Overflow answers, and set -ldflags "-H=windowsgui" on the Go command:

    # This works
    CGO_ENABLED=1 \
    CC="x86_64-w64-mingw32-gcc" \
    GOOS="windows" \
    CGO_LDFLAGS="-L…" \
    go build -ldflags "-H=windowsgui" -o dist/cclm/mainboard.exe ./cmd/cclm'
    

    This works even without the -mwindows switch. Not completely sure why though. I guess MinGW is not actually being used for linking? Or maybe -m only works with C header files? Don’t know. 🤷 But doesn’t matter: the console no longer shows up on launch.

    Screenshot of Mainboard Mayhem running, but with no console window. File browser running in the background
    Mainboard Mayhem without the console window. A much nicer experience now.

    Finally, there was testing it all, and for this I just bit the bullet and set-up a Windows 10 virtual machine in Azure. The rate is something like $0.16 AUD an hour, an easy decision compared to spending time trying to get a VM with Windows 10 running on my machine.

    One remaining thing that’s slightly annoying is Windows Defender refusing to launch it after download, doing effectively the same thing as Gatekeeper on MacOS does:

    Screenshot of Windows Defender SmartScreen indicating that it's refusing to start an unrecognised app. A single button saying 'Don't Run' appears at the bottom of the dialog.
    Gatekeeper a.la. Microsoft.

    I’m sure there’s a way around it but it’s probably not worth learning about it at this stage. It’s easy enough to dismiss: click “More Info” and the click “Run Anyway”:

    Screenshot of Windows Defender SmartScreen indicating that it's refusing to start an unrecognised app, saying the name of the executable and that the publisher is unknown. Two buttons saying 'Run Anyway' and 'Don't Run' appears at the bottom of the dialog.
    Clicking 'More Info' gives you a way to launch the app.

    But other than that, I think the Windows version of Mainboard Mayhem is ready. I’ve updated the website to include the Windows archive if anyone’s interested.

    Spent some time today on Mainboard Mayhem, trying to finish the Windows build. I’ve actually got Windows version of the game being built for a while now. I just haven’t published them, mainly because I haven’t got the app icon set-up yet.

    But this week, Golang Weekly had a link to a blog post by Mahmud Ridwan on how to do so. It looked pretty straightforward, so I thought I’d give it a try.

    And yeah, the instructions themselves were easy enough, and I wish I could say if they worked or not. But in order to test it, I need a Windows machine. And I don’t have one, and I wasn’t about to get one just for this.

    So I tried setting up Windows in a VM using UTM. I got this far:

    A blue Windows install screen within a MacOS window showing a spinner and the message 'Just a moment…' underneath

    Yeah, this infinite spinner has been staring at me pretty much all day. I got a Windows 10 installer ISO using CrystalFetch, and it seemed to work. But it just doesn’t want to boot up for the first time.

    Not actually sure what the problem is. The error message seems to suggest that it’s having trouble connecting to the internet. Might be that? Or maybe the installation didn’t complete properly? Could be anything. 🤷

    So no luck getting this tested yet. I’m wondering if it might be easier to forget virtualisation and just launch a Windows instance in the cloud somewhere instead.

    After having some success getting my early QBasic programs running in the browser I had a bit of a look this morning to see if I could do the same for my Delphi apps. I found a project called BoxedWine which looks promising. Seems to be a minified version of Wine that that runs in browser using WASM.

    I downloaded the example and had a bit of a play around with a Tetris clone I built. It worked… to a degree. It was a little slow, and some of the colours were off. But it ran, and was actually usable.

    A screenshot of a Win32 Tetris clone running with BoxedWine running in Safari

    I did another test with an app that used OpenGL, which was less successful.

    A Windows style exception message indicating a null pointer running in Safari

    I think some of the OpenGL shared objects are not in the minified Wine distribution it was using. It might be possible to include them: there are instructions, and a few demos, on how to load files from the full Wine distribution on demand.

    Anyway, not sure if I’ll pursue this further but it was a fun little exercise nonetheless. I’m pretty impressed that this is even possible at all. The Web stack is pretty freaking awesome.

    Spent some time today building a site for my Go utility packages. A feature I’ve decided to add is a Go template playground, where you can test out Go templates in the browser. Not something I’ll use everyday but I’ve occasionally wished for something like this before. Could be useful.

    The template playground, with a field for the template saying 'hello what', the data which has 'what' equal to 'world' in Json, and the output which is 'Hello, world'

    Mainboard Mayhem

    Project update on Mainboard Mayhem, my Chip’s Challenge fan game. I didn’t get it finished in time for the release deadline, which was last weekend. I blame work for that. We’re going through a bit of a crunch at the moment, and there was a need to work on the weekend.

    The good news is that there wasn’t much left to do, and after a few more evenings, I’m please to say that it’s done. The game is finish, and ready for release.

    So here it is: Mainboard Mayhem: A Chip’s Challenge fan game (and yes, that’s its full title).

    Screenshot of Mainboard Mayhem

    At the moment it’s only available for MacOS. It should work on both Intel and Apple Silicon Macs, although I’ve only tested on my M2 Mac Mini running Ventura.

    It’s good to finally see this project done. It’s been in development for about last ten years, and I spent half of that time wondering whether it was worth getting it finished it at all. Not committing to anything meant any work I did do on it was pretty aimless, and I always felt like I was wasting my time. Giving myself three weeks to either kill it, or release it helped a lot. I’ll start making deadlines for all the other unfinished projects I’m working on.

    As to what that next project will be, I not sure at this stage. Part of me wants to wait until this crunch time ends, but I suspect I’ll get antsy before then and start work on something else. I’ll keep you posted one way or the other.

    But for now, if you happen to give it a try, thank you and I hope you enjoy it.

    The app icon of Mainboard Mayhem

    Project update for Mainboard Madness. Well, today’s the deadline for getting the thing code complete, and what a surprised, it’s not finished.

    To be fair, it’s pretty close. All the levels are more or less done, and the beats of the in-game lore have been added. It all just needs tightening up a little. I spent today working on the end-game phase, which mainly involved coding up the credit sequence, and making sure I include credits for those involved in the original game (and who’s artwork I lifted).

    Mainboard mayhem credit sequence showing the final credit message 'Thanks for playing'

    The work remaining is to finish one or two game elements, adding a proper app icon, and finishing off the website. I’m wondering whether to add sound, but I feel bad enough taking the artwork from the original game, I rather not take the sound effects as well. That will mean the game will remain silent for the time being, but I can probably live with that for now.

    I think we’re still on track for getting this finished by this time next week. Last dash to the finish line, then I can put this 9 year project to rest for a while.

    Small project update on my Chips Challenge fan game.

    Started working on the final level. I was dreading this a little, thanks to my awful level design skills, but I made a great start to it this morning and it’s actually coming along pretty well. It’s a good opportunity to use all the elements that I didn’t get a chance to use in any of the other puzzles, and it’s also shaping up to be one that has a bit of climax.

    I’ve also started working on the website, which is little more than just a landing page. This meant finally coming up with a name. I’ve chosen “Mainboard Mayhem” which is… okay, but it’s one that’s been rattling around in my head for a while, and I really couldn’t use anything close to “Chips Challenge”. I’m already using the tile-set from the original game, I rather not step on any more intellectual property.

    Anyway, one more week of development left to go. Still need to setup the app icon, finish all the levels, and maybe add a menu. Then I think we’re code complete.

    Early Version of This Blog

    I was looking for something in GitHub the other day when I found the repository for the first iteration of this blog. I was curious as to how it looked and I’d thought that I’d boot it up and post a few screenshots of it.1

    It started life as a Hugo site. There a two reasons for that, with the first being that I didn’t have the patients to style a website from scratch, and Hugo came with some pretty nice templates. I chose the Vienna template, which seems to have fallen out date: many of the template variables no longer work with a modern version of Hugo. I’m also please to see that I did end up customising the header image — a photo taken in Macedon of the train line to Bendigo — although that’s pretty much all I customised.

    Believe it or not, I feel a little nostelgic for it. Such simple innocence in trying to summon up the courage to write stuff on the internet. Although don’t let the article count fool you: I think there were a total of 10 posts, with half of those being unfinished drafts. I was still trying to work out whether I’d like to write mainly about software technology, or simply talk about my day. But one thing’s for sure, I was under the impression that “real” blogs required posts with a title and at-least 300 words of content. That’s probably why I only had 5 posts finished in 8 months.

    The second reason why I went with Hugo was that I’d have no excuse to tinker with a CMS. I’d figure that, given that I wasn’t using one, I’d be force to focus on the content. Well, that level of self-discipline didn’t last long. About in the middle of 2020, I started building a CMS for the blog using Buffalo. I was thinking of launching it with the name “72k” (72k.co), named after the milepost the header photo was taken at.

    I got reasonably far with building this CMS but it still lacked a lot, like uploads and an RSS feed. It also involved a really annoying workflow: in order to publish something, you needed to choose a “post type” (whether it’s a long-form post; a link post; or a note), the “stream” the post will appear in, write a summary, and then “review” it. Once all that’s good, you’re free to publish it. This was in service of building this up into a popular, wizz-bang blog with a well-engineered navigation and category-specific feeds (I think that’s what “streams” were). Yeah, these grand plans got the better of me and really crippled the usability of the CMS2. I never launched it, opting instead to move to Micro.blog.

    So that’s what this blog looked like, back in the day. I probably won’t look at these projects again. It’s only been four years and already bit-rot is settling in: it took me all morning trying to hack these into a state where I can open them in a browser. But it’s good to look back at what it was.

    Still really happy I moved it over to Micro.blog.


    1. I don’t deny that part of this is procrastination of other things I should be finishing. ↩︎

    2. To be honest, I think part of this lengthy workflow was to satisfy the “resistance”: self-imposed roadblocks to stop me from publishing anything at all. ↩︎

    Working on my Chips Challenge “fan game” this morning. Added the notion of “lower thirds,” which will show text at the bottom of the play field. I’m hoping to use it for narrative or way-finding, like here in this hub level:

    Demonstration of the lower third indicating the direction of movement towards tutorial levels in a hub map

    Also working on puzzle design. There’s about 19 or so “real” puzzles but I’m wondering if it’s worth adding a few tutorial ones for those that have never played the original Chip Challenge before. I’ve done about 5 such puzzles and I think I need to add maybe 3 or 4 more to cover everything I’m hoping to demonstrate. I wish I liked puzzle design more than I like tinkering on the engine.

    Of course, the big question is why I’m working on this at all. There is, for lack of a better word, a vision for this, in terms of narrative and structure, but this project has been in development on and off for about 9 years or so, and I’m wondering if it’s time to just stop working on it altogether. I really am starting to get sick of it, in a way. And yet, this project has shown remarkable staying power over that time that I feel like if I don’t actually wrap it up, it’ll just continued to be worked on. It feels like the only way to end this project is to finish it, in one way or another.

    So I’ll set myself a dead-line: something releasable in two weeks, and actually released a week after that. After that, no more! I’ll work on something else.

    Attempting to design an app icon for a Chips Challenge fan game I’m working on. Going for something that looks like the fireball sprite in the original game with a hint more realism and tinted in the colour blue. For reference, here’s the original fireball sprite:

    Fireball sprite in Chips Challenge

    And here’s my attempt:

    Blue plasma shaped like the fireball sprite in Chips Challenge

    I started with Stable Diffusion to get the base image:

    Stable Diffusion production of the image described in the caption
    Prompt: a blue plasma fireball shaped like a throwing star with four points on a white background, pixel art

    Then imported into Acorn to rotate it, colourise it, and distort it to look a bit closer to the original sprite.

    Screenshot of Acorn showing the image produced by Stable Diffusion rotated and the effects applied to produced the final image.

    Desaturating the original image got rid of the purple centre, then applying the Glowhoo and Hue Adjust effect recolourised it to the blue I was looking for (I’m not sure what the Glowhoo effect does, but it seems to adjust the colour based on the pixel intensity, so it was good enough for what I wanted). Finally, I added a Twirl Distortion effect to achieve the slight warp in the star.

    And yeah, it’s not going to win any design awards, but it’s good enough for now.

    Oh, and just for kicks, here was my first attempt of producing the sprite using Affinity Designer.

    First attempt at the blue plasma logo, crafted within Affinity Designer

    That’s definitely not going to win any design awards. 😂

    Success! Managed to get a Go app built, signed, and notarised all from within a GitHub Action. It even cross-compiles to ARM, which is something considering that it’s using SDL. Here’s the test app being downloaded and launched in a VM (ignore the black window, the interesting part is the title).

    Spent most of the weekend going down various rabbit holes to get a Go application signed and notarised as a MacOS app. I’m trying to do this in a way that would make this easy to automate using GitHub Actions. This means things like no implicit access to the system keychain: I want to make a temporary keychain, add my secret stuff to it, then delete it once signing and notarisation is done.

    It also means no XCode GUI either: command line stuff only. Not that I had much hope of using XCode here anyway, since this is a Go application.

    But that’s fine, preferable even. I’ve never liked all the manual steps needed to get code signing work with XCode. What are you going to do when you change systems? Would you remember all the steps you took several years ago, when you last setup developer certificates?

    So this is why I’m trying to get it working with the terminal. But it’s not easy. Lots of esoteric commands that I need to learn and be made aware of. Just hope it’s not a huge waste of time.

    Lava Stream

    Another random hack today, but this one sort of developed over the week.

    It all started last Monday. I was doing an exploratory task around single-sign on. I read Scripting News that morning, and I was thinking about the style of writing Dave Winer has adopted for his blog: short notes made across the day, sort of like a running commentary on what he’s working on and what his thinking. I wondered if this would work for my job: having a way to write your thoughts down, even if they’re rough, so you can build atop them. Also works when you need to look back on what you were thinking or working on later on.

    I’ve got an Obsidian vault for my work notes, and I do use the Daily Notes feature quite a bit, but it’s not conducive to the type of running-commentary style of journaling I wanted to see. There is this hosted solution, called Memos, which could work. It’s a bit like a Twitter-like blogging platform but with the ability to keep them private.

    So that Monday I deployed an instance using Pikapod, and used it for my work. I did the job, in that I had a place to jot notes down as they came to me. But despite how well the app feels, it did have some shortcomings.

    The first is the whole split-brain problem with having two places to write notes: where should I put a thought? Really they should go in Obsidian, as that’s where all my other notes are currently. And although I trust Pikapod and Memos to be relatively secure, notes about what I do for work really shouldn’t be on an external server. This became evident on Tuesday, when I wrote this note in Memos:

    Something similar to what this is, except each note goes into the daily note of Obsidian. Here’s how it will work:

    • Press a global hot key to show a markdown notepad
    • Enter a note, much like this one.
    • It will be written to the Obsidian daily notes, under the “Notes” header

    Nothing happened Wednesday, but Thursday I decided to explore this idea a bit. So I whipped up something in XCode to try it out. Here’s what I’ve got so far:

    The main window of the project, with a text field at the top, the date in the middle, and a series of notes with timestamps on the bottom.

    The way it works is you enter a note in the top pane (which is just a plain text editor) and press Cmd+Enter to save it. It will appear in the bottom pane and will be written to the daily note in your Obsidian vault.

    An Obsidian Daily Note with the same notes that appear in the main window written out under a Notes header

    The notes are written to Obsidian in HTML. At the moment you also need to write the notes in HTML, but I’m hoping to support Markdown. It would be nice just to write the notes out as Markdown in Obsidian as well, but I want some way to delineate each note, and HTML seems like the best way to do so. Each note is basically an article tag with a date-stamp:

    <article>
      <time datetime="2023-07-13T01:45:57Z">[11:45:57 am]</time>
      This is a pretty stupid app, but might be useful. Will save me the need
      to run that notes service.
    </article>
    

    But sadly Obsidian doesn’t parse Markdown content within HTML tags. That’s understandable, I guess, but it would be nice if this changed.

    Anyway, we’ll see how this works. I’m calling this little hack Lava Stream at the moment, as an allusion to the geological meaning of Obsidian. And it’s meant to be a way to be an easy way of capturing thoughts, like a stream, of thoughts, into Obsidian… (get it?)

    Like most Random Hacks here, I’m not sure how long I’ll use it for1, or whether it’ll go beyond the pile of awful, crappy code it currently is, not to mention the ugly styling. I’ll spend more time working on it if I continue to see value in it. That’s not a given, but I think it shows promise. I’ve been thinking about something like this for a while, but the concern of storing work-related things on another server seemed like the wrong thing to do. Having a means of writing this way, with using Obsidian as the data store, seems like a pretty good way of doing this.


    1. I am still using that Lisp-based Evans wrapper I mentioned last week. ↩︎

    A Lisp-based Evans Wrapper

    I wanted an excuse to try out this Lisp-like embedded language Go library that was featured in Golang Weekly. Found one today when I was using Evans to test a gRPC endpoint and I wanted a way to automate it. Hacked up something in 30 minutes which takes a method name and a Lisp structure, converts it to JSON and uses Evans to send it as a gRPC message.

    As the afternoon progressed, I added some facilities to send HTTP GET and POST methods with JSON request bodies, plus some facilities to set some global options.

    Here’s a sample script:

    // Set some global options
    (set_opt %grpc_host "www.example.com")
    (set_opt %grpc_port "8080")
    
    // Make a gRPC call. This will use evans to perform the call
    (call "my.fancy.Grpc.Method" (hash
        example: "body"
        message: "This will be serialised to JSON and sent as the body"))
    

    Another script showing the HTTP methods

    // The HTTP methods don't print out the response body by default,
    // so add a post-request hook to pretty print out the JSON.
    (defn hooks_after_response [resp]
        (print_json resp))
    
    // Make a HTTP GET request to a JSON endpoint.
    // The JSON body will be converted to a hash so that the data can be useful
    (def user (rget "https://example.com/user/someone"))
    (def userName (hget user %name))
    
    // Make a HTTP POST with a JSON body.
    (hpost "https://example.com/user/someone" (hash
        new_name: "another"))
    

    It’s a total hack job but already it shows some promise. Evan’s REPL is nice but doesn’t make it easy to retest the same endpoint with the same data multiple times (there’s a lot of copying and pasting involved). For those purposes this is a little more satisfying to use.

    Now that I had a chance to try Zygomys out, there are a few things I wish it did.

    First, I wish it leaned more into the Lisp aspect of the language. The library supports infix notation for a few things, which I guess makes it easier for those who don’t particularly like Lisp, but I think it compromises some of the Lisp aspect of the languages.

    For example, lists can be created using square brackets, but there’s no literal syntax for hashes. Not that there are any in Lisp either, but derivatives Clojure uses square brackets for arrays and curly brackets for hashes. Curly brackets are reserved for inline code blocks in Zygomys, so there’s no way to use them in a more functional context. I suppose something could be added — maybe square brackets with a prefix, #["key" "value"] — but feels like a missed opportunity.

    Another is that there’s no way to use dashes in identifiers. This may have just been an oversight, but I’m wondering if the infix notation support complicates things here as well. It would be nice to use them instead of the underscore. I don’t personally like the underscore. I know it’s just a matter of pressing shift, but when writing identifiers in lowercase anyway, using the dash feels a lot more natural.

    Finally, on the Go API front, it would be nice to have a way to call functions defined in Zygomys in Go, much like those hook functions in the sample above. I think this is just a matter of documentation or adding a method to the API to do this. I see no reason why the engine itself can’t support this. So I’m happy for this to come down the line.

    But in general this library shows promise, and it was fun to build this tool that uses it. Of course, we’ll see if I use this tool a second time when I need to test a gRPC endpoint, and I don’t just hack up yet another one that does essentially the same thing.

    I’ve been working on Micropub Checkins over the last week. It’s been a bit of a rush trying to get it into a usable state for an upcoming trip. And by “usable”, I mean a form that I can tolerate, and when it comes to projects like this, I can tolerate quite a lot. It can have a really dodgy UI (which this does) and miss some really important features that are annoying to work around; but if it works, and doesn’t loose data, I’ll be fine with it.

    The main screen showing three check-ins
    The main screen showing the recent check-ins. Note the lock next to some of them. These won't be published until the locks are removed.

    The last week was dedicated to making the act of checking in distinct from publishing it. Until now, check-ins were published the minute they were entered, meaning that you cannot check-in somewhere unless you’re comfortable with people knowing where you are the minute you do. Yes, some people like it that way, but not me. And I’m aware that this’ll only be the case if people are following my check-in blog, which I’m doubtful of.

    So pressing the floating action button and choosing a check-in type now starts the flow of a new check-in that will get saved in an SQLite database. You can edit the check-in whenever you like, so long as it’s not published. Currently there’s no real way of deleting a check-in unless it’s been published. This is a bit dodgy, but it’s a good example of how tolerant I am with working around these feature gaps for the moment.

    The new styled edit screen with a title, description, rating, and a ready for check-in switch
    The newly styled edit screen. Notice the rating field, which will appear for eateries.

    Check-ins can be published by tapping the upward facing button on the main screen. Any check-in with a lock is private and will not be published until you toggle the “Ready to publish” switch in the properties. Doing so will not change the date of the check-in: it will still have the date and time that check-in was created.

    The target list screen, showing two targets: one test target, and one for my real check-in blog
    The targets are currently hard-coded but they can be turned on or off. I had a bit of trouble publishing a check-in to two targets, so I'm not sure if I'll keep multi-target publishing.

    On the subject of publishing, I had some issues with Dart’s date and time methods. The method on the DateTime class used to produce an ISO-8501 date-stamp don’t include the time-zone if the date and time is not in UTC. This is important as I want the post date and time to be as close to the check-in time as possible, and in the time-zone of the phone. DateTime knows all this, including what the time-zone we’re in. So why didn’t the developers include it in the ISO-8501 date-time string?

    This is really strange. Fortunately, ChatGPT stepped in to help out, writing a function which will add the time-zone offset to the ISO-8501 date-time string:

    String formatTimeZoneOffset(Duration offset) {
      String sign = offset.isNegative ? '-' : '+';
      int hours = offset.inHours.abs();
      int minutes = (offset.inMinutes.abs() % 60);
    
      return '$sign${_padZero(hours)}:${_padZero(minutes)}';
    }
    

    Honestly, ChatGPT has been so helpful over the past week with this project, I probably should give it a credit if I get this polished enough to release.

    Back working on Micropub Checkin. Re-engineered the home page to now include a list of what would eventually be check-ins — both historical and soon to be published — complete with the check-in type emoji as the icon:

    Main screen for Micropub Checkin
    Main screen for Micropub Checkin

    The same list of emoji icons now adorn the check-in type picker as well (except for the airplane one which seems to always be shown as what I can only describe as the “Wingding” representation):

    The check-in type picker
    The check-in type picker

    I went around a bit trying to work out how best to use these emojis icons in the leading slot of the ListTile widget. I expored trying to convert them to IconData, but it turns out just using a Text widget with a large font worked well. I wrapped in in a Widget type with a fixed font-size and so far it looks quite good, at least in the emulator:

    class EmojiIcon extends StatelessWidget {
      final String emoji;
    
      const EmojiIcon({super.key, required this.emoji});
    
      Widget build(BuildContext context) {
        return Text(emoji, style: TextStyle(fontSize: 26.0));
      }
    }
    

    Also started working on a Cubit to handle state for the main page. I had a bit of trouble working ont where the soon-to-be database call to get the list of checkins should go in the cubit. After asking ChatGPT, it looks like the initializer is the best place for it:

    class CheckinListCubit extends Cubit<CheckinListState> {
    
      CheckinListCubit(): super(LoadingCheckinListState()) {
        loadCheckinList();
      }
    
      void loadCheckinList() async {
        var listOfCheckins = await read_database(); 
        emit(FoundCheckinListState(checkins));
      }
    }
    

    I’ve got some scaffolding code in place to simulate this, and so far it seems to work.

    I need to start working on the database layer and having the ability to edit and delete check-ins before they’re published. I think I’ll tackle that next.

    Building F5 To Run

    At the risk of talking about something that I’ve only just started, I’d thought today I write about what I’m working on right now.

    I’ve been going through my digital archives this weekend, trying to get it into something more permenant than the portable USB drives it’s currently stored on. Amongst all that stuff is a bunch of QBasic apps and games I wrote way back when I was a kid. Over the years it’s laid dormant but I do like revising them from time to time.

    Is it a form of nostalgia? An attempts to live past glories? Maybe? I was pretty proud of them at the time, much like anyone else that’s proud of their early stuff while they’re leaning to code. And I know about the risk of living in the past at the expense of the present. But I also know that if I get rid of them, I’d regret it. I already regret loosing the things from the archive so far, due to bad disks or just missing things while copying them from portable hard-drive to portable hard-drive. I don’t want to loose any more.

    So in an act of posterity, I’d figured it’s time to coat them in amber and put them online. So that’s what I’m doing now.

    These apps run without issue in DosBox, and hearing about how the Wayback Machine has managed to make a bunch of DOS games playable within the browser, I wondered if I could do something similar. Anything that deals with virtualisation is always going to be a little bit involved. I guess one thing going for these is that they were written for a pretty slow machine and a pretty thin OS that would be trivial for modern hardware to emulate. The apps themselves, even compiled to an EXE file, are not very taxing on the hardware back then either. But I still expected to do a bit of heavy lifting myself.

    How wrong I was! After a tiny bit of research — and by tiny I mean one Ecosia search — I managed to find a JavaScript library called JS-Dos which provides a DosBox emulator that’s runnable from a browser. All I need to do is prepare a bundle on what I want to run (more on that below) and with a bit of JavaScript, I can start a DosBox machine in the browser and mount it to a HTML element. The library does all the work.

    How To Use JS-Dos

    It’s still early days, but here’s what I learnt about using the library so far.

    First, the library comes as a NPM package, or can be loaded from their CDN in the form of a <script> import. I first tried using the NPM package, but I didn’t know the appropriate import statement to use, and the documentation was not forthcoming on this front.

    So I went with the CDN approach. I’m using Hugo Pipes to fetch the remote JavaScript file and make a local bundle so I can host it from the site itself. It comes with some CSS which I also need to get (note, I’m using parenthesis instead of curly braces here as I’m not sure how to include two curly braces in a code-block).

    (( $jsDosCSS := resources.GetRemote "https://js-dos.com/v7/build/releases/latest/js-dos/js-dos.css" ))
    <link rel="stylesheet" href="(( $jsDosCSS.RelPermalink ))">
            
    (( $jsDosJS := resources.GetRemote "https://js-dos.com/v7/build/releases/latest/js-dos/js-dos.js" ))
    <script src="(( $jsDosJS.RelPermalink ))" defer></script>
    

    I also needed to get an appropriate wdosbox emulator. This comes in the form of a WASM file, plus a small JavaScript file which I assume is some simple bootstrapper. I’ve downloaded these and stored them in the static/emulators directory of my Hugo project. The JSDos library loads them on demand and I needed to set the URL path prefix for these two files so that JSDos knows where to get them:

    emulators.pathPrefix = '/emulators/';
    

    Next, I needed to build a bundle. These are the DOS programs that are launched with DosBox. They’re effectively just Zip files holding some metadata, the DOS executable, and any files needed for the program to run. There’s some basic information about how to make them, and there’s even an online tool which will take on a lot of the tedious work. I’ve used it to make a couple of test bundles and it works quite well. I’d like to eventually make my bundles myself but I’ll stick with the tool for the time being, at least until I’ve got a DosBox configuration that I’m happy with. One thing the tool does is give you the ability to define an overlay so that these DOS apps are usable from within a mobile browsers. I’ll see if I can get away from needing these overlays at this stage. I’m not expecting anyone with a mobile app to try these out.

    The contents of .jsdos/dosbox.conf for the test bundle `logo-2.jsdos`
    [sdl]
    autolock=false
    
    fullscreen=false
    fulldouble=false
    fullresolution=original
    windowresolution=original
    output=surface
    sensitivity=100
    waitonerror=true
    priority=higher,normal
    mapperfile=mapper-jsdos.map
    usescancodes=true
    vsync=false
    [dosbox]
    machine=svga_s3
    
    language=
    captures=capture
    memsize=16
    [cpu]
    core=auto
    cputype=auto
    cycles=max
    
    cycleup=10
    cycledown=20
    [mixer]
    nosound=false
    rate=44100
    
    blocksize=1024
    prebuffer=20
    
    [render]
    # frameskip: How many frames DOSBox skips before drawing one.
    #    aspect: Do aspect correction, if your output method doesn't support scaling this can slow things down!.
    #    scaler: Scaler used to enlarge/enhance low resolution modes.
    #              If 'forced' is appended, then the scaler will be used even if the result might not be desired.
    #            Possible values: none, normal2x, normal3x, advmame2x, advmame3x, advinterp2x, advinterp3x, hq2x, hq3x, 2xsai, super2xsai, supereagle, tv2x, tv3x, rgb2x, rgb3x, scan2x, scan3x.
    
    frameskip=0
    aspect=false
    scaler=none
    
    [midi]
    #     mpu401: Type of MPU-401 to emulate.
    #             Possible values: intelligent, uart, none.
    # mididevice: Device that will receive the MIDI data from MPU-401.
    #             Possible values: default, win32, alsa, oss, coreaudio, coremidi, none.
    # midiconfig: Special configuration options for the device driver. This is usually the id of the device you want to use.
    #               See the README/Manual for more details.
    
    mpu401=intelligent
    mididevice=default
    midiconfig=
    
    [sblaster]
    #  sbtype: Type of Soundblaster to emulate. gb is Gameblaster.
    #          Possible values: sb1, sb2, sbpro1, sbpro2, sb16, gb, none.
    #  sbbase: The IO address of the soundblaster.
    #          Possible values: 220, 240, 260, 280, 2a0, 2c0, 2e0, 300.
    #     irq: The IRQ number of the soundblaster.
    #          Possible values: 7, 5, 3, 9, 10, 11, 12.
    #     dma: The DMA number of the soundblaster.
    #          Possible values: 1, 5, 0, 3, 6, 7.
    #    hdma: The High DMA number of the soundblaster.
    #          Possible values: 1, 5, 0, 3, 6, 7.
    # sbmixer: Allow the soundblaster mixer to modify the DOSBox mixer.
    # oplmode: Type of OPL emulation. On 'auto' the mode is determined by sblaster type. All OPL modes are Adlib-compatible, except for 'cms'.
    #          Possible values: auto, cms, opl2, dualopl2, opl3, none.
    #  oplemu: Provider for the OPL emulation. compat might provide better quality (see oplrate as well).
    #          Possible values: default, compat, fast.
    # oplrate: Sample rate of OPL music emulation. Use 49716 for highest quality (set the mixer rate accordingly).
    #          Possible values: 44100, 49716, 48000, 32000, 22050, 16000, 11025, 8000.
    
    sbtype=sb16
    sbbase=220
    irq=7
    dma=1
    hdma=5
    sbmixer=true
    oplmode=auto
    oplemu=default
    oplrate=44100
    
    [gus]
    #      gus: Enable the Gravis Ultrasound emulation.
    #  gusrate: Sample rate of Ultrasound emulation.
    #           Possible values: 44100, 48000, 32000, 22050, 16000, 11025, 8000, 49716.
    #  gusbase: The IO base address of the Gravis Ultrasound.
    #           Possible values: 240, 220, 260, 280, 2a0, 2c0, 2e0, 300.
    #   gusirq: The IRQ number of the Gravis Ultrasound.
    #           Possible values: 5, 3, 7, 9, 10, 11, 12.
    #   gusdma: The DMA channel of the Gravis Ultrasound.
    #           Possible values: 3, 0, 1, 5, 6, 7.
    # ultradir: Path to Ultrasound directory. In this directory
    #           there should be a MIDI directory that contains
    #           the patch files for GUS playback. Patch sets used
    #           with Timidity should work fine.
    
    gus=false
    gusrate=44100
    gusbase=240
    gusirq=5
    gusdma=3
    ultradir=C:\ULTRASND
    
    [speaker]
    # pcspeaker: Enable PC-Speaker emulation.
    #    pcrate: Sample rate of the PC-Speaker sound generation.
    #            Possible values: 44100, 48000, 32000, 22050, 16000, 11025, 8000, 49716.
    #     tandy: Enable Tandy Sound System emulation. For 'auto', emulation is present only if machine is set to 'tandy'.
    #            Possible values: auto, on, off.
    # tandyrate: Sample rate of the Tandy 3-Voice generation.
    #            Possible values: 44100, 48000, 32000, 22050, 16000, 11025, 8000, 49716.
    #    disney: Enable Disney Sound Source emulation. (Covox Voice Master and Speech Thing compatible).
    
    pcspeaker=true
    pcrate=44100
    tandy=auto
    tandyrate=44100
    disney=true
    
    [joystick]
    # joysticktype: Type of joystick to emulate: auto (default), none,
    #               2axis (supports two joysticks),
    #               4axis (supports one joystick, first joystick used),
    #               4axis_2 (supports one joystick, second joystick used),
    #               fcs (Thrustmaster), ch (CH Flightstick).
    #               none disables joystick emulation.
    #               auto chooses emulation depending on real joystick(s).
    #               (Remember to reset dosbox's mapperfile if you saved it earlier)
    #               Possible values: auto, 2axis, 4axis, 4axis_2, fcs, ch, none.
    #        timed: enable timed intervals for axis. Experiment with this option, if your joystick drifts (away).
    #     autofire: continuously fires as long as you keep the button pressed.
    #       swap34: swap the 3rd and the 4th axis. can be useful for certain joysticks.
    #   buttonwrap: enable button wrapping at the number of emulated buttons.
    
    joysticktype=auto
    timed=true
    autofire=false
    swap34=false
    buttonwrap=false
    
    [serial]
    # serial1: set type of device connected to com port.
    #          Can be disabled, dummy, modem, nullmodem, directserial.
    #          Additional parameters must be in the same line in the form of
    #          parameter:value. Parameter for all types is irq (optional).
    #          for directserial: realport (required), rxdelay (optional).
    #                           (realport:COM1 realport:ttyS0).
    #          for modem: listenport (optional).
    #          for nullmodem: server, rxdelay, txdelay, telnet, usedtr,
    #                         transparent, port, inhsocket (all optional).
    #          Example: serial1=modem listenport:5000
    #          Possible values: dummy, disabled, modem, nullmodem, directserial.
    # serial2: see serial1
    #          Possible values: dummy, disabled, modem, nullmodem, directserial.
    # serial3: see serial1
    #          Possible values: dummy, disabled, modem, nullmodem, directserial.
    # serial4: see serial1
    #          Possible values: dummy, disabled, modem, nullmodem, directserial.
    
    serial1=dummy
    serial2=dummy
    serial3=disabled
    serial4=disabled
    
    [dos]
    #            xms: Enable XMS support.
    #            ems: Enable EMS support.
    #            umb: Enable UMB support.
    # keyboardlayout: Language code of the keyboard layout (or none).
    
    xms=true
    ems=true
    umb=true
    keyboardlayout=auto
    
    [ipx]
    # ipx: Enable ipx over UDP/IP emulation.
    
    ipx=true
    [autoexec]
    echo off
    mount c .
    c:
    
    type jsdos~1/readme.txt
    echo on
    
    LOGO.EXE
    

    I’m keeping the bundles in the static/bundles directory, which sits alongside the emulator WASM file. They’re not huge binaries but I’m still using git lfs to manage them. Best to keep the the Git repository relatively sane.

    Finally, it’s just a matter of adding some JavaScript to start DosBox, load the bundle, and mount it onto a HTML element:

    Dos(document.querySelector("#element-to-mount")).run("/bundles/bundle-to-load.jsdos");
    

    And that’s pretty much it.

    Safari window with a test webpage with JSDos running an editor. The editor has the line 'This is the running Dos program.'
    A test webpage with JSDos running Logo 2, which is one of my Basic programs

    After a few hours, I’ve managed to get a test version of this working. There are a few things that need working on: the app I’m trying require the Alt key, which is not readily available of Apple keyboards, so I may need to do something about that (JSDos comes with a virtual keyboard with Ctrl and Alt so it’s not a complete show-stopper)1. And I’ll need to get styling and more maintainable JavaScript written for this (I’m using StimulusJS for the JavaScript2). But I’m impressed by how well this works, given the minimal amount of effort from my part. Shoulders of giants and all that.


    1. I’ve since learnt that Alt is bound to the Option key in MacOS. ↩︎

    2. One thing I leant about Hugo is that it’s bundled with ESBuild, meaning that it’s resource pipeline supports NPM packages. My understanding is that this is invokable using the js.Build construct. This is super useful to know. ↩︎

    Updates To Dynamo-Browse

    In the off-chance that anyone other than me is reading this, it’s likely that there will be no update next week due to the Easter weekend. It may not be the only weekend without an update either. If I find that I didn’t get much done for a particular week, I probably won’t say anything and leave the well to fill up for the next one (although I do have some topics planned for some of those weekends).

    In fact, I was not expecting to say much this week, given that work was going through a bit of a crunch period. But the needs of work finally pushed me to add a few features to Dynamo-Browse that were sorely lacking. So that’s where most of this week’s side-project budget went to.

    Dynamo-Browse

    A lot of work done to query expressions in Dynamo-Browse this week, some of it touching on a few topics I mentioned in a previous update.

    I’ve finally got around to finishing the between keyword, so that it works with the query planner and actually produces a DynamoDB query when used with a range[^sort] key. This means no more falling back on table scans. It’s still in a branch as of this post, but I feel much less embarrassed with merging it now, given that this support has been added.

    I’ve also made a decision about how to deal with multiple index candidates. Now, when Dynamo-Browse finds that multiple indices can apply for a specific query expression, it will produce an error, requesting you to specify which index to use. This can be done by adding a using suffix to an expression, which specifies how the query should be evaluated:

    color="blue" using index("color-item-index")
    

    This can be used at any time to override the index Dynamo-Browse should use, even if only one index candidate was found. It can also be used to force the query to run as a table scan if you don’t want to use an index at all:

    color="blue" using scan
    

    Ideally you shouldn’t need to use this suffix that often. The whole purpose of query expressions was to eliminate the need for specifying details of how the query should be evaluated. But we don’t live in a perfect world, and it makes sense adding this to deal with cases where it helps to be specific.

    This is also in a branch, but I’m hoping this would be merged soon as well.

    Unified Expression Types and Values

    A relatively large change made this week was how how values and types are represented within query expressions.

    A query expression, once parsed, can be executed in multiple contexts. It can be used to generate a conditional expression for a DynamoDB query or scan, or it can be evaluated within the app itself to produce a result or alter the fields of a DynamoDB record in memory. Each of these contexts have a different set of types the expression operates on. When interpreting the expression in order to produce a result, the expression operates on types that implement types.AttributeType. This fits nicely with what the expression has to work with, which is usually the raw DynamoDB records returned by the Go client. The context used to produces conditional expressions, however, operate on a sort of hybrid type hieararchy, that supports both AttributeType and Go types. This is because to the client used to build the expression accept native Go values, which are sometimes available — particularly if they show up in the expression as a literal — but sometimes not.

    But here’s the problem: I want to be able to add functions to the expression language that can be used in both contexts. I’ll get into what sort of functions I’m thinking of in a minute, but the issue is that with two sets of type hierarchies, I’d have to implement the functions twice.

    Another problem is that an evaluation context operating on AttributeTypes feels very inefficient. Numbers are represented as string, and new attribute values are created on the heap. This is probably not too bad in the grand scheme of things, but it would be nice to use native Go values here, even if it’s just to avoid going from strings to numbers constantly.

    So I spent most of yesterday trying to fix this. I built a new private Go interface called exprValue and added as implementing subtypes all the types supported by DynamoDB — strings, numbers, booleans, lists, etc. Values of these type implement this new interface, and can be converted to Go values or DynamoDB AttributeType values depending on the need.

    Most of the evaluation logic was changed to use these types, including the builtin functions, and already I’m seeing some dramatic improvements of what’s possible now. I can define a function once and it can be evaluated both in the evaluation and query building context (provided that it’s only operating on constant values in the query building context). It also addressed some long standing issues I’ve had with the expression language, such as adding support for using a list with the in keyword; something that was not possible before:

    pk in $someList
    

    This could potentially be helpful with the “fan-out” one I mentioned a few weeks ago.

    This is still early days, but I think it’s been a huge improvement to what was there before. And it’s super satisfying cleaning out all this tech-debt, especially if it means I can add features easily now.

    Date Functions In The Expression Language

    Now with the new type hierarchy in place, the time has come to start adding functions. What existed to date were the operators and functions that DynamoDB’s conditional expression language supported, and little else. It’s time to go beyond that. And to be honest, this was always the plan, especially given that operators like “begins with” (^=) have been there since the start.

    This first thing I’m pondering now is time and date functions. The immediate issue is one of representation: in that I don’t want to settle on any specific one. I’ve seen dates stored as both string date-stamps, usually in ISO 8601, or as integer seconds from the Unix epoch, and it would be good to operate on both of these, in addition to other possible representations, like milliseconds from the Unix epoch, to some other string encoding scheme.

    So what I’m thinking is an abstract date-type, probably something backed by Go’s builtin date.Time type. This will neither be a number or a string, but can be converted to one, maybe by using a keyword like as:

    now() as "S"   -- represent date as ISO-8601 timestamp
    now() as "N"   -- represent date as Unix timestamp
    now()          -- error: need to convert it to something
    

    Or maybe some other mechanism.

    The idea is that all the builtin functions will operate on this type, but will prevent the user from assuming a particular representation, and will force them to choose one.

    I think this is something that will fit nicely with the new type hierarchy system, but for now (pun unintended), I’ll stick with Unix timestamp, just so that I can use something that is easy to implement. But to make it crystal clear that this is temporary, any such functions will have an annoying prefix.

    So two new functions were added this week: the _x_now() function, which returns the current time as seconds from the Unix epoch as a number; and the _x_add(), which returns the sum of two numbers. Much like the time functions, I’d like to eventually add arithmetic operators like + to the expression language, but I needed something now and I didn’t have much time to work on that.

    Attribute Commands

    Finally, a few random notes about commands dealing with attribute values.

    The set-attr command can now accept the switch -to, which can be used to set the attribute to the result of a query expression. No more copying-and-pasting values, and operating on them outside Dynamo-Browse. The good thing about this is that the previous attribute values are available in the value expression, so you can use this switch to set the value of attribute based on other attributes in a row. This comes in super handy with bulk changes. I’ve used this to adjust the value of TTLs in a table I’m working in. To set the TTL to be 10 minutes into the future, I just marked the rows, entered the command set-attr -to ttl, and use the expression _x_add(_x_now(), 600). Super useful.

    Also, I’ve found a bug where the del-attr command does not work with marked items. It’ll only delete attributes from the item that’s selected (i.e. in pink). I haven’t got around to fixing this, but I hope to very soon.

    I think that’s all for this week. Until next time.

Older Posts →