Parsing Time

Setting up Playdate development with C in Emacs

Parsing Time

What's a Playdate?

Ever since Panic announced the Playdate, a little yellow console with a crank and games that release weekly, I was excited to get my hands on one. On top of that, when they requested applications for developer previews, I applied without hesitation. I loved developing under tight constraints with the PICO-8 fantasy console in the past, and this would be like that, except on an actual piece of hardware.

Regrettably I didn't get selected for a developer preview, but it wasn't a problem in the end, as Panic have publicly released an SDK for building home-brew games for the Playdate, complete with a console emulator for testing and even a web-based UI called Pulp.

Fast forward a few years, and I finally have access to an actual Playdate console and a bit of time to try developing a game for it.

In this post, I'm going to go over what I did to get initially set up for development using the C SDK in Emacs on MacOS. It's very much a work in progress, and I will probably add more to this in the future as I figure more stuff out. It's also pretty likely that I had a bunch of stuff already configured that may be required.

C SDK?

But why am I using the C SDK, and not the Lua one? Panic recommend using Lua, except for resource-intensive games that require the extra control over memory usage offered by C. My reasons are pretty much entirely down to personal preference.

While I liked developing with Lua for PICO-8, what I later realised is that I was actually developing with a Lua subset that had a lot of convenient global functions added. I found that without those, I didn't really enjoy coding with Lua as much. Even with them, I'm not a huge fan of the 1-indexed one-stop-shop collection 'table' type, with which I have shot myself in the foot liberally.

On the other hand, I haven't really done any pure C since university, so maybe it would be fun to revisit this language after years away from it. After doing a little programming with it, honestly I found the simplicity quite refreshing.

-I spy

The Playdate SDK comes with a bunch of example projects in both Lua and C, so I opened one of the projects in Emacs and fired up eglot, my LSP client of choice. The first thing I noticed was that header files were reported as missing all over the place. This was because I hadn't set up any include paths for the LSP server, clangd. To do this, all I needed to do was add a compile_flags.txt file at the root of my project and add the -I flag (I for Include) along with the location of all the Playdate SDK header files:

-I
/Users/robsws/Developer/PlaydateSDK/C_API/

Rudely, eglot didn't find this straight away. I found that this was because it's idea of the project root was all wrong - it was using the src directory that the C source file was in, rather than the actual project root one directory up from that.

One trick for this is that one of the clues it uses to determine where the project root is is where the git metadata is stored, i.e. .git. I hadn't set up a git repository yet, so one git init later and eglot got it's act together.

Preprocessor flags

Even now the include errors had drastically reduced, clangd was still reporting a problem - a compiler error in pd_api_gfx.h. Looking through the code for that file, I found that several types were defined within a preprocessor #if block, that was only triggering if the TARGET_EXTENSION preprocessor macro was set.

I searched this symbol in the Playdate C documentation and found that the XCode setup instructions tell you to set both this macro and PLAYDATE_SIMULATOR to 1 in the compiler flags. Thankfully I'd already figured out how to set compiler flags above, so I added -D TARGET_EXTENSION and -D PLAYDATE_SIMULATOR to the compile_flags.txt. Upon restarting eglot again, the problem was gone and flymake was reporting 0 problems. Hooray!

-I
/Users/robsws/Developer/PlaydateSDK/C_API/
-D
TARGET_EXTENSION=1
-D
PLAYDATE_SIMULATOR=1

Compiler

clangd and eglot are now in a happy place, but I haven't actually compiled any code yet. Thankfully the example projects all have a Makefile, so compiling with make is pretty simple. Emacs makes this even easier - just run M-x compile and enter the specific shell command you want to run, and the output will show up in a new window.

The only thing I needed to tweak here was the specific shell command that runs. Most of the time, I'm running M-x compile from a source file inside the src directory, but the Makefile is the directory above that. make however has you covered - you can pass the -C argument along with a directory path to have make switch to that directory before doing anything else. Emacs will then remember the command for future runs, so you don't have to enter it again.

Conclusion

That was enough for me to start making tweaks to the Hello World Playdate examples and get them compiled and running through Emacs on MacOS. I'll most likely add further posts in the future as I learn more about modern C, using the Playdate SDK, and doing all of the above through Emacs.