shithub: npe

Download patch

ref: 3ad15c411ee11022d633b655f7b42066a52eb60e
parent: 2b10549c4fcfed1d97488ba633f045ae6da393e3
parent: 5c0d011f240bb77c9889c3d92548a6f799cf0fca
author: Jacob Moody <moody@posixcafe.org>
date: Mon Mar 6 00:22:47 EST 2023

merge

--- a/include/npe/SDL2/SDL.h
+++ b/include/npe/SDL2/SDL.h
@@ -33,6 +33,8 @@
 typedef int SDL_SystemCursor;
 typedef union SDL_Color SDL_Color;
 typedef struct SDL_Palette SDL_Palette;
+typedef struct SDL_RendererInfo SDL_RendererInfo;
+typedef struct SDL_mutex SDL_mutex;
 
 #pragma incomplete SDL_Cursor
 #pragma incomplete SDL_Renderer
@@ -60,6 +62,12 @@
 SDL_bool SDL_HasSSE(void);
 SDL_bool SDL_HasSSE2(void);
 int SDL_Init(int);
+int SDL_InitSubSystem(int);
+int 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(int toggle);
 Uint64 SDL_GetPerformanceFrequency(void);
@@ -135,8 +143,21 @@
 char *SDL_GetPrefPath(char *org, char *app);
 int SDL_SetRenderDrawColor(SDL_Renderer *r, Uint8 r, Uint8 g, Uint8 b, Uint8 a);
 int SDL_GetCurrentDisplayMode(int displayIndex, SDL_DisplayMode *mode);
+int SDL_GetNumDisplayModes(int displayIndex);
 void SDL_ShowWindow(SDL_Window *w);
 int SDL_RenderSetIntegerScale(SDL_Renderer *r, SDL_bool enable);
+int SDL_GetNumVideoDisplays(void);
+void SDL_SetModState(SDL_Keymod modstate);
+SDL_mutex* SDL_CreateMutex(void);
+void SDL_DestroyMutex(SDL_mutex*);
+int SDL_LockMutex(SDL_mutex*);
+int SDL_UnlockMutex(SDL_mutex*);
+#define SDL_mutexV(x) SDL_UnlockMutex(x)
+#define SDL_mutexP(x) SDL_LockMutex(x)
+int SDL_FillRect(SDL_Surface *dst, const SDL_Rect *rect, Uint32 color);
+int SDL_SetPaletteColors(SDL_Palette *palette, const SDL_Color *colors, int firstcolor, int ncolors);
+int SDL_BlitSurface(SDL_Surface *src, const 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);
  
 enum {
 	SDL_QUERY = -1,
@@ -156,9 +177,10 @@
 	SDL_WINDOWPOS_CENTERED = -1,
 	SDL_WINDOWPOS_UNDEFINED = -2,
 
-	SDL_INIT_AUDIO = 1<<0,
-	SDL_INIT_VIDEO = 1<<1,
-	SDL_INIT_JOYSTICK = 1<<2,
+	SDL_INIT_TIMER = 1<<0,
+	SDL_INIT_AUDIO = 1<<1,
+	SDL_INIT_VIDEO = 1<<2,
+	SDL_INIT_JOYSTICK = 0,
 
 	SDL_BLENDMODE_NONE = 0,
 	SDL_BLENDMODE_BLEND,
@@ -169,6 +191,7 @@
 
 	SDL_PIXELFORMAT_ARGB8888 = 0x30128888,
 	SDL_PIXELFORMAT_XRGB8888 = 0x16161804,
+	SDL_PIXELFORMAT_INDEX8 = 0x13000801,
 	SDL_PIXELFORMAT_RGB24 = 0x17101803,
 	SDL_PIXELFORMAT_RGB888 = SDL_PIXELFORMAT_XRGB8888,
 
@@ -175,8 +198,12 @@
 	/* 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_INIT_NOPARACHUTE = 0,
+	SDL_RENDERER_SOFTWARE = 0,
+	SDL_SWSURFACE = 0,
 
 	/* FIXME steal from rio and add missing? */
 	SDL_SYSTEM_CURSOR_ARROW = 0,
@@ -201,13 +228,19 @@
 	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"
 
 struct SDL_Point {
 	int x, y;
@@ -225,7 +258,9 @@
 	int keyset;
 	int w, h;
 	int pitch;
-	uchar pixels[];
+	int n;
+	void *i;
+	uchar *pixels;
 };
 
 struct SDL_DisplayMode {
@@ -249,6 +284,15 @@
 struct SDL_PixelFormat {
 	SDL_Palette *palette;
 	int format;
+};
+
+struct SDL_RendererInfo {
+	int max_texture_width;
+	int max_texture_height;
+};
+
+struct SDL_mutex {
+	Lock l;
 };
 
 #endif
--- /dev/null
+++ b/include/npe/SDL2/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
--- a/include/npe/SDL2/SDL_events.h
+++ b/include/npe/SDL2/SDL_events.h
@@ -17,12 +17,23 @@
 	SDL_WINDOWEVENT,
 	SDL_WINDOWEVENT_HIDDEN,
 	SDL_WINDOWEVENT_SHOWN,
-	SDL_WINDOWEVENT_MINIMIZED,
 	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_PRESSED = SDL_KEYDOWN,
+	SDL_RELEASED = SDL_KEYUP,
+
 	SDL_TEXTINPUTEVENT_TEXT_SIZE = UTFmax,
 
 	SDL_ADDEVENT = 0,
@@ -30,27 +41,61 @@
 	SDL_GETEVENT,
 };
 
+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_Event SDL_Event;
 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;
+};
+
 struct SDL_Event {
 	int type;
+	SDL_WindowEvent window;
 	struct {
-		int event;
-	}window;
-	struct {
-		struct {
-			SDL_Scancode scancode;
-			SDL_Keycode sym;
-		}keysym;
+		SDL_Keysym keysym;
 		int repeat;
+		int state;
 	}key;
+	SDL_MouseButtonEvent button;
 	struct {
-		int x, y;
 		int button;
-	}button;
-	struct {
-		int button;
 		int state;
 	}cbutton;
 	struct {
@@ -61,15 +106,18 @@
 		int axis;
 	}jaxis;
 	struct {
+		int xrel;
+		int yrel;
+	}jball;
+	struct {
 		int x, y;
+		int xrel, yrel;
 	}motion;
 	struct {
 		char text[SDL_TEXTINPUTEVENT_TEXT_SIZE+1];
 	}text;
+	SDL_MouseWheelEvent wheel;
 	struct {
-		int x, y;
-	}wheel;
-	struct {
 		char *file;
 	}drop;
 };
@@ -80,5 +128,7 @@
 int SDL_WaitEvent(SDL_Event *event);
 int 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);
 
 #endif
--- a/include/npe/SDL2/SDL_joystick.h
+++ b/include/npe/SDL2/SDL_joystick.h
@@ -5,5 +5,16 @@
 
 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
--- a/include/npe/SDL2/SDL_keycode.h
+++ b/include/npe/SDL2/SDL_keycode.h
@@ -76,6 +76,15 @@
 	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? */
@@ -90,6 +99,24 @@
 	SDLK_CAPSLOCK,
 	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,
 
 	/* FIXME no distinction */
 	KMOD_LSHIFT = 1<<0,
--- /dev/null
+++ b/include/npe/SDL2/SDL_mixer.h
@@ -1,0 +1,51 @@
+#ifndef _npe_SDL_mixer_h_
+#define _npe_SDL_mixer_h_
+
+#include <SDL2/SDL_rwops.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);
+
+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_RWops *src, int freesrc);
+Mix_Music* Mix_LoadMUS(char *filename);
+
+enum {
+	MIX_INIT_MID = 1,
+
+	MIX_DEFAULT_FORMAT = 1,
+};
+
+#endif
--- a/include/npe/SDL2/SDL_rwops.h
+++ b/include/npe/SDL2/SDL_rwops.h
@@ -21,6 +21,7 @@
 };
 
 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);
--- a/include/npe/SDL2/SDL_scancode.h
+++ b/include/npe/SDL2/SDL_scancode.h
@@ -101,6 +101,9 @@
 	SDL_SCANCODE_KP_0,
 	SDL_SCANCODE_KP_PERIOD,
 	SDL_SCANCODE_NONUSBACKSLASH,
+	SDL_SCANCODE_COMMA,
+	SDL_SCANCODE_PERIOD,
+	SDL_SCANCODE_SYSREQ,
 
 	SDL_SCANCODE_MENU = 0x76,
 
@@ -119,6 +122,8 @@
 
 	SDL_SCANCODE_MODE = 0x101,
 	SDL_SCANCODE_AUDIOMUTE = 0x106,
+
+	SDL_NUM_SCANCODES,
 };
 
 #endif
--- /dev/null
+++ b/include/npe/config.h
@@ -1,0 +1,4 @@
+#ifndef _npe_config_h_
+#define _npe_config_h_
+
+#endif
--- a/include/npe/inttypes.h
+++ b/include/npe/inttypes.h
@@ -3,7 +3,9 @@
 
 #include <stdint.h>
 
+#ifndef CHAR_BIT
 #define CHAR_BIT 8
+#endif
 
 #define PRId64 "%lld"
 #define PRIu64 "%llud"
--- /dev/null
+++ b/include/npe/locale.h
@@ -1,0 +1,4 @@
+#ifndef _npe_locale_h_
+#define _npe_locale_h_
+
+#endif
--- a/include/npe/wchar.h
+++ b/include/npe/wchar.h
@@ -1,1 +1,6 @@
 #include <npe.h>
+
+typedef Rune wchar_t;
+
+#define mblen(r,n) (utfnlen(r, n))
+#define mbtowc(r,s,n) (chartorune(r,s))
--- a/libnpe_sdl2/_sdl.h
+++ b/libnpe_sdl2/_sdl.h
@@ -12,6 +12,9 @@
 
 struct npe_sdl {
 	Mousectl *mctl;
+	Rectangle grabout;
+	Point center;
+	int mgrab;
 	Mouse m, om;
 	int hints;
 	int mredraw;
--- a/libnpe_sdl2/events.c
+++ b/libnpe_sdl2/events.c
@@ -28,6 +28,7 @@
 
 static int rune2scancode(Rune r);
 static void kbdproc(void *);
+static void mouseproc(void *);
 
 int
 npe_sdl_init_input(void)
@@ -37,16 +38,26 @@
 
 	salt[Ckey].c = chancreate(sizeof(Rune), 20);
 	salt[Ckeytype].c = chancreate(sizeof(int), 20);
-	salt[Cmouse].c = npe_sdl.mctl->c;
+	salt[Cmouse].c = chancreate(sizeof(Mouse), 20);
 	salt[Cresize].c = npe_sdl.mctl->resizec;
 	kctl.c = salt[Ckey].c; /* for enter() */
 
-	if(salt[Ckey].c == nil || salt[Ckeytype].c == nil || proccreate(kbdproc, nil, 4096) < 0)
+	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)
 {
@@ -80,6 +91,12 @@
 	return quitreq;
 }
 
+void
+SDL_PumpEvents(void)
+{
+	/* FIXME does it matter? */
+}
+
 #define ISTEXT(r) ((r) >= 0x20 && ((r) < KF || (r) >= KF+0x1000))
 
 int
@@ -109,14 +126,27 @@
 			e->key.repeat = t == Rrepeat;
 			e->key.keysym.scancode = rune2scancode(rune);
 			e->key.keysym.sym = rune;
+			e->key.state = e->type;
 			return 1;
 		}
 		break;
 
 	case Cmouse:
+		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.m.buttons == npe_sdl.om.buttons){
@@ -156,13 +186,11 @@
 	case Cresize:
 		memset(e, 0, sizeof(*e));
 		npe_sdl.fullredraw = 1;
-		if(getwindow(display, Refnone) < 0){
-			fprint(2, "%r\n");
-			/* FIXME do something here? */
-			//threadexitsall(nil);
-		}
+		while(getwindow(display, Refnone) != 1)
+			;
 		e->type = SDL_WINDOWEVENT;
 		e->window.event = SDL_WINDOWEVENT_EXPOSED;
+		e->window.windowID = 1; //TODO more then one?
 		return 1;
 	}
 
@@ -335,11 +363,10 @@
 						kmod &= ~KMOD_LSHIFT;
 					else if(r == Kctl)
 						kmod &= ~KMOD_LCTRL;
-					else{
-						t = Rup;
-						send(salt[Ckey].c, &r);
-						send(salt[Ckeytype].c, &t);
-					}
+
+					t = Rup;
+					send(salt[Ckey].c, &r);
+					send(salt[Ckeytype].c, &t);
 				}
 			}
 			break;
@@ -348,4 +375,20 @@
 	}
 
 	threadexits(nil);
+}
+
+static void
+mouseproc(void *)
+{
+	Mouse m;
+	for(;;){
+		recv(npe_sdl.mctl->c, &m);
+		if(npe_sdl.mgrab == SDL_TRUE){
+			if(!ptinrect(m.xy, npe_sdl.grabout)){
+				moveto(npe_sdl.mctl, npe_sdl.center);
+				m.xy = Pt(-1,-1);
+			}
+		}
+		send(salt[Cmouse].c, &m);
+	}
 }
--- /dev/null
+++ b/libnpe_sdl2/mixer.c
@@ -1,0 +1,285 @@
+#include "_sdl.h"
+
+#include <SDL2/SDL_mixer.h>
+
+int audiofd = -1;
+int musicpipe[2] = {-1, -1};
+
+static int forkerpid = -1;
+static int audiopid = -1;
+
+void musicproc(void *);
+
+static int musicvol = 128;
+static int musicpaused = 0;
+
+/* FIXME proper chains per channel */
+Mix_EffectFunc_t effunc = nil;
+
+static int doneinit = 0;
+static int devopen = 0;
+
+enum { Maxchan = 16 };
+SDL_AudioSpec channels[Maxchan];
+int nchannels = 0;
+
+int
+Mix_OpenAudio(int freq, Uint16 format, int nch, int chunk)
+{
+	SDL_AudioSpec *as;
+	int sz;
+
+	if(devopen)
+		return 0;
+
+	switch(format){
+	case 1:
+	case 2:
+		sz = 1;
+		break;
+	case 3:
+	case 4:
+		sz = 2;
+		break;
+	default:
+		werrstr("unsupported format");
+		return -1;
+	}
+
+	as = &channels[nchannels++];
+	assert(nchannels < Maxchan);
+	as->freq = freq;
+	as->format = format;
+	as->channels = nch;
+	as->samples = chunk / sz;
+	return 0;
+}
+
+char*
+Mix_GetError(void)
+{
+	return "";
+}
+
+static void
+translate(void *arg, Uint8 *buf, int len)
+{
+	Mix_EffectFunc_t f;
+
+	f = (Mix_EffectFunc_t)arg;
+
+	f(0, buf, len, nil);
+}
+
+int
+Mix_RegisterEffect(int chan, Mix_EffectFunc_t f, Mix_EffectDone_t d, void *arg)
+{
+	SDL_AudioSpec *as;
+	int n;
+
+	USED(arg); USED(d);
+	as = channels + chan;
+	as->userdata = f;
+	as->callback = translate;
+	if(devopen){
+		werrstr("device already open");
+		return -1;
+	}
+	n = SDL_OpenAudioDevice(nil, 0, as, nil, 0);
+	assert(n >= 0);
+	SDL_PauseAudioDevice(n, 0);
+	return 1;
+}
+
+Mix_Chunk*
+Mix_QuickLoad_RAW(Uint8 *mem, Uint32 len)
+{
+	USED(mem);
+	USED(len);
+	return nil;
+}
+
+int
+Mix_PlayChannel(int chan, Mix_Chunk *chunk, int loops)
+{
+	USED(chan);
+	USED(chunk);
+	USED(loops);
+	return -1;
+}
+
+int
+Mix_HaltChannel(int channel)
+{
+	USED(channel);
+	return 0;
+}
+
+void
+Mix_FreeChunk(Mix_Chunk *c)
+{
+	USED(c);
+}
+
+void
+Mix_CloseAudio(void)
+{
+	if(audiopid > 0)
+		postnote(PNPROC, audiopid, "quit");
+	audiopid = -1;
+}
+
+int
+Mix_Init(int flags)
+{
+	if(doneinit)
+		return flags;
+	doneinit = 1;
+	memset(channels, 0, sizeof channels);
+	rfork(RFNAMEG);
+	if(fork() == 0){
+		execl("/bin/audio/mixfs", "mixfs", nil);
+		sysfatal("exec: %r\n");
+	}
+	waitpid();
+	pipe(musicpipe);
+	if(fork() == 0){
+		musicproc(nil);
+		sysfatal("exec musicproc: %r");
+	}
+	return flags;
+}
+
+int
+Mix_VolumeMusic(int vol)
+{
+	if(vol < 0)
+		vol = 0;
+	else if(vol > 128)
+		vol = 128;
+	musicvol = vol;
+	return 0;
+}
+
+int
+Mix_PlayingMusic(void)
+{
+	return forkerpid > 0 && !musicpaused;
+}
+
+int
+Mix_PausedMusic(void)
+{
+	return musicpaused;
+}
+void
+Mix_ResumeMusic(void)
+{
+	musicpaused = 0;	
+}
+
+void
+Mix_PauseMusic(void)
+{
+	musicpaused = 1;
+}
+
+int
+Mix_HaltMusic(void)
+{
+	if(forkerpid < 0)
+		return 0;
+
+	postnote(PNGROUP, forkerpid, "halt");
+	forkerpid = -1;
+	return 0;
+}
+
+int
+Mix_PlayMusic(Mix_Music *music, int loops)
+{
+	Waitmsg *wm;
+	int n;
+
+	if(forkerpid > 0)
+		Mix_HaltMusic();
+
+	if((forkerpid = rfork(RFPROC|RFNOTEG)) != 0)
+		return 0;
+
+	n = loops;
+	while(loops == -1 || n-- > 0){
+		switch(rfork(RFPROC|RFFDG)){
+		case 0:
+			dup(musicpipe[1], 1);
+			close(musicpipe[1]);
+			close(musicpipe[0]);
+			execl("/bin/games/midi", "midi", "-c", music->loc, nil);
+			sysfatal("exec: %r");
+			break;
+		default:
+			wm = wait();
+			if(wm->msg != nil && wm->msg[0] != '\0'){
+				fprint(2, "playmusic: %s\n", wm->msg);
+				threadexits(nil);
+			}
+			free(wm);
+			break;
+		}
+	}
+	threadexits(nil);
+	return -1;
+}
+
+Mix_Music*
+Mix_LoadMUS(char *filename)
+{
+	Mix_Music *m;
+
+	m = calloc(1, sizeof(*m));
+	m->loc = strdup(filename);
+	m->fd = open(filename, OREAD);
+	if(m->fd < 0)
+		sysfatal("LoadMUS: %r");
+	return m;
+}
+
+Mix_Music*
+Mix_LoadMUS_RW(SDL_RWops *src, int freesrc)
+{
+	Mix_Music *m;
+	char buf[1024];
+	int n;
+
+	m = calloc(1, sizeof(*m));
+	m->loc = smprint("/tmp/npesdl.mus.%d", getpid());
+	m->fd = create(m->loc, ORDWR|ORCLOSE, 0666);
+	while((n = SDL_RWread(src, buf, 1, sizeof buf)) > 0)
+		write(m->fd, buf, n);
+	seek(m->fd, 0, 0);
+
+	if(freesrc)
+		SDL_RWclose(src);
+	return m;
+}
+
+void
+musicproc(void *)
+{
+	int fd;
+	static char buf[8192];
+	int n;
+
+	fd = open("/dev/audio", OWRITE);
+	if(fd < 0)
+		sysfatal("musicproc: %r");
+
+	close(musicpipe[1]);
+	threadsetname("musicproc");
+	for(;;){
+		n = read(musicpipe[0], buf, sizeof buf);
+		if(n < 0)
+			threadexits(nil);
+		write(fd, buf, n);
+	}
+}
--- a/libnpe_sdl2/mkfile
+++ b/libnpe_sdl2/mkfile
@@ -12,6 +12,7 @@
 	rwops.$O\
 	scale.$O\
 	sdl2.$O\
+	mixer.$O\
 	threads.$O\
 
 UPDATE=\
--- a/libnpe_sdl2/rwops.c
+++ b/libnpe_sdl2/rwops.c
@@ -1,8 +1,23 @@
 #include "_sdl.h"
 #include <bio.h>
 
+typedef struct {
+	uchar *memdata;
+	int memn;
+	int mempos;
+} Membuf;
+
+static vlong memsize(struct SDL_RWops *);
+static vlong memseek(struct SDL_RWops *, vlong, int);
+static size_t memread(struct SDL_RWops *, void *, size_t, size_t);
+static size_t memwrite(struct SDL_RWops *, const void *, size_t, size_t);
+static int memclose(struct SDL_RWops *);
+
 struct npe_sdl_rwops {
-	Biobuf;
+	union {
+		Biobuf;
+		Membuf;
+	};
 };
 
 static vlong bsize(struct SDL_RWops *);
@@ -54,6 +69,30 @@
 	return nil;
 }
 
+SDL_RWops*
+SDL_RWFromMem(void *mem, int size)
+{
+	SDL_RWops *o;
+	Membuf *b;
+
+	o = calloc(1, sizeof(*o)+sizeof(npe_sdl_rwops));
+	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_RWread(SDL_RWops *o, void *b, size_t sz, size_t n)
 {
@@ -153,4 +192,86 @@
 bclose(struct SDL_RWops *o)
 {
 	return Bterm(o->p);
+}
+
+static vlong
+memseek(struct SDL_RWops *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_RWops *o, void *b, size_t sz, size_t n)
+{
+	Membuf *buf;
+	uchar *p, *dot;
+	uchar *end;
+	size_t i;
+
+	buf = (Membuf*)o->p;
+	end = buf->memdata + buf->memn;
+	for(i = 0, p = b; i < n; i++, p += sz){
+		dot = buf->memdata + buf->mempos;
+		if(dot + sz >= end){
+			memmove(p, dot, end - dot);
+			buf->mempos = buf->memn;
+			dot = end;
+		}
+		if(dot == end)
+			return i;
+
+		assert(dot < end);
+		memmove(p, dot, sz);
+		buf->mempos += sz;
+	}
+
+	return i;
+}
+
+static size_t
+memwrite(struct SDL_RWops *o, const void *b, size_t sz, size_t n)
+{
+	Membuf *buf;
+	const uchar *p;
+	size_t i;
+
+	buf = (Membuf*)o->p;
+	for(i = 0, p = b; i < n; i++, p += sz){
+		memmove(buf->mempos + buf->memdata, p, sz);
+		buf->mempos += sz;
+	}
+	return i;
+}
+
+static vlong
+memsize(struct SDL_RWops *o)
+{
+	Membuf *b;
+	b = (Membuf*)o->p;
+
+	return b->memn;
+}
+
+static int
+memclose(struct SDL_RWops *o)
+{
+	return 0;
 }
--- a/libnpe_sdl2/sdl2.c
+++ b/libnpe_sdl2/sdl2.c
@@ -55,8 +55,35 @@
 	},
 };
 
+int
+SDL_InitSubSystem(int mask)
+{
+	/* FIXME implement */
+	USED(mask);
+	return 0;
+}
 
 int
+SDL_QuitSubSystem(int mask)
+{
+	/* FIXME implement */
+	USED(mask);
+	return 0;
+}
+
+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);
+}
+
+int
 SDL_Init(int mask)
 {
 	/* FIXME actually use the mask? */
@@ -305,15 +332,34 @@
 {
 	SDL_Surface *s;
 	int n;
+	Memimage *i;
 
 	USED(flags, rm, gm, bm, am); /* FIXME flags & masks */
 
 	n = w*h*bpp/8;
-	if((s = calloc(1, sizeof(*s)+n)) == nil){
+	if((s = calloc(1, sizeof(*s))) == nil){
 		werrstr("SDL_CreateRGBSurface: memory");
 		return nil;
 	}
-	s->format = &argb8888;
+	s->i = i = allocmemimage(Rect(0,0,w,h), screen->chan);
+	s->format = calloc(1, sizeof(SDL_PixelFormat));
+	switch(bpp){
+	case 32:
+		s->format->format = SDL_PIXELFORMAT_ARGB8888;
+		s->pixels = i->data->bdata;
+		break;
+	case 8:
+		s->format->format = SDL_PIXELFORMAT_INDEX8;
+		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, n);
+		break;
+	default:
+		werrstr("non supported bpp");
+		return nil;
+	}
+	
 	s->w = w;
 	s->h = h;
 	s->pitch = w*bpp/8;
@@ -321,6 +367,7 @@
 	s->clip_rect.y = 0;
 	s->clip_rect.w = w;
 	s->clip_rect.h = h;
+	s->n = n;
 
 	return s;
 }
@@ -365,9 +412,149 @@
 	return s;
 }
 
+int
+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:
+		p = (Uint32*)dst->pixels;
+		for(i = 0; i < dst->n / sizeof(*p); i++)
+			p[i] = color;
+		break;
+	case SDL_PIXELFORMAT_INDEX8:
+		for(i = 0; i < dst->n; i++)
+			dst->pixels[i] = color;
+		break;
+	}
+
+	return 0;
+}
+
+int
+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 0;
+}
+
+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;
+	}
+}
+
+int
+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);
+
+	/* FIXME */
+	assert(dst->format->format != SDL_PIXELFORMAT_INDEX8);
+	memimagedraw(dst->i, r2, src->i, ZP, nil, ZP, S);
+	return 0;
+}
+
+int
+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 0;
+}
+
 void
 SDL_FreeSurface(SDL_Surface *surface)
 {
+	freememimage(surface->i);
 	memset(surface, 0, sizeof(surface));
 	free(surface);
 }
@@ -581,6 +768,7 @@
 {
 	Rectangle sr, dr;
 	int logiw, logih;
+	ulong chan;
 
 	if(rend->logiw > 0 && rend->logih > 0){
 		logiw = rend->logiw;
@@ -604,7 +792,11 @@
 
 	if(back == nil || Dx(back->r) != logiw || Dy(back->r) != logih){
 		freememimage(back);
-		back = allocmemimage(Rect(0, 0, logiw, logih), ARGB32);
+		if(screen)
+			chan = screen->chan;
+		else
+			chan = ARGB32;
+		back = allocmemimage(Rect(0, 0, logiw, logih), chan);
 		if(back == nil){
 			werrstr("SDL_RenderCopy: %r");
 			return -1;
@@ -675,6 +867,8 @@
 			replclipr(screen, 0, clipr);
 		}
 	}
+	while(screen == nil && getwindow(display, Refnone) != 1)
+		/* drawterm window change lag */;
 	draw(screen, screen->r, front, nil, ZP);
 	if(cursor != nil && showcursor)
 		draw(screen, r, cursor->i, cursor->m, ZP);
@@ -755,7 +949,8 @@
 			n = fprint(f, "resize -dx %d -dy %d", w+Borderwidth*2, h+Borderwidth*2);
 			close(f);
 			if(n > 0){
-				getwindow(display, Refnone);
+				while(getwindow(display, Refnone) != 1)
+					;
 				npe_sdl.fullredraw = 1;
 			}
 		}
@@ -794,8 +989,11 @@
 		n = fprint(f, "move -minx %d -miny %d", x, y);
 		close(f);
 		if(n > 0){
-			getwindow(display, Refnone);
+			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));
 		}
 	}
 }
@@ -820,6 +1018,23 @@
 	return SDL_GetDesktopDisplayMode(displayIndex, mode);
 }
 
+int
+SDL_GetNumDisplayModes(int displayIndex)
+{
+	if(displayIndex != 0)
+		return -1;
+	return 1;
+}
+
+int
+SDL_GetDisplayMode(int displayIndex, int modeIndex, SDL_DisplayMode *mode)
+{
+	USED(modeIndex);
+	physw = 1280;
+	physh = 960;
+	return SDL_GetDesktopDisplayMode(displayIndex, mode);
+}
+
 void
 SDL_SetWindowTitle(SDL_Window *, char *title)
 {
@@ -831,6 +1046,13 @@
 	}
 }
 
+int
+SDL_GetNumVideoDisplays(void)
+{
+	/* FIXME implement multihead for plan9 */
+	return 1;
+}
+
 void
 SDL_DestroyTexture(SDL_Texture *t)
 {
@@ -1077,15 +1299,137 @@
 }
 
 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;
+}
+
+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_ShowCursor(0);
+		else
+			SDL_ShowCursor(1);
+	}
+	npe_sdl.mgrab = enabled;
+	return 0;
+}
+
+void
 SDL_SetMainReady(void)
 {
 }
 
+int
+SDL_GetRelativeMouseMode(void)
+{
+	return npe_sdl.mgrab;
+}
+
+SDL_mutex*
+SDL_CreateMutex(void)
+{
+	SDL_mutex *m;
+
+	m = mallocz(sizeof *m, 1);
+	return m;
+}
+
 void
-SDL_GetVersion(SDL_version *v)
+SDL_DestroyMutex(SDL_mutex *m)
 {
-	/* these are arbitrary */
-	v->major = 2;
-	v->minor = 24;
-	v->patch = 1;
+	free(m);
+}
+
+int
+SDL_LockMutex(SDL_mutex *m)
+{
+	lock(&m->l);
+	return 0;
+}
+
+int
+SDL_UnlockMutex(SDL_mutex *m)
+{
+	unlock(&m->l);
+	return 0;
+}
+
+void
+SDL_SetModState(SDL_Keymod modstate)
+{
+	/* FIXME: do we care? */
+	USED(modstate);
 }