ref: 979ae53cfee913c2d303ab0f95aaa65dace1c7b5
parent: 2aee89298cbbf5c1b4d50e70733b52ae75c706fd
author: qwx <qwx@sciops.net>
date: Tue Feb 17 19:34:19 EST 2026
wip sdl3 port
--- /dev/null
+++ b/include/npe/SDL3/SDL.h
@@ -1,0 +1,298 @@
+#ifndef _npe_SDL_h_
+#define _npe_SDL_h_
+
+#pragma lib "libnpe_sdl3.a"
+
+#include <npe.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+typedef u8int Uint8;
+typedef u16int Uint16;
+typedef u32int Uint32;
+typedef u64int Uint64;
+typedef s8int Sint8;
+typedef s16int Sint16;
+typedef s32int Sint32;
+typedef s64int Sint64;
+typedef enum {SDL_FALSE, SDL_TRUE} SDL_bool;+
+typedef struct SDL_Window SDL_Window;
+typedef int SDL_BlendMode;
+typedef struct SDL_Cursor SDL_Cursor;
+typedef struct SDL_Point SDL_Point;
+typedef int SDL_RendererFlip;
+typedef struct SDL_DisplayMode SDL_DisplayMode;
+typedef int SDL_SystemCursor;
+typedef struct SDL_Color SDL_Color;
+typedef struct SDL_Palette SDL_Palette;
+typedef struct SDL_RendererInfo SDL_RendererInfo;
+
+struct SDL_Color {+ Uint8 r;
+ Uint8 g;
+ Uint8 b;
+ Uint8 a;
+};
+
+struct SDL_Palette {+ int ncolors;
+ SDL_Color *colors;
+};
+
+#pragma incomplete SDL_Cursor
+#pragma incomplete SDL_Window
+
+#define SDLCALL
+
+#define SDL_zero(x) do{ memset(&(x), 0, sizeof(x)); }while(0)+#define SDL_atoi atoi
+#define SDL_memset memset
+#define SDL_malloc malloc
+#define SDL_realloc realloc
+#define SDL_calloc calloc
+#define SDL_free free
+#define SDL_memcpy memcpy
+#define SDL_memmove memmove
+#define SDL_memcmp memcmp
+#define SDL_printf printf
+#define SDL_snprintf snprintf
+#define SDL_strcmp strcmp
+#define SDL_strcasecmp cistrcmp
+#define SDL_strdup strdup
+#define SDL_strlen strlen
+#define SDL_strlcpy strlcpy
+#define SDL_strstr strstr
+#define SDL_strncmp strncmp
+
+size_t SDL_strlcpy(char *dst, const char *src, size_t maxlen);
+char* SDL_strcasestr(const char *haystack, const char *needle);
+
+#include <SDL3/SDL_init.h>
+#include <SDL3/SDL_mutex.h>
+#include <SDL3/SDL_iostream.h>
+#include <SDL3/SDL_keycode.h>
+#include <SDL3/SDL_scancode.h>
+#include <SDL3/SDL_keyboard.h>
+#include <SDL3/SDL_audio.h>
+#include <SDL3/SDL_joystick.h>
+#include <SDL3/SDL_gamepad.h>
+#include <SDL3/SDL_thread.h>
+#include <SDL3/SDL_quit.h>
+#include <SDL3/SDL_version.h>
+#include <SDL3/SDL_video.h>
+#include <SDL3/SDL_log.h>
+#include <SDL3/SDL_render.h>
+#include <SDL3/SDL_surface.h>
+#include <SDL3/SDL_properties.h>
+#include <SDL3/SDL_events.h>
+
+void SDL_StopTextInput(void);
+SDL_bool SDL_HasSSE(void);
+SDL_bool SDL_HasSSE2(void);
+bool SDL_Init(int);
+bool SDL_InitSubSystem(int);
+bool SDL_QuitSubSystem(int);
+int SDL_SetRelativeMouseMode(SDL_bool enabled);
+int SDL_GetRelativeMouseMode(void);
+void SDL_SetWindowIcon(SDL_Window*,SDL_Surface*);
+void SDL_SetWindowBordered(SDL_Window*,SDL_bool);
+SDL_Keymod SDL_GetModState(void);
+int SDL_ShowCursor(void);
+bool SDL_HideCursor(void);
+Uint64 SDL_GetPerformanceFrequency(void);
+Uint64 SDL_GetPerformanceCounter(void);
+char *SDL_GetError(void);
+char *SDL_GetClipboardText(void);
+int SDL_SetClipboardText(char *);
+SDL_bool SDL_HasClipboardText(void);
+void SDL_RestoreWindow(SDL_Window *window);
+void SDL_RaiseWindow(SDL_Window *window);
+int SDL_GetWindowDisplayIndex(SDL_Window *window);
+Uint32 SDL_GetGlobalMouseState(int *x, int *y);
+void SDL_Quit(void);
+void SDL_free(void *);
+SDL_Cursor *SDL_GetDefaultCursor(void);
+void SDL_SetCursor(SDL_Cursor *cursor);
+void SDL_FreeCursor(SDL_Cursor *cursor);
+void SDL_GetRGB(Uint32 pixel, const SDL_PixelFormat *format, Uint8 *r, Uint8 *g, Uint8 *b);
+void SDL_WarpMouseInWindow(SDL_Window *window, int x, int y);
+void SDL_GetWindowSize(SDL_Window *window, int *w, int *h);
+void SDL_GetWindowPosition(SDL_Window *window, int *x, int *y);
+void SDL_SetWindowMinimumSize(SDL_Window *window, int min_w, int min_h);
+Uint32 SDL_GetWindowPixelFormat(SDL_Window *window);
+SDL_bool SDL_PixelFormatEnumToMasks(Uint32 format, int *bpp, Uint32 *Rmask, Uint32 *Gmask, Uint32 *Bmask, Uint32 *Amask);
+Uint32 SDL_GetRelativeMouseState(int *x, int *y);
+Uint32 SDL_GetMouseState(int *x, int *y);
+SDL_bool SDL_IsTextInputActive(void);
+void SDL_StartTextInput(void);
+void SDL_Delay(Uint32 ms);
+void SDL_SetMainReady(void);
+Uint32 SDL_GetWindowFlags(SDL_Window *window);
+void SDL_SetWindowSize(SDL_Window *window, int w, int h);
+int SDL_ShowSimpleMessageBox(Uint32 flags, char *title, char *message, SDL_Window *window);
+int SDL_SetWindowFullscreen(SDL_Window *window, Uint32 flags);
+void SDL_SetWindowGrab(SDL_Window *window, SDL_bool grabbed);
+void SDL_SetWindowPosition(SDL_Window *window, int x, int y);
+void SDL_DestroyWindow(SDL_Window *window);
+int SDL_GetDisplayUsableBounds(int displayIndex, SDL_Rect *rect);
+int SDL_GetDisplayBounds(int displayIndex, SDL_Rect *rect);
+int SDL_GetDesktopDisplayMode(int displayIndex, SDL_DisplayMode *mode);
+void SDL_SetWindowTitle(SDL_Window *window, char *title);
+SDL_bool SDL_SetHint(char *name, char *value);
+SDL_Window *SDL_CreateWindow(char *title, int x, int y, int w, int h, Uint32 flags);
+char *SDL_GetCurrentVideoDriver(void);
+void SDL_EnableScreenSaver(void);
+Uint32 SDL_GetTicks(void);
+void SDL_ClearError(void);
+int SDL_OpenURL(char *url);
+SDL_Cursor *SDL_CreateSystemCursor(SDL_SystemCursor id);
+char *SDL_GetBasePath(void);
+char *SDL_GetPrefPath(char *org, char *app);
+int SDL_GetCurrentDisplayMode(int displayIndex, SDL_DisplayMode *mode);
+int SDL_GetNumDisplayModes(int displayIndex);
+void SDL_ShowWindow(SDL_Window *w);
+int SDL_GetNumVideoDisplays(void);
+void SDL_SetModState(SDL_Keymod modstate);
+int SDL_FillRect(SDL_Surface *dst, const SDL_Rect *rect, Uint32 color);
+SDL_Palette *SDL_AllocPalette(int ncolors);
+int SDL_SetPaletteColors(SDL_Palette *palette, const SDL_Color *colors, int firstcolor, int ncolors);
+int SDL_GetWindowBordersSize(SDL_Window *window, int *top, int *left, int *bot, int *right);
+
+#define SDL_min(x, y) (((x) < (y)) ? (x) : (y))
+#define SDL_max(x, y) (((x) > (y)) ? (x) : (y))
+#define SDL_abs(x) ((x) < 0 ? -(x) : (x))
+#define SDL_cosf(x) cos(x)
+#define SDL_sinf(x) sin(x)
+#define SDL_sqrtf(x) sqrt(x)
+#define SDL_atanf(x) atan(x)
+
+enum {+ SDL_QUERY = -1,
+ SDL_DISABLE,
+ SDL_ENABLE,
+
+ SDL_MESSAGEBOX_ERROR = 0,
+
+ SDL_WINDOW_MINIMIZED = 1<<0,
+ SDL_WINDOW_FULLSCREEN_DESKTOP = 1<<1,
+ SDL_WINDOW_INPUT_FOCUS = 1<<2,
+ SDL_WINDOW_ALLOW_HIGHDPI = 1<<3,
+ SDL_WINDOW_SHOWN = 1<<4,
+ SDL_WINDOW_RESIZABLE = 1<<5,
+ SDL_WINDOW_HIDDEN = 1<<6,
+ SDL_WINDOW_MAXIMIZED = 1<<7,
+ SDL_WINDOW_FULLSCREEN = 1<<8,
+ SDL_WINDOW_BORDERLESS = 1<<9,
+
+ SDL_WINDOWPOS_CENTERED = -1,
+ SDL_WINDOWPOS_UNDEFINED = -2,
+
+ SDL_INIT_TIMER = 1<<0,
+ SDL_INIT_AUDIO = 1<<1,
+ SDL_INIT_VIDEO = 1<<2,
+ SDL_INIT_GAMEPAD = 1<<13,
+ SDL_INIT_EVENTS = 1<<14,
+ SDL_INIT_JOYSTICK = 0,
+ SDL_INIT_GAMECONTROLLER = 0,
+
+ SDL_BLENDMODE_NONE = 0,
+ SDL_BLENDMODE_BLEND,
+
+ SDL_FLIP_NONE = 0,
+ SDL_FLIP_HORIZONTAL,
+ SDL_FLIP_VERTICAL,
+
+ SDL_PIXELFORMAT_ARGB8888 = 0x30128888,
+ SDL_PIXELFORMAT_XRGB8888 = 0x16161804,
+ SDL_PIXELFORMAT_INDEX8 = 0x13000801,
+ SDL_PIXELFORMAT_RGB24 = 0x17101803,
+ SDL_PIXELFORMAT_ABGR8888 = 0x16762004,
+ SDL_PIXELFORMAT_XBGR8888 = 0x16561804,
+ SDL_PIXELFORMAT_BGR24 = 0x17401803,
+ SDL_PIXELFORMAT_RGB888 = SDL_PIXELFORMAT_XRGB8888,
+ SDL_PIXELFORMAT_BGR888 = SDL_PIXELFORMAT_XBGR8888,
+
+ /* shit no one cares about */
+ SDL_TEXTUREACCESS_STREAMING = 0,
+ SDL_TEXTUREACCESS_STATIC = 0,
+ SDL_TEXTUREACCESS_TARGET = 0,
+ SDL_RENDERER_ACCELERATED = 0,
+ SDL_RENDERER_PRESENTVSYNC = 0,
+ SDL_RENDERER_TARGETTEXTURE = 0x8,
+ SDL_INIT_NOPARACHUTE = 0,
+ SDL_RENDERER_SOFTWARE = 0,
+ SDL_SWSURFACE = 0,
+
+ /* FIXME steal from rio and add missing? */
+ SDL_SYSTEM_CURSOR_ARROW = 0,
+ SDL_SYSTEM_CURSOR_IBEAM,
+ SDL_SYSTEM_CURSOR_WAIT,
+ SDL_SYSTEM_CURSOR_CROSSHAIR,
+ SDL_SYSTEM_CURSOR_WAITARROW,
+ SDL_SYSTEM_CURSOR_SIZENWSE,
+ SDL_SYSTEM_CURSOR_SIZENESW,
+ SDL_SYSTEM_CURSOR_SIZEWE,
+ SDL_SYSTEM_CURSOR_SIZENS,
+
+ SDL_ALPHA_OPAQUE = 0xff,
+ SDL_ALPHA_TRANSPARENT = 0x00,
+};
+
+enum {+ SDL_BUTTON_LEFT = 0,
+ SDL_BUTTON_MIDDLE = 1,
+ SDL_BUTTON_RIGHT = 2,
+
+ SDL_BUTTON_LMASK = 1<<SDL_BUTTON_LEFT,
+ SDL_BUTTON_MMASK = 1<<SDL_BUTTON_MIDDLE,
+ SDL_BUTTON_RMASK = 1<<SDL_BUTTON_RIGHT,
+
+ KMOD_NONE = 0,
+};
+
+
+#define SDL_BUTTON(x) (1<<(x))
+
+#define SDL_MUSTLOCK(surface) (SDL_FALSE)
+
+#define SDL_HINT_RENDER_SCALE_QUALITY "SDL_HINT_RENDER_SCALE_QUALITY"
+#define SDL_HINT_WINDOWS_NO_CLOSE_ON_ALT_F4 "SDL_WINDOWS_NO_CLOSE_ON_ALT_F4"
+#define SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH "SDL_MOUSE_FOCUS_CLICKTHROUGH"
+#define SDL_HINT_WINDOWS_DISABLE_THREAD_NAMING "SDL_WINDOWS_DISABLE_THREAD_NAMING"
+#define SDL_HINT_RENDER_VSYNC "SDL_RENDER_VSYNC"
+#define SDL_HINT_VIDEO_ALLOW_SCREENSAVER "SDL_VIDEO_ALLOW_SCREENSAVER"
+#define SDL_HINT_MAIN_CALLBACK_RATE "SDL_MAIN_CALLBACK_RATE"
+#define SDL_HINT_IOS_HIDE_HOME_INDICATOR "SDL_IOS_HIDE_HOME_INDICATOR"
+#define SDL_HINT_AUDIO_DEVICE_SAMPLE_FRAMES "SDL_AUDIO_DEVICE_SAMPLE_FRAMES"
+
+struct SDL_Point {+ int x, y;
+};
+
+struct SDL_DisplayMode {+ int format;
+ int w;
+ int h;
+ int refresh_rate;
+};
+
+struct SDL_RendererInfo {+ int max_texture_width;
+ int max_texture_height;
+};
+
+typedef enum {+ SDL_HITTEST_NORMAL,
+ SDL_HITTEST_DRAGGABLE,
+ SDL_HITTEST_RESIZE_TOPLEFT,
+ SDL_HITTEST_RESIZE_TOP,
+ SDL_HITTEST_RESIZE_TOPRIGHT,
+ SDL_HITTEST_RESIZE_RIGHT,
+ SDL_HITTEST_RESIZE_BOTTOMRIGHT,
+ SDL_HITTEST_RESIZE_BOTTOM,
+ SDL_HITTEST_RESIZE_BOTTOMLEFT,
+ SDL_HITTEST_RESIZE_LEFT,
+} SDL_HitTestResult;
+
+#endif
--- /dev/null
+++ b/include/npe/SDL3/SDL_audio.h
@@ -1,0 +1,78 @@
+#ifndef _npe_SDL_audio_h_
+#define _npe_SDL_audio_h_
+
+/* FIXME: clean up old shit */
+
+typedef enum SDL_AudioFormat {+ SDL_AUDIO_U8 = 1,
+ SDL_AUDIO_S8,
+ SDL_AUDIO_S16LE,
+ SDL_AUDIO_S16BE,
+ SDL_AUDIO_S32LE,
+ SDL_AUDIO_S32BE,
+ SDL_AUDIO_F32LE,
+ SDL_AUDIO_F32BE, /* FIXME not supported */
+ /* show me that BIG endian device of yours */
+ SDL_AUDIO_S16 = SDL_AUDIO_S16LE,
+ SDL_AUDIO_S32 = SDL_AUDIO_S32LE,
+ SDL_AUDIO_F32 = SDL_AUDIO_F32LE,
+} SDL_AudioFormat;
+
+typedef struct SDL_AudioSpec SDL_AudioSpec;
+typedef int SDL_AudioDeviceID;
+typedef struct SDL_AudioStream SDL_AudioStream;
+
+#pragma incomplete SDL_AudioStream
+
+typedef void (*SDL_AudioStreamCallback)(void *userdata, SDL_AudioStream *stream, int additional_amount, int total_amount);
+
+extern SDL_AudioDeviceID SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK;
+extern SDL_AudioDeviceID SDL_AUDIO_DEVICE_DEFAULT_RECORDING;
+
+/* fuck this and fuck you */
+//#define SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK ((SDL_AudioDeviceID) 0xFFFFFFFFu)
+//#define SDL_AUDIO_DEVICE_DEFAULT_RECORDING ((SDL_AudioDeviceID) 0xFFFFFFFEu)
+
+struct SDL_AudioSpec {+ SDL_AudioFormat format;
+ int channels;
+ int freq;
+};
+
+int SDL_GetNumAudioDevices(int);
+
+void SDL_LockAudioDevice(SDL_AudioDeviceID);
+void SDL_UnlockAudioDevice(SDL_AudioDeviceID);
+
+SDL_AudioDeviceID SDL_OpenAudioDevice(SDL_AudioDeviceID devid, const SDL_AudioSpec *spec);
+void SDL_PauseAudioDevice(SDL_AudioDeviceID, SDL_bool);
+void SDL_CloseAudioDevice(SDL_AudioDeviceID);
+
+void SDL_PauseAudio(int pause_on);
+
+typedef struct SDL_AudioCVT SDL_AudioCVT;
+struct SDL_AudioCVT {+ SDL_AudioFormat src_format;
+ SDL_AudioFormat dst_format;
+ Uint8 *buf;
+ int len;
+ int len_mult;
+};
+
+int SDL_BuildAudioCVT(SDL_AudioCVT *cvt, SDL_AudioFormat src_format, Uint8 src_channels, int src_rate, SDL_AudioFormat dst_format, Uint8 dst_channels, int dst_rate);
+int SDL_ConvertAudio(SDL_AudioCVT *cvt);
+
+int SDL_GetAudioStreamAvailable(SDL_AudioStream *stream);
+int SDL_GetAudioStreamData(SDL_AudioStream *stream, void *buf, int len);
+bool SDL_PutAudioStreamData(SDL_AudioStream *stream, const void *buf, int len);
+bool SDL_ResumeAudioStreamDevice(SDL_AudioStream *stream);
+bool SDL_PauseAudioStreamDevice(SDL_AudioStream *stream);
+SDL_AudioDeviceID* SDL_GetAudioRecordingDevices(int *count);
+const char* SDL_GetAudioDeviceName(SDL_AudioDeviceID devid);
+SDL_AudioDeviceID* SDL_GetAudioPlaybackDevices(int *count);
+SDL_AudioStream* SDL_OpenAudioDeviceStream(SDL_AudioDeviceID devid, const SDL_AudioSpec *spec, SDL_AudioStreamCallback callback, void *userdata);
+bool SDL_GetAudioDeviceFormat(SDL_AudioDeviceID devid, SDL_AudioSpec *spec, int *sample_frames);
+bool SDL_SetAudioStreamFormat(SDL_AudioStream *stream, const SDL_AudioSpec *src_spec, const SDL_AudioSpec *dst_spec);
+void SDL_DestroyAudioStream(SDL_AudioStream *stream);
+
+#endif
--- /dev/null
+++ b/include/npe/SDL3/SDL_endian.h
@@ -1,0 +1,60 @@
+#ifndef _npe_SDL_endian_h_
+#define _npe_SDL_endian_h_
+
+#define SDL_LIL_ENDIAN 1234
+#define SDL_BIG_ENDIAN 4321
+
+#if defined(__amd64__) || defined(__386__) || defined(__arm__) || defined(__arm64__) || defined(__spim__)
+#define SDL_BYTEORDER SDL_LIL_ENDIAN
+#elif defined(__mips__) || defined(__power__)
+#define SDL_BYTEORDER SDL_BIG_ENDIAN
+#endif
+
+static u16int
+SDL_Swap16(u16int x)
+{+ return (x<<8) | (x>>8);
+}
+
+static u32int
+SDL_Swap32(u32int x)
+{+ return ((x << 24) | ((x << 8) & 0x00FF0000) | ((x >> 8) & 0x0000FF00) | (x >> 24));
+}
+
+static u64int
+SDL_Swap64(u64int x)
+{+ u32int hi, lo;
+
+ /* Separate into high and low 32-bit values and swap them */
+ lo = x & 0xFFFFFFFF;
+ x >>= 32;
+ hi = x & 0xFFFFFFFF;
+ x = SDL_Swap32(lo);
+ x <<= 32;
+ x |= SDL_Swap32(hi);
+ return (x);
+}
+
+#if SDL_BYTEORDER == SDL_LIL_ENDIAN
+#define SDL_SwapLE16(X) (X)
+#define SDL_SwapLE32(X) (X)
+#define SDL_SwapLE64(X) (X)
+#define SDL_SwapFloatLE(X) (X)
+#define SDL_SwapBE16(X) SDL_Swap16(X)
+#define SDL_SwapBE32(X) SDL_Swap32(X)
+#define SDL_SwapBE64(X) SDL_Swap64(X)
+#define SDL_SwapFloatBE(X) SDL_SwapFloat(X)
+#else
+#define SDL_SwapLE16(X) SDL_Swap16(X)
+#define SDL_SwapLE32(X) SDL_Swap32(X)
+#define SDL_SwapLE64(X) SDL_Swap64(X)
+#define SDL_SwapFloatLE(X) SDL_SwapFloat(X)
+#define SDL_SwapBE16(X) (X)
+#define SDL_SwapBE32(X) (X)
+#define SDL_SwapBE64(X) (X)
+#define SDL_SwapFloatBE(X) (X)
+#endif
+
+#endif
--- /dev/null
+++ b/include/npe/SDL3/SDL_events.h
@@ -1,0 +1,233 @@
+#ifndef _npe_SDL_events_h_
+#define _npe_SDL_events_h_
+
+#include <SDL3/SDL.h>
+
+/* FIXME: cleanup: remove old shit */
+enum {+ SDL_FIRSTEVENT,
+ SDL_KEYDOWN,
+ SDL_KEYUP,
+ SDL_JOYAXISMOTION,
+ SDL_JOYBUTTONDOWN,
+ SDL_JOYBUTTONUP,
+ SDL_MOUSEBUTTONDOWN,
+ SDL_MOUSEBUTTONUP,
+ SDL_MOUSEWHEEL,
+ SDL_MOUSEMOTION,
+ SDL_QUIT,
+ SDL_DROPFILE,
+ SDL_TEXTINPUT,
+ SDL_WINDOWEVENT,
+ SDL_WINDOWEVENT_HIDDEN,
+ SDL_WINDOWEVENT_SHOWN,
+ SDL_WINDOWEVENT_FOCUS_LOST,
+ SDL_WINDOWEVENT_FOCUS_GAINED,
+ SDL_WINDOWEVENT_MOVED,
+ SDL_WINDOWEVENT_EXPOSED,
+ SDL_WINDOWEVENT_SIZE_CHANGED,
+ SDL_WINDOWEVENT_RESIZED = SDL_WINDOWEVENT_SIZE_CHANGED, /* FIXME I don't even fucking know... */
+ SDL_WINDOWEVENT_MINIMIZED,
+ SDL_WINDOWEVENT_MAXIMIZED,
+ SDL_WINDOWEVENT_RESTORED,
+ SDL_WINDOWEVENT_ENTER,
+ SDL_WINDOWEVENT_LEAVE,
+ SDL_WINDOWEVENT_CLOSE,
+ SDL_JOYBALLMOTION,
+ SDL_JOYHATMOTION,
+ SDL_LASTEVENT,
+
+ SDL_PRESSED = SDL_KEYDOWN,
+ SDL_RELEASED = SDL_KEYUP,
+
+ SDL_TEXTINPUTEVENT_TEXT_SIZE = UTFmax,
+
+ SDL_ADDEVENT = 0,
+ SDL_PEEKEVENT,
+ SDL_GETEVENT,
+
+ SDL_HAT_LEFTUP = 0,
+ SDL_HAT_LEFT,
+ SDL_HAT_LEFTDOWN,
+ SDL_HAT_UP,
+ SDL_HAT_CENTERED,
+ SDL_HAT_DOWN,
+ SDL_HAT_RIGHTUP,
+ SDL_HAT_RIGHT,
+ SDL_HAT_RIGHTDOWN,
+};
+
+typedef struct SDL_WindowEvent SDL_WindowEvent;
+typedef struct SDL_MouseWheelEvent SDL_MouseWheelEvent;
+typedef struct SDL_MouseButtonEvent SDL_MouseButtonEvent;
+typedef struct SDL_Keysym SDL_Keysym;
+typedef struct SDL_KeyboardEvent SDL_KeyboardEvent;
+typedef struct SDL_GamepadAxisEvent SDL_GamepadAxisEvent;
+typedef struct SDL_GamepadButtonEvent SDL_GamepadButtonEvent;
+typedef int SDL_eventaction;
+
+struct SDL_WindowEvent {+ Uint32 type;
+ Uint32 timestamp;
+ Uint32 windowID;
+ Uint8 event;
+ Sint32 data1;
+ Sint32 data;
+};
+
+struct SDL_Keysym {+ SDL_Scancode scancode;
+ SDL_Keycode sym;
+ Uint16 mod;
+};
+
+struct SDL_MouseWheelEvent {+ Uint32 type;
+ Uint32 timestamp;
+ Uint32 windowID;
+ Uint32 which;
+ Sint32 x;
+ Sint32 y;
+ Uint32 direction;
+};
+
+struct SDL_MouseButtonEvent {+ Uint32 type;
+ Uint32 timestamp;
+ Uint32 windowID;
+ Uint32 which;
+ Uint8 button;
+ Uint8 state;
+ Uint8 clicks;
+ Sint32 x;
+ Sint32 y;
+};
+
+typedef enum SDL_EventType {+ SDL_EVENT_QUIT = 0x100,
+ SDL_EVENT_TERMINATING,
+ SDL_EVENT_LOW_MEMORY,
+ SDL_EVENT_WILL_ENTER_BACKGROUND,
+ SDL_EVENT_DID_ENTER_BACKGROUND,
+ SDL_EVENT_WILL_ENTER_FOREGROUND,
+ SDL_EVENT_DID_ENTER_FOREGROUND,
+ SDL_EVENT_WINDOW_SHOWN = 0x202,
+ SDL_EVENT_WINDOW_HIDDEN,
+ SDL_EVENT_WINDOW_EXPOSED,
+ SDL_EVENT_WINDOW_MOVED,
+ SDL_EVENT_WINDOW_RESIZED,
+ SDL_EVENT_KEY_DOWN = 0x300,
+ SDL_EVENT_KEY_UP,
+ SDL_EVENT_GAMEPAD_AXIS_MOTION = 0x650,
+ SDL_EVENT_GAMEPAD_BUTTON_DOWN,
+ SDL_EVENT_GAMEPAD_BUTTON_UP,
+ SDL_EVENT_GAMEPAD_ADDED,
+ SDL_EVENT_GAMEPAD_REMOVED,
+ SDL_EVENT_USER = 0x8000,
+ SDL_EVENT_LAST = 0xFFFF,
+} SDL_EventType;
+
+struct SDL_KeyboardEvent {+ SDL_EventType type; /**< SDL_EVENT_KEY_DOWN or SDL_EVENT_KEY_UP */
+ //Uint32 reserved;
+ Uint64 timestamp; /**< In nanoseconds, populated using SDL_GetTicksNS() */
+ SDL_WindowID windowID; /**< The window with keyboard focus, if any */
+ SDL_KeyboardID which; /**< The keyboard instance id, or 0 if unknown or virtual */
+ SDL_Scancode scancode; /**< SDL physical key code */
+ SDL_Keycode key; /**< SDL virtual key code */
+ SDL_Keymod mod; /**< current key modifiers */
+ Rune raw; /**< The platform dependent scancode for this event */
+ bool down; /**< true if the key is pressed */
+ bool repeat; /**< true if this is a key repeat */
+};
+struct SDL_GamepadAxisEvent {+ SDL_EventType type; /**< SDL_EVENT_GAMEPAD_AXIS_MOTION */
+ //Uint32 reserved;
+ Uint64 timestamp; /**< In nanoseconds, populated using SDL_GetTicksNS() */
+ SDL_JoystickID which; /**< The joystick instance id */
+ Uint8 axis; /**< The gamepad axis (SDL_GamepadAxis) */
+ //Uint8 padding1;
+ //Uint8 padding2;
+ //Uint8 padding3;
+ Sint16 value; /**< The axis value (range: -32768 to 32767) */
+ //Uint16 padding4;
+};
+struct SDL_GamepadButtonEvent {+ SDL_EventType type; /**< SDL_EVENT_GAMEPAD_BUTTON_DOWN or SDL_EVENT_GAMEPAD_BUTTON_UP */
+ //Uint32 reserved;
+ Uint64 timestamp; /**< In nanoseconds, populated using SDL_GetTicksNS() */
+ SDL_JoystickID which; /**< The joystick instance id */
+ Uint8 button; /**< The gamepad button (SDL_GamepadButton) */
+ bool down; /**< true if the button is pressed */
+ //Uint8 padding1;
+ //Uint8 padding2;
+};
+typedef union SDL_Event {+ Uint32 type;
+ SDL_KeyboardEvent key;
+ SDL_GamepadAxisEvent gaxis;
+ SDL_GamepadButtonEvent gbutton;
+ //Uint8 padding[128];
+} SDL_Event;
+
+typedef int (*SDL_EventFilter)(void *userdata, SDL_Event *event);
+
+/*
+struct SDL_Event {+ int type;
+ SDL_WindowEvent window;
+ struct {+ SDL_Keysym keysym;
+ int repeat;
+ int state;
+ }key;
+ SDL_MouseButtonEvent button;
+ struct {+ int button;
+ int state;
+ }cbutton;
+ struct {+ int button;
+ }jbutton;
+ struct {+ int value;
+ int axis;
+ }jaxis;
+ struct {+ int xrel;
+ int yrel;
+ }jball;
+ struct {+ int hat;
+ int value;
+ }jhat;
+ struct {+ int x, y;
+ int xrel, yrel;
+ int state;
+ }motion;
+ struct {+ char text[SDL_TEXTINPUTEVENT_TEXT_SIZE+1];
+ }text;
+ SDL_MouseWheelEvent wheel;
+ struct {+ char *file;
+ }drop;
+};
+*/
+
+int SDL_EventState(Uint32, int);
+int SDL_PollEvent(SDL_Event *event);
+int SDL_PushEvent(SDL_Event *event);
+int SDL_WaitEvent(SDL_Event *event);
+Uint32 SDL_RegisterEvents(int);
+int SDL_PeepEvents(SDL_Event *events, int numevents, SDL_eventaction action, Uint32 minType, Uint32 maxType);
+Uint32 SDL_GetWindowID(SDL_Window *window);
+void SDL_PumpEvents(void);
+void SDL_SetEventFilter(SDL_EventFilter filter, void *userdata);
+SDL_Scancode SDL_GetScancodeFromKey(SDL_Keycode key);
+Uint8* SDL_GetKeyboardState(int *numkeys);
+char* SDL_GetKeyName(SDL_Keycode key);
+SDL_Keycode SDL_GetKeyFromName(char *name);
+
+#endif
--- /dev/null
+++ b/include/npe/SDL3/SDL_gamepad.h
@@ -1,0 +1,89 @@
+#ifndef _npe_SDL_gamepad_h_
+#define _npe_SDL_gamepad_h_
+
+typedef struct SDL_Gamepad SDL_Gamepad;
+
+#pragma incomplete SDL_Gamepad
+
+typedef enum SDL_GamepadButton {+ SDL_GAMEPAD_BUTTON_INVALID = -1,
+ SDL_GAMEPAD_BUTTON_SOUTH,
+ SDL_GAMEPAD_BUTTON_EAST,
+ SDL_GAMEPAD_BUTTON_WEST,
+ SDL_GAMEPAD_BUTTON_NORTH,
+ SDL_GAMEPAD_BUTTON_BACK,
+ SDL_GAMEPAD_BUTTON_GUIDE,
+ SDL_GAMEPAD_BUTTON_START,
+ SDL_GAMEPAD_BUTTON_LEFT_STICK,
+ SDL_GAMEPAD_BUTTON_RIGHT_STICK,
+ SDL_GAMEPAD_BUTTON_LEFT_SHOULDER,
+ SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER,
+ SDL_GAMEPAD_BUTTON_DPAD_UP,
+ SDL_GAMEPAD_BUTTON_DPAD_DOWN,
+ SDL_GAMEPAD_BUTTON_DPAD_LEFT,
+ SDL_GAMEPAD_BUTTON_DPAD_RIGHT,
+ SDL_GAMEPAD_BUTTON_MISC1,
+ SDL_GAMEPAD_BUTTON_RIGHT_PADDLE1,
+ SDL_GAMEPAD_BUTTON_LEFT_PADDLE1,
+ SDL_GAMEPAD_BUTTON_RIGHT_PADDLE2,
+ SDL_GAMEPAD_BUTTON_LEFT_PADDLE2,
+ SDL_GAMEPAD_BUTTON_TOUCHPAD,
+ SDL_GAMEPAD_BUTTON_MISC2,
+ SDL_GAMEPAD_BUTTON_MISC3,
+ SDL_GAMEPAD_BUTTON_MISC4,
+ SDL_GAMEPAD_BUTTON_MISC5,
+ SDL_GAMEPAD_BUTTON_MISC6,
+ SDL_GAMEPAD_BUTTON_COUNT
+} SDL_GamepadButton;
+
+typedef enum SDL_GamepadAxis {+ SDL_GAMEPAD_AXIS_INVALID = -1,
+ SDL_GAMEPAD_AXIS_LEFTX,
+ SDL_GAMEPAD_AXIS_LEFTY,
+ SDL_GAMEPAD_AXIS_RIGHTX,
+ SDL_GAMEPAD_AXIS_RIGHTY,
+ SDL_GAMEPAD_AXIS_LEFT_TRIGGER,
+ SDL_GAMEPAD_AXIS_RIGHT_TRIGGER,
+ SDL_GAMEPAD_AXIS_COUNT
+} SDL_GamepadAxis;
+
+typedef enum SDL_GamepadButtonLabel {+ SDL_GAMEPAD_BUTTON_LABEL_UNKNOWN,
+ SDL_GAMEPAD_BUTTON_LABEL_A,
+ SDL_GAMEPAD_BUTTON_LABEL_B,
+ SDL_GAMEPAD_BUTTON_LABEL_X,
+ SDL_GAMEPAD_BUTTON_LABEL_Y,
+ SDL_GAMEPAD_BUTTON_LABEL_CROSS,
+ SDL_GAMEPAD_BUTTON_LABEL_CIRCLE,
+ SDL_GAMEPAD_BUTTON_LABEL_SQUARE,
+ SDL_GAMEPAD_BUTTON_LABEL_TRIANGLE
+} SDL_GamepadButtonLabel;
+
+typedef enum SDL_GamepadType {+ SDL_GAMEPAD_TYPE_UNKNOWN = 0,
+ SDL_GAMEPAD_TYPE_STANDARD,
+ SDL_GAMEPAD_TYPE_XBOX360,
+ SDL_GAMEPAD_TYPE_XBOXONE,
+ SDL_GAMEPAD_TYPE_PS3,
+ SDL_GAMEPAD_TYPE_PS4,
+ SDL_GAMEPAD_TYPE_PS5,
+ SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_PRO,
+ SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_LEFT,
+ SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT,
+ SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_PAIR,
+ SDL_GAMEPAD_TYPE_GAMECUBE,
+ SDL_GAMEPAD_TYPE_COUNT
+} SDL_GamepadType;
+
+SDL_JoystickID* SDL_GetGamepads(int *count);
+int SDL_AddGamepadMappingsFromIO(SDL_IOStream *src, bool closeio);
+bool SDL_IsGamepad(SDL_JoystickID instance_id);
+SDL_Gamepad* SDL_OpenGamepad(SDL_JoystickID instance_id);
+const char* SDL_GetGamepadName(SDL_Gamepad *gamepad);
+const char* SDL_GetGamepadStringForAxis(SDL_GamepadAxis axis);
+const char* SDL_GetGamepadStringForButton(SDL_GamepadButton button);
+SDL_GamepadButtonLabel SDL_GetGamepadButtonLabelForType(SDL_GamepadType type, SDL_GamepadButton button);
+SDL_GamepadButtonLabel SDL_GetGamepadButtonLabel(SDL_Gamepad *gamepad, SDL_GamepadButton button);
+void SDL_CloseGamepad(SDL_Gamepad *gamepad);
+
+#endif
--- /dev/null
+++ b/include/npe/SDL3/SDL_image.h
@@ -1,0 +1,41 @@
+#ifndef _npe_SDL_image_h_
+#define _npe_SDL_image_h_
+
+#define SDL_IMAGE_MAJOR_VERSION 2
+#define SDL_IMAGE_MINOR_VERSION 0
+#define SDL_IMAGE_PATCHLEVEL 6
+
+enum {+ IMG_INIT_JPG = 1<<0,
+ IMG_INIT_PNG = 1<<1,
+ IMG_INIT_TIF = 1<<2,
+};
+
+int IMG_Init(int flags);
+void IMG_Quit(void);
+
+SDL_Surface *IMG_LoadTyped_RW(SDL_IOStream *src, int freesrc, const char *type);
+SDL_Surface *IMG_Load(const char *file);
+SDL_Surface *IMG_Load_RW(SDL_IOStream *src, int freesrc);
+
+SDL_Texture *IMG_LoadTexture(SDL_Renderer *renderer, const char *file);
+SDL_Texture *IMG_LoadTexture_RW(SDL_Renderer *renderer, SDL_IOStream *src, int freesrc);
+SDL_Texture *IMG_LoadTextureTyped_RW(SDL_Renderer *renderer, SDL_IOStream *src, int freesrc, const char *type);
+
+SDL_Surface *IMG_LoadBMP_RW(SDL_IOStream *src);
+SDL_Surface *IMG_LoadGIF_RW(SDL_IOStream *src);
+SDL_Surface *IMG_LoadJPG_RW(SDL_IOStream *src);
+SDL_Surface *IMG_LoadPNG_RW(SDL_IOStream *src);
+SDL_Surface *IMG_LoadPNM_RW(SDL_IOStream *src);
+SDL_Surface *IMG_LoadTGA_RW(SDL_IOStream *src);
+SDL_Surface *IMG_LoadTIF_RW(SDL_IOStream *src);
+
+int IMG_SavePNG(SDL_Surface *surface, const char *file);
+int IMG_SavePNG_RW(SDL_Surface *surface, SDL_IOStream *dst, int freedst);
+int IMG_SaveJPG(SDL_Surface *surface, const char *file, int quality);
+int IMG_SaveJPG_RW(SDL_Surface *surface, SDL_IOStream *dst, int freedst, int quality);
+
+#define IMG_SetError SDL_SetError
+#define IMG_GetError SDL_GetError
+
+#endif
--- /dev/null
+++ b/include/npe/SDL3/SDL_init.h
@@ -1,0 +1,12 @@
+#ifndef _npe_SDL_init_h_
+#define _npe_SDL_init_h_
+
+typedef enum SDL_AppResult {+ SDL_APP_CONTINUE,
+ SDL_APP_SUCCESS,
+ SDL_APP_FAILURE,
+} SDL_AppResult;
+
+bool SDL_SetAppMetadata(const char *appname, const char *appversion, const char *appidentifier);
+
+#endif
--- /dev/null
+++ b/include/npe/SDL3/SDL_iostream.h
@@ -1,0 +1,23 @@
+#ifndef _npe_SDL_iostream_h_
+#define _npe_SDL_iostream_h_
+
+typedef struct SDL_IOStream SDL_IOStream;
+
+#pragma incomplete SDL_IOStream
+
+enum {+ SDL_IO_SEEK_SET,
+ SDL_IO_SEEK_CUR,
+ SDL_IO_SEEK_END,
+};
+
+SDL_IOStream* SDL_IOFromFile(const char *file, const char *mode);
+SDL_IOStream* SDL_IOFromConstMem(const void *mem, size_t size);
+size_t SDL_WriteIO(SDL_IOStream *context, const void *ptr, size_t size);
+bool SDL_CloseIO(SDL_IOStream *context);
+
+/* FIXME */
+size_t SDL_ReadIO(SDL_IOStream *, void *, size_t);
+
+
+#endif
--- /dev/null
+++ b/include/npe/SDL3/SDL_joystick.h
@@ -1,0 +1,22 @@
+#ifndef _npe_SDL_joystick_h_
+#define _npe_SDL_joystick_h_
+
+typedef Uint32 SDL_JoystickID;
+
+typedef struct SDL_Joystick SDL_Joystick;
+
+int SDL_NumJoysticks(void);
+SDL_Joystick *SDL_JoystickOpen(int n);
+void SDL_JoystickClose(SDL_Joystick*);
+int SDL_JoystickNumAxes(SDL_Joystick*);
+int SDL_JoystickNumButtons(SDL_Joystick*);
+int SDL_JoystickNumHats(SDL_Joystick*);
+int SDL_JoystickNumBalls(SDL_Joystick*);
+int SDL_JoystickEventState(int);
+void SDL_JoystickUpdate(void);
+Uint8 SDL_JoystickGetHat(SDL_Joystick*,int);
+Sint16 SDL_JoystickGetAxis(SDL_Joystick*,int);
+Uint8 SDL_JoystickGetButton(SDL_Joystick*,int);
+char* SDL_JoystickName(SDL_Joystick*);
+
+#endif
--- /dev/null
+++ b/include/npe/SDL3/SDL_keyboard.h
@@ -1,0 +1,8 @@
+#ifndef _npe_SDL_keyboard_h_
+#define _npe_SDL_keyboard_h_
+
+typedef Uint32 SDL_KeyboardID;
+
+const char* SDL_GetScancodeName(SDL_Scancode scancode);
+
+#endif
--- /dev/null
+++ b/include/npe/SDL3/SDL_keycode.h
@@ -1,0 +1,137 @@
+#ifndef _npe_SDL_keycode_h_
+#define _npe_SDL_keycode_h_
+
+#include <keyboard.h>
+
+typedef int SDL_Keycode;
+typedef int SDL_Keymod;
+
+enum {+ SDLK_a = 'a',
+ SDLK_b,
+ SDLK_c,
+ SDLK_d,
+ SDLK_e,
+ SDLK_f,
+ SDLK_g,
+ SDLK_h,
+ SDLK_i,
+ SDLK_j,
+ SDLK_k,
+ SDLK_l,
+ SDLK_m,
+ SDLK_n,
+ SDLK_o,
+ SDLK_p,
+ SDLK_q,
+ SDLK_r,
+ SDLK_s,
+ SDLK_t,
+ SDLK_u,
+ SDLK_v,
+ SDLK_w,
+ SDLK_x,
+ SDLK_y,
+ SDLK_z,
+ SDLK_0 = '0',
+ SDLK_1,
+ SDLK_2,
+ SDLK_3,
+ SDLK_4,
+ SDLK_5,
+ SDLK_6,
+ SDLK_7,
+ SDLK_8,
+ SDLK_9,
+ SDLK_DELETE = Kdel,
+ SDLK_RETURN = '\r',
+ SDLK_ESCAPE = Kesc,
+ SDLK_LESS = '<',
+ SDLK_SPACE = ' ',
+ SDLK_TAB = '\t',
+ SDLK_LEFT = Kleft,
+ SDLK_RIGHT = Kright,
+ SDLK_DOWN = Kdown,
+ SDLK_UP = Kup,
+ SDLK_F1 = KF|1,
+ SDLK_F2,
+ SDLK_F3,
+ SDLK_F4,
+ SDLK_F5,
+ SDLK_F6,
+ SDLK_F7,
+ SDLK_F8,
+ SDLK_F9,
+ SDLK_F10,
+ SDLK_F11,
+ SDLK_F12,
+
+ SDLK_INSERT = Kins,
+ SDLK_PAGEUP = Kpgup,
+ SDLK_PAGEDOWN = Kpgdown,
+ SDLK_HOME = Khome,
+ SDLK_END = Kend,
+ SDLK_BACKSPACE = Kbs,
+ SDLK_MINUS = '-',
+ SDLK_PLUS = '+',
+ SDLK_EQUALS = '=',
+ SDLK_UNDERSCORE = '_',
+ SDLK_LEFTBRACKET = '[',
+ SDLK_RIGHTBRACKET = ']',
+ SDLK_SEMICOLON = ';',
+ SDLK_QUOTE = '\'',
+ SDLK_BACKQUOTE = '`',
+ SDLK_BACKSLASH = '\\',
+ SDLK_COMMA = ',',
+ SDLK_PERIOD = '.',
+ SDLK_SLASH = '/',
+
+ SDLK_LALT = Kalt,
+ SDLK_RALT = Kaltgr, /* FIXME what about keyboards without it? */
+ SDLK_LSHIFT = Kshift,
+ SDLK_RSHIFT = Kshift,
+ SDLK_LCTRL = Kctl,
+ SDLK_RCTRL = Kctl,
+ SDLK_CAPSLOCK = Kcaps,
+
+
+ /* FIXME not bound to anything */
+ SDLK_UNKNOWN = -99999,
+ SDLK_KP_ENTER,
+ SDLK_AC_BACK,
+ SDLK_PAUSE,
+ SDLK_KP_DIVIDE,
+ SDLK_MODE,
+ SDLK_KP_PLUS,
+ SDLK_NUMLOCKCLEAR,
+ SDLK_SCROLLLOCK,
+ SDLK_KP_PERIOD,
+ SDLK_KP_7,
+ SDLK_KP_8,
+ SDLK_KP_9,
+ SDLK_KP_MINUS,
+ SDLK_KP_4,
+ SDLK_KP_5,
+ SDLK_KP_6,
+ SDLK_KP_1,
+ SDLK_KP_2,
+ SDLK_KP_3,
+ SDLK_KP_0,
+
+ SDL_KMOD_LSHIFT = 1<<0,
+ SDL_KMOD_RSHIFT = 1<<1,
+ SDL_KMOD_LCTRL = 1<<6,
+ SDL_KMOD_RCTRL = 1<<7,
+ SDL_KMOD_LALT = 1<<8,
+ SDL_KMOD_RALT = 1<<9,
+ SDL_KMOD_LGUI = 1<<10,
+ SDL_KMOD_RGUI = SDL_KMOD_LGUI,
+ SDL_KMOD_CAPS = 1<<13,
+
+ SDL_KMOD_SHIFT = SDL_KMOD_LSHIFT|SDL_KMOD_RSHIFT,
+ SDL_KMOD_CTRL = SDL_KMOD_LCTRL|SDL_KMOD_RCTRL,
+ SDL_KMOD_ALT = SDL_KMOD_LALT|SDL_KMOD_RALT,
+ SDL_KMOD_GUI = SDL_KMOD_LGUI|SDL_KMOD_RGUI,
+};
+
+#endif
--- /dev/null
+++ b/include/npe/SDL3/SDL_log.h
@@ -1,0 +1,46 @@
+#ifndef _npe_SDL_log_h_
+#define _npe_SDL_log_h_
+
+typedef enum SDL_LogCategory {+ SDL_LOG_CATEGORY_APPLICATION,
+ SDL_LOG_CATEGORY_ERROR,
+ SDL_LOG_CATEGORY_ASSERT,
+ SDL_LOG_CATEGORY_SYSTEM,
+ SDL_LOG_CATEGORY_AUDIO,
+ SDL_LOG_CATEGORY_VIDEO,
+ SDL_LOG_CATEGORY_RENDER,
+ SDL_LOG_CATEGORY_INPUT,
+ SDL_LOG_CATEGORY_TEST,
+ SDL_LOG_CATEGORY_COUNT,
+} SDL_LogCategory;
+
+typedef enum SDL_LogPriority {+ SDL_LOG_PRIORITY_INVALID,
+ SDL_LOG_PRIORITY_TRACE,
+ SDL_LOG_PRIORITY_VERBOSE,
+ SDL_LOG_PRIORITY_DEBUG,
+ SDL_LOG_PRIORITY_INFO,
+ SDL_LOG_PRIORITY_WARN,
+ SDL_LOG_PRIORITY_ERROR,
+ SDL_LOG_PRIORITY_CRITICAL,
+ SDL_LOG_PRIORITY_COUNT
+} SDL_LogPriority;
+
+typedef void (*SDL_LogOutputFunction)(void *userdata, int category, SDL_LogPriority priority, const char *message);
+
+void SDL_Log(const char *fmt, ...);
+void SDL_LogDebug(int category, const char *fmt, ...);
+void SDL_LogInfo(int category, const char *fmt, ...);
+void SDL_LogError(int category, const char *fmt, ...);
+void SDL_LogCritical(int category, const char *fmt, ...);
+void SDL_GetLogOutputFunction(SDL_LogOutputFunction *callback, void **userdata);
+void SDL_SetLogOutputFunction(SDL_LogOutputFunction callback, void *userdata);
+void SDL_SetLogPriorities(SDL_LogPriority priority);
+
+#pragma varargck argpos SDL_Log 1
+#pragma varargck argpos SDL_LogDebug 2
+#pragma varargck argpos SDL_LogInfo 2
+#pragma varargck argpos SDL_LogError 2
+#pragma varargck argpos SDL_LogCritical 2
+
+#endif
--- /dev/null
+++ b/include/npe/SDL3/SDL_main.h
@@ -1,0 +1,36 @@
+#ifndef _npe_SDL_main_h_
+#define _npe_SDL_main_h_
+
+SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[]);
+SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event);
+SDL_AppResult SDL_AppIterate(void *appstate);
+void SDL_AppQuit(void *appstate, SDL_AppResult result);
+
+#ifdef SDL_MAIN_USE_CALLBACKS
+int
+npe_main_renamed(int argc, char **argv)
+{+ int r;
+ void *p;
+ SDL_Event e;
+
+ p = nil;
+ r = SDL_AppInit(&p, argc, argv);
+ /* FIXME: start event readers, or do ifdefs */
+ for(;r == SDL_APP_CONTINUE;){+ while(SDL_PollEvent(&e))
+ if((r = SDL_AppEvent(p, &e)) != SDL_APP_CONTINUE)
+ break;
+ if(r != SDL_APP_CONTINUE)
+ break;
+ r = SDL_AppIterate(p);
+ }
+ SDL_AppQuit(p, r);
+ if(r == SDL_APP_FAILURE)
+ sysfatal("%r");+ SDL_Quit();
+ return 0;
+}
+#endif
+
+#endif
--- /dev/null
+++ b/include/npe/SDL3/SDL_mixer.h
@@ -1,0 +1,60 @@
+#ifndef _npe_SDL_mixer_h_
+#define _npe_SDL_mixer_h_
+
+#include <SDL2/SDL_io.h>
+
+/* The internal format for an audio chunk */
+typedef struct Mix_Chunk {+ int allocated;
+ Uint8 *abuf;
+ Uint32 alen;
+ Uint8 volume; /* Per-sample volume, 0-128 */
+} Mix_Chunk;
+
+typedef struct Mix_Music {+ int type;
+ int loops;
+ int fd;
+ char *loc;
+} Mix_Music;
+
+typedef void (*Mix_EffectFunc_t)(int chan, void *stream, int len, void *udata);
+typedef void (*Mix_EffectDone_t)(int chan, void *udata);
+typedef void (*Mix_MixCallback)(void *udata, Uint8 *stream, int len);
+
+int Mix_OpenAudio(int,Uint16,int,int);
+char* Mix_GetError(void);
+int Mix_RegisterEffect(int,Mix_EffectFunc_t,Mix_EffectDone_t,void*);
+Mix_Chunk* Mix_QuickLoad_RAW(Uint8*, Uint32);
+int Mix_PlayChannel(int,Mix_Chunk*,int);
+int Mix_HaltChannel(int);
+void Mix_FreeChunk(Mix_Chunk*);
+void Mix_CloseAudio(void);
+int Mix_Init(int);
+int Mix_VolumeMusic(int);
+int Mix_PlayingMusic(void);
+int Mix_PausedMusic(void);
+void Mix_ResumeMusic(void);
+void Mix_PauseMusic(void);
+int Mix_PlayingMusic(void);
+int Mix_PausedMusic(void);
+int Mix_HaltMusic(void);
+int Mix_PlayMusic(Mix_Music *music, int loops);
+Mix_Music* Mix_LoadMUS_RW(SDL_IOStream *src, int freesrc);
+Mix_Music* Mix_LoadMUS(char *filename);
+int Mix_SetPanning(int channel, Uint8 left, Uint8 right);
+int Mix_Playing(int channel);
+int Mix_QuerySpec(int *frequency, Uint16 *format, int *channels);
+int Mix_AllocateChannels(int numchans);
+void Mix_HookMusic(Mix_MixCallback mix_func, void *arg);
+
+enum {+ MIX_INIT_MID = 1,
+
+ MIX_DEFAULT_FORMAT = 1,
+
+ SDL_MIX_MAXVOLUME = 100,
+ MIX_MAX_VOLUME = SDL_MIX_MAXVOLUME,
+};
+
+#endif
--- /dev/null
+++ b/include/npe/SDL3/SDL_mutex.h
@@ -1,0 +1,26 @@
+#ifndef _npe_SDL_mutex_h_
+#define _npe_SDL_mutex_h_
+
+typedef struct SDL_Mutex SDL_Mutex;
+typedef struct SDL_Condition SDL_Condition;
+
+struct SDL_Mutex {+ Lock l;
+};
+
+struct SDL_Condition {+ QLock;
+ Rendez;
+};
+
+SDL_Mutex* SDL_CreateMutex(void);
+void SDL_DestroyMutex(SDL_Mutex*);
+int SDL_LockMutex(SDL_Mutex*);
+int SDL_UnlockMutex(SDL_Mutex*);
+
+SDL_Condition* SDL_CreateCondition(void);
+void SDL_SignalCondition(SDL_Condition *cond);
+void SDL_WaitCondition(SDL_Condition *cond, SDL_Mutex *mutex);
+void SDL_DestroyCondition(SDL_Condition *cond);
+
+#endif
--- /dev/null
+++ b/include/npe/SDL3/SDL_properties.h
@@ -1,0 +1,13 @@
+#ifndef _npe_SDL_properties_h_
+#define _npe_SDL_properties_h_
+
+//typedef Uint32 SDL_PropertiesID;
+typedef SDL_Texture* SDL_PropertiesID;
+
+#define SDL_PROP_TEXTURE_WIDTH_NUMBER "SDL.texture.width"
+#define SDL_PROP_TEXTURE_HEIGHT_NUMBER "SDL.texture.height"
+
+SDL_PropertiesID SDL_GetTextureProperties(SDL_Texture *texture);
+Sint64 SDL_GetNumberProperty(SDL_PropertiesID props, const char *name, Sint64 default_value);
+
+#endif
--- /dev/null
+++ b/include/npe/SDL3/SDL_quit.h
@@ -1,0 +1,6 @@
+#ifndef _npe_SDL_quit_h_
+#define _npe_SDL_quit_h_
+
+int SDL_QuitRequested(void);
+
+#endif
--- /dev/null
+++ b/include/npe/SDL3/SDL_render.h
@@ -1,0 +1,78 @@
+#ifndef _npe_SDL_render_h_
+#define _npe_SDL_render_h_
+
+typedef struct SDL_Renderer SDL_Renderer;
+typedef struct SDL_Rect SDL_Rect;
+typedef struct SDL_FPoint SDL_FPoint;
+typedef struct SDL_FRect SDL_FRect;
+typedef struct SDL_Texture SDL_Texture;
+
+#pragma incomplete SDL_Renderer
+#pragma incomplete SDL_Texture
+
+struct SDL_Rect {+ int x, y, w, h;
+};
+
+struct SDL_FPoint {+ float x;
+ float y;
+};
+
+struct SDL_FRect {+ float x;
+ float y;
+ float w;
+ float h;
+};
+
+typedef enum SDL_ScaleMode {+ SDL_SCALEMODE_INVALID = -1,
+ SDL_SCALEMODE_NEAREST, /**< nearest pixel sampling */
+ SDL_SCALEMODE_LINEAR, /**< linear filtering */
+ SDL_SCALEMODE_PIXELART /**< nearest pixel sampling with improved scaling for pixel art, available since SDL 3.4.0 */
+} SDL_ScaleMode;
+
+typedef enum SDL_RendererLogicalPresentation {+ SDL_LOGICAL_PRESENTATION_DISABLED,
+ SDL_LOGICAL_PRESENTATION_STRETCH,
+ SDL_LOGICAL_PRESENTATION_LETTERBOX,
+ SDL_LOGICAL_PRESENTATION_OVERSCAN,
+ SDL_LOGICAL_PRESENTATION_INTEGER_SCALE
+} SDL_RendererLogicalPresentation;
+
+bool SDL_CreateWindowAndRenderer(const char *title, int width, int height, SDL_WindowFlags window_flags, SDL_Window **window, SDL_Renderer **renderer);
+int SDL_RenderClear(SDL_Renderer *renderer);
+int SDL_RenderFillRect(SDL_Renderer *r, SDL_FRect *rect);
+void SDL_RenderGetScale(SDL_Renderer *renderer, float *scaleX, float *scaleY);
+int SDL_RenderReadPixels(SDL_Renderer *rend, SDL_Rect *rect, Uint32 fmt, void *pixels, int pitch);
+bool SDL_RenderPresent(SDL_Renderer *renderer);
+bool SDL_SetRenderLogicalPresentation(SDL_Renderer *renderer, int w, int h, SDL_RendererLogicalPresentation mode);
+void SDL_DestroyRenderer(SDL_Renderer *renderer);
+SDL_Renderer *SDL_CreateRenderer(SDL_Window *window, char *name);
+int SDL_SetRenderDrawBlendMode(SDL_Renderer *renderer, SDL_BlendMode blendMode);
+int SDL_GetRendererOutputSize(SDL_Renderer *renderer, int *w, int *h);
+int SDL_GetRendererInfo(SDL_Renderer *renderer, SDL_RendererInfo *info);
+void SDL_RenderGetViewport(SDL_Renderer *rebderer, SDL_Rect *rect);
+int SDL_SetRenderDrawColor(SDL_Renderer *r, Uint8 r, Uint8 g, Uint8 b, Uint8 a);
+int SDL_RenderSetIntegerScale(SDL_Renderer *r, SDL_bool enable);
+int SDL_SetRenderTarget(SDL_Renderer *renderer, SDL_Texture *texture);
+SDL_Texture* SDL_GetRenderTarget(SDL_Renderer *renderer);
+bool SDL_RenderTexture(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_FRect *srcrect, const SDL_FRect *dstrect);
+bool SDL_RenderLines(SDL_Renderer *renderer, const SDL_FPoint *points, int count);
+bool SDL_RenderPoints(SDL_Renderer *renderer, const SDL_FPoint *points, int count);
+bool SDL_SetRenderVSync(SDL_Renderer *renderer, int vsync);
+
+/* FIXME */
+int SDL_UpdateTexture(SDL_Texture *texture, SDL_Rect *rect, void *pixels, int pitch);
+SDL_Texture *SDL_CreateTexture(SDL_Renderer *renderer, Uint32 format, int access, int w, int h);
+int SDL_SetTextureBlendMode(SDL_Texture *texture, SDL_BlendMode blendMode);
+bool SDL_SetTextureScaleMode(SDL_Texture *texture, SDL_ScaleMode scaleMode);
+int SDL_SetTextureAlphaMod(SDL_Texture *texture, Uint8 alpha);
+int SDL_SetTextureColorMod(SDL_Texture *texture, Uint8 r, Uint8 g, Uint8 b);
+bool SDL_GetTextureSize(SDL_Texture *texture, float *w, float *h);
+int SDL_LockTexture(SDL_Texture *texture, const SDL_Rect *rect, void **pixels, int *pitch);
+int SDL_UnlockTexture(SDL_Texture *texture);
+void SDL_DestroyTexture(SDL_Texture *texture);
+
+#endif
--- /dev/null
+++ b/include/npe/SDL3/SDL_rwops.h
@@ -1,0 +1,32 @@
+#ifndef _npe_SDL_rwops_h_
+#define _npe_SDL_rwops_h_
+
+typedef struct SDL_RWops SDL_RWops;
+typedef struct npe_sdl_rwops npe_sdl_rwops;
+#pragma incomplete npe_sdl_rwops
+
+enum {+ RW_SEEK_SET,
+ RW_SEEK_CUR,
+ RW_SEEK_END,
+};
+
+struct SDL_RWops {+ vlong (*size)(struct SDL_RWops *);
+ vlong (*seek)(struct SDL_RWops *, vlong, int);
+ size_t (*read)(struct SDL_RWops *, void *, size_t, size_t);
+ size_t (*write)(struct SDL_RWops *, const void *, size_t, size_t);
+ int (*close)(struct SDL_RWops *);
+ npe_sdl_rwops *p;
+};
+
+SDL_RWops *SDL_RWFromFile(const char *, const char *);
+SDL_RWops *SDL_RWFromMem(void*, int);
+size_t SDL_RWread(SDL_RWops *, void *, size_t, size_t);
+size_t SDL_RWwrite(SDL_RWops *, const void *, size_t, size_t);
+vlong SDL_RWseek(SDL_RWops *, vlong, int);
+vlong SDL_RWtell(SDL_RWops *);
+vlong SDL_RWsize(SDL_RWops *);
+int SDL_RWclose(SDL_RWops *);
+
+#endif
--- /dev/null
+++ b/include/npe/SDL3/SDL_scancode.h
@@ -1,0 +1,132 @@
+#ifndef _npe_SDL_scancode_h_
+#define _npe_SDL_scancode_h_
+
+typedef enum SDL_Scancode {+ /* these HAVE to be in this order for notes to work in ft2-clone */
+ SDL_SCANCODE_UNKNOWN,
+ SDL_SCANCODE_A = 0x04,
+ SDL_SCANCODE_B,
+ SDL_SCANCODE_C,
+ SDL_SCANCODE_D,
+ SDL_SCANCODE_E,
+ SDL_SCANCODE_F,
+ SDL_SCANCODE_G,
+ SDL_SCANCODE_H,
+ SDL_SCANCODE_I,
+ SDL_SCANCODE_J,
+ SDL_SCANCODE_K,
+ SDL_SCANCODE_L,
+ SDL_SCANCODE_M,
+ SDL_SCANCODE_N,
+ SDL_SCANCODE_O,
+ SDL_SCANCODE_P,
+ SDL_SCANCODE_Q,
+ SDL_SCANCODE_R,
+ SDL_SCANCODE_S,
+ SDL_SCANCODE_T,
+ SDL_SCANCODE_U,
+ SDL_SCANCODE_V,
+ SDL_SCANCODE_W,
+ SDL_SCANCODE_X,
+ SDL_SCANCODE_Y,
+ SDL_SCANCODE_Z,
+ SDL_SCANCODE_1,
+ SDL_SCANCODE_2,
+ SDL_SCANCODE_3,
+ SDL_SCANCODE_4,
+ SDL_SCANCODE_5,
+ SDL_SCANCODE_6,
+ SDL_SCANCODE_7,
+ SDL_SCANCODE_8,
+ SDL_SCANCODE_9,
+ SDL_SCANCODE_0,
+ SDL_SCANCODE_RETURN,
+ SDL_SCANCODE_ESCAPE,
+ SDL_SCANCODE_BACKSPACE,
+ SDL_SCANCODE_TAB,
+ SDL_SCANCODE_SPACE,
+ SDL_SCANCODE_MINUS,
+ SDL_SCANCODE_EQUALS,
+ SDL_SCANCODE_LEFTBRACKET,
+ SDL_SCANCODE_RIGHTBRACKET,
+ SDL_SCANCODE_BACKSLASH,
+ SDL_SCANCODE_NONUSHASH,
+ SDL_SCANCODE_SEMICOLON,
+ SDL_SCANCODE_APOSTROPHE,
+ SDL_SCANCODE_GRAVE,
+ SDL_SCANCODE_COMMA,
+ SDL_SCANCODE_PERIOD,
+ SDL_SCANCODE_SLASH,
+ SDL_SCANCODE_CAPSLOCK,
+ SDL_SCANCODE_F1,
+ SDL_SCANCODE_F2,
+ SDL_SCANCODE_F3,
+ SDL_SCANCODE_F4,
+ SDL_SCANCODE_F5,
+ SDL_SCANCODE_F6,
+ SDL_SCANCODE_F7,
+ SDL_SCANCODE_F8,
+ SDL_SCANCODE_F9,
+ SDL_SCANCODE_F10,
+ SDL_SCANCODE_F11,
+ SDL_SCANCODE_F12,
+ SDL_SCANCODE_PRINTSCREEN,
+ SDL_SCANCODE_SCROLLLOCK,
+ SDL_SCANCODE_PAUSE,
+ SDL_SCANCODE_INSERT,
+ SDL_SCANCODE_HOME,
+ SDL_SCANCODE_PAGEUP,
+ SDL_SCANCODE_DELETE,
+ SDL_SCANCODE_END,
+ SDL_SCANCODE_PAGEDOWN,
+ SDL_SCANCODE_RIGHT,
+ SDL_SCANCODE_LEFT,
+ SDL_SCANCODE_DOWN,
+ SDL_SCANCODE_UP,
+ SDL_SCANCODE_NUMLOCKCLEAR,
+ SDL_SCANCODE_KP_DIVIDE,
+ SDL_SCANCODE_KP_MULTIPLY,
+ SDL_SCANCODE_KP_MINUS,
+ SDL_SCANCODE_KP_PLUS,
+ SDL_SCANCODE_KP_ENTER,
+ SDL_SCANCODE_KP_1,
+ SDL_SCANCODE_KP_2,
+ SDL_SCANCODE_KP_3,
+ SDL_SCANCODE_KP_4,
+ SDL_SCANCODE_KP_5,
+ SDL_SCANCODE_KP_6,
+ SDL_SCANCODE_KP_7,
+ SDL_SCANCODE_KP_8,
+ SDL_SCANCODE_KP_9,
+ SDL_SCANCODE_KP_0,
+ SDL_SCANCODE_KP_PERIOD,
+ SDL_SCANCODE_NONUSBACKSLASH,
+ SDL_SCANCODE_APPLICATION,
+ SDL_SCANCODE_POWER,
+ SDL_SCANCODE_KP_EQUALS,
+
+ SDL_SCANCODE_SYSREQ = 0x9a,
+
+ SDL_SCANCODE_MENU = 0x76,
+
+ SDL_SCANCODE_MUTE = 0x7f,
+ SDL_SCANCODE_VOLUMEUP,
+ SDL_SCANCODE_VOLUMEDOWN,
+ SDL_SCANCODE_PLAYPAUSE,
+
+ SDL_SCANCODE_LCTRL = 0xe0,
+ SDL_SCANCODE_LSHIFT,
+ SDL_SCANCODE_LALT,
+ SDL_SCANCODE_LGUI,
+ SDL_SCANCODE_RCTRL,
+ SDL_SCANCODE_RSHIFT,
+ SDL_SCANCODE_RALT,
+ SDL_SCANCODE_RGUI,
+
+ SDL_SCANCODE_MODE = 0x101,
+ SDL_SCANCODE_AUDIOMUTE = 0x106,
+
+ SDL_NUM_SCANCODES,
+} SDL_Scancode;
+
+#endif
--- /dev/null
+++ b/include/npe/SDL3/SDL_surface.h
@@ -1,0 +1,47 @@
+#ifndef _npe_SDL_surface_h_
+#define _npe_SDL_surface_h_
+
+typedef struct SDL_PixelFormat SDL_PixelFormat;
+typedef struct SDL_Surface SDL_Surface;
+
+struct SDL_PixelFormat {+ SDL_Palette *palette;
+ int format;
+ int BytesPerPixel;
+};
+
+struct SDL_Surface {+ SDL_PixelFormat *format;
+ SDL_Rect clip_rect;
+ Uint32 flags;
+ Uint32 key;
+ int keyset;
+ int w, h;
+ int pitch;
+ int n;
+ void *i;
+ uchar *pixels;
+};
+
+SDL_Surface *SDL_CreateRGBSurfaceWithFormat(Uint32 flags, int w, int h, int bpp, Uint32 fmt);
+SDL_Surface *SDL_CreateRGBSurface(Uint32 flags, int width, int height, int depth, Uint32 rm, Uint32 gm, Uint32 bm, Uint32 am);
+SDL_Surface *SDL_CreateRGBSurfaceFrom(void *pixels, int w, int h, int bpp, int pitch, Uint32 rm, Uint32 gm, Uint32 bm, Uint32 am);
+Uint32 SDL_MapRGB(SDL_PixelFormat *format, Uint8 r, Uint8 g, Uint8 b);
+Uint32 SDL_MapSurfaceRGB(SDL_Surface *surface, Uint8 r, Uint8 g, Uint8 b);
+bool SDL_SetSurfaceColorKey(SDL_Surface *surface, bool enabled, Uint32 key);
+int SDL_SetSurfaceRLE(SDL_Surface *surface, int flag);
+int SDL_LockSurface(SDL_Surface *surface);
+int SDL_UnlockSurface(SDL_Surface *surface);
+int SDL_SetSurfaceBlendMode(SDL_Surface *surface, SDL_BlendMode blendMode);
+SDL_Cursor *SDL_CreateColorCursor(SDL_Surface *surface, int hot_x, int hot_y);
+void SDL_DestroySurface(SDL_Surface *surface);
+SDL_Surface* SDL_LoadBMP_IO(SDL_IOStream *src, bool closeio);
+int SDL_SaveBMP(SDL_Surface *s, const char *file);
+int SDL_SetSurfacePalette(SDL_Surface *s, SDL_Palette *palette);
+int SDL_BlitSurface(SDL_Surface *src, const SDL_Rect *srcrect, SDL_Surface *dst, SDL_Rect *dstrect);
+int SDL_LowerBlit(SDL_Surface *src, SDL_Rect *srcrect, SDL_Surface *dst, SDL_Rect *dstrect);
+int SDL_SoftStretch(SDL_Surface *src, const SDL_Rect *srcrect, SDL_Surface *dst, const SDL_Rect *dstrect);
+
+SDL_Texture *SDL_CreateTextureFromSurface(SDL_Renderer *r, SDL_Surface *s);
+
+#endif
--- /dev/null
+++ b/include/npe/SDL3/SDL_thread.h
@@ -1,0 +1,23 @@
+#ifndef _npe_SDL_thread_h_
+#define _npe_SDL_thread_h_
+
+enum {+ SDL_THREAD_PRIORITY_LOW,
+ SDL_THREAD_PRIORITY_NORMAL,
+ SDL_THREAD_PRIORITY_HIGH,
+ SDL_THREAD_PRIORITY_TIME_CRITICAL,
+};
+
+typedef struct SDL_Thread SDL_Thread;
+typedef int (*SDL_ThreadFunction)(void *);
+#pragma incomplete SDL_Thread
+
+SDL_Thread *SDL_CreateThread(SDL_ThreadFunction, char *, void *);
+SDL_Thread *SDL_CreateThreadWithStackSize(SDL_ThreadFunction, const char *, size_t, void *);
+
+void SDL_DetachThread(SDL_Thread *);
+void SDL_WaitThread(SDL_Thread *, int *);
+
+void SDL_SetCurrentThreadPriority(int);
+
+#endif
--- /dev/null
+++ b/include/npe/SDL3/SDL_version.h
@@ -1,0 +1,18 @@
+#ifndef _npe_SDL_version_h_
+#define _npe_SDL_version_h_
+
+typedef struct SDL_version SDL_version;
+
+struct SDL_version {+ u8int major, minor, patch;
+};
+
+void SDL_GetVersion(SDL_version *v);
+
+#define SDL_VERSIONNUM(X, Y, Z) ((X)*1000 + (Y)*100 + (Z))
+#define SDL_MAJOR_VERSION 3
+#define SDL_MINOR_VERSION 3
+#define SDL_MICRO_VERSION 7
+#define SDL_COMPILEDVERSION SDL_VERSIONNUM(SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL)
+
+#endif
--- /dev/null
+++ b/include/npe/SDL3/SDL_video.h
@@ -1,0 +1,15 @@
+#ifndef _npe_SDL_video_h_
+#define _npe_SDL_video_h_
+
+typedef Uint32 SDL_WindowID;
+typedef Uint64 SDL_WindowFlags;
+
+enum {+ SDL_WINDOW_OPENGL = 1ULL<<1,
+ SDL_WINDOW_HIGH_PIXEL_DENSITY = 1ULL<<13,
+};
+
+bool SDL_GetWindowSizeInPixels(SDL_Window *window, int *w, int *h);
+bool SDL_SyncWindow(SDL_Window *window);
+
+#endif
--- /dev/null
+++ b/include/npe/SDL3/audio.c
@@ -1,0 +1,299 @@
+#include "_sdl.h"
+
+enum {+ Aout = 2,
+ Arec,
+
+ Audiosamples = 8192,
+};
+
+typedef struct Audiodev Audiodev;
+
+struct Audiodev {+ Lock;
+ void (*cb)(void *, Uint8 *, int);
+ void *userdata;
+ char *name;
+ Channel *wait;
+ Uint8 *buf;
+ int bufsz;
+ int paused;
+ int fd;
+ int pid;
+ int pidconv;
+ int mode;
+};
+
+/* FIXME extra USB audio devices? */
+static Audiodev au[4] = {+ [0] = {.fd = -1, .mode = -1},+ [1] = {.fd = -1, .mode = -1},+ [Aout] = {.name = "/dev/audio", .fd = -1, .pid = -1, .mode = OWRITE},+ [Arec] = {.name = "/dev/audio", .fd = -1, .pid = -1, .mode = OREAD},+};
+
+static struct {+ char *spec;
+ int ssz; /* samples size */
+}fmts[] = {+ [SDL_AUDIO_U8] = {"u8", 1},+ [SDL_AUDIO_S8] = {"s8", 1},+ [SDL_AUDIO_S16LE] = {"s16", 2},+ [SDL_AUDIO_S16BE] = {"S16", 2},+ [SDL_AUDIO_S32LE] = {"s32", 4},+ [SDL_AUDIO_S32BE] = {"S32", 4},+ [SDL_AUDIO_F32LE] = {"f32", 4},+ [SDL_AUDIO_F32BE] = {"F32", -1}, /* FIXME big endian f32 not supported by pcmconv */+};
+
+int
+SDL_GetNumAudioDevices(int iscapture)
+{+ /* FIXME look for extra USB devices? */
+ USED(iscapture);
+ return 1;
+}
+
+char *
+SDL_GetAudioDeviceName(int index, int iscapture)
+{+ /* FIXME look for extra USB devices? */
+ USED(index);
+ return au[iscapture ? Arec : Aout].name;
+}
+
+void
+SDL_LockAudioDevice(SDL_AudioDeviceID id)
+{+ lock(&au[id]);
+}
+
+void
+SDL_UnlockAudioDevice(SDL_AudioDeviceID id)
+{+ unlock(&au[id]);
+}
+
+static void
+audiothread(void *p)
+{+ Audiodev *a;
+
+ a = p;
+ threadsetname("%s (%s)", a->name, a->mode == OREAD ? "out" : "in");+
+ for(;;){+ if(a->mode == OREAD && readn(a->fd, a->buf, a->bufsz) != a->bufsz)
+ break;
+
+ lock(a);
+ if(a->mode == OWRITE && a->paused)
+ memset(a->buf, 0, a->bufsz);
+ else
+ a->cb(a->userdata, a->buf, a->bufsz);
+ unlock(a);
+
+ if(a->mode == OWRITE && write(a->fd, a->buf, a->bufsz) != a->bufsz)
+ break;
+ }
+
+ lock(a);
+ (a->mode == OWRITE ? write : read)(a->fd, a->buf, 0);
+ chanclose(a->wait);
+ unlock(a);
+
+ threadexits(nil);
+}
+
+void
+SDL_PauseAudioDevice(SDL_AudioDeviceID id, SDL_bool pause)
+{+ Audiodev *a;
+
+ a = &au[id];
+ if(a->paused && !pause){+ if(a->pid < 0)
+ a->pid = proccreate(audiothread, a, 4096);
+ a->paused = 0;
+ }else if(!a->paused && pause){+ a->paused = 1;
+ }
+}
+
+void
+SDL_PauseAudio(int pause_on)
+{+ SDL_PauseAudioDevice(1, pause_on);
+}
+
+static int
+convspec(SDL_AudioSpec *s, char *spec, int n)
+{+ int ssz;
+
+ ssz = -1;
+ if(s->format < 0 || s->format >= nelem(fmts))
+ werrstr("invalid audio format: #%d", s->format);+ else if(fmts[s->format].ssz < 1)
+ werrstr("unsupported audio format: #%d", s->format);+ else if(s->channels < 1)
+ werrstr("invalid number of channels: %d", s->channels);+ else if(s->freq < 1)
+ werrstr("invalid sampling rate: %d", s->freq);+ else if(snprint(spec, n, "%sc%dr%d", fmts[s->format].spec, s->channels, s->freq) >= n)
+ werrstr("audio spec does not fit");+ else
+ ssz = fmts[s->format].ssz;
+
+ return ssz;
+}
+
+// https://wiki.libsdl.org/SDL3/SDL_ConvertAudio
+int
+SDL_ConvertAudio(SDL_AudioCVT *cvt)
+{+ USED(cvt);
+ return 0;
+}
+
+// https://wiki.libsdl.org/SDL3/SDL_BuildAudioCVT
+int
+SDL_BuildAudioCVT(SDL_AudioCVT *cvt, SDL_AudioFormat src_format, Uint8 src_channels, int src_rate, SDL_AudioFormat dst_format, Uint8 dst_channels, int dst_rate)
+{+ USED(cvt);
+ USED(src_format, src_channels, src_rate);
+ USED(dst_format, dst_channels, dst_rate);
+ return 0;
+}
+
+static void
+setpipebuf(int f, int sz)
+{+ Dir d;
+
+ nulldir(&d);
+ d.length = sz;
+ dirfwstat(f, &d);
+}
+
+SDL_AudioDeviceID
+SDL_OpenAudioDevice(char *dev, int rec, SDL_AudioSpec *want, SDL_AudioSpec *have, u32int change)
+{+ SDL_AudioDeviceID id;
+ int p[2], ssz, fd;
+ char spec[16];
+ Audiodev *a;
+
+ /* FIXME look for extra USB devices? */
+ USED(dev);
+
+ id = rec ? Arec : Aout;
+ a = &au[id];
+
+ if(have == nil)
+ have = want;
+ *have = *want;
+ if(have->freq < 44100 && (change & SDL_AUDIO_ALLOW_FREQUENCY_CHANGE) != 0)
+ have->freq = 44100;
+ if(have->format <= 0 || have->format >= nelem(fmts) || fmts[have->format].ssz < 1 && (change & SDL_AUDIO_ALLOW_FORMAT_CHANGE) != 0)
+ have->format = AUDIO_S16;
+ if(have->channels < 1 && (change & SDL_AUDIO_ALLOW_CHANNELS_CHANGE) != 0)
+ have->channels = 2;
+ if(have->samples < 2 || (have->samples & (have->samples-1)) != 0){+ if(change & SDL_AUDIO_ALLOW_SAMPLES_CHANGE)
+ have->samples = Audiosamples;
+ else{+ werrstr("invalid number of samples: %d", have->samples);+ goto err;
+ }
+ }
+
+ if((ssz = convspec(have, spec, sizeof(spec))) < 1)
+ goto err;
+
+ a->userdata = have->userdata;
+ a->cb = have->callback;
+ a->wait = chancreate(sizeof(ulong), 0);
+ a->bufsz = have->samples * ssz * have->channels;
+ a->buf = malloc(a->bufsz);
+ if(a->wait == nil || a->buf == nil){+ werrstr("memory");+ goto err;
+ }
+
+ a->paused = 1;
+ a->pid = -1;
+ a->pidconv = -1;
+ if(have->freq != 44100 || have->format != AUDIO_S16 || have->channels != 2){+ if((fd = open(a->name, a->mode)) < 0)
+ goto err;
+ pipe(p);
+ setpipebuf(p[0], a->bufsz);
+ if((a->pidconv = rfork(RFPROC|RFNOTEG|RFFDG|RFCENVG)) == 0){+ dup(fd, rec ? 0 : 1); close(fd);
+ dup(p[0], rec ? 1 : 0); close(p[0]);
+ close(p[1]);
+ //close(2);
+ if(execl("/bin/audio/pcmconv", "pcmconv", rec ? "-o" : "-i", spec, nil) != 0)+ exits("%r");+ }else if(a->pidconv < 0){+ werrstr("pcmconv: %r");+ close(fd);
+ goto err;
+ }
+ a->fd = p[1];
+ close(p[0]);
+ close(fd);
+ }else if(a->fd < 0 && (a->fd = open(a->name, a->mode|OCEXEC)) < 0)
+ goto err;
+
+ return id;
+err:
+ werrstr("SDL_OpenAudioDevice: %r");+ if(a->fd >= 0){+ close(a->fd);
+ a->fd = -1;
+ }
+ free(a->buf);
+ a->buf = nil;
+ if(a->wait != nil){+ chanfree(a->wait);
+ a->wait = nil;
+ }
+
+ return 0;
+}
+
+void
+SDL_CloseAudioDevice(SDL_AudioDeviceID id)
+{+ Audiodev *a;
+ Waitmsg *w;
+ int pid;
+
+ a = &au[id];
+
+ if(a->fd < 0)
+ return;
+
+ lock(a);
+ close(a->fd);
+ unlock(a);
+
+ if(a->pid >= 0)
+ recvul(a->wait);
+ chanfree(a->wait);
+
+ free(a->buf);
+ a->fd = -1;
+ a->pid = -1;
+again:
+ if(a->pidconv >= 0 && (w = wait()) != nil){+ if(w->msg[0])
+ fprint(2, "SDL_CloseAudioDevice: %s: %s\n", a->name, w->msg);
+ pid = w->pid;
+ free(w);
+ if(pid != a->pidconv)
+ goto again;
+ }
+}
--- /dev/null
+++ b/libnpe_sdl3/_sdl.h
@@ -1,0 +1,67 @@
+#include <SDL3/SDL.h>
+#include <sys/stat.h>
+#include <stdint.h>
+#include <string.h>
+#include <tos.h>
+#include <thread.h>
+#include <draw.h>
+#include <memdraw.h>
+#include <mouse.h>
+#include <cursor.h>
+#include <plumb.h>
+#include "_npe.h"
+#undef waitpid
+
+struct SDL_Texture {+ Memimage *m;
+ Memimage *mod;
+ SDL_BlendMode blend;
+};
+struct SDL_Renderer {+ int logiw;
+ int logih;
+};
+
+struct npe_sdl {+ Mousectl *mctl;
+ Rectangle grabout;
+ Point center;
+ int mgrab;
+ Mouse m, om;
+ Point Δ;
+ int hints;
+ int mredraw;
+ int fullredraw;
+ int textinput;
+ int physw, physh;
+ float scale;
+ struct {+ Uint32 r, g, b, a;
+ } defmask;
+ Memimage *back;
+ Memimage *rendcol;
+ SDL_Texture *target;
+};
+
+enum {+ Altf4noclose = 1<<0,
+};
+
+extern struct npe_sdl npe_sdl;
+
+int npe_sdl_init_input(void);
+int npe_sdl_init_draw(void);
+int npe_sdl_init_audio(void);
+int npe_sdl_init_tex(void);
+int npe_sdl_init_gamepad(void);
+void npe_sdl_kill_audio(void);
+void npe_sdl_kill_draw(void);
+void npe_sdl_kill_gamepad(void);
+void npe_sdl_kill_input(void);
+void* npe_sdl_scale(u32int*, int, int, u32int*, int, int);
+void npe_draw_cursor(void);
+
+ulong mask2chan(int, Uint32, Uint32, Uint32, Uint32);
+int chan2mask(Uint32, int*, Uint32*, Uint32*, Uint32*, Uint32*);
+Uint32 chan2pixel(ulong);
+ulong pixel2chan(Uint32);
--- /dev/null
+++ b/libnpe_sdl3/audio.c
@@ -1,0 +1,743 @@
+#include "_sdl.h"
+
+#ifdef FIXME
+enum {+ Aout = 2,
+ Arec,
+
+ Audiosamples = 8192,
+};
+
+typedef struct Audiodev Audiodev;
+
+struct Audiodev {+ Lock;
+ void (*cb)(void *, Uint8 *, int);
+ void *userdata;
+ char *name;
+ Channel *wait;
+ Uint8 *buf;
+ int bufsz;
+ int paused;
+ int fd;
+ int pid;
+ int pidconv;
+ int mode;
+};
+
+/* FIXME extra USB audio devices? */
+static Audiodev au[4] = {+ [0] = {.fd = -1, .mode = -1},+ [1] = {.fd = -1, .mode = -1},+ [Aout] = {.name = "/dev/audio", .fd = -1, .pid = -1, .mode = OWRITE},+ [Arec] = {.name = "/dev/audio", .fd = -1, .pid = -1, .mode = OREAD},+};
+
+static struct {+ char *spec;
+ int ssz; /* samples size */
+}fmts[] = {+ [SDL_AUDIO_U8] = {"u8", 1},+ [SDL_AUDIO_S8] = {"s8", 1},+ [SDL_AUDIO_S16LE] = {"s16", 2},+ [SDL_AUDIO_S16BE] = {"S16", 2},+ [SDL_AUDIO_S32LE] = {"s32", 4},+ [SDL_AUDIO_S32BE] = {"S32", 4},+ [SDL_AUDIO_F32LE] = {"f32", 4},+ [SDL_AUDIO_F32BE] = {"F32", -1}, /* FIXME big endian f32 not supported by pcmconv */+};
+
+static void
+audiothread(void *p)
+{+ Audiodev *a;
+
+ a = p;
+ threadsetname("%s (%s)", a->name, a->mode == OREAD ? "out" : "in");+
+ for(;;){+ if(a->mode == OREAD && readn(a->fd, a->buf, a->bufsz) != a->bufsz)
+ break;
+
+ lock(a);
+ if(a->mode == OWRITE && a->paused)
+ memset(a->buf, 0, a->bufsz);
+ else
+ a->cb(a->userdata, a->buf, a->bufsz);
+ unlock(a);
+
+ if(a->mode == OWRITE && write(a->fd, a->buf, a->bufsz) != a->bufsz)
+ break;
+ }
+
+ lock(a);
+ (a->mode == OWRITE ? write : read)(a->fd, a->buf, 0);
+ chanclose(a->wait);
+ unlock(a);
+
+ threadexits(nil);
+}
+
+void
+SDL_PauseAudioDevice(SDL_AudioDeviceID id, SDL_bool pause)
+{+ Audiodev *a;
+
+ a = &au[id];
+ if(a->paused && !pause){+ if(a->pid < 0)
+ a->pid = proccreate(, a, 4096);
+ a->paused = 0;
+ }else if(!a->paused && pause){+ a->paused = 1;
+ }
+}
+
+void
+SDL_PauseAudio(int pause_on)
+{+ SDL_PauseAudioDevice(1, pause_on);
+}
+
+static int
+convspec(SDL_AudioSpec *s, char *spec, int n)
+{+ int ssz;
+
+ ssz = -1;
+ if(s->format < 0 || s->format >= nelem(fmts))
+ werrstr("invalid audio format: #%d", s->format);+ else if(fmts[s->format].ssz < 1)
+ werrstr("unsupported audio format: #%d", s->format);+ else if(s->channels < 1)
+ werrstr("invalid number of channels: %d", s->channels);+ else if(s->freq < 1)
+ werrstr("invalid sampling rate: %d", s->freq);+ else if(snprint(spec, n, "%sc%dr%d", fmts[s->format].spec, s->channels, s->freq) >= n)
+ werrstr("audio spec does not fit");+ else
+ ssz = fmts[s->format].ssz;
+
+ return ssz;
+}
+
+// https://wiki.libsdl.org/SDL3/SDL_ConvertAudio
+int
+SDL_ConvertAudio(SDL_AudioCVT *cvt)
+{+ USED(cvt);
+ return 0;
+}
+
+// https://wiki.libsdl.org/SDL3/SDL_BuildAudioCVT
+int
+SDL_BuildAudioCVT(SDL_AudioCVT *cvt, SDL_AudioFormat src_format, Uint8 src_channels, int src_rate, SDL_AudioFormat dst_format, Uint8 dst_channels, int dst_rate)
+{+ USED(cvt);
+ USED(src_format, src_channels, src_rate);
+ USED(dst_format, dst_channels, dst_rate);
+ return 0;
+}
+
+static void
+setpipebuf(int f, int sz)
+{+ Dir d;
+
+ nulldir(&d);
+ d.length = sz;
+ dirfwstat(f, &d);
+}
+
+void
+SDL_CloseAudioDevice(SDL_AudioDeviceID id)
+{+ Audiodev *a;
+ Waitmsg *w;
+ int pid;
+
+ a = &au[id];
+
+ if(a->fd < 0)
+ return;
+
+ lock(a);
+ close(a->fd);
+ unlock(a);
+
+ if(a->pid >= 0)
+ recvul(a->wait);
+ chanfree(a->wait);
+
+ free(a->buf);
+ a->fd = -1;
+ a->pid = -1;
+again:
+ if(a->pidconv >= 0 && (w = wait()) != nil){+ if(w->msg[0])
+ fprint(2, "SDL_CloseAudioDevice: %s: %s\n", a->name, w->msg);
+ pid = w->pid;
+ free(w);
+ if(pid != a->pidconv)
+ goto again;
+ }
+}
+
+SDL_AudioDeviceID
+SDL_OpenAudioDevice(char *dev, int rec, SDL_AudioSpec *want, SDL_AudioSpec *have, u32int change)
+{+ SDL_AudioDeviceID id;
+ int p[2], ssz, fd;
+ char spec[16];
+ Audiodev *a;
+
+ /* FIXME look for extra USB devices? */
+ USED(dev);
+
+ id = rec ? Arec : Aout;
+ a = &au[id];
+
+ if(have == nil)
+ have = want;
+ *have = *want;
+ if(have->freq < 44100 && (change & SDL_AUDIO_ALLOW_FREQUENCY_CHANGE) != 0)
+ have->freq = 44100;
+ if(have->format <= 0 || have->format >= nelem(fmts) || fmts[have->format].ssz < 1 && (change & SDL_AUDIO_ALLOW_FORMAT_CHANGE) != 0)
+ have->format = SDL_AUDIO_S16;
+ if(have->channels < 1 && (change & SDL_AUDIO_ALLOW_CHANNELS_CHANGE) != 0)
+ have->channels = 2;
+ if(have->samples < 2 || (have->samples & (have->samples-1)) != 0){+ if(change & SDL_AUDIO_ALLOW_SAMPLES_CHANGE)
+ have->samples = Audiosamples;
+ else{+ werrstr("invalid number of samples: %d", have->samples);+ goto err;
+ }
+ }
+
+ if((ssz = convspec(have, spec, sizeof(spec))) < 1)
+ goto err;
+
+ a->userdata = have->userdata;
+ a->cb = have->callback;
+ a->wait = chancreate(sizeof(ulong), 0);
+ a->bufsz = have->samples * ssz * have->channels;
+ a->buf = malloc(a->bufsz);
+ if(a->wait == nil || a->buf == nil){+ werrstr("memory");+ goto err;
+ }
+
+ a->paused = 1;
+ a->pid = -1;
+ a->pidconv = -1;
+ if(have->freq != 44100 || have->format != AUDIO_S16 || have->channels != 2){+ if((fd = open(a->name, a->mode)) < 0)
+ goto err;
+ pipe(p);
+ setpipebuf(p[0], a->bufsz);
+ if((a->pidconv = rfork(RFPROC|RFNOTEG|RFFDG|RFCENVG)) == 0){+ dup(fd, rec ? 0 : 1); close(fd);
+ dup(p[0], rec ? 1 : 0); close(p[0]);
+ close(p[1]);
+ //close(2);
+ if(execl("/bin/audio/pcmconv", "pcmconv", rec ? "-o" : "-i", spec, nil) != 0)+ exits("%r");+ }else if(a->pidconv < 0){+ werrstr("pcmconv: %r");+ close(fd);
+ goto err;
+ }
+ a->fd = p[1];
+ close(p[0]);
+ close(fd);
+ }else if(a->fd < 0 && (a->fd = open(a->name, a->mode|OCEXEC)) < 0)
+ goto err;
+
+ return id;
+err:
+ werrstr("SDL_OpenAudioDevice: %r");+ if(a->fd >= 0){+ close(a->fd);
+ a->fd = -1;
+ }
+ free(a->buf);
+ a->buf = nil;
+ if(a->wait != nil){+ chanfree(a->wait);
+ a->wait = nil;
+ }
+
+ return 0;
+}
+#endif
+
+
+
+
+SDL_AudioDeviceID SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK = -1; /* 0 is null/invalid */
+SDL_AudioDeviceID SDL_AUDIO_DEVICE_DEFAULT_RECORDING = -1;
+
+struct SDL_AudioStream {+ QLock;
+ int fd;
+ int type;
+ char *name;
+ SDL_AudioStreamCallback fn;
+ void *aux;
+ SDL_AudioSpec;
+ int delay;
+ int tail;
+ int framesz;
+ int nframes;
+ uchar *buf;
+ int avail;
+ int paused; /* ugh */
+ Channel *c;
+ Channel *pausec;
+};
+static SDL_AudioStream *devs;
+static Channel *ach;
+static int ndevs;
+
+bool
+SDL_GetAudioDeviceFormat(SDL_AudioDeviceID id, SDL_AudioSpec *spec, int *nsamp)
+{+ SDL_AudioStream *as;
+
+ if(--id < 0 || id >= ndevs){+ werrstr("invalid device id %d", id);+ return false;
+ }
+ as = devs + id;
+ if(spec != nil)
+ *spec = as->SDL_AudioSpec;
+ if(nsamp != nil)
+ *nsamp = as->delay;
+ return true;
+}
+
+bool
+SDL_GetAudioStreamFormat(SDL_AudioStream *as, SDL_AudioSpec *in, SDL_AudioSpec *out)
+{+ if(in != nil)
+ *in = as->SDL_AudioSpec;
+ if(out != nil)
+ *out = as->SDL_AudioSpec;
+ return true;
+}
+
+bool
+SDL_SetAudioStreamFormat(SDL_AudioStream *as, SDL_AudioSpec *in, SDL_AudioSpec *out)
+{+ /* FIXME: set new input and output formats from ptr */
+ if(in != nil){+ if(memcmp(&as->SDL_AudioSpec, in, sizeof *in) != 0){+ /* FIXME */
+ }
+ as->SDL_AudioSpec = *in;
+ }
+ if(out != nil){+ if(memcmp(&as->SDL_AudioSpec, out, sizeof *out) != 0){+ /* FIXME */
+ }
+ as->SDL_AudioSpec = *out;
+ }
+ return true;
+}
+
+int
+SDL_GetAudioStreamAvailable(SDL_AudioStream *as)
+{+ return as->avail;
+}
+
+int
+SDL_GetAudioStreamData(SDL_AudioStream *as, void *buf, int n)
+{+ uchar *p;
+
+ if(n <= 0 || as->avail < as->framesz)
+ return 0;
+ if(as->avail < n)
+ n = as->avail;
+ else if(n > as->framesz)
+ n = as->framesz;
+ qlock(as);
+ p = as->buf + as->tail * as->framesz;
+ memcpy(buf, p, n);
+ as->avail -= n;
+ as->tail = (as->tail + 1) % as->nframes;
+ qunlock(as);
+ return n;
+}
+
+bool
+SDL_PutAudioStreamData(SDL_AudioStream *as, const void *buf, int n)
+{+ int i, m;
+ uchar *p, *t, *e;
+
+ if(as->buf == nil)
+ return false;
+ i = as->tail; /* FIXME: actually head */
+ for(; n>0; n-=m){+ p = as->buf + i * as->framesz;
+ t = p + as->avail;
+ e = as->buf + as->nframes * as->framesz;
+ m = e - t < n ? e - t : n;
+ qlock(as);
+ memcpy(t, buf, m);
+ as->avail += m;
+ qunlock(as);
+ while(as->avail >= as->framesz){+ if(send(as->c, &i) < 0)
+ return false; /* FIXME: recovery? */
+ i = (i + 1) % as->nframes;
+ qlock(as);
+ as->avail -= as->framesz;
+ qunlock(as);
+ }
+ }
+ return true;
+}
+
+SDL_AudioDeviceID *
+SDL_GetAudioRecordingDevices(int *count)
+{+ int nbuf;
+ SDL_AudioDeviceID *buf;
+ SDL_AudioStream *as, *ae;
+
+ nbuf = 0;
+ buf = nil;
+ if(count != nil)
+ *count = 0;
+ for(as=devs, ae=as+ndevs; as<ae; as++){+ if(as->type == OWRITE)
+ continue;
+ if((buf = realloc(buf, (nbuf+1) * sizeof *buf)) == nil)
+ return nil;
+ buf[nbuf++] = as - devs + 1;
+ if(count != nil)
+ (*count)++;
+ }
+ if((buf = realloc(buf, (nbuf+1) * sizeof *buf)) == nil)
+ return nil;
+ buf[nbuf] = 0;
+ return buf;
+}
+
+SDL_AudioDeviceID *
+SDL_GetAudioPlaybackDevices(int *count)
+{+ int nbuf;
+ SDL_AudioDeviceID *buf;
+ SDL_AudioStream *as, *ae;
+
+ nbuf = 0;
+ buf = nil;
+ if(count != nil)
+ *count = 0;
+ for(as=devs, ae=as+ndevs; as<ae; as++){+ if(as->type == OREAD)
+ continue;
+ if((buf = realloc(buf, (nbuf+1) * sizeof *buf)) == nil)
+ return nil;
+ buf[nbuf++] = as - devs + 1;
+ if(count != nil)
+ (*count)++;
+ }
+ if((buf = realloc(buf, (nbuf+1) * sizeof *buf)) == nil)
+ return nil;
+ buf[nbuf] = 0;
+ return buf;
+}
+
+const char*
+SDL_GetAudioDeviceName(SDL_AudioDeviceID id)
+{+ SDL_AudioStream *as;
+
+ if(--id < 0 || id >= ndevs){+ werrstr("invalid device id %d", id);+ return nil;
+ }
+ as = devs + id;
+ return as->name;
+}
+
+static void
+arproc(void *arg)
+{+ int i, n;
+ uchar *p;
+ SDL_AudioStream *as, *ad, *ae;
+
+ for(ad=nil, as=devs, ae=as+ndevs; as<ae; as++)
+ if(strcmp(as->name, "/dev/audio") == 0){+ ad = as;
+ break;
+ }
+ as = arg;
+ i = 0;
+ for(;;){+ if(as->paused)
+ recvul(as->pausec);
+ p = as->buf + i * as->framesz;
+ if((n = read(as->fd, p, as->framesz)) < 0) /* FIXME: no eof? */
+ break;
+ if(ad->fd >= 0){+ fprint(2, "wrote directly %d\n", n);
+ write(ad->fd, p, n);
+ continue;
+ }
+ if(n == 0)
+ continue;
+ qlock(as);
+ if(as->fd < 0){+ qunlock(as);
+ break;
+ }
+ as->avail += n;
+ if(as->avail > as->nframes * as->framesz)
+ as->avail = as->nframes * as->framesz;
+ nbsend(ach, &n);
+ i = (i + 1) % as->nframes;
+ if(i == as->tail)
+ as->tail = (i + 1) % as->nframes;
+ qunlock(as);
+ }
+ qlock(as);
+ chanfree(as->pausec);
+ chanfree(as->c);
+ as->pausec = as->c = nil;
+ free(as->buf);
+ as->buf = nil;
+ qunlock(as);
+}
+
+static void
+awproc(void *arg)
+{+ int i, n;
+ uchar *p;
+ SDL_AudioStream *as;
+
+ as = arg;
+ for(;;){+ if(as->paused)
+ recvul(as->pausec);
+ if(recv(as->c, &i) < 0)
+ break;
+ n = as->framesz;
+ p = as->buf + i * n;
+ fprint(2, "write %d to %s\n", n, as->name);
+ if(write(as->fd, p, n) != n){+ fprint(2, "awproc: %r\n");
+ break;
+ }
+ }
+ qlock(as);
+ chanfree(as->pausec);
+ chanfree(as->c);
+ as->pausec = as->c = nil;
+ free(as->buf);
+ as->buf = nil;
+ qunlock(as);
+}
+
+/* FIXME: bullshit */
+static void
+acbproc(void *arg)
+{+ int n;
+ SDL_AudioStream *as, *ae;
+ Channel *c;
+
+ c = arg;
+ for(;;){+ if(recv(c, &n) < 0)
+ break;
+ for(as=devs, ae=as+ndevs; as<ae; as++)
+ if(as->fd >= 0 && as->fn != nil)
+ as->fn(as->aux, as, n, as->avail);
+ }
+ chanfree(c);
+}
+
+bool
+SDL_ResumeAudioStreamDevice(SDL_AudioStream *as)
+{+ if(!as->paused)
+ return true;
+ as->paused = 0;
+ nbsendul(as->pausec, 1);
+ return true;
+}
+
+bool
+SDL_PauseAudioStreamDevice(SDL_AudioStream *as)
+{+ if(as->paused)
+ return true;
+ as->paused = 1;
+ return true;
+}
+
+SDL_AudioStream*
+SDL_OpenAudioDeviceStream(SDL_AudioDeviceID id, const SDL_AudioSpec *f, SDL_AudioStreamCallback fn, void *aux)
+{+ SDL_AudioStream *as;
+
+ if(--id < 0 || id >= ndevs){+ werrstr("invalid device id %d", id);+ return nil;
+ }
+ as = devs + id;
+ /* FIXME: duplicate entries for rw devices? */
+ //if(as->fd >= 0 || !canqlock(as)){+ if(as->fd >= 0){+ werrstr("in use");+ return nil;
+ }
+ if((as->fd = open(as->name, as->type)) < 0)
+ return nil;
+ as->aux = aux;
+ as->tail = 0;
+ /* FIXME: format */
+ if((as->buf = mallocz(as->nframes * as->framesz, 1)) == nil)
+ goto Err;
+ if((as->c = chancreate(sizeof(int), 8)) == nil)
+ goto Err;
+ if((as->pausec = chancreate(sizeof(int), 0)) == nil)
+ goto Err;
+ if(proccreate(as->type != OREAD ? awproc : arproc, as, 8192) < 0)
+ goto Err;
+ USED(f); /* FIXME: format conversion */
+ as->fn = fn;
+ return as;
+Err:
+ if(as->c != nil){+ chanfree(as->c);
+ as->c = nil;
+ }
+ if(as->pausec != nil){+ chanfree(as->pausec);
+ as->pausec = nil;
+ }
+ close(as->fd);
+ as->fd = -1;
+ free(as->buf);
+ return nil;
+}
+
+void
+SDL_DestroyAudioStream(SDL_AudioStream *as)
+{+ if(as->fd < 0)
+ return;
+ qlock(as);
+ close(as->fd);
+ as->fd = -1;
+ chanclose(as->c);
+ chanclose(as->pausec);
+ as->fn = nil;
+ as->aux = nil;
+ qunlock(as);
+}
+
+static int
+probe(void)
+{+ int i, n, dfd, type;
+ ulong len;
+ char *name, dir[] = "/dev/";
+ SDL_AudioStream as;
+ Dir *d;
+
+ if((dfd = open(dir, OREAD)) < 0)
+ return -1;
+ while((n = dirread(dfd, &d)) > 0){+ for(i = 0; i < n; i++){+ len = strlen(d[i].name);
+ if(d[i].mode & DMDIR
+ || len < 5
+ || strncmp(d[i].name, "audio", 5) != 0
+ || strncmp(d[i].name+5, "ctl", 3) == 0
+ || strncmp(d[i].name+5, "stat", 4) == 0)
+ continue;
+ if((name = smprint("%s%s", dir, d[i].name)) == nil){+ free(d);
+ return -1;
+ }
+ type = -1;
+ if(access(name, AREAD) == 0)
+ type = OREAD;
+ if(access(name, AWRITE) == 0){+ /*
+ if(type != -1)
+ type = ORDWR;
+ else
+ */
+ type = OWRITE;
+ }
+ if(type == -1){+ free(name);
+ continue;
+ }
+ if(strcmp(d[i].name, "audio") == 0){+ if(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK == -1 && type != OREAD)
+ SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK = ndevs + 1;
+ if(SDL_AUDIO_DEVICE_DEFAULT_RECORDING == -1 && type != OWRITE)
+ SDL_AUDIO_DEVICE_DEFAULT_RECORDING = ndevs + 1;
+ }
+ memset(&as, 0, sizeof as);
+ as.fd = -1;
+ as.type = type;
+ as.name = name;
+ /* FIXME: read */
+ as.freq = 44100;
+ as.channels = 2;
+ as.format = SDL_AUDIO_S16LE;
+ as.delay = 1764;
+ as.framesz = as.delay * 2 * as.channels;
+ as.nframes = 8;
+ if((devs = realloc(devs, (ndevs+1) * sizeof *devs)) == nil){+ free(name);
+ free(d);
+ return -1; /* FIXME: devs is fucked now, we'll just segfault */
+ }
+ devs[ndevs++] = as;
+ }
+ free(d);
+ }
+ close(dfd);
+ if(ndevs == 0){+ werrstr("no devices found");+ return -1;
+ }
+ return 0;
+}
+
+void
+npe_sdl_kill_audio(void)
+{+ SDL_AudioStream *as, *ae;
+
+ chanclose(ach);
+ for(as=devs, ae=as+ndevs; as<ae; as++)
+ SDL_DestroyAudioStream(as);
+}
+
+int
+npe_sdl_init_audio(void)
+{+ if(probe() < 0)
+ return -1;
+ if((ach = chancreate(sizeof(int), 16)) == nil)
+ return -1;
+ if(proccreate(acbproc, ach, 8192) < 0)
+ return -1;
+ return 0;
+}
--- /dev/null
+++ b/libnpe_sdl3/cursor.c
@@ -1,0 +1,144 @@
+#include "_sdl.h"
+
+struct SDL_Cursor {+ Image *i;
+ Image *m;
+ Point hot;
+};
+static SDL_Cursor *oldcursor, *cursor;
+static Cursor nocursor = {+ {0, 0},+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+};
+static int showcursor = SDL_ENABLE;
+
+bool
+SDL_ShowCursor(void)
+{+ showcursor = 1;
+ setcursor(npe_sdl.mctl, cursor == nil ? nil : &nocursor);
+ return true;
+}
+
+bool
+SDL_HideCursor(void)
+{+ showcursor = 0;
+ setcursor(npe_sdl.mctl, &nocursor);
+ return true;
+}
+
+SDL_Cursor *
+SDL_CreateColorCursor(SDL_Surface *s, int hot_x, int hot_y)
+{+ SDL_Cursor *c;
+ Rectangle r;
+ uchar *m;
+ int n;
+
+ m = nil;
+ if((c = calloc(1, sizeof(*c))) == nil){+ werrstr("memory");+ goto err;
+ }
+
+ r = Rect(0, 0, s->w, s->h);
+ if(s->keyset){+ if((c->m = allocimage(display, r, GREY8, 0, DTransparent)) == nil)
+ goto err;
+ if((m = malloc(s->w * s->h)) == nil)
+ goto err;
+ for(n = 0; n < s->w * s->h; n++){+ m[n] = ((u32int*)s->pixels)[n] == s->key ? 0x00 : 0xff;
+ if(m[n] == 0)
+ ((u32int*)s->pixels)[n] = 0;
+ }
+ if(loadimage(c->m, r, m, n) < 1)
+ goto err;
+ free(m);
+ m = nil;
+ }
+ if((c->i = allocimage(display, r, s->keyset ? XRGB32 : ARGB32, 0, DTransparent)) == nil)
+ goto err;
+ n = s->w * s->h * 4; /* FIXME non-ARGB8888 */
+ if(loadimage(c->i, r, s->pixels, n) < 1)
+ goto err;
+
+ c->hot = Pt(hot_x, hot_y);
+
+ return c;
+err:
+ werrstr("SDL_CreateColorCursor: %r");+ if(c != nil){+ freeimage(c->i);
+ freeimage(c->m);
+ }
+ free(c);
+ free(m);
+ return nil;
+}
+
+SDL_Cursor *
+SDL_GetDefaultCursor(void)
+{+ return nil;
+}
+
+SDL_Cursor *
+SDL_CreateSystemCursor(SDL_SystemCursor id)
+{+ /* FIXME */
+ USED(id);
+
+ return nil;
+}
+
+void
+SDL_SetCursor(SDL_Cursor *c)
+{+ if(cursor != c){+ cursor = c;
+ npe_sdl.mredraw = 1;
+ setcursor(npe_sdl.mctl, (cursor == nil && showcursor) ? nil : &nocursor);
+ }
+}
+
+void
+SDL_FreeCursor(SDL_Cursor *c)
+{+ freeimage(c->i);
+ free(c);
+ if(cursor == c){+ oldcursor = nil;
+ cursor = nil;
+ }
+}
+
+void
+npe_draw_cursor(void)
+{+ Rectangle r, clipr;
+
+ if(cursor == nil || !showcursor)
+ return;
+ r.min = subpt(npe_sdl.m.xy, cursor->hot);
+ r.max = addpt(r.min, cursor->i->r.max);
+ if(!npe_sdl.fullredraw && oldcursor != nil){+ clipr.min = subpt(npe_sdl.om.xy, oldcursor->hot);
+ clipr.max = addpt(clipr.min, oldcursor->i->r.max);
+ combinerect(&clipr, r);
+ replclipr(screen, 0, clipr);
+ npe_sdl.om.xy = npe_sdl.m.xy;
+ }
+ draw(screen, r, cursor->i, cursor->m, ZP);
+ oldcursor = cursor;
+}
--- /dev/null
+++ b/libnpe_sdl3/events.c
@@ -1,0 +1,637 @@
+#include "_sdl.h"
+
+enum {+ /* FIXME missing plumber→dropfile */
+ Ckey,
+ Ckeytype,
+ Cmouse,
+ Cresize,
+ Cevent,
+ Numchan,
+
+ Rdown = 0,
+ Rup,
+ Rrepeat,
+};
+
+static int kmod;
+static Rune rune;
+static Keyboardctl kctl;
+static int quitreq;
+static SDL_Event evt;
+
+static Alt salt[Numchan+1] = {+ [Ckey] = { nil, &rune, CHANRCV },+ [Ckeytype] = { nil, nil, CHANNOP },+ [Cmouse] = { nil, &npe_sdl.m, CHANRCV },+ [Cresize] = { nil, nil, CHANRCV },+ [Cevent] = { nil, &evt, CHANRCV },+ [Numchan] = { nil, nil, CHANNOBLK },+};
+
+static void kbdproc(void *);
+static void mouseproc(void *);
+
+void
+npe_sdl_kill_input(void)
+{+}
+
+int
+npe_sdl_init_input(void)
+{+ if((npe_sdl.mctl = initmouse(nil, screen)) == nil)
+ return -1;
+
+ salt[Ckey].c = chancreate(sizeof(Rune), 20);
+ salt[Ckeytype].c = chancreate(sizeof(int), 20);
+ salt[Cmouse].c = chancreate(sizeof(Mouse), 20);
+ salt[Cresize].c = npe_sdl.mctl->resizec;
+ salt[Cevent].c = chancreate(sizeof(SDL_Event), 20);
+ kctl.c = salt[Ckey].c; /* for enter() */
+
+ if(salt[Ckey].c == nil || salt[Ckeytype].c == nil || salt[Cmouse].c == nil)
+ return -1;
+
+ if(proccreate(kbdproc, nil, 4096) < 0 || proccreate(mouseproc, nil, 4096) < 0)
+ return -1;
+
+ return 0;
+}
+
+Uint32
+SDL_GetWindowID(SDL_Window *win)
+{+ USED(win);
+ return 1;
+}
+
+SDL_Keymod
+SDL_GetModState(void)
+{+ return kmod;
+}
+
+int
+SDL_EventState(Uint32, int)
+{+ return 0;
+}
+
+Uint32
+SDL_RegisterEvents(int n)
+{+ static Uint32 userevent = SDL_EVENT_USER;
+ Uint32 u;
+
+ if(userevent+n > SDL_EVENT_LAST || n < 0)
+ u = (Uint32)-1;
+ else
+ u = userevent += n;
+
+ return u;
+}
+
+int
+SDL_PushEvent(SDL_Event *event)
+{+ fprint(2, "push event %d\n", event->type);
+ return send(salt[Cevent].c, event) > 0 ? 1 : -1;
+}
+
+int
+SDL_QuitRequested(void)
+{+ return quitreq;
+}
+
+struct {+ SDL_EventFilter f;
+ void *aux;
+} filter = {+ nil,
+ nil,
+};
+
+void
+SDL_SetEventFilter(SDL_EventFilter f, void *userdata)
+{+ filter.f = f;
+ filter.aux = userdata;
+}
+
+void
+SDL_PumpEvents(void)
+{+ /* FIXME does it matter? */
+}
+
+/* FIXME */
+static Uint8
+runetojoy(Rune r)
+{+ /* FIXME */
+ switch(r){+ case Kleft: return SDL_GAMEPAD_BUTTON_DPAD_LEFT;
+ case Kright: return SDL_GAMEPAD_BUTTON_DPAD_RIGHT;
+ case Kup: return SDL_GAMEPAD_BUTTON_DPAD_UP;
+ case Kdown: return SDL_GAMEPAD_BUTTON_DPAD_DOWN;
+ case 'z': return SDL_GAMEPAD_BUTTON_SOUTH;
+ case 'x': return SDL_GAMEPAD_BUTTON_EAST;
+ case ' ': return SDL_GAMEPAD_BUTTON_BACK;
+ case '\n': return SDL_GAMEPAD_BUTTON_START;
+ case 'r': return SDL_GAMEPAD_BUTTON_LEFT_STICK;
+ case 'q': return SDL_GAMEPAD_BUTTON_RIGHT_STICK;
+ }
+ sysfatal("runetojoy: %r");+}
+
+#define ISTEXT(r) ((r) >= 0x20 && ((r) < KF || (r) >= KF+0x1000))
+
+int
+SDL_PollEvent(SDL_Event *e)
+{+ int x, t;
+
+ if(e == nil) /* FIXME need to buffer the event so it won't get lost */
+ return 0;
+
+ switch(x = alt(salt)){+ case -1:
+ fprint(2, "alt: %r");
+ break;
+ Quit:
+ e->type = SDL_EVENT_QUIT;
+ quitreq = 1;
+ goto Filter;
+ Joybut:
+ e->gbutton.down = t != Rup;
+ e->gbutton.type = e->gbutton.down ? SDL_EVENT_GAMEPAD_BUTTON_DOWN : SDL_EVENT_GAMEPAD_BUTTON_UP;
+ e->type = e->gbutton.type;
+ e->gbutton.timestamp = nsec();
+ e->gbutton.which = 0;
+ e->gbutton.button = runetojoy(rune);
+ goto Filter;
+ case Ckey:
+ memset(e, 0, sizeof(*e));
+ recv(salt[Ckeytype].c, &t);
+ /* FIXME
+ if(npe_sdl.textinput && ISTEXT(rune)){+ if(t == Rup)
+ break;
+ e->type = SDL_TEXTINPUT;
+ e->text.text[runetochar(e->text.text, &rune)] = 0;
+ goto Filter;
+ }else */
+ if((npe_sdl.hints & Altf4noclose) == 0
+ && (kmod & SDL_KMOD_LALT) != 0 && rune == (KF|4)){+ goto Quit;
+ }else{+ /* FIXME */
+ switch(rune){+ case Kdel:
+ goto Quit;
+ case Kleft:
+ case Kright:
+ case Kup:
+ case Kdown:
+ case 'z':
+ case 'x':
+ case ' ':
+ case '\n':
+ case 'q':
+ case 'r':
+ goto Joybut;
+ }
+ e->key.down = t != Rup;
+ e->type = e->key.down ? SDL_EVENT_KEY_DOWN : SDL_EVENT_KEY_UP;
+ e->key.repeat = t == Rrepeat;
+ e->key.scancode = SDL_GetScancodeFromKey(rune);
+ e->key.raw = rune;
+ if(rune == '\n')
+ rune = SDLK_RETURN;
+ e->key.key = rune;
+ goto Filter;
+ }
+ break;
+
+ case Cmouse:
+ break;
+#ifdef fuck
+ if(screen == nil)
+ break;
+
+ if(eqpt(npe_sdl.m.xy, Pt(-1, -1))){+ npe_sdl.m.xy = npe_sdl.center;
+ npe_sdl.om.xy = npe_sdl.center;
+ return 0; /* swallow */
+ }
+
+ memset(e, 0, sizeof(*e));
+ e->motion.x = (npe_sdl.m.xy.x - screen->r.min.x) * npe_sdl.scale;
+ e->motion.y = (npe_sdl.m.xy.y - screen->r.min.y) * npe_sdl.scale;
+ e->motion.xrel = (npe_sdl.m.xy.x - npe_sdl.om.xy.x) * npe_sdl.scale;
+ e->motion.yrel = (npe_sdl.m.xy.y - npe_sdl.om.xy.y) * npe_sdl.scale;
+
+ if(!eqpt(npe_sdl.m.xy, npe_sdl.om.xy)){+ npe_sdl.mredraw = 1;
+ if(npe_sdl.mgrab){1+ npe_sdl.Δ.x += npe_sdl.m.xy.x - npe_sdl.om.xy.x;
+ npe_sdl.Δ.y += npe_sdl.m.xy.y - npe_sdl.om.xy.y;
+ npe_sdl.om.xy = npe_sdl.m.xy;
+ }
+ if(npe_sdl.m.buttons == npe_sdl.om.buttons){+ e->type = SDL_MOUSEMOTION;
+ e->motion.state = npe_sdl.m.buttons;
+ goto Filter;
+ }
+ }
+ if(npe_sdl.m.buttons == npe_sdl.om.buttons)
+ break;
+ /* FIXME there is a lot of hope for multiple buttons to never change its state at the same time */
+ if((down = (npe_sdl.m.buttons & 1)) != (npe_sdl.om.buttons & 1)){ /* left */+ e->type = down ? SDL_MOUSEBUTTONDOWN : SDL_MOUSEBUTTONUP;
+ e->button.button = SDL_BUTTON_LEFT;
+ npe_sdl.om.buttons = (npe_sdl.om.buttons & ~1) | (npe_sdl.m.buttons & 1);
+ goto Filter;
+ }
+ if((down = (npe_sdl.m.buttons & 2)) != (npe_sdl.om.buttons & 2)){ /* middle */+ e->type = down ? SDL_MOUSEBUTTONDOWN : SDL_MOUSEBUTTONUP;
+ e->button.button = SDL_BUTTON_MIDDLE;
+ npe_sdl.om.buttons = (npe_sdl.om.buttons & ~2) | (npe_sdl.m.buttons & 2);
+ goto Filter;
+ }
+ if((down = (npe_sdl.m.buttons & 4)) != (npe_sdl.om.buttons & 4)){ /* right */+ e->type = down ? SDL_MOUSEBUTTONDOWN : SDL_MOUSEBUTTONUP;
+ e->button.button = SDL_BUTTON_RIGHT;
+ npe_sdl.om.buttons = (npe_sdl.om.buttons & ~4) | (npe_sdl.m.buttons & 4);
+ goto Filter;
+ }
+ if(npe_sdl.m.buttons & (8|16)){+ e->type = SDL_MOUSEWHEEL;
+ e->wheel.x = 0;
+ e->wheel.y = (npe_sdl.m.buttons & 8) ? 1 : -1;
+ goto Filter;
+ }
+ break;
+#endif
+
+ case Cresize:
+ memset(e, 0, sizeof(*e));
+ npe_sdl.fullredraw = 1;
+ while(getwindow(display, Refnone) != 1)
+ ;
+ e->type = SDL_EVENT_WINDOW_RESIZED;
+ /*
+ e->window.event = SDL_WINDOWEVENT_EXPOSED;
+ e->window.windowID = 1; //TODO more then one?
+ */
+ goto Filter;
+
+ case Cevent:
+ memcpy(e, &evt, sizeof(*e));
+ goto Filter;
+ }
+
+ return 0;
+
+Filter:
+ if(filter.f != nil && filter.f(filter.aux, e) == 0)
+ return 0;
+ return 1;
+}
+
+int
+SDL_WaitEvent(SDL_Event *e)
+{+ int r;
+ SDL_Event ee;
+
+ salt[Numchan].op = CHANEND;
+ if(e == nil){+ r = SDL_PollEvent(&ee);
+ SDL_PushEvent(&ee);
+ } else
+ r = SDL_PollEvent(e);
+ salt[Numchan].op = CHANNOBLK;
+
+ return r;
+}
+
+SDL_Scancode
+SDL_GetScancodeFromKey(SDL_Keycode r)
+{+ if(r >= 'a' && r <= 'z')
+ return r - 'a' + SDL_SCANCODE_A;
+ if(r >= '1' && r <= '9')
+ return r - '1' + SDL_SCANCODE_1;
+ if(r == '0') return SDL_SCANCODE_0;
+ if(r == '\n') return SDL_SCANCODE_RETURN;
+ if(r == Kesc) return SDL_SCANCODE_ESCAPE;
+ if(r == Kbs) return SDL_SCANCODE_BACKSPACE;
+ if(r == '\t') return SDL_SCANCODE_TAB;
+ if(r == ' ') return SDL_SCANCODE_SPACE;
+ if(r == '-') return SDL_SCANCODE_MINUS;
+ if(r == '=') return SDL_SCANCODE_EQUALS;
+ if(r == '[') return SDL_SCANCODE_LEFTBRACKET;
+ if(r == ']') return SDL_SCANCODE_RIGHTBRACKET;
+ if(r == '\\') return SDL_SCANCODE_BACKSLASH;
+ if(r == ';') return SDL_SCANCODE_SEMICOLON;
+ if(r == '\'') return SDL_SCANCODE_APOSTROPHE;
+ if(r == '/') return SDL_SCANCODE_SLASH;
+
+ if(r == Kright) return SDL_SCANCODE_RIGHT;
+ if(r == Kleft) return SDL_SCANCODE_LEFT;
+ if(r == Kdown) return SDL_SCANCODE_DOWN;
+ if(r == Kup) return SDL_SCANCODE_UP;
+ if(r == Kins) return SDL_SCANCODE_INSERT;
+ if(r == Khome) return SDL_SCANCODE_HOME;
+ if(r == Kpgup) return SDL_SCANCODE_PAGEUP;
+ if(r == Kdel) return SDL_SCANCODE_DELETE;
+ if(r == Kend) return SDL_SCANCODE_END;
+ if(r == Kpgdown) return SDL_SCANCODE_PAGEDOWN;
+ if(r == Kctl) return SDL_SCANCODE_LCTRL;
+ if(r == Kshift) return SDL_SCANCODE_LSHIFT;
+ if(r == Kalt) return SDL_SCANCODE_LALT;
+ if(r == Kmod4) return SDL_SCANCODE_LGUI;
+ if(r == Kaltgr) return SDL_SCANCODE_RALT;
+
+ if(r >= (KF|1) && r <= (KF|12)) return SDL_SCANCODE_F1 + r - (KF|1);
+
+/* FIXME
+ SDL_SCANCODE_PRINTSCREEN = 0x46,
+ SDL_SCANCODE_SCROLLLOCK,
+ SDL_SCANCODE_NUMLOCKCLEAR,
+ SDL_SCANCODE_KP_DIVIDE,
+ SDL_SCANCODE_KP_MULTIPLY,
+ SDL_SCANCODE_KP_MINUS,
+ SDL_SCANCODE_KP_PLUS,
+ SDL_SCANCODE_KP_ENTER,
+ SDL_SCANCODE_KP_1,
+ SDL_SCANCODE_KP_2,
+ SDL_SCANCODE_KP_3,
+ SDL_SCANCODE_KP_4,
+ SDL_SCANCODE_KP_5,
+ SDL_SCANCODE_KP_6,
+ SDL_SCANCODE_KP_7,
+ SDL_SCANCODE_KP_8,
+ SDL_SCANCODE_KP_9,
+ SDL_SCANCODE_KP_0,
+ SDL_SCANCODE_KP_PERIOD,
+ SDL_SCANCODE_NONUSBACKSLASH,
+ SDL_SCANCODE_NONUSHASH,
+*/
+ /* FIXME there are some missing */
+
+ if(r == L'`' || r == L'´') /* FIXME this is most likely wrong */
+ return SDL_SCANCODE_GRAVE;
+
+ return r;
+}
+
+static Uint8 kbdstate[SDL_NUM_SCANCODES];
+
+Uint8*
+SDL_GetKeyboardState(int *numkeys)
+{+ if(numkeys != nil)
+ *numkeys = SDL_NUM_SCANCODES;
+ return kbdstate;
+}
+
+char*
+SDL_GetKeyName(SDL_Keycode key)
+{+ /* upstream quirk: return value is valid only until next call */
+ static char res[64];
+
+ res[0] = res[1] = '\0';
+ if(key >= '0' && key <= '9' || key == '-' || key == '=' || key == '\'')
+ res[0] = key;
+ else if(key == '[' || key == ']' || key == '\\' || key == ';' || key == '/')
+ res[0] = key;
+ else if(key >= 'a' && key <= 'z')
+ res[0] = toupper(key);
+ else if(key >= (KF|1) && key <= (KF|12))
+ snprint(res, sizeof(res), "F%d", key - KF);
+ else if(key == '\n')
+ return "Return";
+ else if(key == Kesc)
+ return "Escape";
+ else if(key == Kbs)
+ return "Backspace";
+ else if(key == '\t')
+ return "Tab";
+ else if(key == ' ')
+ return "Space";
+ else if(key == Kright)
+ return "Right";
+ else if(key == Kleft)
+ return "Left";
+ else if(key == Kup)
+ return "Up";
+ else if(key == Kdown)
+ return "Down";
+ else if(key == Kins)
+ return "Insert";
+ else if(key == Khome)
+ return "Home";
+ else if(key == Kpgup)
+ return "PageUp";
+ else if(key == Kpgdown)
+ return "PageDown";
+ else if(key == Kdel)
+ return "Delete";
+ else if(key == Kend)
+ return "End";
+ else if(key == Kalt)
+ return "Left Alt";
+ else if(key == Kctl)
+ return "Left Ctrl";
+ else if(key == Kmod4)
+ return "Left GUI";
+ else if(key == Kaltgr)
+ return "Right Alt";
+ else if(key == Kshift)
+ return "Left Shift";
+
+ return res;
+}
+
+char *
+SDL_GetScancodeName(SDL_Scancode)
+{+ /* FIXME */
+ return "";
+}
+
+SDL_Keycode
+SDL_GetKeyFromName(char *name)
+{+ char *e;
+ long p;
+
+ if(strcmp(name, "Return") == 0) return '\n';
+ if(strcmp(name, "Escape") == 0) return Kesc;
+ if(strcmp(name, "Backspace") == 0) return Kbs;
+ if(strcmp(name, "Tab") == 0) return '\t';
+ if(strcmp(name, "Space") == 0) return ' ';
+ if(strcmp(name, "Right") == 0) return Kright;
+ if(strcmp(name, "Left") == 0) return Kleft;
+ if(strcmp(name, "Up") == 0) return Kup;
+ if(strcmp(name, "Down") == 0) return Kdown;
+ if(strcmp(name, "Insert") == 0) return Kins;
+ if(strcmp(name, "Home") == 0) return Khome;
+ if(strcmp(name, "PageUp") == 0) return Kpgup;
+ if(strcmp(name, "PageDown") == 0) return Kpgdown;
+ if(strcmp(name, "Delete") == 0) return Kdel;
+ if(strcmp(name, "End") == 0) return Kend;
+ if(strcmp(name, "Left Alt") == 0) return Kalt;
+ if(strcmp(name, "Left Ctrl") == 0) return Kctl;
+ if(strcmp(name, "Left GUI") == 0) return Kmod4;
+ if(strcmp(name, "Right Alt") == 0) return Kaltgr;
+ if(strcmp(name, "Left Shift") == 0) return Kshift;
+ if(name[0] == 'F' && name[1] != '\0'){+ p = strtol(name+1, &e, 10);
+ if(e == name + 1 || p < 1 || p > 12)
+ return SDLK_UNKNOWN;
+ return KF | p;
+ }
+ if(name[0] >= '0' && name[0] <= '9')
+ return name[0];
+ if(name[0] >= 'A' && name[0] <= 'Z')
+ return tolower(name[0]);
+ if(name[0] >= 'a' && name[0] <= 'z')
+ return name[0];
+ if(name[0] == '-' || name[0] == '=' || name[0] == '\'')
+ return name[0];
+ if(name[0] == '[' || name[0] == ']' || name[0] == '\\' || name[0] == ';' || name[0] == '/')
+ return name[0];
+ return SDLK_UNKNOWN;
+}
+
+static void
+kbdproc(void *)
+{+ char buf[128], buf2[128], *s;
+ int kfd, n, kbin, t;
+ Rune r, scan, o;
+
+ threadsetname("kbdproc");+ if((kfd = open("/dev/kbd", OREAD|OCEXEC)) < 0)+ sysfatal("/dev/kbd: %r");+ if((kbin = open("/dev/kbin", OWRITE|OCEXEC)) < 0)+ sysfatal("kbdproc: %r");+
+ buf2[0] = 0;
+ buf2[1] = 0;
+ buf[0] = 0;
+ kmod = 0;
+ for(;;){+ if(buf[0] != 0){+ n = strlen(buf)+1;
+ memmove(buf, buf+n, sizeof(buf)-n);
+ }
+ if(buf[0] == 0){+ n = read(kfd, buf, sizeof(buf)-1);
+ if(n <= 0)
+ break;
+ buf[n-1] = 0;
+ buf[n] = 0;
+ }
+
+ switch(buf[0]){+ case 'c':
+ if(chartorune(&r, buf+1) > 0 && r != Runeerror){+ if(ISTEXT(r))
+ o = r;
+ send(salt[Ckey].c, &o);
+ send(salt[Ckeytype].c, &t);
+ t = Rrepeat;
+ }
+ default:
+ continue;
+
+ case 'k':
+ s = buf+1;
+ memset(kbdstate, 0, sizeof(kbdstate));
+ while(*s){+ s += chartorune(&r, s);
+ scan = SDL_GetScancodeFromKey(r);
+ if(scan < nelem(kbdstate))
+ kbdstate[scan] = 1;
+ if(utfrune(buf2+1, r) == nil){+ t = Rdown;
+ if(r == Kalt){+ /* magic trick: write Alt scancode to disable the "compose" mode */
+ /* FIXME: does this work in both native AND drawterm? */
+ write(kbin, "\x46", 1);
+ kmod |= SDL_KMOD_LALT;
+ }else if (r == Kshift)
+ kmod |= SDL_KMOD_LSHIFT;
+ else if(r == Kctl)
+ kmod |= SDL_KMOD_LCTRL;
+ else if(r == Kaltgr)
+ kmod |= SDL_KMOD_RALT;
+ else if(r == Kmod4)
+ kmod |= SDL_KMOD_LGUI;
+ else{+ o = npe_sdl.textinput ? r : tolowerrune(r);
+ continue;
+ }
+ o = r;
+ send(salt[Ckey].c, &r);
+ send(salt[Ckeytype].c, &t);
+ t = Rrepeat;
+ }
+ }
+ break;
+
+ case 'K':
+ s = buf2+1;
+ memset(kbdstate, 0, sizeof(kbdstate));
+ while(*s){+ s += chartorune(&r, s);
+ scan = SDL_GetScancodeFromKey(r);
+ if(scan < nelem(kbdstate))
+ kbdstate[scan] = 1;
+ if(utfrune(buf+1, r) == nil){+ if(r == Kalt)
+ kmod &= ~SDL_KMOD_LALT;
+ else if(r == Kshift)
+ kmod &= ~SDL_KMOD_LSHIFT;
+ else if(r == Kctl)
+ kmod &= ~SDL_KMOD_LCTRL;
+
+ t = Rup;
+ send(salt[Ckey].c, &r);
+ send(salt[Ckeytype].c, &t);
+ }
+ }
+ break;
+ }
+ strcpy(buf2, buf);
+ }
+ if(n < 0)
+ fprint(2, "kbdproc: %r\n");
+ threadexits(nil);
+}
+
+static void
+mouseproc(void *)
+{+ Mouse m;
+ for(;;){+ recv(npe_sdl.mctl->c, &m);
+ send(salt[Cmouse].c, &m);
+ if(npe_sdl.mgrab == SDL_TRUE){+ if(!ptinrect(m.xy, npe_sdl.grabout)){+ moveto(npe_sdl.mctl, npe_sdl.center);
+ /* both events need to be pushed to make sure that the
+ * next delta makes sense; this one is discarded */
+ m.xy = Pt(-1,-1);
+ send(salt[Cmouse].c, &m);
+ }
+ }
+ }
+}
--- /dev/null
+++ b/libnpe_sdl3/gamepad.c
@@ -1,0 +1,99 @@
+#include "_sdl.h"
+
+enum{+ Dummy,
+};
+struct SDL_Gamepad{+ char *name;
+};
+static SDL_Gamepad dummy = {+ .name "dummy",
+};
+
+SDL_JoystickID*
+SDL_GetGamepads(int *count)
+{+ int n;
+ SDL_JoystickID *js;
+
+ //werrstr("SDL_GetGamepads: not implemented");+ //return nil; // nil on failure or null terminated array to be freed with SDL_free
+
+ n = 1;
+ if((js = mallocz((n+1) * sizeof *js, 1)) == nil)
+ sysfatal("SDL_GetGamepads: %r");+ js[0] = Dummy;
+ if(count != nil)
+ *count = n;
+ return js;
+}
+
+int
+SDL_AddGamepadMappingsFromIO(SDL_IOStream *src, bool closeio)
+{+ return 8;
+ //return 0; // number of mappings or -1 on fail
+}
+
+bool
+SDL_IsGamepad(SDL_JoystickID id)
+{+ if(id == Dummy)
+ return true;
+ return false;
+}
+
+SDL_Gamepad*
+SDL_OpenGamepad(SDL_JoystickID id)
+{+ if(id != Dummy)
+ return nil;
+ return &dummy;
+}
+
+const char*
+SDL_GetGamepadName(SDL_Gamepad *g)
+{+ return g->name;
+}
+
+const char*
+SDL_GetGamepadStringForAxis(SDL_GamepadAxis a)
+{+ return "";
+}
+
+const char*
+SDL_GetGamepadStringForButton(SDL_GamepadButton b)
+{+ return "";
+}
+
+SDL_GamepadButtonLabel
+SDL_GetGamepadButtonLabelForType(SDL_GamepadType t, SDL_GamepadButton b)
+{+ return SDL_GAMEPAD_BUTTON_LABEL_UNKNOWN;
+}
+
+SDL_GamepadButtonLabel
+SDL_GetGamepadButtonLabel(SDL_Gamepad *g, SDL_GamepadButton b)
+{+ return SDL_GAMEPAD_BUTTON_LABEL_UNKNOWN;
+}
+
+void
+SDL_CloseGamepad(SDL_Gamepad *g)
+{+ USED(g);
+}
+
+void
+npe_sdl_kill_gamepad(void)
+{+}
+
+int
+npe_sdl_init_gamepad(void)
+{+ return 0;
+}
--- /dev/null
+++ b/libnpe_sdl3/iostream.c
@@ -1,0 +1,265 @@
+#include "_sdl.h"
+#include <bio.h>
+
+typedef struct npe_sdl_io npe_sdl_io;
+
+typedef struct {+ const uchar *memdata;
+ int memn;
+ int mempos;
+} Membuf;
+
+static vlong memsize(struct SDL_IOStream *);
+static vlong memseek(struct SDL_IOStream *, vlong, int);
+static size_t memread(struct SDL_IOStream *, void *, size_t);
+static size_t memwrite(struct SDL_IOStream *, const void *, size_t);
+static int memclose(struct SDL_IOStream *);
+
+static vlong bsize(struct SDL_IOStream *);
+static vlong bseek(struct SDL_IOStream *, vlong, int);
+static size_t bread(struct SDL_IOStream *, void *, size_t);
+static size_t bwrite(struct SDL_IOStream *, const void *, size_t);
+static int bclose(struct SDL_IOStream *);
+
+struct npe_sdl_io {+ union {+ Biobuf;
+ Membuf;
+ };
+};
+
+struct SDL_IOStream {+ vlong (*size)(struct SDL_IOStream *);
+ vlong (*seek)(struct SDL_IOStream *, vlong, int);
+ size_t (*read)(struct SDL_IOStream *, void *, size_t);
+ size_t (*write)(struct SDL_IOStream *, const void *, size_t);
+ int (*close)(struct SDL_IOStream *);
+ npe_sdl_io *p;
+};
+
+SDL_IOStream *
+SDL_IOFromFile(const char *file, const char *m)
+{+ SDL_IOStream *o;
+ int f, mode;
+
+ o = nil;
+ mode = -1;
+ for(; m != nil && *m; m++){+ if(*m == 'r'){+ if(mode == OWRITE){+badmode:
+ werrstr("either read or write supported only");+ return nil;
+ }
+ mode = OREAD;
+ }else if(*m == 'w'){+ if(mode == OREAD)
+ goto badmode;
+ mode = OWRITE;
+ }
+ }
+ if(mode < 0)
+ goto badmode;
+
+ mode |= OCEXEC;
+ f = mode & OREAD ? open(file, mode) : create(file, mode, 0644);
+ if(f >= 0 &&
+ (o = calloc(1, sizeof(*o)+sizeof(npe_sdl_io))) != nil &&
+ Binit((o->p = (void*)(o+1)), f, mode) == 0){+ o->size = bsize;
+ o->seek = bseek;
+ o->read = bread;
+ o->write = bwrite;
+ o->close = bclose;
+ return o;
+ }
+
+ if(f >= 0)
+ close(f);
+ free(o);
+
+ return nil;
+}
+
+SDL_IOStream*
+SDL_IOFromConstMem(const void *mem, size_t size)
+{+ SDL_IOStream *o;
+ Membuf *b;
+
+ o = calloc(1, sizeof(*o)+sizeof(npe_sdl_io));
+ if(o == nil)
+ return nil;
+ o->p = (void*)(o+1);
+ b = (void*)o->p;
+ b->memdata = mem;
+ b->memn = size;
+ b->mempos = 0;
+
+ o->size = memsize;
+ o->seek = memseek;
+ o->read = memread;
+ o->write = memwrite;
+ o->close = memclose;
+ return o;
+}
+
+size_t
+SDL_ReadIO(SDL_IOStream *o, void *b, size_t n)
+{+ return o->read ? o->read(o, b, n) : 0;
+}
+
+size_t
+SDL_WriteIO(SDL_IOStream *o, const void *b, size_t n)
+{+ return o->write ? o->write(o, b, n) : 0;
+}
+
+vlong
+SDL_SeekIO(SDL_IOStream *o, vlong off, int whence)
+{+ return o->seek ? o->seek(o, off, whence) : -1;
+}
+
+vlong
+SDL_TellIO(SDL_IOStream *o)
+{+ return o->seek ? o->seek(o, 0, 1) : -1;
+}
+
+vlong
+SDL_GetIOSize(SDL_IOStream *o)
+{+ return o->size ? o->size(o) : -1;
+}
+
+bool
+SDL_CloseIO(SDL_IOStream *o)
+{+ int r;
+
+ r = o->close ? o->close(o) : 0;
+ if(r == 0)
+ free(o);
+ return r < 0 ? false : true;
+}
+
+static vlong
+bseek(struct SDL_IOStream *o, vlong off, int whence)
+{+ return Bseek(o->p, off, whence);
+}
+
+static size_t
+bread(struct SDL_IOStream *o, void *b, size_t n)
+{+ vlong x;
+
+ if((x = Bread(o->p, b, n)) != n){+ if(x > 0)
+ Bseek(o->p, -x, 1);
+ }
+ return x < 0 ? 0 : x;
+}
+
+static size_t
+bwrite(struct SDL_IOStream *o, const void *b, size_t n)
+{+ vlong x;
+
+ x = Bwrite(o->p, b, n); /* FIXME dunno what to do with partial writes */
+ return x < 0 ? 0 : x;
+}
+
+static vlong
+bsize(struct SDL_IOStream *o)
+{+ Dir *s;
+ vlong sz;
+
+ sz = -1;
+ if((s = dirfstat(Bfildes(o->p))) != nil){+ sz = s->length;
+ free(s);
+ }
+
+ return sz;
+}
+
+static int
+bclose(struct SDL_IOStream *o)
+{+ return Bterm(o->p);
+}
+
+static vlong
+memseek(struct SDL_IOStream *o, vlong off, int whence)
+{+ Membuf *b;
+
+ b = (Membuf*)o->p;
+ switch(whence){+ case 0:
+ b->mempos = off;
+ break;
+ case 1:
+ b->mempos += off;
+ break;
+ case 2:
+ b->mempos = b->memn - 1;
+ b->mempos -= off;
+ break;
+ }
+ if(b->mempos < 0)
+ b->mempos = 0;
+
+ return b->mempos;
+}
+
+static size_t
+memread(struct SDL_IOStream *o, void *b, size_t n)
+{+ Membuf *buf;
+ const uchar *dot, *end;
+
+ buf = (Membuf*)o->p;
+ end = buf->memdata + buf->memn;
+ dot = buf->memdata + buf->mempos;
+ assert(dot <= end);
+ if(dot + n >= end)
+ n = end - dot;
+ memmove(b, dot, n);
+ buf->mempos += n;
+ return n;
+}
+
+static size_t
+memwrite(struct SDL_IOStream *o, const void *b, size_t n)
+{+ Membuf *buf;
+
+ if(n <= 0)
+ return 0;
+ buf = (Membuf*)o->p;
+ memmove(buf->mempos + buf->memdata, b, n);
+ buf->mempos += n;
+ return n;
+}
+
+static vlong
+memsize(struct SDL_IOStream *o)
+{+ Membuf *b;
+ b = (Membuf*)o->p;
+
+ return b->memn;
+}
+
+static int
+memclose(struct SDL_IOStream *o)
+{+ USED(o);
+ return 0;
+}
--- /dev/null
+++ b/libnpe_sdl3/log.c
@@ -1,0 +1,145 @@
+#include "_sdl.h"
+
+static SDL_LogPriority logpri[SDL_LOG_CATEGORY_COUNT] = {+ [SDL_LOG_CATEGORY_APPLICATION] SDL_LOG_PRIORITY_INFO,
+ [SDL_LOG_CATEGORY_ASSERT] SDL_LOG_PRIORITY_WARN,
+ [SDL_LOG_CATEGORY_TEST] SDL_LOG_PRIORITY_VERBOSE,
+};
+static char *logcat[SDL_LOG_CATEGORY_COUNT] = {+ [SDL_LOG_CATEGORY_APPLICATION] "app",
+ [SDL_LOG_CATEGORY_ERROR] "error",
+ [SDL_LOG_CATEGORY_ASSERT] "assert",
+ [SDL_LOG_CATEGORY_SYSTEM] "system",
+ [SDL_LOG_CATEGORY_AUDIO] "audio",
+ [SDL_LOG_CATEGORY_VIDEO] "video",
+ [SDL_LOG_CATEGORY_RENDER] "render",
+ [SDL_LOG_CATEGORY_INPUT] "input",
+ [SDL_LOG_CATEGORY_TEST] "test",
+};
+
+static void warn(void*, int, SDL_LogPriority, const char*);
+
+static SDL_LogOutputFunction logfn = warn;
+static void *logaux;
+
+static char*
+evsmprint(char *fmt, va_list arg)
+{+ char *s;
+
+ if((s = vsmprint(fmt, arg)) == nil)
+ sysfatal("smprint: %r");+ setmalloctag(s, getcallerpc(&fmt));
+ return s;
+}
+
+static void
+warn(void *, int cat, SDL_LogPriority p, const char *msg)
+{+ char *l;
+
+ if(cat < 0 || cat >= nelem(logcat))
+ l = "user";
+ else{+ if(logpri[cat] == SDL_LOG_PRIORITY_INVALID)
+ logpri[cat] = SDL_LOG_PRIORITY_ERROR;
+ if(p < logpri[cat])
+ return;
+ l = logcat[cat];
+ }
+ fprint(2, "[%s] %s\n", l, msg);
+}
+
+void
+SDL_SetLogPriorities(SDL_LogPriority p)
+{+ SDL_LogPriority *lp;
+
+ if(p <= SDL_LOG_PRIORITY_INVALID || p >= SDL_LOG_PRIORITY_COUNT){+ SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL_SetLogPriorities: invalid priority %d", p);
+ return;
+ }
+ for(lp=logpri; lp<logpri+nelem(logpri);)
+ *lp++ = p;
+}
+
+void
+SDL_GetLogOutputFunction(SDL_LogOutputFunction *callback, void **userdata)
+{+ if(callback != nil)
+ *callback = logfn;
+ if(userdata != nil)
+ *userdata = logaux;
+}
+
+void
+SDL_SetLogOutputFunction(SDL_LogOutputFunction callback, void *userdata)
+{+ if(callback == nil){ /* FIXME: verify */+ SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL_SetLogOutputFunction: invalid callback");
+ return;
+ }
+ logfn = callback;
+ logaux = userdata;
+}
+
+static void
+logmsg(int cat, SDL_LogPriority p, const char *fmt, va_list arg)
+{+ char *s;
+
+ s = evsmprint(fmt, arg);
+ logfn(logaux, cat, p, s);
+ free(s);
+}
+
+void
+SDL_Log(const char *fmt, ...)
+{+ va_list arg;
+
+ va_start(arg, fmt);
+ logmsg(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO, fmt, arg);
+ va_end(arg);
+}
+
+void
+SDL_LogDebug(int cat, const char *fmt, ...)
+{+ va_list arg;
+
+ va_start(arg, fmt);
+ logmsg(cat, SDL_LOG_PRIORITY_DEBUG, fmt, arg);
+ va_end(arg);
+}
+
+void
+SDL_LogInfo(int cat, const char *fmt, ...)
+{+ va_list arg;
+
+ va_start(arg, fmt);
+ logmsg(cat, SDL_LOG_PRIORITY_INFO, fmt, arg);
+ va_end(arg);
+}
+
+void
+SDL_LogError(int cat, const char *fmt, ...)
+{+ va_list arg;
+
+ va_start(arg, fmt);
+ logmsg(cat, SDL_LOG_PRIORITY_ERROR, fmt, arg);
+ va_end(arg);
+}
+
+void
+SDL_LogCritical(int cat, const char *fmt, ...)
+{+ va_list arg;
+
+ va_start(arg, fmt);
+ logmsg(cat, SDL_LOG_PRIORITY_CRITICAL, fmt, arg);
+ va_end(arg);
+}
+
--- /dev/null
+++ b/libnpe_sdl3/mkfile
@@ -1,0 +1,33 @@
+</$objtype/mkfile
+
+LIB=/$objtype/lib/libnpe_sdl3.a
+CFLAGS=$CFLAGS -p -I/sys/include/npe -I../libnpe -D__plan9__ -D__${objtype}__+
+HFILES=\
+ _sdl.h\
+
+OFILES=\
+ audio.$O\
+ cursor.$O\
+ events.$O\
+ gamepad.$O\
+ iostream.$O\
+ log.$O\
+ mutex.$O\
+ pixels.$O\
+ render.$O\
+ scale.$O\
+ sdl3.$O\
+ surface.$O\
+ texture.$O\
+ threads.$O\
+ window.$O\
+
+# FIXME: ???
+UPDATE=\
+ mkfile\
+ $HFILES\
+ ${OFILES:%.$O=%.c}\+ ${LIB:/$objtype/%=/386/%}\+
+</sys/src/cmd/mksyslib
--- /dev/null
+++ b/libnpe_sdl3/mutex.c
@@ -1,0 +1,65 @@
+#include "_sdl.h"
+
+SDL_Mutex*
+SDL_CreateMutex(void)
+{+ SDL_Mutex *m;
+
+ m = mallocz(sizeof(*m), 1);
+ return m;
+}
+
+void
+SDL_DestroyMutex(SDL_Mutex *m)
+{+ free(m);
+}
+
+bool
+SDL_LockMutex(SDL_Mutex *m)
+{+ lock(&m->l);
+ return true;
+}
+
+bool
+SDL_UnlockMutex(SDL_Mutex *m)
+{+ unlock(&m->l);
+ return true;
+}
+
+SDL_Condition*
+SDL_CreateCondition(void)
+{+ SDL_Condition *p;
+
+ if((p = mallocz(sizeof *p, 1)) == nil)
+ sysfatal("SDL_CreateCondition: %r");+ p->l = &p->QLock;
+ return p;
+}
+
+void
+SDL_SignalCondition(SDL_Condition *p)
+{+ qlock(p);
+ rwakeup(p);
+ qunlock(p);
+}
+
+void
+SDL_WaitCondition(SDL_Condition *p, SDL_Mutex *m)
+{+ SDL_UnlockMutex(m);
+ qlock(p);
+ rsleep(p);
+ qunlock(p);
+ SDL_LockMutex(m);
+}
+
+void
+SDL_DestroyCondition(SDL_Condition *p)
+{+ free(p);
+}
--- /dev/null
+++ b/libnpe_sdl3/pixels.c
@@ -1,0 +1,232 @@
+#include "_sdl.h"
+
+int
+chan2mask(Uint32 chan, int *bpp, Uint32 *rm, Uint32 *gm, Uint32 *bm, Uint32 *am)
+{+ switch(chan){+ case ARGB32:
+ *am = 0xFF000000;
+ *rm = 0x00FF0000;
+ *gm = 0x0000FF00;
+ *bm = 0x000000FF;
+ *bpp = 32;
+ break;
+ case XRGB32:
+ *am = 0x00000000;
+ *rm = 0x00FF0000;
+ *gm = 0x0000FF00;
+ *bm = 0x000000FF;
+ *bpp = 32;
+ break;
+ case ABGR32:
+ *am = 0xFF000000;
+ *rm = 0x000000FF;
+ *gm = 0x0000FF00;
+ *bm = 0x00FF0000;
+ *bpp = 32;
+ case XBGR32:
+ *am = 0x00000000;
+ *rm = 0x000000FF;
+ *gm = 0x0000FF00;
+ *bm = 0x00FF0000;
+ *bpp = 32;
+ break;
+ case RGB24:
+ *am = 0x00000000;
+ *rm = 0x00FF0000;
+ *gm = 0x0000FF00;
+ *bm = 0x000000FF;
+ *bpp = 24;
+ break;
+ case BGR24:
+ *am = 0x00000000;
+ *rm = 0x000000FF;
+ *gm = 0x0000FF00;
+ *bm = 0x00FF0000;
+ *bpp = 24;
+ break;
+ case CMAP8:
+ *am = *rm = *gm = *bm = 0x00000000;
+ *bpp = 8;
+ break;
+ default:
+ assert(0);
+ }
+ return 0;
+}
+
+ulong
+mask2chan(int bpp, Uint32 rm, Uint32 gm, Uint32 bm, Uint32 am)
+{+ USED(gm, bm);
+
+ switch(bpp){+ case 8:
+ return CMAP8;
+ case 24:
+ if(rm & 0xFF0000)
+ return RGB24;
+ else
+ return BGR24;
+ case 32:
+ if(am == 0){+ if(rm & 0xFF0000)
+ return XRGB32;
+ else
+ return XBGR32;
+ } else {+ if(rm & 0xFF0000)
+ return ARGB32;
+ else
+ return ABGR32;
+ }
+ }
+ assert(0);
+ return 0;
+}
+
+Uint32
+chan2pixel(ulong chan)
+{+ switch(chan){+ case ARGB32:
+ return SDL_PIXELFORMAT_ARGB8888;
+ case XRGB32:
+ return SDL_PIXELFORMAT_XRGB8888;
+ case RGB24:
+ return SDL_PIXELFORMAT_RGB24;
+ case ABGR32:
+ return SDL_PIXELFORMAT_ABGR8888;
+ case XBGR32:
+ return SDL_PIXELFORMAT_XBGR8888;
+ case BGR24:
+ return SDL_PIXELFORMAT_BGR24;
+ case CMAP8:
+ return SDL_PIXELFORMAT_INDEX8;
+ }
+ assert(0);
+ return 0;
+}
+
+ulong
+pixel2chan(Uint32 format)
+{+ switch(format){+ case SDL_PIXELFORMAT_ARGB8888:
+ return ARGB32;
+ case SDL_PIXELFORMAT_XRGB8888:
+ return XRGB32;
+ case SDL_PIXELFORMAT_RGB24:
+ return RGB24;
+ case SDL_PIXELFORMAT_ABGR8888:
+ return ABGR32;
+ case SDL_PIXELFORMAT_XBGR8888:
+ return XBGR32;
+ case SDL_PIXELFORMAT_BGR24:
+ return BGR24;
+ case SDL_PIXELFORMAT_INDEX8:
+ return CMAP8;
+ }
+ assert(0);
+ return 0;
+}
+
+void
+SDL_GetRGB(Uint32 pixel, SDL_PixelFormat *fmt, Uint8 *r, Uint8 *g, Uint8 *b)
+{+ SDL_Color *c;
+
+ switch(fmt->format){+ case SDL_PIXELFORMAT_ARGB8888:
+ case SDL_PIXELFORMAT_XRGB8888:
+ case SDL_PIXELFORMAT_RGB24:
+ *r = pixel>>16;
+ *g = pixel>>8;
+ *b = pixel;
+ break;
+ case SDL_PIXELFORMAT_ABGR8888:
+ case SDL_PIXELFORMAT_XBGR8888:
+ case SDL_PIXELFORMAT_BGR24:
+ *b = pixel>>16;
+ *g = pixel>>8;
+ *r = pixel;
+ break;
+ case SDL_PIXELFORMAT_INDEX8:
+ assert(fmt->palette);
+ assert(pixel < fmt->palette->ncolors);
+ c = fmt->palette->colors + pixel;
+ *r = c->r;
+ *g = c->g;
+ *b = c->b;
+ break;
+ default:
+ assert(0);
+ }
+}
+
+SDL_bool
+SDL_PixelFormatEnumToMasks(Uint32 format, int *bpp, Uint32 *Rmask, Uint32 *Gmask, Uint32 *Bmask, Uint32 *Amask)
+{+ ulong c;
+
+ if(bpp == nil || Rmask == nil || Gmask == nil || Bmask == nil || Amask == nil)
+ return SDL_FALSE;
+ c = pixel2chan(format);
+ chan2mask(c, bpp, Rmask, Gmask, Bmask, Amask);
+ return SDL_TRUE;
+}
+
+Uint32
+SDL_MapRGB(SDL_PixelFormat *fmt, Uint8 r, Uint8 g, Uint8 b)
+{+ SDL_Color *c;
+
+ switch(fmt->format){+ case SDL_PIXELFORMAT_ARGB8888:
+ case SDL_PIXELFORMAT_XRGB8888:
+ case SDL_PIXELFORMAT_RGB24:
+ return 0xff<<24 | r<<16 | g<<8 | b;
+ case SDL_PIXELFORMAT_ABGR8888:
+ case SDL_PIXELFORMAT_XBGR8888:
+ case SDL_PIXELFORMAT_BGR24:
+ return 0xff<<24 | b<<16 | g<<8 | r;
+ case SDL_PIXELFORMAT_INDEX8:
+ assert(fmt->palette);
+ for(c = fmt->palette->colors; c < fmt->palette->colors + fmt->palette->ncolors; c++){+ if(c->r == r && c->g == g && c->b == b)
+ return c - fmt->palette->colors;
+ }
+ default:
+ assert(0);
+ return 0;
+ }
+}
+
+void
+npe_sdl_kill_draw(void)
+{+}
+
+int
+npe_sdl_init_draw(void)
+{+ int bpp;
+
+ if(screen != nil)
+ return 0;
+ if(memimageinit() < 0 || initdraw(nil, nil, argv0) < 0)
+ return -1;
+ draw(screen, screen->r, display->black, nil, ZP);
+ if(flushimage(display, 1) < 0)
+ fprint(2, "npe_sdl_init_draw: %r\n");
+ npe_sdl.physw = Dx(screen->r);
+ npe_sdl.physh = Dy(screen->r);
+ npe_sdl.scale = 1;
+ if(npe_sdl_init_tex() < 0)
+ return -1;
+ if(chan2mask(screen->chan, &bpp, &npe_sdl.defmask.r, &npe_sdl.defmask.g, &npe_sdl.defmask.b, &npe_sdl.defmask.a) < 0){+ werrstr("unsupported screen channel");+ return -1;
+ }
+ return 0;
+}
--- /dev/null
+++ b/libnpe_sdl3/render.c
@@ -1,0 +1,326 @@
+#include "_sdl.h"
+
+static SDL_Renderer oneren;
+static u8int *backcopy;
+static Image *front;
+static u32int renddrawcol = DBlack;
+
+bool
+SDL_GetRenderVSync(SDL_Renderer *, int *)
+{+ return false;
+}
+
+bool
+SDL_SetRenderVSync(SDL_Renderer *, int)
+{+ return true;
+}
+
+bool
+SDL_SetRenderDrawBlendMode(SDL_Renderer *, SDL_BlendMode blendMode)
+{+ if(blendMode != SDL_BLENDMODE_NONE){+ werrstr("SDL_SetRenderDrawBlendMode: only SDL_BLENDMODE_NONE is supported");+ return false;
+ }
+ return true;
+}
+
+bool
+SDL_GetRendererInfo(SDL_Renderer *, SDL_RendererInfo *info)
+{+ if(info == nil)
+ return false;
+ info->max_texture_width = npe_sdl.physw;
+ info->max_texture_height = npe_sdl.physh;
+ return true;
+}
+
+bool
+SDL_SetRenderDrawColor(SDL_Renderer *, Uint8 r, Uint8 g, Uint8 b, Uint8 a)
+{+ renddrawcol = r<<24 | g<<16 | b<<8 | a;
+ if(npe_sdl.rendcol != nil)
+ memfillcolor(npe_sdl.rendcol, renddrawcol);
+ return true;
+}
+
+bool
+SDL_SetRenderLogicalPresentation(SDL_Renderer *r, int w, int h, SDL_RendererLogicalPresentation)
+{+ if(r->logiw != w || r->logih != h){+ r->logiw = w;
+ r->logih = h;
+ npe_sdl.fullredraw = 1;
+ }
+ return true;
+}
+
+bool
+SDL_GetRendererOutputSize(SDL_Renderer *r, int *w, int *h)
+{+ if(w != nil)
+ *w = r->logiw;
+ if(h != nil)
+ *h = r->logih;
+ return true;
+}
+
+void
+SDL_RenderGetViewport(SDL_Renderer *r, SDL_Rect *rect)
+{+ rect->x = rect->y = 0;
+ rect->w = r->logiw;
+ rect->h = r->logih;
+}
+
+bool
+SDL_RenderClear(SDL_Renderer *)
+{+ if(npe_sdl.back != nil)
+ memfillcolor(npe_sdl.back, renddrawcol);
+ return true;
+}
+
+bool
+SDL_RenderFillRect(SDL_Renderer *r, SDL_FRect *rect)
+{+ Rectangle rr;
+
+ if(npe_sdl.back == nil)
+ return true;
+ if(rect == nil)
+ return SDL_RenderClear(r);
+ if(npe_sdl.rendcol == nil)
+ return true;
+ rr = Rect(rect->x, rect->y, rect->x+rect->w, rect->y+rect->h);
+ memimagedraw(npe_sdl.back, rr, npe_sdl.rendcol, ZP, nil, ZP, SoverD);
+ return true;
+}
+
+bool
+SDL_RenderLines(SDL_Renderer *, SDL_FPoint *pp, int n)
+{+ Point p0, p1;
+ SDL_FPoint *p;
+
+ if(npe_sdl.rendcol == nil)
+ return false;
+ for(p=pp; n>1; n--, p++){+ if(p == pp){+ p0 = Pt(p->x, p->y);
+ p++;
+ }
+ p1 = Pt(p->x, p->y);
+ memimageline(npe_sdl.back, p0, p1, 0, 0, 0, npe_sdl.rendcol, ZP, SoverD);
+ p0 = p1;
+ }
+ return true;
+}
+
+bool
+SDL_RenderPoints(SDL_Renderer *, SDL_FPoint *pp, int n)
+{+ Point c;
+ SDL_FPoint *p;
+
+ if(npe_sdl.rendcol == nil)
+ return false;
+ for(p=pp; n>0; n--, p++){+ c = Pt(p->x, p->y);
+ memellipse(npe_sdl.back, c, 0, 0, 0, npe_sdl.rendcol, ZP, SoverD);
+ }
+ return true;
+}
+
+static int
+duff(SDL_BlendMode mode)
+{+ if(mode == SDL_BLENDMODE_BLEND)
+ return SoverD;
+ return S;
+}
+
+bool
+SDL_RenderTexture(SDL_Renderer *rend, SDL_Texture *t, const SDL_FRect *sre, const SDL_FRect *dre)
+{+ Rectangle sr, dr;
+ int logiw, logih;
+ ulong chan;
+ Memimage *m;
+
+ if(rend->logiw > 0 && rend->logih > 0){+ logiw = rend->logiw;
+ logih = rend->logih;
+ }else{+ logiw = npe_sdl.physw;
+ logih = npe_sdl.physh;
+ }
+
+ sr = t->m->r;
+ if(sre != nil){+ sr.min = Pt(sre->x, sre->y);
+ sr.max = addpt(sr.min, Pt(sre->w, sre->h));
+ }
+
+ dr = Rect(0, 0, logiw, logih);
+ if(dre != nil){+ dr.min = Pt(dre->x, dre->y);
+ dr.max = addpt(dr.min, Pt(dre->w, dre->h));
+ }
+
+ if(npe_sdl.back == nil || Dx(npe_sdl.back->r) != logiw || Dy(npe_sdl.back->r) != logih){+ freememimage(npe_sdl.back);
+ if(screen)
+ chan = screen->chan;
+ else
+ chan = ARGB32;
+ npe_sdl.back = allocmemimage(Rect(0, 0, logiw, logih), chan);
+ if(npe_sdl.back == nil){+ werrstr("SDL_RenderCopy: %r");+ return false;
+ }
+ npe_sdl.rendcol = allocmemimage(Rect(0, 0, logiw, 1), npe_sdl.back->chan);
+ if(npe_sdl.rendcol != nil){+ memfillcolor(npe_sdl.rendcol, renddrawcol);
+ npe_sdl.rendcol->flags |= Frepl;
+ npe_sdl.rendcol->clipr = memwhite->clipr;
+ }
+ npe_sdl.target->m = npe_sdl.back;
+ free(backcopy);
+ backcopy = malloc(logiw*logih*4);
+ }
+
+ m = t->mod == nil ? t->m : t->mod;
+
+ if(Dx(dr)/Dx(sr) > 1 || Dy(dr)/Dy(sr) > 1)
+ npe_sdl_scale((u32int*)byteaddr(m, ZP), Dx(sr), Dy(sr), (u32int*)byteaddr(npe_sdl.back, ZP), logiw, logih);
+ else
+ memimagedraw(npe_sdl.back, dr, m, sr.min, nil, ZP, duff(t->blend));
+ return true;
+}
+
+bool
+SDL_RenderPresent(SDL_Renderer *rend)
+{+ Rectangle r;
+ static u32int *b;
+ uchar *rb;
+ int logiw, logih;
+
+ /* may be called before SDL_RenderCopy is ever called;
+ * nanobsp does this during initialization */
+ if(npe_sdl.back == nil)
+ return false;
+
+ if(rend->logiw > 0 && rend->logih > 0){+ logiw = rend->logiw;
+ logih = rend->logih;
+ }else{+ logiw = npe_sdl.physw;
+ logih = npe_sdl.physh;
+ }
+
+ npe_sdl.scale = (float)logiw / (float)npe_sdl.physw;
+
+ if(!npe_sdl.fullredraw && (npe_sdl.fullredraw = memcmp(backcopy, byteaddr(npe_sdl.back, ZP), logiw*logih*4)) == 0 && !npe_sdl.mredraw)
+ return false;
+
+ r = Rect(0, 0, npe_sdl.physw, npe_sdl.physh);
+ if(front != nil && (Dx(front->r) != npe_sdl.physw || Dy(front->r) != npe_sdl.physh)){+ freeimage(front);
+ front = nil;
+ free(b);
+ b = nil;
+ }
+ if(b == nil && (b = realloc(b, npe_sdl.physw*npe_sdl.physh*4)) == nil){+ fprint(2, "SDL_RenderPresent: %r\n");
+ return false;
+ }
+ if(npe_sdl.fullredraw || front == nil){+ rb = npe_sdl_scale((u32int*)byteaddr(npe_sdl.back, ZP), Dx(npe_sdl.back->r), Dy(npe_sdl.back->r), b, npe_sdl.physw, npe_sdl.physh);
+ if(front == nil && (front = allocimage(display, r, XRGB32, 0, DNofill)) == nil){+ fprint(2, "SDL_RenderPresent: %r\n");
+ return false;
+ }
+ if(loadimage(front, r, rb, Dx(r)*Dy(r)*4) < 0){+ fprint(2, "SDL_RenderPresent: %r\n");
+ return false;
+ }
+ }
+ while(screen == nil && getwindow(display, Refnone) != 1)
+ /* drawterm window change lag */;
+ draw(screen, screen->r, front, nil, ZP);
+ npe_draw_cursor();
+ npe_sdl.mredraw = 0;
+
+ flushimage(display, 1);
+
+ if(npe_sdl.fullredraw)
+ memmove(backcopy, byteaddr(npe_sdl.back, ZP), logiw*logih*4);
+ else
+ replclipr(screen, 0, screen->r);
+ npe_sdl.fullredraw = 0;
+ return true;
+}
+
+bool
+SDL_RenderReadPixels(SDL_Renderer *rend, SDL_Rect *rect, Uint32 fmt, void *pixels, int pitch)
+{+ Rectangle r, r2;
+ Memimage *m;
+ int n;
+
+ USED(pitch); /* FIXME pitch */
+
+ if(rect != nil)
+ r = Rect(rect->x, rect->y, rect->x+rect->w, rect->y+rect->h);
+ else
+ r = Rect(0, 0, rend->logiw, rend->logih);
+
+ n = Dx(r)*Dy(r);
+ switch(fmt){+ case SDL_PIXELFORMAT_ARGB8888:
+ case SDL_PIXELFORMAT_XRGB8888:
+ n *= 4;
+ m = npe_sdl.back;
+ break;
+ case SDL_PIXELFORMAT_ABGR8888:
+ n *= 4;
+ r2 = Rect(0,0,Dx(r),Dy(r));
+ if((m = allocmemimage(r2, ABGR32)) == nil)
+ return false;
+ memfillcolor(m, DBlack);
+ memimagedraw(m, r2, npe_sdl.back, r.min, nil, ZP, S);
+ break;
+ case SDL_PIXELFORMAT_RGB24:
+ n *= 3;
+ r2 = Rect(0,0,Dx(r),Dy(r));
+ if((m = allocmemimage(r2, RGB24)) == nil)
+ return false;
+ memfillcolor(m, DBlack);
+ memimagedraw(m, r2, npe_sdl.back, r.min, nil, ZP, S);
+ break;
+ default:
+ werrstr("SDL_RenderReadPixels: format not supported");+ return false;
+ }
+
+ unloadmemimage(m, r, pixels, n);
+ if(m != npe_sdl.back)
+ freememimage(m);
+ return true;
+}
+
+void
+SDL_DestroyRenderer(SDL_Renderer *)
+{+ /* nothing to do here */
+}
+
+SDL_Renderer *
+SDL_CreateRenderer(SDL_Window *, char *)
+{+ return &oneren;
+}
--- /dev/null
+++ b/libnpe_sdl3/scale.c
@@ -1,0 +1,27 @@
+#include "_sdl.h"
+
+void *
+npe_sdl_scale(u32int *src, int iw, int ih, u32int *dst, int ow, int oh)
+{+ int i, j, m, n;
+ u32int *d;
+
+ if(iw == ow && ih == oh)
+ return src;
+
+ d = dst;
+ n = ow/iw;
+ for(; ih > 0 && oh > 0; ih--){+ for(i = j = 0; i < iw; i++, src++)
+ for(m = 0; m < n && j < ow; m++, j++)
+ *dst++ = *src;
+ oh--;
+ for(m = 1; m < n && oh > 0; m++){+ memmove(dst, dst-j, j*4);
+ dst += j;
+ oh--;
+ }
+ }
+
+ return d;
+}
--- /dev/null
+++ b/libnpe_sdl3/sdl3.c
@@ -1,0 +1,618 @@
+#include "_sdl.h"
+#include <bio.h>
+
+struct npe_sdl npe_sdl;
+
+static char basepath[PATH_MAX];
+
+bool
+SDL_InitSubSystem(int mask)
+{+ if(basepath[0] == 0){+ if(getwd(basepath, sizeof(basepath)) == nil)
+ strcpy(basepath, "/");
+ }
+ if(mask & SDL_INIT_VIDEO && npe_sdl_init_draw() < 0)
+ goto err;
+ if(mask & SDL_INIT_AUDIO && npe_sdl_init_audio() < 0)
+ goto err;
+ if(mask & SDL_INIT_EVENTS && npe_sdl_init_input() < 0)
+ goto err;
+ if(mask & SDL_INIT_GAMEPAD && npe_sdl_init_gamepad() < 0)
+ goto err;
+ return true;
+err:
+ return false;
+}
+
+bool
+SDL_QuitSubSystem(int mask)
+{+ if(mask & SDL_INIT_AUDIO)
+ npe_sdl_kill_audio();
+ if(mask & SDL_INIT_VIDEO)
+ npe_sdl_kill_draw();
+ if(mask & SDL_INIT_EVENTS)
+ npe_sdl_kill_input();
+ if(mask & SDL_INIT_GAMEPAD)
+ npe_sdl_kill_gamepad();
+ /* FIXME: ... */
+ return true;
+}
+
+bool
+SDL_Init(int mask)
+{+ return SDL_InitSubSystem(mask);
+}
+
+bool
+SDL_SetAppMetadata(const char *name, const char *ver, const char *id)
+{+ USED(name, ver, id);
+ return true;
+}
+
+Uint64
+SDL_GetPerformanceFrequency(void)
+{+ return _tos->cyclefreq;
+}
+
+Uint64
+SDL_GetPerformanceCounter(void)
+{+ u64int x;
+
+ cycles(&x);
+
+ return x;
+}
+
+char *
+SDL_GetError(void)
+{+ static char err[256];
+
+ snprint(err, sizeof(err), "%r");
+
+ return err;
+}
+
+static void *
+readfile(char *path, int *got)
+{+ void *data, *data2;
+ int f, n, r, sz;
+
+ if((f = open(path, OREAD|OCEXEC)) < 0)
+ return nil;
+
+ sz = 32768;
+ data = nil;
+ for(n = 0;; n += r){+ if(sz-n < 65536){+ sz *= 2;
+ if((data2 = realloc(data, sz)) == nil)
+ goto err;
+ data = data2;
+ }
+ if((r = read(f, (char*)data+n, sz-n-1)) < 0)
+ goto err;
+ if(r == 0)
+ break;
+ }
+
+ if(got != nil)
+ *got = n;
+ ((char*)data)[n] = 0;
+
+ return data;
+err:
+ free(data);
+ close(f);
+ return nil;
+}
+
+char *
+SDL_GetClipboardText(void)
+{+ return readfile("/dev/snarf", nil);+}
+
+int
+SDL_SetClipboardText(char *s)
+{+ int f, n;
+
+ n = -1;
+ if((f = open("/dev/snarf", OWRITE|OTRUNC|OCEXEC)) >= 0){+ n = strlen(s);
+ n = write(f, s, n) == n ? 0 : -1;
+ close(f);
+ }
+
+ if(n != 0)
+ werrstr("SDL_SetClipboardText: %r");+
+ return n;
+}
+
+bool
+SDL_GetWindowDisplayIndex(SDL_Window *)
+{+ return true;
+}
+
+void
+SDL_Quit(void)
+{+ /* FIXME deinitialize */
+}
+
+bool
+SDL_FillRect(SDL_Surface *dst, const SDL_Rect *rect, Uint32 color)
+{+ Uint32 *p;
+ int i;
+ USED(rect);
+
+ switch(dst->format->format){+ case SDL_PIXELFORMAT_XRGB8888:
+ case SDL_PIXELFORMAT_ARGB8888:
+ case SDL_PIXELFORMAT_XBGR8888:
+ case SDL_PIXELFORMAT_ABGR8888:
+ p = (Uint32*)dst->pixels;
+ for(i = 0; i < dst->n / sizeof(*p); i++)
+ p[i] = color;
+ break;
+ case SDL_PIXELFORMAT_RGB24:
+ for(i = 0; i < dst->n; i += 3){+ dst->pixels[i+0] = color;
+ dst->pixels[i+1] = color>>8;
+ dst->pixels[i+2] = color>>16;
+ }
+ break;
+ case SDL_PIXELFORMAT_INDEX8:
+ for(i = 0; i < dst->n; i++)
+ dst->pixels[i] = color;
+ break;
+ }
+ return true;
+}
+
+SDL_Palette*
+SDL_AllocPalette(int ncolors)
+{+ SDL_Palette *p;
+
+ p = malloc(sizeof(*p));
+ p->ncolors = ncolors;
+ p->colors = mallocz(sizeof(SDL_Color)*ncolors, 1);
+ return p;
+}
+
+bool
+SDL_SetPaletteColors(SDL_Palette *palette, const SDL_Color *colors, int firstcolor, int ncolors)
+{+ int i;
+
+ assert(palette->ncolors >= firstcolor + ncolors);
+ for(i = firstcolor; i < firstcolor + ncolors; i++)
+ palette->colors[i] = colors[i - firstcolor];
+ return true;
+}
+
+void
+SDL_WarpMouseInWindow(SDL_Window *, int x, int y)
+{+ moveto(npe_sdl.mctl, Pt(screen->r.min.x+x, screen->r.min.y+y));
+}
+
+Uint32
+SDL_GetGlobalMouseState(int *x, int *y)
+{+ Uint32 b;
+
+ if(x != nil)
+ *x = npe_sdl.m.xy.x;
+ if(y != nil)
+ *y = npe_sdl.m.xy.y;
+
+ b = 0;
+ if(npe_sdl.m.buttons & 1)
+ b |= SDL_BUTTON_LMASK;
+ if(npe_sdl.m.buttons & 2)
+ b |= SDL_BUTTON_MMASK;
+ if(npe_sdl.m.buttons & 4)
+ b |= SDL_BUTTON_RMASK;
+
+ return b;
+}
+
+Uint32
+SDL_GetMouseState(int *x, int *y)
+{+ Uint32 b;
+
+ b = SDL_GetGlobalMouseState(nil, nil);
+ if(x != nil)
+ *x = npe_sdl.m.xy.x - screen->r.min.x;
+ if(y != nil)
+ *y = npe_sdl.m.xy.y - screen->r.min.y;
+
+ return b;
+}
+
+void
+SDL_RenderGetScale(SDL_Renderer *, float *scaleX, float *scaleY)
+{+ *scaleX = 1.0;
+ *scaleY = 1.0;
+}
+
+bool
+SDL_RenderSetIntegerScale(SDL_Renderer *, SDL_bool enable)
+{+ /* FIXME */
+ USED(enable);
+ return true;
+}
+
+SDL_bool
+SDL_IsTextInputActive(void)
+{+ return npe_sdl.textinput;
+}
+
+void
+SDL_StartTextInput(void)
+{+ npe_sdl.textinput = SDL_TRUE;
+}
+
+void
+SDL_StopTextInput(void)
+{+ npe_sdl.textinput = SDL_FALSE;
+}
+
+void
+SDL_Delay(Uint32 ms)
+{+ npe_nsleep((uvlong)ms * Nmsec);
+}
+
+Uint32
+SDL_GetWindowFlags(SDL_Window *)
+{+ /* FIXME is this correct? */
+ return SDL_WINDOW_INPUT_FOCUS;
+}
+
+bool
+SDL_GetDisplayUsableBounds(int displayIndex, SDL_Rect *r)
+{+ if(displayIndex != 0)
+ return false;
+
+ if(r == nil)
+ return true;
+
+ r->x = display->image->r.min.x;
+ r->y = display->image->r.min.y;
+ r->w = Dx(display->image->r);
+ r->h = Dy(display->image->r);
+ return true;
+}
+
+bool
+SDL_GetDisplayBounds(int displayIndex, SDL_Rect *r)
+{+ return SDL_GetDisplayUsableBounds(displayIndex, r);
+}
+
+bool
+SDL_GetDesktopDisplayMode(int displayIndex, SDL_DisplayMode *mode)
+{+ if(displayIndex != 0)
+ return false;
+ mode->w = Dx(display->image->r);
+ mode->h = Dy(display->image->r);
+ mode->format = SDL_PIXELFORMAT_ARGB8888;
+ mode->refresh_rate = 0;
+ return true;
+}
+
+int
+SDL_GetCurrentDisplayMode(int displayIndex, SDL_DisplayMode *mode)
+{+ return SDL_GetDesktopDisplayMode(displayIndex, mode);
+}
+
+int
+SDL_GetNumDisplayModes(int displayIndex)
+{+ if(displayIndex != 0)
+ return -1;
+ return 1;
+}
+
+bool
+SDL_GetDisplayMode(int displayIndex, int modeIndex, SDL_DisplayMode *mode)
+{+ USED(modeIndex);
+ return SDL_GetDesktopDisplayMode(displayIndex, mode);
+}
+
+void
+SDL_SetWindowTitle(SDL_Window *, char *title)
+{+ int f;
+
+ if(title == nil)
+ return;
+ if((f = open("/dev/label", OWRITE|OTRUNC|OCEXEC)) >= 0 || (f = open("/mnt/term/dev/label", OWRITE|OTRUNC|OCEXEC)) >= 0){+ write(f, title, strlen(title));
+ close(f);
+ }
+}
+
+int
+SDL_GetNumVideoDisplays(void)
+{+ /* FIXME implement multihead for plan9 */
+ return 1;
+}
+
+SDL_bool
+SDL_SetHint(char *name, char *value)
+{+ /* FIXME anyone cares about name="SDL_RENDER_SCALE_QUALITY" value="(best|nearest)"? */
+ if(strcmp(name, SDL_HINT_WINDOWS_NO_CLOSE_ON_ALT_F4) == 0){+ npe_sdl.hints = (npe_sdl.hints & ~Altf4noclose) | (atoi(value) ? Altf4noclose : 0);
+ return SDL_TRUE;
+ }
+
+ return SDL_FALSE;
+}
+
+char *
+SDL_GetCurrentVideoDriver(void)
+{+ return "/dev/draw";
+}
+
+Uint32
+SDL_GetTicks(void)
+{+ return npe_nanosec() / Nmsec;
+}
+
+bool
+SDL_OpenURL(char *url)
+{+ char tmp[PATH_MAX];
+ Plumbmsg m;
+ int f, r;
+
+ if((f = plumbopen("send", OWRITE|OCEXEC)) < 0)+ return false;
+
+ memset(&m, 0, sizeof(m));
+ m.src = argv0;
+ m.wdir = getwd(tmp, sizeof(tmp));
+ m.type = "text";
+ m.data = url;
+ m.ndata = -1;
+ r = plumbsend(f, &m);
+ close(f);
+
+ return r < 0 ? false : true;
+}
+
+char *
+SDL_GetBasePath(void)
+{+ return strdup(basepath);
+}
+
+char *
+SDL_GetPrefPath(char *, char *app)
+{+ char *home, *p;
+
+ p = nil;
+ if((home = getenv("home")) != nil){+ if((p = smprint("%s/lib/%s/", home, app)) != nil)+ npe_mkdirp(p, 0755);
+ }
+
+ return p;
+}
+
+SDL_bool
+SDL_HasClipboardText(void)
+{+ /* most def */
+ return SDL_TRUE;
+}
+
+SDL_bool
+SDL_HasSSE(void)
+{+ /* it's not like we have builtins anyway */
+ return SDL_FALSE;
+}
+
+SDL_bool
+SDL_HasSSE2(void)
+{+ /* it's not like we have builtins anyway */
+ return SDL_FALSE;
+}
+
+void
+SDL_EnableScreenSaver(void)
+{+}
+
+void
+SDL_ClearError(void)
+{+}
+
+int
+SDL_PeepEvents(SDL_Event *events, int numevents, SDL_eventaction action, Uint32 minType, Uint32 maxType)
+{+ /* FIXME implement */
+ USED(events, numevents, action, minType, maxType);
+ return 0;
+}
+
+int
+SDL_NumJoysticks(void)
+{+ /* FIXME implement */
+ return 0;
+}
+
+SDL_Joystick *
+SDL_JoystickOpen(int n)
+{+ /* FIXME implement */
+ USED(n);
+ return nil;
+}
+
+void
+SDL_JoystickClose(SDL_Joystick *js)
+{+ USED(js);
+}
+
+int
+SDL_JoystickNumAxes(SDL_Joystick *js)
+{+ USED(js);
+ return -1;
+}
+
+int
+SDL_JoystickNumButtons(SDL_Joystick *js)
+{+ USED(js);
+ return -1;
+}
+
+int
+SDL_JoystickNumHats(SDL_Joystick *js)
+{+ USED(js);
+ return -1;
+}
+
+int
+SDL_JoystickNumBalls(SDL_Joystick *js)
+{+ USED(js);
+ return -1;
+}
+
+int
+SDL_JoystickEventState(int state)
+{+ USED(state);
+ return 0;
+}
+
+void
+SDL_JoystickUpdate(void)
+{+}
+
+char*
+SDL_JoystickName(SDL_Joystick *js)
+{+ USED(js);
+ return nil;
+}
+
+Sint16
+SDL_JoystickGetAxis(SDL_Joystick *js, int axis)
+{+ USED(js); USED(axis);
+ return 0;
+}
+
+Uint8
+SDL_JoystickGetHat(SDL_Joystick *js, int hat)
+{+ USED(js); USED(hat);
+ return 0;
+}
+
+Uint8
+SDL_JoystickGetButton(SDL_Joystick *js, int button)
+{+ USED(js); USED(button);
+ return 0;
+}
+
+/* FIXME: not sdl3 */
+int
+SDL_SetRelativeMouseMode(SDL_bool enabled)
+{+ if(screen){+ npe_sdl.grabout = insetrect(screen->r, Dx(screen->r)/8);
+ npe_sdl.center = addpt(screen->r.min, Pt(Dx(screen->r)/2, Dy(screen->r)/2));
+ if(enabled)
+ SDL_HideCursor();
+ else
+ SDL_ShowCursor();
+ }
+ npe_sdl.mgrab = enabled;
+ return 0;
+}
+
+void
+SDL_SetMainReady(void)
+{+}
+
+int
+SDL_GetRelativeMouseMode(void)
+{+ return npe_sdl.mgrab;
+}
+
+Uint32
+SDL_GetRelativeMouseState(int *x, int *y)
+{+ Uint32 b;
+
+ b = SDL_GetGlobalMouseState(nil, nil);
+ if(x != nil)
+ *x = npe_sdl.Δ.x;
+ if(y != nil)
+ *y = npe_sdl.Δ.y;
+ npe_sdl.Δ = ZP;
+ return b;
+}
+
+void
+SDL_SetModState(SDL_Keymod modstate)
+{+ /* FIXME: do we care? */
+ USED(modstate);
+}
+
+/* FIXME */
+void
+SDL_GetVersion(SDL_version *v)
+{+ /* these are arbitrary */
+ v->major = 2;
+ v->minor = 24;
+ v->patch = 1;
+}
--- /dev/null
+++ b/libnpe_sdl3/surface.c
@@ -1,0 +1,410 @@
+#include "_sdl.h"
+#include <SDL3/SDL_iostream.h>
+#include <bio.h>
+
+static void
+setformat(SDL_Surface *s, ulong chan, int bpp)
+{+ if((s->format = calloc(1, sizeof(SDL_PixelFormat))) == nil)
+ sysfatal("setformat: %r");+ s->format->BytesPerPixel = bpp / 8;
+ s->format->format = chan2pixel(chan);
+ if(chan == CMAP8){+ if((s->format->palette = calloc(1, sizeof(SDL_Palette))) == nil)
+ sysfatal("setformat: %r");+ s->format->palette->ncolors = 256;
+ if((s->format->palette->colors = calloc(1, sizeof(SDL_Color) * 256)) == nil)
+ sysfatal("setformat: %r");+ }
+}
+
+static SDL_Surface *
+sfrommem(Memimage *i)
+{+ int bpp;
+ SDL_Surface *s;
+
+ if((s = mallocz(sizeof *s, 1)) == nil)
+ return nil;
+ s->i = i;
+ s->w = Dx(i->r);
+ s->h = Dy(i->r);
+ bpp = i->depth;
+ s->pitch = s->w * (bpp / 8);
+ s->clip_rect.x = 0;
+ s->clip_rect.y = 0;
+ s->clip_rect.w = s->w;
+ s->clip_rect.h = s->h;
+ s->n = s->pitch * s->h;
+ if(i->chan == CMAP8)
+ s->pixels = calloc(1, s->n);
+ else
+ s->pixels = i->data->bdata;
+ if(s->pixels == nil)
+ return nil;
+ setformat(s, i->chan, bpp);
+ return s;
+}
+
+SDL_Surface *
+SDL_CreateRGBSurface(Uint32, int w, int h, int bpp, Uint32 rm, Uint32 gm, Uint32 bm, Uint32 am)
+{+ SDL_Surface *s;
+ int n;
+ ulong chan;
+
+ rm = rm ? rm : npe_sdl.defmask.r;
+ gm = gm ? gm : npe_sdl.defmask.g;
+ bm = bm ? bm : npe_sdl.defmask.b;
+ if((chan = mask2chan(bpp, rm, gm, bm, am)) == 0){+ werrstr("bad bpp and/or mask");+ return nil;
+ }
+ n = w*h*bpp/8;
+ if((s = calloc(1, sizeof(*s))) == nil){+ werrstr("SDL_CreateRGBSurface: memory");+ return nil;
+ }
+ if(chan == CMAP8){+ s->i = allocmemimage(Rect(0,0,w,h), screen->chan);
+ s->format->palette = calloc(1, sizeof(SDL_Palette));
+ s->format->palette->ncolors = 256;
+ s->format->palette->colors = calloc(1, sizeof(SDL_Color) * 256);
+ s->pixels = calloc(1, s->n);
+ }else{+ s->i = allocmemimage(Rect(0,0,s->w,s->h), chan);
+ s->pixels = ((Memimage*)s->i)->data->bdata;
+ }
+
+ s->w = w;
+ s->h = h;
+ s->pitch = w*bpp/8;
+ s->clip_rect.x = 0;
+ s->clip_rect.y = 0;
+ s->clip_rect.w = w;
+ s->clip_rect.h = h;
+ s->n = n;
+
+ return s;
+}
+
+SDL_Surface *
+SDL_CreateRGBSurfaceFrom(void *pixels, int w, int h, int bpp, int pitch, Uint32 rm, Uint32 gm, Uint32 bm, Uint32 am)
+{+ SDL_Surface *s;
+ u8int *p;
+ int n, y;
+
+ if((s = SDL_CreateRGBSurface(0, w, h, bpp, rm, gm, bm, am)) == nil)
+ return nil;
+
+ n = w*bpp/8;
+ for(y = 0, p = pixels; y < h; y++, p += pitch)
+ memmove(s->pixels + y*n, p, n);
+
+ return s;
+}
+
+SDL_Surface *
+SDL_CreateRGBSurfaceWithFormat(Uint32 flags, int w, int h, int fbpp, Uint32 fmt)
+{+ SDL_Surface *s;
+ int bpp;
+ ulong chan;
+ Uint32 rm, gm, bm, am;
+
+ if((chan = pixel2chan(fmt)) == 0){+ werrstr("SDL_CreateRGBSurfaceWithFormat: FIXME format %8ux not implemented", fmt);+ return nil;
+ }
+ chan2mask(chan, &bpp, &rm, &gm, &bm, &am);
+ if(bpp != fbpp && fbpp != 0)
+ sysfatal("FIXME SDL_CreateRGBSurfaceWithFormat passes wrong bpp for format: %d not %d", fbpp, bpp);+ if((s = SDL_CreateRGBSurface(flags, w, h, bpp, rm, bm, gm, am)) == nil)
+ return nil;
+ return s;
+}
+
+Uint32
+SDL_MapSurfaceRGB(SDL_Surface *surface, Uint8 r, Uint8 g, Uint8 b)
+{+ return SDL_MapRGB(surface->format, r, g, b);
+}
+
+bool
+SDL_SetSurfacePalette(SDL_Surface *s, SDL_Palette *palette)
+{+ s->format->palette = palette;
+ return true;
+}
+
+bool
+SDL_SetSurfaceColorKey(SDL_Surface *s, bool enable, Uint32 key)
+{+ s->keyset = enable;
+ s->key = key;
+ return true;
+}
+
+static void
+syncpalette(SDL_Surface *s)
+{+ SDL_Color *c;
+ Uint8 *to;
+ int j;
+
+ to = ((Memimage*)s->i)->data->bdata;
+ for(j = 0; j < s->n; j++){+ c = s->format->palette->colors + s->pixels[j];
+ *to++ = c->b;
+ *to++ = c->g;
+ *to++ = c->r;
+ *to++ = c->a;
+ }
+}
+
+static void
+synctopalette(SDL_Surface *s)
+{+ SDL_Color c, *f;
+ Uint32 *from;
+ int j, k;
+ Memimage *i;
+ SDL_PixelFormat fmt;
+
+ i = s->i;
+ fmt.format = chan2pixel(screen->chan);
+ from = (void*)i->data->bdata;
+ for(j = 0; j < s->n; j++){+ SDL_GetRGB(from[j], &fmt, &c.r, &c.g, &c.b);
+ for(k = 0; k < s->format->palette->ncolors; k++){+ f = s->format->palette->colors + k;
+ if(c.r == f->r && c.g == f->g && c.b == f->b)
+ break;
+ }
+ if(k == s->format->palette->ncolors)
+ s->pixels[j] = 0; /* FIXME */
+ else
+ s->pixels[j] = k;
+ }
+}
+
+bool
+SDL_BlitSurface(SDL_Surface *src, const SDL_Rect *srcrect, SDL_Surface *dst, SDL_Rect *dstrect)
+{+ Rectangle r, r2;
+
+ r = srcrect == nil ? Rect(0, 0, src->w, src->h) : Rect(srcrect->x, srcrect->y, srcrect->x+srcrect->w, srcrect->y+srcrect->h);
+ r2 = dstrect == nil ? Rect(0, 0, dst->w, dst->h) : Rect(dstrect->x, dstrect->y, dstrect->x+dstrect->w, dstrect->y+dstrect->h);
+
+ if(src->format->format == SDL_PIXELFORMAT_INDEX8)
+ syncpalette(src);
+
+ memimagedraw(dst->i, r2, src->i, ZP, nil, ZP, S);
+
+ if(dst->format->format == SDL_PIXELFORMAT_INDEX8)
+ synctopalette(dst);
+ return true;
+}
+
+bool
+SDL_SetSurfaceRLE(SDL_Surface *, int)
+{+ /* nothing to do here */
+ return true;
+}
+
+bool
+SDL_SetSurfaceBlendMode(SDL_Surface *, SDL_BlendMode blendMode)
+{+ if(blendMode != SDL_BLENDMODE_NONE){+ werrstr("SDL_SetSurfaceBlendMode: only SDL_BLENDMODE_NONE is supported");+ return false;
+ }
+ return true;
+}
+
+bool
+SDL_LockSurface(SDL_Surface *)
+{+ /* nothing to do here */
+ return true;
+}
+
+bool
+SDL_UnlockSurface(SDL_Surface *)
+{+ /* nothing to do here */
+ return true;
+}
+
+// https://wiki.libsdl.org/SDL3/SDL_LowerBlit
+bool
+SDL_LowerBlit(SDL_Surface *src, SDL_Rect *srcrect, SDL_Surface *dst, SDL_Rect *dstrect)
+{+ return SDL_BlitSurface(src, srcrect, dst, dstrect);
+}
+
+bool
+SDL_SoftStretch(SDL_Surface *src, const SDL_Rect *srcrect, SDL_Surface *dst, const SDL_Rect *dstrect)
+{+ Rectangle r, r2;
+ Memimage *rowimg;
+ int w, h;
+ int scale;
+ ulong *s, *d, *e;
+ ulong *out;
+ int i, y;
+
+ r = srcrect == nil ? Rect(0, 0, src->w, src->h) : Rect(srcrect->x, srcrect->y, srcrect->x+srcrect->w, srcrect->y+srcrect->h);
+ r2 = dstrect == nil ? Rect(0, 0, dst->w, dst->h) : Rect(dstrect->x, dstrect->y, dstrect->x+dstrect->w, dstrect->y+dstrect->h);
+
+ w = Dx(r);
+ h = Dy(r);
+
+ scale = Dx(r2)/w;
+ if(scale <= 0)
+ scale = 1;
+ else if(scale > 12)
+ scale = 12;
+
+ rowimg = allocmemimage(Rect(0, 0, scale*w, 1), ((Memimage*)src->i)->chan);
+
+ assert(dst->format->format != SDL_PIXELFORMAT_INDEX8);
+ if(src->format->format == SDL_PIXELFORMAT_INDEX8)
+ syncpalette(src);
+
+ for(y = 0; y < h; y++){+ s = wordaddr(src->i, Pt(0, y));
+ d = (ulong*)rowimg->data->bdata;
+ e = s + w;
+ for(; s < e; s++){+ switch(scale){+ case 12:
+ *d++ = *s;
+ case 11:
+ *d++ = *s;
+ case 10:
+ *d++ = *s;
+ case 9:
+ *d++ = *s;
+ case 8:
+ *d++ = *s;
+ case 7:
+ *d++ = *s;
+ case 6:
+ *d++ = *s;
+ case 5:
+ *d++ = *s;
+ case 4:
+ *d++ = *s;
+ case 3:
+ *d++ = *s;
+ case 2:
+ *d++ = *s;
+ case 1:
+ *d++ = *s;
+ }
+ }
+ d = (ulong*)rowimg->data->bdata;
+ for(i = 0; i < scale; i++){+ out = wordaddr(dst->i, Pt(0, y*scale + i));
+ memcpy(out, d, scale*w*4);
+ }
+ }
+ freememimage(rowimg);
+ return true;
+}
+
+void
+SDL_DestroySurface(SDL_Surface *surface)
+{+ freememimage(surface->i);
+ memset(surface, 0, sizeof(surface));
+ free(surface);
+}
+
+bool
+SDL_SaveBMP(SDL_Surface *s, const char *file)
+{+ u8int h[54];
+ Biobuf *f;
+ int sz, i;
+
+ if(s->format->format != SDL_PIXELFORMAT_RGB24){+ werrstr("SDL_SaveBMP: not rgb24");+ return false;
+ }
+ if((f = Bopen(file, OWRITE|OTRUNC)) == nil)
+ return false;
+ sz = sizeof(h) + s->n;
+ memset(h, 0, sizeof(h));
+ h[0] = 'B';
+ h[1] = 'M';
+ h[0x02+0] = sz;
+ h[0x02+1] = sz>>8;
+ h[0x02+2] = sz>>16;
+ h[0x02+3] = sz>>24;
+ h[0x0a] = sizeof(h);
+ h[0x0e] = sizeof(h) - 14;
+ h[0x12+0] = s->w;
+ h[0x12+1] = s->w>>8;
+ h[0x12+2] = s->w>>16;
+ h[0x12+3] = s->w>>24;
+ h[0x16+0] = s->h;
+ h[0x16+1] = s->h>>8;
+ h[0x16+2] = s->h>>16;
+ h[0x16+3] = s->h>>24;
+ h[0x1a] = 1;
+ h[0x1c] = 24;
+ Bwrite(f, h, sizeof(h));
+ memset(h, 0, 4);
+ for(i = s->h-1; i >= 0; i--){+ Bwrite(f, s->pixels+i*s->w*3, s->w*3);
+ if(s->w & 3)
+ Bwrite(f, h, 4-(s->w&3));
+ }
+ Bterm(f);
+ return true;
+}
+
+/* fight me */
+SDL_Surface*
+SDL_LoadBMP_IO(SDL_IOStream *o, bool closeio)
+{+ int n, pfd[2];
+ uchar buf[8192];
+ Memimage *i, *i2;
+
+ if(pipe(pfd) < 0)
+ sysfatal("SDL_LoadBMP_IO: %r");+ switch(fork()){+ case -1: sysfatal("SDL_LoadBMP_IO: %r");+ case 0:
+ dup(pfd[0], 0);
+ dup(pfd[0], 1);
+ close(pfd[0]);
+ close(pfd[1]);
+ execl("/bin/bmp", "bmp", "-9tv", nil);+ sysfatal("SDL_LoadBMP_IO: %r");+ default:
+ close(pfd[0]);
+ }
+ while((n = SDL_ReadIO(o, buf, sizeof buf)) > 0)
+ if(write(pfd[1], buf, n) != n)
+ sysfatal("SDL_LoadBMP_IO: %r");+ if(closeio)
+ SDL_CloseIO(o);
+ write(pfd[1], buf, 0);
+ if((i = readmemimage(pfd[1])) == nil)
+ sysfatal("SDL_LoadBMP_IO: %r");+ close(pfd[1]);
+ /* bmp(1) doesn't do RGBA, SDL3 only does ARGB1555, RGB24 and ARGB32 */
+ if((i2 = allocmemimage(i->r, ARGB32)) == nil){+ freememimage(i);
+ return nil;
+ }
+ memimagedraw(i2, i2->r, i, ZP, nil, ZP, S);
+ freememimage(i);
+ return sfrommem(i2);
+}
--- /dev/null
+++ b/libnpe_sdl3/texture.c
@@ -1,0 +1,240 @@
+#include "_sdl.h"
+
+static SDL_Texture backtex;
+
+bool
+SDL_SetTextureScaleMode(SDL_Texture *, SDL_ScaleMode)
+{+ return true;
+}
+
+// https://wiki.libsdl.org/SDL3/SDL_SetRenderTarget
+bool
+SDL_SetRenderTarget(SDL_Renderer *, SDL_Texture *texture)
+{+ if(texture == nil)
+ npe_sdl.target = &backtex;
+ else
+ npe_sdl.target = texture;
+ npe_sdl.back = npe_sdl.target->m;
+ npe_sdl.fullredraw = 1;
+ return true;
+}
+
+SDL_Texture*
+SDL_GetRenderTarget(SDL_Renderer *)
+{+ return npe_sdl.target;
+}
+
+bool
+SDL_GetTextureSize(SDL_Texture *t, float *w, float *h)
+{+ if(t == nil){+ werrstr("null texture");+ return false;
+ }
+ if(w != nil)
+ *w = Dx(t->m->r) * (t->m->depth / 8);
+ if(h != nil)
+ *h = Dy(t->m->r);
+ return true;
+}
+
+SDL_PropertiesID
+SDL_GetTextureProperties(SDL_Texture *t)
+{+ return t;
+}
+
+Sint64
+SDL_GetNumberProperty(SDL_PropertiesID t, const char *name, Sint64 def)
+{+ if(strcmp(SDL_PROP_TEXTURE_WIDTH_NUMBER, name) == 0)
+ return Dx(t->m->r) * (t->m->depth / 8);
+ else if(strcmp(SDL_PROP_TEXTURE_HEIGHT_NUMBER, name) == 0)
+ return Dy(t->m->r);
+ else
+ return def;
+}
+
+SDL_Texture *
+SDL_CreateTexture(SDL_Renderer *, Uint32 format, int, int w, int h)
+{+ SDL_Texture *t;
+ int dformat;
+
+ if((dformat = pixel2chan(format)) == 0){+ werrstr("SDL_CreateTexture: format is not supported");+ goto err;
+ }
+ if((t = malloc(sizeof(*t))) == nil)
+ goto err;
+ if((t->m = allocmemimage(Rect(0, 0, w, h), dformat)) == nil){+ free(t);
+ goto err;
+ }
+ t->mod = nil;
+ memfillcolor(t->m, DBlack);
+
+ return t;
+err:
+ werrstr("SDL_CreateTexture: %r");+ return nil;
+}
+
+SDL_Texture *
+SDL_CreateTextureFromSurface(SDL_Renderer *r, SDL_Surface *s)
+{+ SDL_Texture *t;
+ SDL_Rect re;
+
+ if((t = SDL_CreateTexture(r, s->format->format, 0, s->w, s->h)) != nil){+ re.x = 0;
+ re.y = 0;
+ re.w = s->w;
+ re.h = s->h;
+ SDL_UpdateTexture(t, &re, s->pixels, s->pitch);
+ }
+
+ return t;
+}
+
+bool
+SDL_LockTexture(SDL_Texture *t, const SDL_Rect *re, void **pixels, int *pitch)
+{+ Rectangle r;
+
+ r = re ? Rect(re->x, re->y, re->x+re->w, re->y+re->h) : t->m->r;
+ *pitch = Dx(r)*(t->m->depth/8);
+ *pixels = t->m->data->bdata;
+ return true;
+}
+
+bool
+SDL_UnlockTexture(SDL_Texture *t)
+{+ USED(t);
+ return true;
+}
+
+bool
+SDL_UpdateTexture(SDL_Texture *t, SDL_Rect *re, void *pixels, int pitch)
+{+ Rectangle r;
+ u8int *pix;
+ int y, my;
+
+ r = re ? Rect(re->x, re->y, re->x+re->w, re->y+re->h) : t->m->r;
+ pix = pixels;
+ if(pitch == Dx(r)*4){+ if(loadmemimage(t->m, r, pix, Dx(r)*Dy(r)*4) < 0){+ werrstr("SDL_UpdateTexture: %r");+ return false;
+ }
+ }else{+ my = Dy(r);
+ for(y = 0; y < my; y++, pix += pitch, r.min.y += 1){+ r.max.y = r.min.y + 1;
+ if(loadmemimage(t->m, r, pix, Dx(r)*4) < 0){+ werrstr("SDL_UpdateTexture: %r");+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+/* FIXME: do this better */
+bool
+SDL_SetTextureAlphaMod(SDL_Texture *t, Uint8 a)
+{+ int w, h;
+ u8int sa;
+ u32int c, *s, *e;
+ Point p;
+ Memimage *i;
+ Rectangle ir;
+
+ ir = t->m->r;
+ if(t->mod == nil){+ if((t->mod = allocmemimage(ir, ARGB32)) == nil) /* FIXME */
+ return false;
+ }
+ memimagedraw(t->mod, ir, t->m, ZP, nil, ZP, S);
+ i = t->mod;
+ w = Dx(ir);
+ h = Dy(ir);
+ p = ZP;
+ for(; p.y<h; p.y++){+ s = (u32int *)byteaddr(i, p);
+ for(e=s+w; s<e; s++){+ c = *s;
+ sa = (c >> 24 & 0xff) * (a / 255.0);
+ *s = sa << 24 | c & 0xffffff;
+ }
+ }
+ return true;
+}
+
+/* FIXME: do this better */
+bool
+SDL_SetTextureColorMod(SDL_Texture *t, Uint8 r, Uint8 g, Uint8 b)
+{+ int w, h;
+ u8int u[4];
+ u32int c, *s, *e;
+ Point p;
+ Memimage *i;
+ Rectangle ir;
+
+ ir = t->m->r;
+ if(t->mod == nil){+ if((t->mod = allocmemimage(ir, ARGB32)) == nil) /* FIXME */
+ return false;
+ }
+ memimagedraw(t->mod, ir, t->m, ZP, nil, ZP, S);
+ i = t->mod;
+ w = Dx(ir);
+ h = Dy(ir);
+ p = ZP;
+ for(; p.y<h; p.y++){+ s = (u32int *)byteaddr(i, p);
+ for(e=s+w; s<e; s++){+ c = *s;
+ u[0] = c >> 24;
+ u[1] = (c >> 16 & 0xff) * (r / 255.0);
+ u[2] = (c >> 8 & 0xff) * (g / 255.0);
+ u[3] = (c & 0xff) * (b / 255.0);
+ *s = u[0] << 24 | u[1] << 16 | u[2] << 8 | u[3];
+ }
+ }
+ return true;
+}
+
+bool
+SDL_SetTextureBlendMode(SDL_Texture *t, SDL_BlendMode blendMode)
+{+ if(blendMode != SDL_BLENDMODE_NONE && blendMode != SDL_BLENDMODE_BLEND){+ werrstr("SDL_SetTextureBlendMode: unsupported blend mode %d", blendMode);+ return false;
+ }
+ t->blend = blendMode;
+ return true;
+}
+
+void
+SDL_DestroyTexture(SDL_Texture *t)
+{+ if(t == nil)
+ return;
+ freememimage(t->m);
+ free(t);
+}
+
+int
+npe_sdl_init_tex(void)
+{+ npe_sdl.target = &backtex;
+ return 0;
+}
--- /dev/null
+++ b/libnpe_sdl3/threads.c
@@ -1,0 +1,99 @@
+#include "_sdl.h"
+
+struct SDL_Thread {+ SDL_ThreadFunction f;
+ const char *name;
+ void *userdata;
+ Channel *wait;
+};
+
+static int prio[] = {+ [SDL_THREAD_PRIORITY_LOW] = 5,
+ [SDL_THREAD_PRIORITY_NORMAL] = 10,
+ [SDL_THREAD_PRIORITY_HIGH] = 13,
+ [SDL_THREAD_PRIORITY_TIME_CRITICAL] = 19,
+};
+
+static void sdlthread(void *p);
+
+SDL_Thread *
+SDL_CreateThreadWithStackSize(SDL_ThreadFunction f, const char *name, size_t stacksz, void *data)
+{+ SDL_Thread *t;
+
+ if((t = calloc(1, sizeof(*t))) == nil)
+ return nil;
+
+ t->f = f;
+ t->name = name;
+ t->userdata = data;
+ t->wait = chancreate(sizeof(int), 0);
+
+ if(t->wait == nil || proccreate(sdlthread, t, stacksz) < 0){+ if(t->wait != nil)
+ chanfree(t->wait);
+ free(t);
+ t = nil;
+ }
+
+ return t;
+}
+
+SDL_Thread *
+SDL_CreateThread(SDL_ThreadFunction f, char *name, void *data)
+{+ return SDL_CreateThreadWithStackSize(f, name, mainstacksize, data);
+}
+
+void
+SDL_DetachThread(SDL_Thread *t)
+{+ if(t != nil)
+ chanclose(t->wait);
+}
+
+void
+SDL_WaitThread(SDL_Thread *t, int *status)
+{+ int r;
+
+ if(t != nil){+ recv(t->wait, &r);
+ if(status != nil)
+ *status = r;
+ }
+}
+
+void
+SDL_SetCurrentThreadPriority(int p)
+{+ char t[32];
+ int f;
+
+ if(p < 0 || p >= nelem(prio))
+ return;
+
+ snprint(t, sizeof(t), "/proc/%d/ctl", getpid());
+ if((f = open(t, OWRITE)) >= 0){+ fprint(f, "pri %d", prio[p]);
+ close(f);
+ }
+}
+
+static void
+sdlthread(void *p)
+{+ SDL_Thread *t;
+ int res;
+
+ t = p;
+ if(t->name != nil)
+ threadsetname(t->name);
+
+ res = t->f(t->userdata);
+ send(t->wait, &res);
+ chanfree(t->wait);
+ free(t);
+
+ threadexits(res == 0 ? nil : "error");
+}
--- /dev/null
+++ b/libnpe_sdl3/window.c
@@ -1,0 +1,193 @@
+#include "_sdl.h"
+
+struct SDL_Window {+ int dummy;
+};
+static SDL_Window onewin;
+
+void
+SDL_SetWindowIcon(SDL_Window *w, SDL_Surface *icon)
+{+ USED(w); USED(icon);
+}
+
+void
+SDL_SetWindowBordered(SDL_Window *w, SDL_bool flag)
+{+ USED(w); USED(flag);
+}
+
+Uint32
+SDL_GetWindowPixelFormat(SDL_Window *)
+{+ if(screen != nil)
+ return chan2pixel(screen->chan);
+ return 0;
+}
+
+void
+SDL_GetWindowSize(SDL_Window *, int *w, int *h)
+{+ /* no matter what rio decides */
+ *w = npe_sdl.physw;
+ *h = npe_sdl.physh;
+}
+
+bool
+SDL_GetWindowSizeInPixels(SDL_Window *, int *w, int *h)
+{+ *w = Dx(screen->r);
+ *h = Dy(screen->r);
+ return true;
+}
+
+void
+SDL_GetWindowPosition(SDL_Window *, int *x, int *y)
+{+ *x = screen->r.min.x;
+ *y = screen->r.min.y;
+}
+
+int
+SDL_GetWindowBordersSize(SDL_Window *, int *t, int *l, int *b, int *r)
+{+ if(t != nil)
+ *t = Borderwidth;
+ if(l != nil)
+ *l = Borderwidth;
+ if(b != nil)
+ *b = Borderwidth;
+ if(r != nil)
+ *r = Borderwidth;
+ return 0;
+}
+
+void
+SDL_SetWindowSize(SDL_Window *, int w, int h)
+{+ int f, n;
+
+ if(w == 0 || h == 0)
+ return;
+ if(npe_sdl.physw != w || npe_sdl.physh != h){+ if((f = open("/dev/wctl", OWRITE|OCEXEC)) >= 0){+ n = fprint(f, "resize -dx %d -dy %d", w+Borderwidth*2, h+Borderwidth*2);
+ if(n > 0){+ while(getwindow(display, Refnone) != 1)
+ ;
+ npe_sdl.physw = w;
+ npe_sdl.physh = h;
+ npe_sdl.fullredraw = 1;
+ }else{+ fprint(2, "SDL_SetWindowSize: resize: %r\n");
+ }
+ close(f);
+ }else{+ fprint(2, "SDL_SetWindowSize: open: %r\n");
+ }
+ }
+}
+
+void
+SDL_SetWindowMinimumSize(SDL_Window *window, int min_w, int min_h)
+{+ SDL_SetWindowSize(window, min_w, min_h);
+}
+
+int
+SDL_ShowSimpleMessageBox(Uint32, char *title, char *message, SDL_Window *)
+{+ /* FIXME display a GUI window? */
+ fprint(2, "%s: %s\n", title, message);
+ return 0;
+}
+
+int
+SDL_SetWindowFullscreen(SDL_Window *, Uint32)
+{+ /* FIXME again, ft2 does NOT check the error code, figure something out */
+ werrstr("SDL_SetWindowFullscreen: not implemented");+ return -1;
+}
+
+void
+SDL_SetWindowGrab(SDL_Window *, SDL_bool grabbed)
+{+ /* FIXME not sure if it's worth anything */
+ USED(grabbed);
+}
+
+void
+SDL_SetWindowPosition(SDL_Window *, int x, int y)
+{+ int f, n;
+
+ if((f = open("/dev/wctl", OWRITE|OCEXEC)) >= 0){+ n = fprint(f, "move -minx %d -miny %d", x, y);
+ close(f);
+ if(n > 0){+ while(getwindow(display, Refnone) != 1)
+ ;
+ npe_sdl.fullredraw = 1;
+ npe_sdl.grabout = insetrect(screen->r, Dx(screen->r)/8);
+ npe_sdl.center = addpt(screen->r.min, Pt(Dx(screen->r)/2, Dy(screen->r)/2));
+ }
+ }
+}
+
+void
+SDL_RestoreWindow(SDL_Window *)
+{+ /* nothing to do here */
+}
+
+void
+SDL_RaiseWindow(SDL_Window *)
+{+ /* nothing to do here */
+}
+
+void
+SDL_ShowWindow(SDL_Window *)
+{+ /* nothing to do here */
+}
+
+bool
+SDL_SyncWindow(SDL_Window *)
+{+ return true;
+}
+
+bool
+SDL_CreateWindowAndRenderer(const char *title, int w, int h, SDL_WindowFlags f, SDL_Window **win, SDL_Renderer **rend)
+{+ USED(f);
+ SDL_SetWindowSize(&onewin, w, h);
+ *win = &onewin;
+ *rend = SDL_CreateRenderer(&onewin, nil);
+ SDL_SetWindowTitle(&onewin, title);
+ return true;
+}
+
+void
+SDL_DestroyWindow(SDL_Window *)
+{+}
+
+SDL_Window *
+SDL_CreateWindow(char *title, int x, int y, int w, int h, Uint32)
+{+ SDL_SetWindowTitle(&onewin, title);
+ SDL_SetWindowSize(&onewin, w, h);
+
+ if(x != SDL_WINDOWPOS_UNDEFINED && y != SDL_WINDOWPOS_UNDEFINED){ /* FIXME either of these can be undefined */+ if(x == SDL_WINDOWPOS_CENTERED)
+ x = display->image->r.min.x + (Dx(display->image->r) - w)/2;
+ if(y == SDL_WINDOWPOS_CENTERED)
+ y = display->image->r.min.y + (Dy(display->image->r) - h)/2;
+ SDL_SetWindowPosition(&onewin, x, y);
+ }
+
+ return &onewin;
+}
--- a/mkfile
+++ b/mkfile
@@ -5,6 +5,8 @@
libnpe_portmidi\
libnpe_pthread\
libnpe_sdl2\
+ libnpe_sdl3\
+ libnpe_rtmidi\
default:VQ:
mk all
--
⑨