Setting up Playdate development with C in Emacs
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.