SDL icon indicating copy to clipboard operation
SDL copied to clipboard

add initial playdate support

Open ericlewis opened this issue 3 years ago • 22 comments

Description

This PR adds initial support for the Playdate platform.

Supported features:

  • video
  • joystick
  • power
  • sensor
  • timer

Questions

This is my first time creating any sort of patch for SDL2 let alone adding support for a new hardware platform, so I am very open to any and all suggestions. It is quite likely things are incorrect / not fully supported. I also have some questions up-front:

  • How do I go about adding this to the config/make files?
    • Playdate has a simulator that runs on window/macOS, as well as the target arm device
    • The way I currently am able to compile is by passing the correct flags (TARGET_PLAYDATE or PLAYDATE)
    • Each target uses a completely different toolchain, the hardware toolchain is specific
  • Any advice on having the joystick also support game controller?
  • Any advice on tackling audio support?
  • Would it be preferred that these are smaller patches?
  • Is there a test suite that must pass? That may be a challenge.
  • Limited testing on actual hardware currently, would it be better to wait to upstream until it is more thoroughly tested?
  • Where would something like the crank fall under in hardware support? Sensor? Joystick?
  • What do we do about main support? Playdate has its own run loop, so setting up for playdate is slightly different.
  • There are some syscalls needed that playdate doesn't have which I implemented here in src/main/playdate, is this an appropriate place for including them?
  • I am curious about openGL/gles support, but lack the knowledge to understand what is required for supporting those platforms. There isn't really any hardware accel, but software based rendering might be fine.
  • Renderer seems close to how some things are drawn on playdate via draw commands, would it make sense to add a playdate renderer?
  • This requires an imported pd_api.h from the pd sdk that must be included in order to build, what is the best way to provide this?
  • advice on how to better work on SDL2 locally, I currently just copy the folder into a project and setup the project make file manually
  • playdate has a 1bit b&w display and surface input gets converted and dithered automatically by the driver, what would be a good way to hint to the developer that this is happening?
  • Similar to above, what would be an idiomatic way of allowing developers to control the style of dithering?
  • There are other edge cases too, is there a way to provide a logging primitive to SDL as well? printf isn't supported for instance.

Notes

  • I am happy to convert this to a draft if it is more appropriate to do so.
  • The SDL_config_playdate.h is kind of a mess.. advice on how to better orient this and handle things would be awesome.

ericlewis avatar Mar 30 '22 17:03 ericlewis

  • How do I go about adding this to the config/make files?

Right now we support both autotools and CMake. If your build environment is primarily CMake, then it's fine just add it there. I don't know CMake, so I don't have any advice on the implementation details, but I imagine you'd just follow the pattern for other platforms and possibly have a CMake define that turns this support on.

  • Any advice on having the joystick also support game controller?

Go ahead and add a section to SDL_gamecontrollerdb.h with your platform define (there's typically one added for each platform in SDL_platform.h, e.g. __PLAYDATE__)

  • Any advice on tackling audio support?

@icculus?

  • Would it be preferred that these are smaller patches?

One big PR for the initial platform support is fine, then additional fixes should each have their own PR.

  • Is there a test suite that must pass? That may be a challenge.

Not a formal suite, but there's some automated and manual tests in the test directory that are useful in validating platform functionality.

  • Limited testing on actual hardware currently, would it be better to wait to upstream until it is more thoroughly tested?

Yes, this should wait until it's more mature and has good testing on actual hardware before we merge it. Feel free to update this PR iterating until you're comfortable with it. Converting it to a draft now and then converting back when you're ready will be a good way to signal to us that you consider it ready. I imagine we probably won't have time to look at this much before then.

  • Where would something like the crank fall under in hardware support? Sensor? Joystick?

I don't know how the data is represented, but it looks like a custom sensor type is probably best.

  • What do we do about main support? Playdate has its own run loop, so setting up for playdate is slightly different.

SDL games are in charge of their own run loop, so you'll need to find some way to support that. Putting the relevant code in src/main/playdate is fine. Android, for example, creates a thread where the SDL main loop goes, and has interop between that thread and the main process. That road is fraught with danger, but is an option if that's the only way to make it work.

  • There are some syscalls needed that playdate doesn't have which I implemented here in src/main/playdate, is this an appropriate place for including them?

Usually those would go in src/core/playdate

  • I am curious about openGL/gles support, but lack the knowledge to understand what is required for supporting those platforms. There isn't really any hardware accel, but software based rendering might be fine.

Just leave that unimplemented. It sounds the software renderer is the way to go here.

  • Renderer seems close to how some things are drawn on playdate via draw commands, would it make sense to add a playdate renderer?

Yes, if it has full support for SDL's rendering capabilities. You might be better off just using SDL's software renderer instead.

  • This requires an imported pd_api.h from the pd sdk that must be included in order to build, what is the best way to provide this?

You should create a file docs/README-playdate.md that has any information needed to set up the build environment and run applications. Having a step of setting up include paths or copying a required header is fine.

  • advice on how to better work on SDL2 locally, I currently just copy the folder into a project and setup the project make file manually

That's probably fine. :)

  • playdate has a 1bit b&w display and surface input gets converted and dithered automatically by the driver, what would be a good way to hint to the developer that this is happening?

Documentation. If someone is building for the platform they should be aware of what's happening and how to control it.

  • Similar to above, what would be an idiomatic way of allowing developers to control the style of dithering?

Use an SDL hint. There are lots of examples in SDL_hints.h

  • There are other edge cases too, is there a way to provide a logging primitive to SDL as well? printf isn't supported for instance.

Yes, SDL_Log() is the standard way of providing logging. You can extend SDL_LogOutput() with the required functionality.

  • The SDL_config_playdate.h is kind of a mess.. advice on how to better orient this and handle things would be awesome.

What you've done is fine. The config files are generally not meant to be human readable for people not familiar with SDL's internals.

slouken avatar Mar 30 '22 18:03 slouken

SDL games are in charge of their own run loop, so you'll need to find some way to support that. Putting the relevant code in src/main/playdate is fine. Android, for example, creates a thread where the SDL main loop goes, and has interop between that thread and the main process. That road is fraught with danger, but is an option if that's the only way to make it work.

Thank you for the thoughtful response! I think I need more clarification as to what the SDL main loop is doing/is for. I've ignored it thus far without issue, but that seems problematic. Is it a way to call a user defined entry point or something? Is there something SDL has to do other than say "main is ready"? For now I just stubbed it, but it'll never get called anyway. Would it be better to user the platforms version of main to then call a user implemented main once it is setup?

ericlewis avatar Mar 30 '22 19:03 ericlewis

Thank you for the thoughtful response! I think I need more clarification as to what the SDL main loop is doing/is for. I've ignored it thus far without issue, but that seems problematic. Is it a way to call a user defined entry point or something? Is there something SDL has to do other than say "main is ready"? For now I just stubbed it, but it'll never get called anyway. Would it be better to user the platforms version of main to then call a user implemented main once it is setup?

I'd have to understand better how the platform works, but if you're not doing anything and the application is entering it's main() function and everything is working, then you may not need the SDL main loop stuff at all. That's only needed if there's platform specific prologue that needs to happen before the application main(). In that case, the application main is renamed SDL_main() and called from the prologue code. The Windows code is a good example of that, where there's a WinMain() that does some setup and then calls the application SDL_main() function. Again, maybe you don't need that at all?

slouken avatar Mar 30 '22 20:03 slouken

Any advice on tackling audio support?

The likely most direct example is the "arts" driver, which is used for an extremely basic (and obsolete) network audio server;

https://github.com/libsdl-org/SDL/blob/7e15ad2fc4f610482c2f7b23a6fb2838c8759f93/src/audio/arts/SDL_artsaudio.c

Most of the heavy lifting is going to happen in the OpenDevice implementation.

The primary goals are to allocate driver-specific data...

https://github.com/libsdl-org/SDL/blob/7e15ad2fc4f610482c2f7b23a6fb2838c8759f93/src/audio/arts/SDL_artsaudio.c#L225-L231

...find the closest audio format the hardware supports to what was requested by the app...

https://github.com/libsdl-org/SDL/blob/7e15ad2fc4f610482c2f7b23a6fb2838c8759f93/src/audio/arts/SDL_artsaudio.c#L233-L251

...prep the hardware to play sound...

https://github.com/libsdl-org/SDL/blob/7e15ad2fc4f610482c2f7b23a6fb2838c8759f93/src/audio/arts/SDL_artsaudio.c#L253-L268

...and make sure you have a mixing buffer...

https://github.com/libsdl-org/SDL/blob/7e15ad2fc4f610482c2f7b23a6fb2838c8759f93/src/audio/arts/SDL_artsaudio.c#L292-L298

Note that in there it sets this->spec's fields to values the hardware actually wants to be fed. If these are different than what the app wanted, SDL will either tell the app there were differences or transparently convert audio to this format on behalf of the app. So if the Playdate can only take audio in exactly one format/number of channels/sample rate, that's okay, SDL will manage that detail for you and the app.

Once you're up and running, audio runs in a background thread managed by SDL. If the Playdate doesn't have threads, we're going to have to talk further about how to deal with this.

That background thread, more or less, will just keep calling your WaitDevice and PlayDevice implementations in a loop...

https://github.com/libsdl-org/SDL/blob/7e15ad2fc4f610482c2f7b23a6fb2838c8759f93/src/audio/arts/SDL_artsaudio.c#L139-L164

https://github.com/libsdl-org/SDL/blob/7e15ad2fc4f610482c2f7b23a6fb2838c8759f93/src/audio/arts/SDL_artsaudio.c#L166-L186

...WaitDevice is meant to block until more audio can be fed to the device, PlayDevice is meant to feed the audio.

GetDeviceBuf is meant to provide memory where SDL can write audio buffers:

https://github.com/libsdl-org/SDL/blob/7e15ad2fc4f610482c2f7b23a6fb2838c8759f93/src/audio/arts/SDL_artsaudio.c#L188-L192

Most cases just have the implementation allocate a block of memory during Open, but some drivers can provide a block of their own that the OS or hardware reads out of directly, saving the need to do an extra copy.

Is there a microphone on the Playdate? If not, anything that refers to "capture" (audio recording) can be ignored. Otherwise, there are a few more methods to implement, but that can wait until playback works.

icculus avatar Mar 30 '22 23:03 icculus

I don't know how the data is represented, but it looks like a custom sensor type is probably best.

...maybe a mouse that only reports relative motion on an infinite Y axis?

icculus avatar Mar 30 '22 23:03 icculus

I don't know how the data is represented, but it looks like a custom sensor type is probably best.

...maybe a mouse that only reports relative motion on an infinite Y axis?

Re: Crank

The official Playdate API reports values as an absolute angle or a relative change in angle. Could it make sense to report the angles as the X and Y values of an analog stick or mouse? C API - https://sdk.play.date/inside-playdate-with-c/#_crank Lua API - https://sdk.play.date/inside-playdate/#crank

The crank can also be docked or undocked. Docked status could be a button. Having said that, it could also be a mouse button. In that case, the crank could just be a mouse.

For rudimentary interoperability and testing on PC, it might make sense to map the crank docked status to the left mouse click (mouse down == undocked, mouse up = docked) and the crank angles to the mouse wheel. SDL's mouse wheel model has x and y values which could be used for absolute (y/preciseY) and relative angles (x/preciseX; positive=clockwise). In this case, just reset the absolute angle to 0 every time time crank is undocked. Does this sound reasonable?

sgeos avatar Mar 31 '22 22:03 sgeos

Thank you for the thoughtful response! I think I need more clarification as to what the SDL main loop is doing/is for. I've ignored it thus far without issue, but that seems problematic. Is it a way to call a user defined entry point or something? Is there something SDL has to do other than say "main is ready"? For now I just stubbed it, but it'll never get called anyway. Would it be better to user the platforms version of main to then call a user implemented main once it is setup?

I'd have to understand better how the platform works, but if you're not doing anything and the application is entering it's main() function and everything is working, then you may not need the SDL main loop stuff at all. That's only needed if there's platform specific prologue that needs to happen before the application main(). In that case, the application main is renamed SDL_main() and called from the prologue code. The Windows code is a good example of that, where there's a WinMain() that does some setup and then calls the application SDL_main() function. Again, maybe you don't need that at all?

Re: main() not available on Playdate

There is no main() function available to programmers on the Playdate. The Playdate C API has an eventHandler() function that can be used to set up an update() function. My understanding is that the game is loaded as a dynamic library and the firmware then becomes responsible for calling the eventHandler() and update() functions at the appropriate time. Strictly speaking, eventHandler() is the application entry point. https://sdk.play.date/1.9.3/Inside%20Playdate%20with%20C.html#_game_initialization

A minimal example of the Playdate game structure looks something like this.

#include "pd_api.h"

struct ProgramState {
  PlaydateAPI *pd;
  // ... other fields here ...
};

void initProgramState(struct ProgramState *ps, PlaydateAPI *pd)
{
  ps->pd = pd;
  // ... initialize other fields here ...
}

static int update(void* userdata)
{
  struct ProgramState *ps = (struct ProgramState *)userdata;
  PlaydateAPI *pd = ps->pd;
  // ... game logic here ...
  return 1;
}

#ifdef _WINDLL
__declspec(dllexport)
#endif
int eventHandler(PlaydateAPI* pd, PDSystemEvent event, uint32_t arg)
{
  (void)arg; // only used for kEventKeyPressed == event
  static struct ProgramState *ps = NULL;

  switch (event) {
    case kEventInit:
      ps = pd->system->realloc(ps, sizeof(struct ProgramState));
      initProgramState(ps, pd);
      pd->system->setUpdateCallback(update, (void *)ps);
      break;
    case kEventTerminate:
      pd->system->realloc(ps, 0);
      ps = NULL;
      break;
    default:
      // do nothing
    break;
  };

  return 0;
}

sgeos avatar Mar 31 '22 23:03 sgeos

Any advice on tackling audio support?

SNIP

Once you're up and running, audio runs in a background thread managed by SDL. If the Playdate doesn't have threads, we're going to have to talk further about how to deal with this.

SNIP

Looks like further discussion is necessary. The Playdate does not support threads. See also.

sgeos avatar Apr 01 '22 02:04 sgeos

Looks like further discussion is necessary. The Playdate does not support threads. See also.

To be clear, though, we also work correctly on single-threaded Emscripten, so while SDL will try to spin a thread by default, audio backends can be made to manage this on their own.

In Emscripten's case, we mark the backend as "providing it's own thread" ...

https://github.com/libsdl-org/SDL/blob/1c9299b00d95485447f5b1b915d80c4cda3df8c9/src/audio/emscripten/SDL_emscriptenaudio.c#L357-L359

...so SDL doesn't even try to create a thread there. While this was meant for things like CoreAudio where the OS spins a thread for us, it's close enough. :) Then the emscripten backend a callback that fires when the main thread is idle, and the browser knows to fire it regularly. I assume Playdate will do something similar.

Although it might have to do some tapdancing to manage format conversion, etc, so you might have to wrap some SDL_AudioStreams around things where SDL_RunAudio would have managed it for you otherwise.

Here's the function CoreAudio calls from the system-created thread:

https://github.com/libsdl-org/SDL/blob/1c9299b00d95485447f5b1b915d80c4cda3df8c9/src/audio/coreaudio/SDL_coreaudio.m#L520-L590

...as you can see, it's slightly more involved than other platforms that just need to dump audio data to the hardware, because SDL handled all this at a higher level from its own thread for them. But it's not too awful to manage.

icculus avatar Apr 01 '22 16:04 icculus

Looks like further discussion is necessary. The Playdate does not support threads. See also.

To be clear, though, we also work correctly on single-threaded Emscripten, so while SDL will try to spin a thread by default, audio backends can be made to manage this on their own.

In Emscripten's case, we mark the backend as "providing it's own thread" ...

https://github.com/libsdl-org/SDL/blob/1c9299b00d95485447f5b1b915d80c4cda3df8c9/src/audio/emscripten/SDL_emscriptenaudio.c#L357-L359

...so SDL doesn't even try to create a thread there. While this was meant for things like CoreAudio where the OS spins a thread for us, it's close enough. :) Then the emscripten backend a callback that fires when the main thread is idle, and the browser knows to fire it regularly. I assume Playdate will do something similar.

Although it might have to do some tapdancing to manage format conversion, etc, so you might have to wrap some SDL_AudioStreams around things where SDL_RunAudio would have managed it for you otherwise.

Here's the function CoreAudio calls from the system-created thread:

https://github.com/libsdl-org/SDL/blob/1c9299b00d95485447f5b1b915d80c4cda3df8c9/src/audio/coreaudio/SDL_coreaudio.m#L520-L590

...as you can see, it's slightly more involved than other platforms that just need to dump audio data to the hardware, because SDL handled all this at a higher level from its own thread for them. But it's not too awful to manage.

Single thread will have to be the way to go. There is a way to create an audio buffer than can then be played, the buffer itself doesn't get copied so it might be possible to continuously update said buffer. There is also a callback from the audio player that indicates when playback is finished. The sound formats playback supports is listed below:

kSound8bitMono = 0,
kSound8bitStereo = 1,
kSound16bitMono = 2,
kSound16bitStereo = 3,
kSoundADPCMMono = 4,
kSoundADPCMStereo = 5

Not entirely sure what these actually equate to yet. I will probably start by seeing how writing to the buffer works anyway. That said, I don't feel that doing audio right now is necessary to upstreaming what we have so far.

On the note of the single threadedness of playdate, there is a main loop and it's called as fast as possible to schedule any and all work that must be done. I have noticed however that audio will remain playing whilst doing other things, so there is some amount of threading under the hood. Not sure if this makes any difference.

ericlewis avatar Apr 01 '22 19:04 ericlewis

@ericlewis Is there a basic HOWTO on building this? Is it "just download the SDK and click this thing?" or it is using CMake, or something else?

icculus avatar Apr 05 '22 14:04 icculus

@ericlewis Is there a basic HOWTO on building this? Is it "just download the SDK and click this thing?" or it is using CMake, or something else?

I will try to do a write up for that soon or I can share a makefile. One problem is it requires a single line patch in the SDK currently, which seems out of scope for this and I am still investigating how to not need such a change. The way this is built will be using a cmake or make file, in my demo project the makefile looks like this:

HEAP_SIZE      = 8388208
STACK_SIZE     = 61800

PRODUCT = gnu.pdx

SDK = ${PLAYDATE_SDK_PATH}
ifeq ($(SDK),)
	SDK = $(shell egrep '^\s*SDKRoot' ~/.Playdate/config | head -n 1 | cut -c9-)
endif

ifeq ($(SDK),)
$(error SDK path not found; set ENV value PLAYDATE_SDK_PATH)
endif

SRC = \
	SDL/src/stdlib/SDL_malloc.c \
	SDL/src/main/playdate/SDL_playdate_main.c \
	SDL/src/SDL.c \
	SDL/src/SDL_assert.c \
	SDL/src/SDL_dataqueue.c \
	SDL/src/SDL_error.c \
	SDL/src/SDL_hints.c \
	SDL/src/SDL_log.c \
	SDL/src/audio/SDL_audio.c \
	SDL/src/audio/SDL_mixer.c \
	SDL/src/audio/SDL_wave.c \
	SDL/src/audio/SDL_audiocvt.c \
	SDL/src/audio/SDL_audiotypecvt.c \
	SDL/src/audio/dummy/SDL_dummyaudio.c \
	SDL/src/atomic/SDL_atomic.c \
	SDL/src/atomic/SDL_spinlock.c \
	SDL/src/cpuinfo/SDL_cpuinfo.c \
	SDL/src/events/imKStoUCS.c \
	SDL/src/events/SDL_clipboardevents.c \
	SDL/src/events/SDL_displayevents.c \
	SDL/src/events/SDL_dropevents.c \
	SDL/src/events/SDL_events.c \
	SDL/src/events/SDL_gesture.c \
	SDL/src/events/SDL_keyboard.c \
	SDL/src/events/SDL_mouse.c \
	SDL/src/events/SDL_quit.c \
	SDL/src/events/SDL_touch.c \
	SDL/src/events/SDL_windowevents.c \
	SDL/src/file/SDL_rwops.c \
	SDL/src/joystick/SDL_gamecontroller.c \
	SDL/src/joystick/SDL_joystick.c \
	SDL/src/joystick/virtual/SDL_virtualjoystick.c \
	SDL/src/joystick/playdate/SDL_sysjoystick.c \
	SDL/src/power/playdate/SDL_syspower.c \
	SDL/src/power/SDL_power.c \
	SDL/src/render/SDL_d3dmath.c \
	SDL/src/render/SDL_render.c \
	SDL/src/render/SDL_yuv_sw.c \
	SDL/src/render/software/SDL_drawpoint.c \
	SDL/src/render/software/SDL_blendfillrect.c \
	SDL/src/render/software/SDL_blendline.c \
	SDL/src/render/software/SDL_blendpoint.c \
	SDL/src/render/software/SDL_drawline.c \
	SDL/src/render/software/SDL_render_sw.c \
	SDL/src/render/software/SDL_rotate.c \
	SDL/src/render/software/SDL_triangle.c \
	SDL/src/sensor/playdate/SDL_playdatesensor.c \
	SDL/src/sensor/SDL_sensor.c \
	SDL/src/stdlib/SDL_crc32.c \
	SDL/src/stdlib/SDL_getenv.c \
	SDL/src/stdlib/SDL_iconv.c \
	SDL/src/stdlib/SDL_qsort.c \
	SDL/src/stdlib/SDL_stdlib.c \
	SDL/src/stdlib/SDL_string.c \
	SDL/src/stdlib/SDL_strtokr.c \
	SDL/src/libm/e_atan2.c \
	SDL/src/libm/e_exp.c \
	SDL/src/libm/e_fmod.c \
	SDL/src/libm/e_log.c \
	SDL/src/libm/e_log10.c \
	SDL/src/libm/e_pow.c \
	SDL/src/libm/e_rem_pio2.c \
	SDL/src/libm/e_sqrt.c \
	SDL/src/libm/k_cos.c \
	SDL/src/libm/k_rem_pio2.c \
	SDL/src/libm/k_sin.c \
	SDL/src/libm/k_tan.c \
	SDL/src/libm/s_atan.c \
	SDL/src/libm/s_copysign.c \
	SDL/src/libm/s_cos.c \
	SDL/src/libm/s_fabs.c \
	SDL/src/libm/s_floor.c \
	SDL/src/libm/s_scalbn.c \
	SDL/src/libm/s_sin.c \
	SDL/src/libm/s_tan.c \
	SDL/src/thread/SDL_thread.c \
	SDL/src/thread/generic/SDL_syscond.c \
	SDL/src/thread/generic/SDL_sysmutex.c \
	SDL/src/thread/generic/SDL_syssem.c \
	SDL/src/thread/generic/SDL_systhread.c \
	SDL/src/thread/generic/SDL_systls.c \
	SDL/src/timer/SDL_timer.c \
	SDL/src/timer/playdate/SDL_systimer.c \
	SDL/src/video/SDL_blit.c \
	SDL/src/video/SDL_blit_0.c \
	SDL/src/video/SDL_blit_1.c \
	SDL/src/video/SDL_blit_A.c \
	SDL/src/video/SDL_blit_auto.c \
	SDL/src/video/SDL_blit_copy.c \
	SDL/src/video/SDL_blit_N.c \
	SDL/src/video/SDL_blit_slow.c \
	SDL/src/video/SDL_bmp.c \
	SDL/src/video/SDL_clipboard.c \
	SDL/src/video/SDL_egl.c \
	SDL/src/video/SDL_fillrect.c \
	SDL/src/video/SDL_pixels.c \
	SDL/src/video/SDL_rect.c \
	SDL/src/video/SDL_RLEaccel.c \
	SDL/src/video/SDL_shape.c \
	SDL/src/video/SDL_stretch.c \
	SDL/src/video/SDL_surface.c \
	SDL/src/video/SDL_video.c \
	SDL/src/video/SDL_vulkan_utils.c \
	SDL/src/video/SDL_yuv.c \
	SDL/src/video/yuv2rgb/yuv_rgb.c \
	SDL/src/video/playdate/SDL_playdate_events.c \
	SDL/src/video/playdate/SDL_playdate_framebuffer.c \
	SDL/src/video/playdate/SDL_playdate_video.c \
	main.c

ASRC = # setup.s

# List all user directories here
UINCDIR = SDL/include

# List all user C define here, like -D_DEBUG=1
UDEFS =

# Define ASM defines here
UADEFS =

# List the user directory to look for the libraries here
DINCDIR = SDL/include

# List all user libraries here
ULIBS =

CLANGFLAGS = -DPLAYDATE=1 # needed for SDL2

include $(SDK)/C_API/buildsupport/common.mk

SDL is just copied into the same directory as the project and then you compile and it should work. But I have not done much clean room testing on this to ensure it does work that easily.

ericlewis avatar Apr 06 '22 17:04 ericlewis

I have a device now. Let the fun begin!

ericlewis avatar May 06 '22 19:05 ericlewis

Is this draft still viable, or would it be better at this point to start from scratch?

moocow1452 avatar Apr 20 '24 20:04 moocow1452

This was for SDL2, before SDL2 moved off to a branch. The main branch is SDL3 now, which is why there are massive merge conflicts.

My suspicion is that this PR will work against the SDL2 branch just fine, but I don't think GitHub lets us retarget a PR to a different branch, short of just generating a new PR. I might take a run at doing this if I get a moment, if Eric doesn't want to, because I think this is a fun thing to have in SDL.

icculus avatar Apr 21 '24 13:04 icculus

My suspicion is that this PR will work against the SDL2 branch just fine, but I don't think GitHub lets us retarget a PR to a different branch, short of just generating a new PR. I might take a run at doing this if I get a moment, if Eric doesn't want to, because I think this is a fun thing to have in SDL.

The GitHub UI lets you change the target branch (just below the title). I changed the target branch to SDL2.

madebr avatar Apr 21 '24 13:04 madebr

The GitHub UI lets you change the target branch (just below the title).

Ohhhh, it's hidden behind the "edit" button (which I thought only changed the title). Nice one!

I'll see about resolving the SDL2 conflicts, then. :)

icculus avatar Apr 21 '24 13:04 icculus

Should this be an SDL3 feature?

slouken avatar Apr 21 '24 15:04 slouken

Well I just force-pushed the resolved version to eric's clone instead of mine by accident (yikes!) but this seems to have not mangled the PR, so that's good. I still need to fix the build up.

icculus avatar Apr 21 '24 21:04 icculus

Thank you for this huge work! I hope to see it in the upstream soon. ❤️‍🔥 I'll just put it here. If you need some easy & fast solution to setup Playdate SDK on CI - here I am with our cross-platform neat action. I hope it can be helpful for you.

boozook avatar Apr 22 '24 17:04 boozook

Using a fresh pull from Eric's branch with the custom Makefile specified in the previous comment, it seems to get part way through the compile, then chokes on an undeclared variable partway through SDL joystick.

user@user-reks:~/Projects/VVVVVV/third_party$ make
detected_OS is "Linux"
mkdir -p build
mkdir -p build/dep
mkdir -p `dirname build/SDL/src/joystick/playdate/SDL_sysjoystick.o`
/usr/bin/arm-none-eabi-gcc -g3 -c -mthumb -mcpu=cortex-m7 -mfloat-abi=hard -mfpu=fpv5-sp-d16 -D__FPU_USED=1 -O2 -falign-functions=16 -fomit-frame-pointer -gdwarf-2 -Wall -Wno-unused -Wstrict-prototypes -Wno-unknown-pragmas -fverbose-asm -Wdouble-promotion -mword-relocations -fno-common -ffunction-sections -fdata-sections -Wa,-ahlms=build/SDL_sysjoystick.lst -DTARGET_PLAYDATE=1 -DTARGET_EXTENSION=1  -MD -MP -MF build/dep/SDL_sysjoystick.o.d -I . -I . -I /home/user/Downloads/PlaydateSDK-2.4.2/C_API -I SDL/include SDL/src/joystick/playdate/SDL_sysjoystick.c -o build/SDL/src/joystick/playdate/SDL_sysjoystick.o
SDL/src/joystick/playdate/SDL_sysjoystick.c: In function 'PLAYDATE_JoystickUpdate':
SDL/src/joystick/playdate/SDL_sysjoystick.c:54:9: error: 'pd' undeclared (first use in this function)
   54 |         pd->system->getButtonState(&current, NULL, NULL);
      |         ^~
SDL/src/joystick/playdate/SDL_sysjoystick.c:54:9: note: each undeclared identifier is reported only once for each function it appears in
SDL/src/joystick/playdate/SDL_sysjoystick.c: At top level:
SDL/src/joystick/playdate/SDL_sysjoystick.c:198:5: warning: initialization of 'const char * (*)(int)' from incompatible pointer type 'int (*)(int)' [-Wincompatible-pointer-types]
  198 |     PLAYDATE_JoystickGetDevicePlayerIndex,
      |     ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
SDL/src/joystick/playdate/SDL_sysjoystick.c:198:5: note: (near initialization for 'SDL_PLAYDATE_JoystickDriver.GetDevicePath')
SDL/src/joystick/playdate/SDL_sysjoystick.c:199:5: warning: initialization of 'int (*)(int)' from incompatible pointer type 'void (*)(int,  int)' [-Wincompatible-pointer-types]
  199 |     PLAYDATE_JoystickSetDevicePlayerIndex,
      |     ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
SDL/src/joystick/playdate/SDL_sysjoystick.c:199:5: note: (near initialization for 'SDL_PLAYDATE_JoystickDriver.GetDeviceSteamVirtualGamepadSlot')
SDL/src/joystick/playdate/SDL_sysjoystick.c:200:5: warning: initialization of 'int (*)(int)' from incompatible pointer type 'SDL_JoystickGUID (*)(int)' {aka 'SDL_GUID (*)(int)'} [-Wincompatible-pointer-types]
  200 |     PLAYDATE_JoystickGetDeviceGUID,
      |     ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
SDL/src/joystick/playdate/SDL_sysjoystick.c:200:5: note: (near initialization for 'SDL_PLAYDATE_JoystickDriver.GetDevicePlayerIndex')
SDL/src/joystick/playdate/SDL_sysjoystick.c:201:5: warning: initialization of 'void (*)(int,  int)' from incompatible pointer type 'SDL_JoystickID (*)(int)' {aka 'long int (*)(int)'} [-Wincompatible-pointer-types]
  201 |     PLAYDATE_JoystickGetDeviceInstanceID,
      |     ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
SDL/src/joystick/playdate/SDL_sysjoystick.c:201:5: note: (near initialization for 'SDL_PLAYDATE_JoystickDriver.SetDevicePlayerIndex')
SDL/src/joystick/playdate/SDL_sysjoystick.c:202:5: warning: initialization of 'SDL_JoystickGUID (*)(int)' {aka 'SDL_GUID (*)(int)'} from incompatible pointer type 'int (*)(SDL_Joystick *, int)' {aka 'int (*)(struct _SDL_Joystick *, int)'} [-Wincompatible-pointer-types]
  202 |     PLAYDATE_JoystickOpen,
      |     ^~~~~~~~~~~~~~~~~~~~~
SDL/src/joystick/playdate/SDL_sysjoystick.c:202:5: note: (near initialization for 'SDL_PLAYDATE_JoystickDriver.GetDeviceGUID')
SDL/src/joystick/playdate/SDL_sysjoystick.c:203:5: warning: initialization of 'SDL_JoystickID (*)(int)' {aka 'long int (*)(int)'} from incompatible pointer type 'int (*)(SDL_Joystick *, Uint16,  Uint16)' {aka 'int (*)(struct _SDL_Joystick *, short unsigned int,  short unsigned int)'} [-Wincompatible-pointer-types]
  203 |     PLAYDATE_JoystickRumble,
      |     ^~~~~~~~~~~~~~~~~~~~~~~
SDL/src/joystick/playdate/SDL_sysjoystick.c:203:5: note: (near initialization for 'SDL_PLAYDATE_JoystickDriver.GetDeviceInstanceID')
SDL/src/joystick/playdate/SDL_sysjoystick.c:204:5: warning: initialization of 'int (*)(SDL_Joystick *, int)' {aka 'int (*)(struct _SDL_Joystick *, int)'} from incompatible pointer type 'int (*)(SDL_Joystick *, Uint16,  Uint16)' {aka 'int (*)(struct _SDL_Joystick *, short unsigned int,  short unsigned int)'} [-Wincompatible-pointer-types]
  204 |     PLAYDATE_JoystickRumbleTriggers,
      |     ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
SDL/src/joystick/playdate/SDL_sysjoystick.c:204:5: note: (near initialization for 'SDL_PLAYDATE_JoystickDriver.Open')
SDL/src/joystick/playdate/SDL_sysjoystick.c:205:5: warning: initialization of 'int (*)(SDL_Joystick *, Uint16,  Uint16)' {aka 'int (*)(struct _SDL_Joystick *, short unsigned int,  short unsigned int)'} from incompatible pointer type 'Uint32 (*)(SDL_Joystick *)' {aka 'long unsigned int (*)(struct _SDL_Joystick *)'} [-Wincompatible-pointer-types]
  205 |     PLAYDATE_JoystickGetCapabilities,
      |     ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
SDL/src/joystick/playdate/SDL_sysjoystick.c:205:5: note: (near initialization for 'SDL_PLAYDATE_JoystickDriver.Rumble')
SDL/src/joystick/playdate/SDL_sysjoystick.c:206:5: warning: initialization of 'int (*)(SDL_Joystick *, Uint16,  Uint16)' {aka 'int (*)(struct _SDL_Joystick *, short unsigned int,  short unsigned int)'} from incompatible pointer type 'int (*)(SDL_Joystick *, Uint8,  Uint8,  Uint8)' {aka 'int (*)(struct _SDL_Joystick *, unsigned char,  unsigned char,  unsigned char)'} [-Wincompatible-pointer-types]
  206 |     PLAYDATE_JoystickSetLED,
      |     ^~~~~~~~~~~~~~~~~~~~~~~
SDL/src/joystick/playdate/SDL_sysjoystick.c:206:5: note: (near initialization for 'SDL_PLAYDATE_JoystickDriver.RumbleTriggers')
SDL/src/joystick/playdate/SDL_sysjoystick.c:207:5: warning: initialization of 'Uint32 (*)(SDL_Joystick *)' {aka 'long unsigned int (*)(struct _SDL_Joystick *)'} from incompatible pointer type 'int (*)(SDL_Joystick *, const void *, int)' {aka 'int (*)(struct _SDL_Joystick *, const void *, int)'} [-Wincompatible-pointer-types]
  207 |     PLAYDATE_JoystickSendEffect,
      |     ^~~~~~~~~~~~~~~~~~~~~~~~~~~
SDL/src/joystick/playdate/SDL_sysjoystick.c:207:5: note: (near initialization for 'SDL_PLAYDATE_JoystickDriver.GetCapabilities')
SDL/src/joystick/playdate/SDL_sysjoystick.c:208:5: warning: initialization of 'int (*)(SDL_Joystick *, Uint8,  Uint8,  Uint8)' {aka 'int (*)(struct _SDL_Joystick *, unsigned char,  unsigned char,  unsigned char)'} from incompatible pointer type 'int (*)(SDL_Joystick *, SDL_bool)' {aka 'int (*)(struct _SDL_Joystick *, SDL_bool)'} [-Wincompatible-pointer-types]
  208 |     PLAYDATE_JoystickSetSensorsEnabled,
      |     ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
SDL/src/joystick/playdate/SDL_sysjoystick.c:208:5: note: (near initialization for 'SDL_PLAYDATE_JoystickDriver.SetLED')
SDL/src/joystick/playdate/SDL_sysjoystick.c:209:5: warning: initialization of 'int (*)(SDL_Joystick *, const void *, int)' {aka 'int (*)(struct _SDL_Joystick *, const void *, int)'} from incompatible pointer type 'void (*)(SDL_Joystick *)' {aka 'void (*)(struct _SDL_Joystick *)'} [-Wincompatible-pointer-types]
  209 |     PLAYDATE_JoystickUpdate,
      |     ^~~~~~~~~~~~~~~~~~~~~~~
SDL/src/joystick/playdate/SDL_sysjoystick.c:209:5: note: (near initialization for 'SDL_PLAYDATE_JoystickDriver.SendEffect')
SDL/src/joystick/playdate/SDL_sysjoystick.c:210:5: warning: initialization of 'int (*)(SDL_Joystick *, SDL_bool)' {aka 'int (*)(struct _SDL_Joystick *, SDL_bool)'} from incompatible pointer type 'void (*)(SDL_Joystick *)' {aka 'void (*)(struct _SDL_Joystick *)'} [-Wincompatible-pointer-types]
  210 |     PLAYDATE_JoystickClose,
      |     ^~~~~~~~~~~~~~~~~~~~~~
SDL/src/joystick/playdate/SDL_sysjoystick.c:210:5: note: (near initialization for 'SDL_PLAYDATE_JoystickDriver.SetSensorsEnabled')
SDL/src/joystick/playdate/SDL_sysjoystick.c:211:5: warning: initialization of 'void (*)(SDL_Joystick *)' {aka 'void (*)(struct _SDL_Joystick *)'} from incompatible pointer type 'void (*)(void)' [-Wincompatible-pointer-types]
  211 |     PLAYDATE_JoystickQuit,
      |     ^~~~~~~~~~~~~~~~~~~~~
SDL/src/joystick/playdate/SDL_sysjoystick.c:211:5: note: (near initialization for 'SDL_PLAYDATE_JoystickDriver.Update')
SDL/src/joystick/playdate/SDL_sysjoystick.c:212:5: warning: initialization of 'void (*)(SDL_Joystick *)' {aka 'void (*)(struct _SDL_Joystick *)'} from incompatible pointer type 'SDL_bool (*)(int,  SDL_GamepadMapping *)' {aka 'SDL_bool (*)(int,  struct _SDL_GamepadMapping *)'} [-Wincompatible-pointer-types]
  212 |     PLAYDATE_JoystickGetGamepadMapping
      |     ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
SDL/src/joystick/playdate/SDL_sysjoystick.c:212:5: note: (near initialization for 'SDL_PLAYDATE_JoystickDriver.Close')
make: *** [/home/user/Downloads/PlaydateSDK-2.4.2/C_API/buildsupport/common.mk:149: build/SDL/src/joystick/playdate/SDL_sysjoystick.o] Error 1

Would this be that hacking around that would be required from earlier or is that makefile unnecessary to make playdate compatible libraries?

EDIT: Additionally, I attempted to compile traditionally via cmake using the argument -DCMAKE_TOOLCHAIN_FILE=<path_to_playdate_SDK>/C_API/buildsupport/arm.cmake from https://github.com/nstbayless/playdate-cpp

That gave me this error:

  *** ERROR: Threads are needed by many SDL subsystems and may not be                                                    
  disabled                                                                                                               
Call Stack (most recent call first):                                                                                     
  CMakeLists.txt:3051 (message_error)   

Which lead me to https://github.com/libsdl-org/SDL/issues/6344, implying we would need a custom makefile or some tweaking in the setup to get going.

moocow1452 avatar May 11 '24 21:05 moocow1452

Figured out the pd issue, needed to add extern PlaydateAPI* pd to the required files and tie up loose ends for the temp makefile. It segmentation faults on trying to compile everything, but I don't have the original main.c that @ericlewis was using in his file, so that's expected. I made my own pull request on Eric's repo to track my progress here so I won't flood this pull with minor updates. Thanks for the help and I'll touch base once things look ship shape.

moocow1452 avatar May 26 '24 05:05 moocow1452

This hasn't been updated in a while and we're cleaning house for SDL 3.0. Please feel free to reopen this if you'd like to clean it up and get it in!

slouken avatar Aug 06 '24 15:08 slouken