shithub: npe

Download patch

ref: 396ff35e7d8f1d9b2283a4cd18836194fed1c630
parent: fd731d3c833d9241fdff618b6c949453fb5c0f31
author: Sigrid Solveig Haflínudóttir <ftrvxmtrx@gmail.com>
date: Mon Mar 15 08:58:49 EDT 2021

build & install as syslibs

diff: cannot open b/include/npe/SDL2//null: file does not exist: 'b/include/npe/SDL2//null' diff: cannot open b/include/npe/sys//null: file does not exist: 'b/include/npe/sys//null' diff: cannot open b/include/npe//null: file does not exist: 'b/include/npe//null' diff: cannot open b/include//null: file does not exist: 'b/include//null' diff: cannot open b/libnpe//null: file does not exist: 'b/libnpe//null' diff: cannot open b/libnpe_sdl2//null: file does not exist: 'b/libnpe_sdl2//null'
--- a/SDL2/SDL.h
+++ /dev/null
@@ -1,431 +1,0 @@
-#ifndef _SDL_h_
-#define _SDL_h_
-
-#include "plan9.h"
-#include <keyboard.h>
-
-typedef int SDL_AudioDeviceID;
-typedef struct SDL_AudioSpec SDL_AudioSpec;
-typedef struct SDL_Window SDL_Window;
-typedef struct SDL_Renderer SDL_Renderer;
-typedef struct SDL_Texture SDL_Texture;
-typedef struct SDL_Surface SDL_Surface;
-typedef struct SDL_Rect SDL_Rect;
-typedef int SDL_Keycode;
-typedef int SDL_Scancode;
-typedef int SDL_Keymod;
-typedef struct SDL_Event SDL_Event;
-typedef u8int Uint8;
-typedef u16int Uint16;
-typedef u32int Uint32;
-typedef int SDL_BlendMode;
-typedef struct SDL_Thread SDL_Thread;
-typedef int (*SDL_ThreadFunction)(void *);
-typedef struct SDL_Cursor SDL_Cursor;
-typedef struct SDL_PixelFormat SDL_PixelFormat;
-typedef struct SDL_Point SDL_Point;
-typedef int SDL_RendererFlip;
-typedef struct SDL_DisplayMode SDL_DisplayMode;
-
-#pragma incomplete SDL_Cursor
-#pragma incomplete SDL_PixelFormat
-#pragma incomplete SDL_Renderer
-#pragma incomplete SDL_Texture
-#pragma incomplete SDL_Thread
-#pragma incomplete SDL_Window
-
-typedef enum {SDL_FALSE, SDL_TRUE} SDL_bool;
-#define SDLCALL
-
-void SDL_StopTextInput(void);
-SDL_bool SDL_HasSSE(void);
-SDL_bool SDL_HasSSE2(void);
-int SDL_Init(int);
-int SDL_EventState(Uint32, int);
-SDL_Keymod SDL_GetModState(void);
-char *SDL_GetAudioDeviceName(int, SDL_bool);
-int SDL_GetNumAudioDevices(int iscapture);
-int SDL_ShowCursor(int toggle);
-void SDL_LockAudioDevice(SDL_AudioDeviceID);
-void SDL_UnlockAudioDevice(SDL_AudioDeviceID);
-void SDL_PauseAudioDevice(SDL_AudioDeviceID, SDL_bool);
-void SDL_CloseAudioDevice(SDL_AudioDeviceID);
-SDL_AudioDeviceID SDL_OpenAudioDevice(char *, int, SDL_AudioSpec *, SDL_AudioSpec *, u32int);
-u64int SDL_GetPerformanceFrequency(void);
-u64int SDL_GetPerformanceCounter(void);
-char *SDL_GetError(void);
-SDL_Thread *SDL_CreateThread(SDL_ThreadFunction, char *, void *);
-void SDL_DetachThread(SDL_Thread *);
-char *SDL_GetClipboardText(void);
-int SDL_SetClipboardText(char *);
-void SDL_SetThreadPriority(int);
-SDL_bool SDL_HasClipboardText(void);
-int SDL_PollEvent(SDL_Event *event);
-void SDL_RestoreWindow(SDL_Window *window);
-void SDL_RaiseWindow(SDL_Window *window);
-int SDL_UpdateTexture(SDL_Texture *texture, SDL_Rect *rect, void *pixels, int pitch);
-int SDL_RenderClear(SDL_Renderer *renderer);
-int SDL_GetWindowDisplayIndex(SDL_Window *window);
-void SDL_FreeSurface(SDL_Surface *surface);
-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);
-SDL_Surface *SDL_CreateRGBSurface(Uint32 flags, int width, int height, int depth, Uint32 rm, Uint32 gm, Uint32 bm, Uint32 am);
-SDL_Surface *SDL_CreateRGBSurfaceFrom(Uint32 *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);
-int SDL_SetColorKey(SDL_Surface *surface, int flag, 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);
-int SDL_PushEvent(SDL_Event *event);
-void SDL_WarpMouseInWindow(SDL_Window *window, int x, int y);
-void SDL_RenderGetScale(SDL_Renderer *renderer, float *scaleX, float *scaleY);
-void SDL_GetWindowSize(SDL_Window *window, int *w, int *h);
-void SDL_GetWindowPosition(SDL_Window *window, 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);
-int SDL_RenderCopy(SDL_Renderer *renderer, SDL_Texture *texture, SDL_Rect *srcrect, SDL_Rect *dstrect);
-void SDL_RenderPresent(SDL_Renderer *renderer);
-Uint32 SDL_GetWindowFlags(SDL_Window *window);
-int SDL_RenderSetLogicalSize(SDL_Renderer *renderer, int w, int h);
-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_DestroyTexture(SDL_Texture *texture);
-void SDL_DestroyRenderer(SDL_Renderer *renderer);
-void SDL_DestroyWindow(SDL_Window *window);
-int SDL_GetDesktopDisplayMode(int displayIndex, SDL_DisplayMode *mode);
-void SDL_SetWindowTitle(SDL_Window *window, char *title);
-int SDL_SetTextureBlendMode(SDL_Texture *texture, SDL_BlendMode blendMode);
-SDL_bool SDL_SetHint(char *name, char *value);
-SDL_Window *SDL_CreateWindow(char *title, int x, int y, int w, int h, Uint32 flags);
-SDL_Renderer *SDL_CreateRenderer(SDL_Window *window, int index, Uint32 flags);
-int SDL_SetRenderDrawBlendMode(SDL_Renderer *renderer, SDL_BlendMode blendMode);
-char *SDL_GetCurrentVideoDriver(void);
-SDL_Texture *SDL_CreateTexture(SDL_Renderer *renderer, Uint32 format, int access, int w, int h);
-void SDL_EnableScreenSaver(void);
-Uint32 SDL_GetTicks(void);
-int SDL_GetRendererOutputSize(SDL_Renderer *renderer, int *w, int *h);
-int SDL_SaveBMP(SDL_Surface *s, const char *file);
-
-enum {
-	AUDIO_S16,
-	AUDIO_F32,
-
-	SDL_AUDIO_ALLOW_ANY_CHANGE = ~0,
-
-	SDL_THREAD_PRIORITY_HIGH = 1,
-
-	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_WINDOWPOS_CENTERED = -1,
-	SDL_WINDOWPOS_UNDEFINED = -2,
-
-	SDL_INIT_AUDIO = 1<<0,
-	SDL_INIT_VIDEO = 1<<1,
-	SDL_INIT_JOYSTICK = 1<<2,
-
-	SDL_BLENDMODE_NONE = 0,
-
-	SDL_FLIP_NONE = 0,
-	SDL_FLIP_HORIZONTAL,
-	SDL_FLIP_VERTICAL,
-
-	SDL_PIXELFORMAT_ARGB8888 = 0x30128888,
-
-	/* shit no one cares about */
-	SDL_TEXTUREACCESS_STREAMING = 0,
-	SDL_TEXTUREACCESS_STATIC = 0,
-	SDL_RENDERER_ACCELERATED = 0,
-	SDL_RENDERER_PRESENTVSYNC = 0,
-};
-
-enum {
-	/* these HAVE to be in this order for notes to work in ft2-clone */
-	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_ENTER,
-	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_SLASH,
-	SDL_SCANCODE_CAPSLOCK,
-
-	SDL_SCANCODE_RIGHT = 0x4f,
-	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_MUTE = 0x7f,
-	SDL_SCANCODE_VOLUMEUP,
-	SDL_SCANCODE_VOLUMEDOWN,
-
-	SDL_SCANCODE_AUDIOMUTE = 0x106,
-
-	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 = '\n',
-	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_LALT = Kalt,
-	SDLK_RALT = Kaltgr, /* FIXME what about keyboards without it? */
-	/* FIXME no distinction */
-	SDLK_LSHIFT = Kshift,
-	SDLK_RSHIFT = SDLK_LSHIFT,
-	SDLK_LCTRL = Kctl,
-	SDLK_RCTRL = '>', /* FIXME this is a hack */
-
-	/* FIXME not bound to anything */
-	SDLK_UNKNOWN = -99999,
-	SDLK_CAPSLOCK,
-	SDLK_KP_ENTER,
-
-	/* FIXME no distinction */
-	KMOD_LSHIFT = 1<<0,
-	KMOD_RSHIFT = KMOD_LSHIFT,
-	KMOD_LCTRL = 1<<1,
-	KMOD_RCTRL = KMOD_LCTRL,
-	KMOD_LALT = 1<<2,
-	KMOD_RALT = KMOD_LALT,
-	KMOD_LGUI = 1<<3,
-	KMOD_RGUI = KMOD_LGUI,
-
-	SDL_KEYDOWN = 0,
-	SDL_KEYUP,
-	SDL_MOUSEBUTTONDOWN,
-	SDL_MOUSEBUTTONUP,
-	SDL_MOUSEWHEEL,
-	SDL_MOUSEMOTION,
-	SDL_QUIT,
-	SDL_DROPFILE,
-	SDL_TEXTINPUT,
-	SDL_WINDOWEVENT,
-	SDL_WINDOWEVENT_HIDDEN,
-	SDL_WINDOWEVENT_SHOWN,
-	SDL_WINDOWEVENT_MINIMIZED,
-	SDL_WINDOWEVENT_FOCUS_LOST,
-	SDL_WINDOWEVENT_EXPOSED,
-
-	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,
-};
-
-struct SDL_AudioSpec {
-	void (*callback)(void *, Uint8 *, int);
-	void *userdata;
-	int freq;
-	int format;
-	int channels;
-	int samples;
-};
-
-struct SDL_Point {
-	int x, y;
-};
-
-struct SDL_Rect {
-	int x, y, w, h;
-};
-
-struct SDL_Surface {
-	SDL_PixelFormat *format;
-	SDL_Rect clip_rect;
-	Uint32 flags;
-	Uint32 key;
-	int keyset;
-	int w, h;
-	int pitch;
-	uchar pixels[];
-};
-
-struct SDL_Event {
-	int type;
-	union {
-		struct {
-			int event;
-		}window;
-		struct {
-			struct {
-				SDL_Scancode scancode;
-				SDL_Keycode sym;
-			}keysym;
-			int repeat;
-		}key;
-		struct {
-			int x, y;
-			int button;
-		}button;
-		struct {
-			int x, y;
-		}motion;
-		struct {
-			char text[UTFmax+1];
-		}text;
-		struct {
-			int x, y;
-		}wheel;
-		struct {
-			char *file;
-		}drop;
-	};
-};
-
-struct SDL_DisplayMode
-{
-	int format;
-	int w;
-	int h;
-	int refresh_rate;
-};
-
-#endif
--- a/alloca.h
+++ /dev/null
@@ -1,1 +1,0 @@
-#include "plan9.h"
--- a/assert.h
+++ /dev/null
@@ -1,1 +1,0 @@
-#include "plan9.h"
--- a/ctype.h
+++ /dev/null
@@ -1,7 +1,0 @@
-#ifndef _ctype_h_
-#define _ctype_h_
-
-#include "plan9.h"
-#include </sys/include/ctype.h>
-
-#endif
--- a/dirent.c
+++ /dev/null
@@ -1,59 +1,0 @@
-#include <dirent.h>
-
-struct DIR {
-	Dir *d;
-	struct dirent de;
-	int fd;
-	long n; /* number of entries in d */
-	long i; /* current entry to return from readdir */
-};
-
-struct dirent *
-readdir(DIR *d)
-{
-	if(d->i >= d->n){
-		free(d->d);
-		if((d->n = dirread(d->fd, &d->d)) <= 0)
-			return nil;
-		d->i = 0;
-	}
-
-	d->de.d_type = (d->d[d->i].qid.type & QTDIR) ? DT_DIR : DT_FILE;
-	d->de.d_name = d->d[d->i].name;
-	d->i++;
-
-	return &d->de;
-}
-
-DIR *
-opendir(char *name)
-{
-	Dir *dir;
-	DIR *d;
-	int fd;
-
-	dir = nil;
-	d = nil;
-	if((fd = open(name, OREAD|OCEXEC)) >= 0 &&
-	    (dir = dirfstat(fd)) != nil &&
-	    (dir->qid.type & QTDIR) != 0 &&
-	    (d = calloc(1, sizeof(*d))) != nil){
-		d->fd = fd;
-	}
-
-	free(dir);
-	if(d == nil)
-		close(fd);
-
-	return d;
-}
-
-int
-closedir(DIR *d)
-{
-	free(d->d);
-	close(d->fd);
-	free(d);
-
-	return 0;
-}
--- a/dirent.h
+++ /dev/null
@@ -1,25 +1,0 @@
-#ifndef _dirent_h_
-#define _dirent_h_
-
-#include "plan9.h"
-
-enum {
-	DT_LNK = -1,
-	DT_UNKNOWN = 0,
-	DT_FILE,
-	DT_DIR,
-};
-
-struct dirent {
-	int d_type;
-	char *d_name;
-};
-
-typedef struct DIR DIR;
-#pragma incomplete DIR
-
-DIR *opendir(char *name);
-int closedir(DIR *dirp);
-struct dirent *readdir(DIR *dirp);
-
-#endif
--- a/iconv.c
+++ /dev/null
@@ -1,125 +1,0 @@
-#include <iconv.h>
-
-enum {
-	Itranslit = 1<<0, /* no transliteration is performed :( */
-	Iignore = 1<<1, /* this option is ignored (pun TOTALLY intended) if Itranslit is set */
-
-	Tcs = 0,
-	Us,
-};
-
-struct iconv_s {
-	char to[16];
-	char from[16];
-	int fd;
-};
-
-static int
-cp2tcs(char *cp, char *tcs, int sz)
-{
-	int opt, n, i;
-	char *s;
-
-	opt = 0;
-	n = 0;
-	for(s = cp; (s = strstr(s, "//")) != nil;){
-		if(n == 0)
-			n = (int)(s - cp);
-		s += 2;
-		if(strncmp(s, "TRANSLIT", 8) == 0 && (s[8] == 0 || s[8] == '/'))
-			opt |= Itranslit;
-		else if(strncmp(s, "IGNORE", 6) == 0 && (s[6] == 0 || s[6] == '/'))
-			opt |= Iignore;
-	}
-	if(n < 1)
-		n = strlen(cp);
-
-	if(cp[0] >= '0' && cp[0] <= '9'){ /* ibm */
-		n = snprint(tcs, sz, "ibm%.*s", n, cp);
-	}else if(strncmp(cp, "ISO", 3) == 0){
-		if(cp[3] == '-' || cp[3] == '_'){
-			n--;
-			cp++;
-		}
-		n = snprint(tcs, sz, "%.*s", n-3, cp+3);
-	}else if(strncmp(cp, "CP12", 4) == 0){
-		n = snprint(tcs, sz, "windows-12%.*s", n-4, cp+4);
-	}else{ /* last chance, convert to lowercase. FIXME not all are supported, no checks are made */
-		n = snprint(tcs, sz, "%.*s", n, cp);
-		for(i = 0; i < n; i++)
-			tcs[i] = tolower(tcs[i]);
-	}
-
-	return n >= sz ? -1 : opt;
-}
-
-iconv_t
-iconv_open(char *to, char *from)
-{
-	int opt, pid, p[2];
-	iconv_t ic;
-
-	p[0] = p[1] = -1;
-	if((ic = calloc(1, sizeof(*ic))) == nil)
-		goto err;
-	if(cp2tcs(from, ic->from, sizeof(ic->from)) < 0)
-		goto err;
-	if((opt = cp2tcs(to, ic->to, sizeof(ic->to))) < 0)
-		goto err;
-	if(pipe(p) < 0)
-		goto err;
-	if((pid = rfork(RFPROC|RFFDG|RFNOTEG|RFCENVG|RFNOWAIT)) == 0){
-		dup(p[Tcs], 0);
-		dup(p[Tcs], 1);
-		close(p[0]);
-		close(p[1]);
-		close(2); /* comment it out if you need debugging */
-		execl("/bin/tcs", "tcs", "-f", ic->from, "-t", ic->to, (opt == Iignore) ? "-c" : nil, nil);
-		sysfatal("execl: %r");
-	}else if(pid < 0){
-		goto err;
-	}
-	close(p[Tcs]);
-	ic->fd = p[Us];
-
-	setmalloctag(ic, getcallerpc(&to));
-
-	return ic;
-err:
-	close(p[0]);
-	close(p[1]);
-	if(ic != nil)
-		free(ic);
-	return (iconv_t)-1;
-}
-
-void
-iconv_close(iconv_t ic)
-{
-	close(ic->fd);
-	free(ic);
-}
-
-size_t
-iconv(iconv_t ic, char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft)
-{
-	int n;
-
-	if(inbuf == nil || *inbuf == nil){ /* flush */
-		write(ic->fd, "", 0);
-	}else{
-		if((n = write(ic->fd, *inbuf, *inbytesleft)) < 0)
-			return -1;
-		*inbuf += n;
-		*inbytesleft -= n;
-		return 0;
-	}
-	if(outbuf == nil || *outbuf == nil) /* nothing to do */
-		return 0;
-	if((n = read(ic->fd, *outbuf, *outbytesleft)) < 0)
-		return -1;
-	*outbuf += n;
-	*outbytesleft -= n;
-
-	return n;
-}
--- a/iconv.h
+++ /dev/null
@@ -1,14 +1,0 @@
-#ifndef _iconv_h_
-#define _iconv_h_
-
-#include "plan9.h"
-#include <stdint.h>
-
-typedef struct iconv_s *iconv_t;
-#pragma incomplete struct iconv_s
-
-iconv_t iconv_open(char *to, char *from);
-void iconv_close(iconv_t);
-size_t iconv(iconv_t cd, char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft);
-
-#endif
--- /dev/null
+++ b/include/npe/SDL2/SDL.h
@@ -1,0 +1,433 @@
+#ifndef _SDL_h_
+#define _SDL_h_
+
+#include "plan9.h"
+#include <keyboard.h>
+
+#pragma lib "libnpe_sdl2.a"
+
+typedef int SDL_AudioDeviceID;
+typedef struct SDL_AudioSpec SDL_AudioSpec;
+typedef struct SDL_Window SDL_Window;
+typedef struct SDL_Renderer SDL_Renderer;
+typedef struct SDL_Texture SDL_Texture;
+typedef struct SDL_Surface SDL_Surface;
+typedef struct SDL_Rect SDL_Rect;
+typedef int SDL_Keycode;
+typedef int SDL_Scancode;
+typedef int SDL_Keymod;
+typedef struct SDL_Event SDL_Event;
+typedef u8int Uint8;
+typedef u16int Uint16;
+typedef u32int Uint32;
+typedef int SDL_BlendMode;
+typedef struct SDL_Thread SDL_Thread;
+typedef int (*SDL_ThreadFunction)(void *);
+typedef struct SDL_Cursor SDL_Cursor;
+typedef struct SDL_PixelFormat SDL_PixelFormat;
+typedef struct SDL_Point SDL_Point;
+typedef int SDL_RendererFlip;
+typedef struct SDL_DisplayMode SDL_DisplayMode;
+
+#pragma incomplete SDL_Cursor
+#pragma incomplete SDL_PixelFormat
+#pragma incomplete SDL_Renderer
+#pragma incomplete SDL_Texture
+#pragma incomplete SDL_Thread
+#pragma incomplete SDL_Window
+
+typedef enum {SDL_FALSE, SDL_TRUE} SDL_bool;
+#define SDLCALL
+
+void SDL_StopTextInput(void);
+SDL_bool SDL_HasSSE(void);
+SDL_bool SDL_HasSSE2(void);
+int SDL_Init(int);
+int SDL_EventState(Uint32, int);
+SDL_Keymod SDL_GetModState(void);
+char *SDL_GetAudioDeviceName(int, SDL_bool);
+int SDL_GetNumAudioDevices(int iscapture);
+int SDL_ShowCursor(int toggle);
+void SDL_LockAudioDevice(SDL_AudioDeviceID);
+void SDL_UnlockAudioDevice(SDL_AudioDeviceID);
+void SDL_PauseAudioDevice(SDL_AudioDeviceID, SDL_bool);
+void SDL_CloseAudioDevice(SDL_AudioDeviceID);
+SDL_AudioDeviceID SDL_OpenAudioDevice(char *, int, SDL_AudioSpec *, SDL_AudioSpec *, u32int);
+u64int SDL_GetPerformanceFrequency(void);
+u64int SDL_GetPerformanceCounter(void);
+char *SDL_GetError(void);
+SDL_Thread *SDL_CreateThread(SDL_ThreadFunction, char *, void *);
+void SDL_DetachThread(SDL_Thread *);
+char *SDL_GetClipboardText(void);
+int SDL_SetClipboardText(char *);
+void SDL_SetThreadPriority(int);
+SDL_bool SDL_HasClipboardText(void);
+int SDL_PollEvent(SDL_Event *event);
+void SDL_RestoreWindow(SDL_Window *window);
+void SDL_RaiseWindow(SDL_Window *window);
+int SDL_UpdateTexture(SDL_Texture *texture, SDL_Rect *rect, void *pixels, int pitch);
+int SDL_RenderClear(SDL_Renderer *renderer);
+int SDL_GetWindowDisplayIndex(SDL_Window *window);
+void SDL_FreeSurface(SDL_Surface *surface);
+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);
+SDL_Surface *SDL_CreateRGBSurface(Uint32 flags, int width, int height, int depth, Uint32 rm, Uint32 gm, Uint32 bm, Uint32 am);
+SDL_Surface *SDL_CreateRGBSurfaceFrom(Uint32 *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);
+int SDL_SetColorKey(SDL_Surface *surface, int flag, 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);
+int SDL_PushEvent(SDL_Event *event);
+void SDL_WarpMouseInWindow(SDL_Window *window, int x, int y);
+void SDL_RenderGetScale(SDL_Renderer *renderer, float *scaleX, float *scaleY);
+void SDL_GetWindowSize(SDL_Window *window, int *w, int *h);
+void SDL_GetWindowPosition(SDL_Window *window, 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);
+int SDL_RenderCopy(SDL_Renderer *renderer, SDL_Texture *texture, SDL_Rect *srcrect, SDL_Rect *dstrect);
+void SDL_RenderPresent(SDL_Renderer *renderer);
+Uint32 SDL_GetWindowFlags(SDL_Window *window);
+int SDL_RenderSetLogicalSize(SDL_Renderer *renderer, int w, int h);
+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_DestroyTexture(SDL_Texture *texture);
+void SDL_DestroyRenderer(SDL_Renderer *renderer);
+void SDL_DestroyWindow(SDL_Window *window);
+int SDL_GetDesktopDisplayMode(int displayIndex, SDL_DisplayMode *mode);
+void SDL_SetWindowTitle(SDL_Window *window, char *title);
+int SDL_SetTextureBlendMode(SDL_Texture *texture, SDL_BlendMode blendMode);
+SDL_bool SDL_SetHint(char *name, char *value);
+SDL_Window *SDL_CreateWindow(char *title, int x, int y, int w, int h, Uint32 flags);
+SDL_Renderer *SDL_CreateRenderer(SDL_Window *window, int index, Uint32 flags);
+int SDL_SetRenderDrawBlendMode(SDL_Renderer *renderer, SDL_BlendMode blendMode);
+char *SDL_GetCurrentVideoDriver(void);
+SDL_Texture *SDL_CreateTexture(SDL_Renderer *renderer, Uint32 format, int access, int w, int h);
+void SDL_EnableScreenSaver(void);
+Uint32 SDL_GetTicks(void);
+int SDL_GetRendererOutputSize(SDL_Renderer *renderer, int *w, int *h);
+int SDL_SaveBMP(SDL_Surface *s, const char *file);
+
+enum {
+	AUDIO_S16,
+	AUDIO_F32,
+
+	SDL_AUDIO_ALLOW_ANY_CHANGE = ~0,
+
+	SDL_THREAD_PRIORITY_HIGH = 1,
+
+	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_WINDOWPOS_CENTERED = -1,
+	SDL_WINDOWPOS_UNDEFINED = -2,
+
+	SDL_INIT_AUDIO = 1<<0,
+	SDL_INIT_VIDEO = 1<<1,
+	SDL_INIT_JOYSTICK = 1<<2,
+
+	SDL_BLENDMODE_NONE = 0,
+
+	SDL_FLIP_NONE = 0,
+	SDL_FLIP_HORIZONTAL,
+	SDL_FLIP_VERTICAL,
+
+	SDL_PIXELFORMAT_ARGB8888 = 0x30128888,
+
+	/* shit no one cares about */
+	SDL_TEXTUREACCESS_STREAMING = 0,
+	SDL_TEXTUREACCESS_STATIC = 0,
+	SDL_RENDERER_ACCELERATED = 0,
+	SDL_RENDERER_PRESENTVSYNC = 0,
+};
+
+enum {
+	/* these HAVE to be in this order for notes to work in ft2-clone */
+	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_ENTER,
+	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_SLASH,
+	SDL_SCANCODE_CAPSLOCK,
+
+	SDL_SCANCODE_RIGHT = 0x4f,
+	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_MUTE = 0x7f,
+	SDL_SCANCODE_VOLUMEUP,
+	SDL_SCANCODE_VOLUMEDOWN,
+
+	SDL_SCANCODE_AUDIOMUTE = 0x106,
+
+	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 = '\n',
+	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_LALT = Kalt,
+	SDLK_RALT = Kaltgr, /* FIXME what about keyboards without it? */
+	/* FIXME no distinction */
+	SDLK_LSHIFT = Kshift,
+	SDLK_RSHIFT = SDLK_LSHIFT,
+	SDLK_LCTRL = Kctl,
+	SDLK_RCTRL = '>', /* FIXME this is a hack */
+
+	/* FIXME not bound to anything */
+	SDLK_UNKNOWN = -99999,
+	SDLK_CAPSLOCK,
+	SDLK_KP_ENTER,
+
+	/* FIXME no distinction */
+	KMOD_LSHIFT = 1<<0,
+	KMOD_RSHIFT = KMOD_LSHIFT,
+	KMOD_LCTRL = 1<<1,
+	KMOD_RCTRL = KMOD_LCTRL,
+	KMOD_LALT = 1<<2,
+	KMOD_RALT = KMOD_LALT,
+	KMOD_LGUI = 1<<3,
+	KMOD_RGUI = KMOD_LGUI,
+
+	SDL_KEYDOWN = 0,
+	SDL_KEYUP,
+	SDL_MOUSEBUTTONDOWN,
+	SDL_MOUSEBUTTONUP,
+	SDL_MOUSEWHEEL,
+	SDL_MOUSEMOTION,
+	SDL_QUIT,
+	SDL_DROPFILE,
+	SDL_TEXTINPUT,
+	SDL_WINDOWEVENT,
+	SDL_WINDOWEVENT_HIDDEN,
+	SDL_WINDOWEVENT_SHOWN,
+	SDL_WINDOWEVENT_MINIMIZED,
+	SDL_WINDOWEVENT_FOCUS_LOST,
+	SDL_WINDOWEVENT_EXPOSED,
+
+	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,
+};
+
+struct SDL_AudioSpec {
+	void (*callback)(void *, Uint8 *, int);
+	void *userdata;
+	int freq;
+	int format;
+	int channels;
+	int samples;
+};
+
+struct SDL_Point {
+	int x, y;
+};
+
+struct SDL_Rect {
+	int x, y, w, h;
+};
+
+struct SDL_Surface {
+	SDL_PixelFormat *format;
+	SDL_Rect clip_rect;
+	Uint32 flags;
+	Uint32 key;
+	int keyset;
+	int w, h;
+	int pitch;
+	uchar pixels[];
+};
+
+struct SDL_Event {
+	int type;
+	union {
+		struct {
+			int event;
+		}window;
+		struct {
+			struct {
+				SDL_Scancode scancode;
+				SDL_Keycode sym;
+			}keysym;
+			int repeat;
+		}key;
+		struct {
+			int x, y;
+			int button;
+		}button;
+		struct {
+			int x, y;
+		}motion;
+		struct {
+			char text[UTFmax+1];
+		}text;
+		struct {
+			int x, y;
+		}wheel;
+		struct {
+			char *file;
+		}drop;
+	};
+};
+
+struct SDL_DisplayMode
+{
+	int format;
+	int w;
+	int h;
+	int refresh_rate;
+};
+
+#endif
--- /dev/null
+++ b/include/npe/alloca.h
@@ -1,0 +1,1 @@
+#include "plan9.h"
--- /dev/null
+++ b/include/npe/assert.h
@@ -1,0 +1,1 @@
+#include "plan9.h"
--- /dev/null
+++ b/include/npe/ctype.h
@@ -1,0 +1,7 @@
+#ifndef _ctype_h_
+#define _ctype_h_
+
+#include "plan9.h"
+#include </sys/include/ctype.h>
+
+#endif
--- /dev/null
+++ b/include/npe/dirent.h
@@ -1,0 +1,25 @@
+#ifndef _dirent_h_
+#define _dirent_h_
+
+#include "plan9.h"
+
+enum {
+	DT_LNK = -1,
+	DT_UNKNOWN = 0,
+	DT_FILE,
+	DT_DIR,
+};
+
+struct dirent {
+	int d_type;
+	char *d_name;
+};
+
+typedef struct DIR DIR;
+#pragma incomplete DIR
+
+DIR *opendir(char *name);
+int closedir(DIR *dirp);
+struct dirent *readdir(DIR *dirp);
+
+#endif
--- /dev/null
+++ b/include/npe/iconv.h
@@ -1,0 +1,14 @@
+#ifndef _iconv_h_
+#define _iconv_h_
+
+#include "plan9.h"
+#include <stdint.h>
+
+typedef struct iconv_s *iconv_t;
+#pragma incomplete struct iconv_s
+
+iconv_t iconv_open(char *to, char *from);
+void iconv_close(iconv_t);
+size_t iconv(iconv_t cd, char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft);
+
+#endif
--- /dev/null
+++ b/include/npe/limits.h
@@ -1,0 +1,18 @@
+#ifndef _limits_h_
+#define _limits_h_
+
+#define INT16_MAX 0x7fff
+#define INT16_MIN ((int16_t)0x8000)
+#define INT64_MIN ((int64_t)0x8000000000000000ULL)
+#define INT32_MAX 0x7fffffff
+#define INT_MAX INT32_MAX
+#define INT32_MIN (-INT32_MAX-1)
+#define INT_MIN INT32_MIN
+#define SHRT_MAX 0x7fff
+#define SHRT_MIN (-SHRT_MAX-1)
+#define SIZE_MAX 0xffffffffU
+#define UINT32_MAX 0xffffffffU
+#define UINT_MAX UINT32_MAX
+#define UINT64_MAX 0xffffffffffffffffULL
+
+#endif
--- /dev/null
+++ b/include/npe/malloc.h
@@ -1,0 +1,1 @@
+#include "plan9.h"
--- /dev/null
+++ b/include/npe/math.h
@@ -1,0 +1,37 @@
+#ifndef _math_h_
+#define _math_h_
+
+#include "plan9.h"
+
+#define isnan isNaN
+
+#define acosf acos
+#define asinf asin
+#define atan2f atan2
+#define atanf atan
+#define cosf cos
+#define sinf sin
+#define tanf tan
+
+#define ceilf ceil
+#define fabsf fabs
+#define floorf floor
+#define fmodf fmod
+
+
+double exp2(double);
+#define exp2f exp2
+
+double log2(double);
+#define log2f log2
+
+double round(double);
+#define roundf round
+
+double trunc(double);
+#define truncf trunc
+
+int lrintf(float f);
+long lrint(double d);
+
+#endif
--- /dev/null
+++ b/include/npe/plan9.h
@@ -1,0 +1,20 @@
+#ifndef __plan9_h__
+#define __plan9_h__
+
+#include <u.h>
+#include <libc.h>
+#include </sys/include/stdio.h>
+
+#pragma lib "libnpe.a"
+
+#define PATH_MAX 256
+
+#define __attribute__(a)
+#define main __main
+#define NSEC 1000000000ULL
+
+int rm_r(char *dir);
+uvlong nanosec(void);
+void nsleep(uvlong ns);
+
+#endif
--- /dev/null
+++ b/include/npe/signal.h
@@ -1,0 +1,25 @@
+#ifndef _signal_h_
+#define _signal_h_
+
+#include "plan9.h"
+
+enum {
+	SA_RESETHAND = 1<<0, /* not used */
+
+	/* all are treated as SIGSEGV */
+	SIGILL = 4,
+	SIGABRT = 6,
+	SIGFPE = 8,
+	SIGSEGV = 11,
+};
+
+typedef void (*sa_handler)(int);
+
+struct sigaction {
+	sa_handler sa_handler;
+	int sa_flags;
+};
+
+void sigaction(int, struct sigaction *, struct sigaction *);
+
+#endif
--- /dev/null
+++ b/include/npe/stdarg.h
@@ -1,0 +1,1 @@
+#include "plan9.h"
--- /dev/null
+++ b/include/npe/stdbool.h
@@ -1,0 +1,6 @@
+#ifndef _stdbool_h_
+#define _stdbool_h_
+
+typedef enum { false, true } bool;
+
+#endif
--- /dev/null
+++ b/include/npe/stddef.h
@@ -1,0 +1,1 @@
+#include "plan9.h"
--- /dev/null
+++ b/include/npe/stdint.h
@@ -1,0 +1,21 @@
+#ifndef _stdint_h_
+#define _stdint_h_
+
+#include "plan9.h"
+#include <limits.h>
+
+typedef s8int int8_t;
+typedef u8int uint8_t;
+typedef s16int int16_t;
+typedef u16int uint16_t;
+typedef s32int int32_t;
+typedef u32int uint32_t;
+typedef s64int int64_t;
+typedef u64int uint64_t;
+typedef long ssize_t;
+typedef ulong size_t;
+typedef intptr ptrdiff_t;
+typedef intptr intptr_t;
+typedef uintptr uintptr_t;
+
+#endif
--- /dev/null
+++ b/include/npe/stdio.h
@@ -1,0 +1,1 @@
+#include "plan9.h"
--- /dev/null
+++ b/include/npe/stdlib.h
@@ -1,0 +1,8 @@
+#ifndef _stdlib_h_
+#define _stdlib_h_
+
+#include "plan9.h"
+
+#define exit(x) exits(x == 0 ? nil : "error")
+
+#endif
--- /dev/null
+++ b/include/npe/string.h
@@ -1,0 +1,1 @@
+#include "plan9.h"
--- /dev/null
+++ b/include/npe/strings.h
@@ -1,0 +1,7 @@
+#ifndef _strings_h_
+#define _strings_h_
+
+#define strcasecmp cistrcmp
+#define strncasecmp cistrncmp
+
+#endif
--- /dev/null
+++ b/include/npe/sys/stat.h
@@ -1,0 +1,1 @@
+#include "plan9.h"
--- /dev/null
+++ b/include/npe/sys/time.h
@@ -1,0 +1,1 @@
+#include "plan9.h"
--- /dev/null
+++ b/include/npe/sys/types.h
@@ -1,0 +1,1 @@
+#include "plan9.h"
--- /dev/null
+++ b/include/npe/time.h
@@ -1,0 +1,1 @@
+#include "plan9.h"
--- /dev/null
+++ b/include/npe/unistd.h
@@ -1,0 +1,47 @@
+#ifndef _unistd_h_
+#define _unistd_h_
+
+#include "plan9.h"
+
+enum {
+	S_IFMT = 0xff,
+	S_IFDIR = 1<<0,
+
+	F_OK = AEXIST,
+	R_OK = AREAD,
+	W_OK = AWRITE,
+	X_OK = AEXEC,
+
+	S_ISUID = 04000,
+	S_ISGID = 02000,
+	S_IRWXU = 00700,
+	S_IRUSR = 00400,
+	S_IWUSR = 00200,
+	S_IXUSR = 00100,
+	S_IRWXG = 00070,
+	S_IRGRP = 00040,
+	S_IWGRP = 00020,
+	S_IXGRP = 00010,
+	S_IRWXO = 00007,
+	S_IROTH = 00004,
+	S_IWOTH = 00002,
+	S_IXOTH = 00001,
+};
+
+#define getcwd getwd
+#define getenv getenv9
+#define stat stat9
+
+struct stat9 {
+	uvlong st_size;
+	int st_mode;
+};
+
+char *getenv9(char *s);
+int stat9(char *filename, struct stat9 *buf);
+int mkdir(char *path, int perm);
+int access(char *name, int mode);
+int rename(char *old, char *new);
+void usleep(unsigned us);
+
+#endif
--- /dev/null
+++ b/include/npe/wchar.h
@@ -1,0 +1,1 @@
+#include "plan9.h"
--- /dev/null
+++ b/libnpe/_dirent.h
@@ -1,0 +1,7 @@
+struct DIR {
+	Dir *d;
+	struct dirent de;
+	int fd;
+	long n; /* number of entries in d */
+	long i; /* current entry to return from readdir */
+};
--- /dev/null
+++ b/libnpe/_iconv.h
@@ -1,0 +1,13 @@
+enum {
+	Itranslit = 1<<0, /* no transliteration is performed :( */
+	Iignore = 1<<1, /* this option is ignored (pun TOTALLY intended) if Itranslit is set */
+
+	Tcs = 0,
+	Us,
+};
+
+struct iconv_s {
+	char to[16];
+	char from[16];
+	int fd;
+};
--- /dev/null
+++ b/libnpe/_main.c
@@ -1,0 +1,12 @@
+#include "plan9.h"
+#include <thread.h>
+
+void
+threadmain(int argc, char **argv)
+{
+	int __main(int argc, char *argv[]);
+
+	setfcr(getfcr() & ~(FPINVAL|FPOVFL));
+
+	threadexitsall(__main(argc, argv) == 0 ? nil : "error");
+}
--- /dev/null
+++ b/libnpe/_plan9.c
@@ -1,0 +1,58 @@
+#include "plan9.h"
+#include <tos.h>
+
+/*
+ * nsec() is wallclock and can be adjusted by timesync
+ * so need to use cycles() instead, but fall back to
+ * nsec() in case we can't
+ */
+uvlong
+nanosec(void)
+{
+	static uvlong fasthz, xstart;
+	uvlong x, div;
+
+	if(fasthz == ~0ULL)
+		return nsec() - xstart;
+
+	if(fasthz == 0){
+		if(_tos->cyclefreq){
+			cycles(&xstart);
+			fasthz = _tos->cyclefreq;
+		} else {
+			xstart = nsec();
+			fasthz = ~0ULL;
+			fprint(2, "cyclefreq not available, falling back to nsec()\n");
+			fprint(2, "you might want to disable aux/timesync\n");
+			return 0;
+		}
+	}
+	cycles(&x);
+	x -= xstart;
+
+	/* this is ugly */
+	for(div = NSEC; x < 0x1999999999999999ULL && div > 1 ; div /= 10ULL, x *= 10ULL);
+
+	return x / (fasthz / div);
+}
+
+void
+nsleep(uvlong ns)
+{
+	uvlong start, end;
+
+	start = nanosec();
+	end = start + ns;
+	ns = start;
+	do{
+		if(end - ns > 750000000ULL)
+			sleep(70);
+		else if (end - ns > 25000000ULL)
+			sleep(20);
+		else if (end - ns > 10000000ULL)
+			sleep(1);
+		else
+			break;
+		ns = nanosec();
+	}while(ns < end);
+}
--- /dev/null
+++ b/libnpe/closedir.c
@@ -1,0 +1,12 @@
+#include <dirent.h>
+#include "_dirent.h"
+
+int
+closedir(DIR *d)
+{
+	free(d->d);
+	close(d->fd);
+	free(d);
+
+	return 0;
+}
--- /dev/null
+++ b/libnpe/exp2.c
@@ -1,0 +1,9 @@
+#include <math.h>
+
+#define ln2o1 1.4426950408889634073599
+
+double
+exp2(double x)
+{
+	return exp(x/ln2o1);
+}
--- /dev/null
+++ b/libnpe/getenv.c
@@ -1,0 +1,14 @@
+#include <unistd.h>
+
+#undef getenv
+
+char *
+getenv9(char *s)
+{
+	static char *e;
+
+	free(e);
+	e = getenv(strcmp(s, "HOME") == 0 ? "home" : s);
+
+	return e;
+}
--- /dev/null
+++ b/libnpe/iconv.c
@@ -1,0 +1,26 @@
+#include <iconv.h>
+#include "_iconv.h"
+
+size_t
+iconv(iconv_t ic, char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft)
+{
+	int n;
+
+	if(inbuf == nil || *inbuf == nil){ /* flush */
+		write(ic->fd, "", 0);
+	}else{
+		if((n = write(ic->fd, *inbuf, *inbytesleft)) < 0)
+			return -1;
+		*inbuf += n;
+		*inbytesleft -= n;
+		return 0;
+	}
+	if(outbuf == nil || *outbuf == nil) /* nothing to do */
+		return 0;
+	if((n = read(ic->fd, *outbuf, *outbytesleft)) < 0)
+		return -1;
+	*outbuf += n;
+	*outbytesleft -= n;
+
+	return n;
+}
--- /dev/null
+++ b/libnpe/iconv_close.c
@@ -1,0 +1,11 @@
+#include <iconv.h>
+#include "_iconv.h"
+
+void
+iconv_close(iconv_t ic)
+{
+	if(ic != nil){
+		close(ic->fd);
+		free(ic);
+	}
+}
--- /dev/null
+++ b/libnpe/iconv_open.c
@@ -1,0 +1,81 @@
+#include <iconv.h>
+#include "_iconv.h"
+
+static int
+cp2tcs(char *cp, char *tcs, int sz)
+{
+	int opt, n, i;
+	char *s;
+
+	opt = 0;
+	n = 0;
+	for(s = cp; (s = strstr(s, "//")) != nil;){
+		if(n == 0)
+			n = (int)(s - cp);
+		s += 2;
+		if(strncmp(s, "TRANSLIT", 8) == 0 && (s[8] == 0 || s[8] == '/'))
+			opt |= Itranslit;
+		else if(strncmp(s, "IGNORE", 6) == 0 && (s[6] == 0 || s[6] == '/'))
+			opt |= Iignore;
+	}
+	if(n < 1)
+		n = strlen(cp);
+
+	if(cp[0] >= '0' && cp[0] <= '9'){ /* ibm */
+		n = snprint(tcs, sz, "ibm%.*s", n, cp);
+	}else if(strncmp(cp, "ISO", 3) == 0){
+		if(cp[3] == '-' || cp[3] == '_'){
+			n--;
+			cp++;
+		}
+		n = snprint(tcs, sz, "%.*s", n-3, cp+3);
+	}else if(strncmp(cp, "CP12", 4) == 0){
+		n = snprint(tcs, sz, "windows-12%.*s", n-4, cp+4);
+	}else{ /* last chance, convert to lowercase. FIXME not all are supported, no checks are made */
+		n = snprint(tcs, sz, "%.*s", n, cp);
+		for(i = 0; i < n; i++)
+			tcs[i] = tolower(tcs[i]);
+	}
+
+	return n >= sz ? -1 : opt;
+}
+
+iconv_t
+iconv_open(char *to, char *from)
+{
+	int opt, pid, p[2];
+	iconv_t ic;
+
+	p[0] = p[1] = -1;
+	if((ic = calloc(1, sizeof(*ic))) == nil)
+		goto err;
+	if(cp2tcs(from, ic->from, sizeof(ic->from)) < 0)
+		goto err;
+	if((opt = cp2tcs(to, ic->to, sizeof(ic->to))) < 0)
+		goto err;
+	if(pipe(p) < 0)
+		goto err;
+	if((pid = rfork(RFPROC|RFFDG|RFNOTEG|RFCENVG|RFNOWAIT)) == 0){
+		dup(p[Tcs], 0);
+		dup(p[Tcs], 1);
+		close(p[0]);
+		close(p[1]);
+		close(2); /* comment it out if you need debugging */
+		execl("/bin/tcs", "tcs", "-f", ic->from, "-t", ic->to, (opt == Iignore) ? "-c" : nil, nil);
+		sysfatal("execl: %r");
+	}else if(pid < 0){
+		goto err;
+	}
+	close(p[Tcs]);
+	ic->fd = p[Us];
+
+	setmalloctag(ic, getcallerpc(&to));
+
+	return ic;
+err:
+	close(p[0]);
+	close(p[1]);
+	if(ic != nil)
+		free(ic);
+	return (iconv_t)-1;
+}
--- /dev/null
+++ b/libnpe/log2.c
@@ -1,0 +1,15 @@
+#include <math.h>
+
+#define ln2o1 1.4426950408889634073599
+#define huge 1.79769313486231e+308
+
+double
+log2(double x)
+{
+	if(x == 0)
+		return -huge;
+	if(x < 0 || isNaN(x))
+		return NaN();
+
+	return log(x)*ln2o1;
+}
--- /dev/null
+++ b/libnpe/lrint.c
@@ -1,0 +1,11 @@
+#include <math.h>
+
+long
+lrint(double d)
+{
+	long long l;
+
+	*((double*)&l) = d + 6755399441055744.0;
+
+	return l;
+}
--- /dev/null
+++ b/libnpe/lrintf.c
@@ -1,0 +1,11 @@
+#include <math.h>
+
+int
+lrintf(float f)
+{
+	int i;
+
+	*((float*)&i) = f + 12582912.0f;
+
+	return (i & 0x7fffff) - 0x400000;
+}
--- /dev/null
+++ b/libnpe/mkdir.c
@@ -1,0 +1,19 @@
+#include <unistd.h>
+
+int
+mkdir(char *path, int perm)
+{
+	int f;
+
+	if(access(path, AEXIST) == 0){
+		werrstr("%s: already exists", path);
+		return -1;
+	}
+	if((f = create(path, OREAD, DMDIR|perm)) < 0){
+		werrstr("%s: can't create: %r", path);
+		return -1;
+	}
+	close(f);
+
+	return 0;
+}
--- /dev/null
+++ b/libnpe/mkfile
@@ -1,0 +1,37 @@
+</$objtype/mkfile
+
+LIB=/$objtype/lib/libnpe.a
+CFLAGS=$CFLAGS -p -I../include/npe -D__plan9__ -D__${objtype}__
+
+HFILES=\
+	_iconv.h\
+	_dirent.h\
+
+OFILES=\
+	_main.$O\
+	_plan9.$O\
+	closedir.$O\
+	exp2.$O\
+	getenv.$O\
+	iconv.$O\
+	iconv_close.$O\
+	iconv_open.$O\
+	log2.$O\
+	lrint.$O\
+	lrintf.$O\
+	mkdir.$O\
+	opendir.$O\
+	readdir.$O\
+	rename.$O\
+	round.$O\
+	stat.$O\
+	trunc.$O\
+	usleep.$O\
+
+UPDATE=\
+	mkfile\
+	$HFILES\
+	${OFILES:%.$O=%.c}\
+	${LIB:/$objtype/%=/386/%}\
+
+</sys/src/cmd/mksyslib
--- /dev/null
+++ b/libnpe/opendir.c
@@ -1,0 +1,25 @@
+#include <dirent.h>
+#include "_dirent.h"
+
+DIR *
+opendir(char *name)
+{
+	Dir *dir;
+	DIR *d;
+	int fd;
+
+	dir = nil;
+	d = nil;
+	if((fd = open(name, OREAD|OCEXEC)) >= 0 &&
+	    (dir = dirfstat(fd)) != nil &&
+	    (dir->qid.type & QTDIR) != 0 &&
+	    (d = calloc(1, sizeof(*d))) != nil){
+		d->fd = fd;
+	}
+
+	free(dir);
+	if(d == nil)
+		close(fd);
+
+	return d;
+}
--- /dev/null
+++ b/libnpe/readdir.c
@@ -1,0 +1,19 @@
+#include <dirent.h>
+#include "_dirent.h"
+
+struct dirent *
+readdir(DIR *d)
+{
+	if(d->i >= d->n){
+		free(d->d);
+		if((d->n = dirread(d->fd, &d->d)) <= 0)
+			return nil;
+		d->i = 0;
+	}
+
+	d->de.d_type = (d->d[d->i].qid.type & QTDIR) ? DT_DIR : DT_FILE;
+	d->de.d_name = d->d[d->i].name;
+	d->i++;
+
+	return &d->de;
+}
--- /dev/null
+++ b/libnpe/rename.c
@@ -1,0 +1,12 @@
+#include <unistd.h>
+
+int
+rename(char *old, char *new)
+{
+	Dir d;
+
+	nulldir(&d);
+	d.name = new;
+
+	return dirwstat(old, &d) > 0 ? 0 : -1;
+}
--- /dev/null
+++ b/libnpe/round.c
@@ -1,0 +1,33 @@
+#include <math.h>
+
+/* taken from musl */
+
+#define dbleps 2.2204460492503131e-16
+
+static double toint = 1.0/dbleps;
+
+double
+round(double x)
+{
+	union {double f; u64int i;} u = {x};
+	int e = u.i >> 52 & 0x7ff;
+	double y;
+
+	if(e >= 0x3ff+52)
+		return x;
+	if(u.i >> 63)
+		x = -x;
+	if(e < 0x3ff-1)
+		return 0*u.f;
+	y = x + toint - toint - x;
+	if(y > 0.5)
+		y = y + x - 1;
+	else if(y <= -0.5)
+		y = y + x + 1;
+	else
+		y = y + x;
+	if(u.i >> 63)
+		y = -y;
+
+	return y;
+}
--- /dev/null
+++ b/libnpe/stat.c
@@ -1,0 +1,18 @@
+#include <unistd.h>
+
+#undef stat
+
+int
+stat9(char *filename, struct stat9 *buf)
+{
+	Dir *d;
+
+	if((d = dirstat(filename)) == nil)
+		return -1;
+
+	buf->st_size = d->length;
+	buf->st_mode = d->mode;
+	free(d);
+
+	return 0;
+}
--- /dev/null
+++ b/libnpe/trunc.c
@@ -1,0 +1,20 @@
+#include <math.h>
+
+double
+trunc(double x)
+{
+	union {double f; u64int i;} u = {x};
+	int e = (int)(u.i >> 52 & 0x7ff) - 0x3ff + 12;
+	u64int m;
+
+	if(e >= 52 + 12)
+		return x;
+	if(e < 12)
+		e = 1;
+	m = -1ULL >> e;
+	if((u.i & m) == 0)
+		return x;
+	u.i &= ~m;
+
+	return u.f;
+}
--- /dev/null
+++ b/libnpe/usleep.c
@@ -1,0 +1,7 @@
+#include <unistd.h>
+
+void
+usleep(unsigned us)
+{
+	nsleep((uvlong)us*1000ULL);
+}
--- /dev/null
+++ b/libnpe_sdl2/mkfile
@@ -1,0 +1,17 @@
+</$objtype/mkfile
+
+LIB=/$objtype/lib/libnpe_sdl2.a
+CFLAGS=$CFLAGS -p -I../include/npe -D__plan9__ -D__${objtype}__
+
+HFILES=\
+
+OFILES=\
+	sdl2.$O\
+
+UPDATE=\
+	mkfile\
+	$HFILES\
+	${OFILES:%.$O=%.c}\
+	${LIB:/$objtype/%=/386/%}\
+
+</sys/src/cmd/mksyslib
--- /dev/null
+++ b/libnpe_sdl2/sdl2.c
@@ -1,0 +1,1335 @@
+#include "plan9.h"
+#include <stdint.h>
+#include <tos.h>
+#include <draw.h>
+#include <memdraw.h>
+#include <mouse.h>
+#include <cursor.h>
+#include <thread.h>
+#include <SDL2/SDL.h>
+#include <pool.h>
+
+typedef struct Audiodev Audiodev;
+
+enum {
+	Aout = 2,
+	Arec,
+
+	/* FIXME missing plumber→dropfile and enter()→text */
+	Ckey = 0,
+	Ckeytype,
+	Cmouse,
+	Cresize,
+	Numchan,
+
+	Rdown = 0,
+	Rup,
+	Rrepeat,
+
+	Audiobufsz = 16384,
+};
+
+struct SDL_Window {
+	int dummy;
+};
+
+struct SDL_Renderer {
+	int dummy;
+};
+
+struct SDL_Texture {
+	Memimage;
+};
+
+struct SDL_PixelFormat {
+	int format;
+};
+
+struct SDL_Cursor {
+	Image *i;
+	Image *m;
+	Point hot;
+};
+
+struct SDL_Thread {
+	SDL_ThreadFunction f;
+	char *name;
+	void *userdata;
+};
+
+static SDL_PixelFormat argb8888 = {
+	.format = SDL_PIXELFORMAT_ARGB8888,
+};
+
+struct Audiodev {
+	QLock;
+	int paused;
+	int fd;
+	int pid;
+	int mode;
+	Uint8 buf[Audiobufsz];
+	void *userdata;
+	void (*cb)(void *, Uint8 *, int);
+};
+
+/* FIXME extra USB audio devices? */
+static Audiodev au[4] = {
+	[0] = {.fd = -1, .mode = -1},
+	[1] = {.fd = -1, .mode = -1},
+	[Aout] = {.fd = -1, .pid = -1, .mode = OWRITE},
+	[Arec] = {.fd = -1, .pid = -1, .mode = OREAD},
+};
+
+static SDL_Window onewin;
+static SDL_Renderer oneren;
+static int kmod;
+static Mouse mouse, oldmouse;
+static Rune rune;
+static Mousectl *mctl;
+static Keyboardctl kctl;
+static Memimage *back;
+static u8int *backcopy;
+static Image *front;
+static int logiw, logih;
+static int physw, physh;
+static int forceredraw = 1;
+static SDL_Cursor *oldcursor, *cursor;
+static int mouseredraw = 0;
+static int showcursor = SDL_ENABLE;
+static int textinput;
+
+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 Alt salt[Numchan+1] = {
+	[Ckey] = { nil, &rune, CHANRCV },
+	[Ckeytype] = { nil, nil, CHANNOP },
+	[Cmouse] = { nil, &mouse, CHANRCV },
+	[Cresize] = { nil, nil, CHANRCV },
+	[Numchan] = { nil, nil, CHANNOBLK },
+};
+
+static void
+kbdproc(void *)
+{
+	char buf[128], buf2[128], *s;
+	int kfd, n, kbin, t;
+	Rune r, o;
+
+	threadsetname("kbdproc");
+	if((kfd = open("/dev/kbd", OREAD|OCEXEC)) < 0)
+		sysfatal("/dev/kbd: %r");
+	kbin = open("/dev/kbin", OWRITE|OCEXEC);
+
+	buf2[0] = 0;
+	buf2[1] = 0;
+	buf[0] = 0;
+	kmod = 0;
+	o = 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){
+				t = Rrepeat;
+				if(!textinput)
+					r = tolowerrune(r);
+				if(textinput || o != r){
+					send(salt[Ckey].c, &r);
+					send(salt[Ckeytype].c, &t);
+				}
+				o = 0;
+			}
+		default:
+			continue;
+
+		case 'k':
+			s = buf+1;
+			while(*s){
+				s += chartorune(&r, s);
+				if(utfrune(buf2+1, r) == nil){
+					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 |= KMOD_LALT;
+					}else if (r == Kshift)
+						kmod |= KMOD_LSHIFT;
+					else if(r == Kctl)
+						kmod |= KMOD_LCTRL;
+					else{
+						t = Rdown;
+						send(salt[Ckey].c, &r);
+						send(salt[Ckeytype].c, &t);
+						o = r;
+					}
+				}
+			}
+			break;
+
+		case 'K':
+			s = buf2+1;
+			while(*s){
+				s += chartorune(&r, s);
+				if(utfrune(buf+1, r) == nil){
+					if(r == Kalt)
+						kmod &= ~KMOD_LALT;
+					else if(r == Kshift)
+						kmod &= ~KMOD_LSHIFT;
+					else if(r == Kctl)
+						kmod &= ~KMOD_LCTRL;
+					else{
+						t = Rup;
+						send(salt[Ckey].c, &r);
+						send(salt[Ckeytype].c, &t);
+					}
+				}
+			}
+			break;
+		}
+		strcpy(buf2, buf);
+	}
+
+	threadexits(nil);
+}
+
+int
+SDL_Init(int mask)
+{
+	/* FIXME actually use the mask? */
+	USED(mask);
+
+	if(memimageinit() < 0)
+		goto err;
+	if(initdraw(nil, nil, "ft2-clone") < 0)
+		goto err;
+	if((mctl = initmouse(nil, screen)) == nil)
+		goto err;
+
+	salt[Ckey].c = chancreate(sizeof(Rune), 20);
+	salt[Ckeytype].c = chancreate(sizeof(int), 20);
+	salt[Cmouse].c = mctl->c;
+	salt[Cresize].c = mctl->resizec;
+	kctl.c = salt[Ckey].c; /* for enter() */
+
+	if(salt[Ckey].c == nil || salt[Ckeytype].c == nil || proccreate(kbdproc, nil, 4096) < 0)
+		goto err;
+
+	return 0;
+err:
+	werrstr("SDL_Init: %r");
+	return -1;
+}
+
+int
+SDL_EventState(Uint32, int)
+{
+	return 0;
+}
+
+SDL_Keymod
+SDL_GetModState(void)
+{
+	return kmod;
+}
+
+int
+SDL_ShowCursor(int toggle)
+{
+	if(toggle == SDL_QUERY)
+		return showcursor;
+
+	showcursor = toggle == SDL_ENABLE;
+	setcursor(mctl, (cursor == nil && showcursor) ? nil : &nocursor);
+
+	return showcursor;
+}
+
+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); USED(iscapture);
+	return "/dev/audio";
+}
+
+void
+SDL_LockAudioDevice(SDL_AudioDeviceID id)
+{
+	qlock(&au[id]);
+}
+
+void
+SDL_UnlockAudioDevice(SDL_AudioDeviceID id)
+{
+	qunlock(&au[id]);
+}
+
+static void
+audiothread(void *p)
+{
+	Audiodev *d;
+
+	d = p;
+	threadsetname("audio %s", d == &au[Aout] ? "out" : "in");
+
+	for(;;){
+		qlock(d);
+		if(d->paused)
+			memset(d->buf, 0, sizeof(d->buf));
+		else
+			d->cb(d->userdata, d->buf, sizeof(d->buf));
+		qunlock(d);
+
+		if(write(d->fd, d->buf, sizeof(d->buf)) != sizeof(d->buf))
+			break;
+	}
+
+	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, mainstacksize);
+		a->paused = 0;
+	}else if(!a->paused && pause){
+		a->paused = 1;
+	}
+}
+
+SDL_AudioDeviceID
+SDL_OpenAudioDevice(char *dev, int rec, SDL_AudioSpec *want, SDL_AudioSpec *have, u32int change)
+{
+	Audiodev *a;
+	SDL_AudioDeviceID id;
+
+	/* FIXME look for extra USB devices? */
+	USED(dev);
+
+	if(change != SDL_AUDIO_ALLOW_ANY_CHANGE){ /* FIXME sampling in mono */
+		werrstr("SDL_OpenAudioDevice: changes not implemented");
+		return 0;
+	}
+
+	have->freq = 44100;
+	have->format = AUDIO_S16;
+	have->channels = 2;
+	have->samples = want->samples;
+
+	id = rec ? Arec : Aout;
+	a = &au[id];
+
+	if(a->fd < 0 && (a->fd = open("/dev/audio", a->mode|OCEXEC)) < 0){
+		werrstr("SDL_OpenAudioDevice: %r");
+		return 0;
+	}
+
+	a->userdata = want->userdata;
+	a->cb = want->callback;
+	a->paused = 1;
+
+	return id;
+}
+
+void
+SDL_CloseAudioDevice(SDL_AudioDeviceID id)
+{
+	Audiodev *a;
+
+	a = &au[id];
+	qlock(a);
+	close(a->fd);
+	a->fd = -1;
+	a->pid = -1;
+	a->userdata = nil;
+	a->cb = nil;
+	qunlock(a);
+}
+
+u64int
+SDL_GetPerformanceFrequency(void)
+{
+	return _tos->cyclefreq;
+}
+
+u64int
+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
+sdlthread(void *p)
+{
+	SDL_Thread t;
+
+	t = *(SDL_Thread*)p;
+	if(t.name != nil)
+		threadsetname(t.name);
+	free(p);
+
+	threadexits(t.f(t.userdata) == 0 ? nil : "error");
+}
+
+SDL_Thread *
+SDL_CreateThread(SDL_ThreadFunction f, char *name, void *userdata)
+{
+	SDL_Thread *t;
+
+	if((t = calloc(1, sizeof(*t))) == nil)
+		return nil;
+
+	t->f = f;
+	t->name = name;
+	t->userdata = userdata;
+
+	if(proccreate(sdlthread, t, mainstacksize) < 0){
+		free(t);
+		t = nil;
+	}
+
+	return t;
+}
+
+void
+SDL_DetachThread(SDL_Thread *)
+{
+}
+
+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;
+}
+
+SDL_Texture *
+SDL_CreateTexture(SDL_Renderer *, Uint32 format, int, int w, int h)
+{
+	SDL_Texture *t;
+
+	if(format != SDL_PIXELFORMAT_ARGB8888){
+		werrstr("SDL_CreateTexture: only SDL_PIXELFORMAT_ARGB8888 is supported");
+		goto err;
+	}
+	if((t = (SDL_Texture*)allocmemimage(Rect(0, 0, w, h), XRGB32)) == nil) /* FIXME ARGB32? */
+		goto err;
+	memfillcolor(t, DTransparent);
+
+	return t;
+err:
+	werrstr("SDL_CreateTexture: %r");
+	return nil;
+}
+
+int
+SDL_UpdateTexture(SDL_Texture *t, 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->r;
+	/* FIXME non-ARGB8888 */
+	/* FIXME pitch */ USED(pitch);
+	if(loadmemimage(t, r, pixels, Dx(r)*Dy(r)*4) < 0){
+		werrstr("SDL_UpdateTexture: %r");
+		return -1;
+	}
+
+	return 0;
+}
+
+int
+SDL_RenderClear(SDL_Renderer *)
+{
+	if(back != nil)
+		memfillcolor(back, DBlack);
+
+	return 0;
+}
+
+int
+SDL_GetWindowDisplayIndex(SDL_Window *)
+{
+	return 0;
+}
+
+void
+SDL_Quit(void)
+{
+	threadexitsall(nil);
+}
+
+void
+SDL_free(void *p)
+{
+	free(p);
+}
+
+SDL_Surface *
+SDL_CreateRGBSurface(Uint32, int w, int h, int bpp, Uint32 rm, Uint32 gm, Uint32 bm, Uint32 am)
+{
+	SDL_Surface *s;
+	int n;
+
+	USED(rm, gm, bm, am); /* FIXME masks */
+
+	n = w*h*bpp/8;
+	if((s = calloc(1, sizeof(*s)+n)) == nil){
+		werrstr("SDL_CreateRGBSurface: memory");
+		return nil;
+	}
+	s->format = &argb8888;
+	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;
+
+	return s;
+}
+
+SDL_Surface *
+SDL_CreateRGBSurfaceFrom(Uint32 *pixels, int w, int h, int bpp, int pitch, Uint32 rm, Uint32 gm, Uint32 bm, Uint32 am)
+{
+	SDL_Surface *s;
+	int n;
+
+	USED(pitch); /* FIXME pitch */
+
+	if((s = SDL_CreateRGBSurface(0, w, h, bpp, rm, gm, bm, am)) == nil)
+		return nil;
+
+	n = w*h*bpp/8;
+	memmove(s->pixels, pixels, n);
+
+	return s;
+}
+
+void
+SDL_FreeSurface(SDL_Surface *surface)
+{
+	memset(surface, 0, sizeof(surface));
+	free(surface);
+}
+
+Uint32
+SDL_MapRGB(SDL_PixelFormat *, Uint8 r, Uint8 g, Uint8 b)
+{
+	return r<<24 | g<<16 | b<<8 | 0xff;
+}
+
+int
+SDL_SetColorKey(SDL_Surface *s, int flag, Uint32 key)
+{
+	s->keyset = flag;
+	s->key = key;
+	return 0;
+}
+
+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;
+}
+
+void
+SDL_SetCursor(SDL_Cursor *c)
+{
+	if(cursor != c){
+		cursor = c;
+		mouseredraw = 1;
+		setcursor(mctl, (cursor == nil && showcursor) ? nil : &nocursor);
+	}
+}
+
+void
+SDL_FreeCursor(SDL_Cursor *c)
+{
+	freeimage(c->i);
+	free(c);
+	if(cursor == c){
+		oldcursor = nil;
+		cursor = nil;
+	}
+}
+
+static int
+rune2scancode(Rune 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_ENTER;
+	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;
+
+/* FIXME
+	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;
+}
+
+int
+SDL_PollEvent(SDL_Event *e)
+{
+	int t, down;
+
+	if(e == nil) /* FIXME need to buffer the event so it won't get lost */
+		return 0;
+
+	switch(alt(salt)){
+	case Ckey:
+		recv(salt[Ckeytype].c, &t);
+		if(textinput && rune >= 0x20 && rune <= 0x7f){
+			if(t != Rrepeat)
+				break;
+			e->type = SDL_TEXTINPUT;
+			e->text.text[runetochar(e->text.text, &rune)] = 0;
+		}else if(textinput && t != Rrepeat){
+			break;
+		}else{
+			e->type = (t == Rup) ? SDL_KEYUP : SDL_KEYDOWN;
+			e->key.repeat = !textinput && t == Rrepeat;
+			e->key.keysym.scancode = rune2scancode(rune);
+			e->key.keysym.sym = rune;
+		}
+		return 1;
+
+	case Cmouse:
+		e->motion.x = mouse.xy.x - screen->r.min.x;
+		e->motion.y = mouse.xy.y - screen->r.min.y;
+		if(!eqpt(mouse.xy, oldmouse.xy)){
+			mouseredraw = 1;
+			if(mouse.buttons == oldmouse.buttons){
+				e->type = SDL_MOUSEMOTION;
+				return 1;
+			}
+		}
+		if(mouse.buttons == oldmouse.buttons)
+			break;
+		/* FIXME there is a lot of hope for both buttons to never change at the same time */
+		if((down = (mouse.buttons & 1)) != (oldmouse.buttons & 1)){ /* left */
+			e->type = down ? SDL_MOUSEBUTTONDOWN : SDL_MOUSEBUTTONUP;
+			e->button.button = SDL_BUTTON_LEFT;
+			oldmouse.buttons = (oldmouse.buttons & ~1) | (mouse.buttons & 1);
+			return 1;
+		}
+		if((down = (mouse.buttons & 4)) != (oldmouse.buttons & 4)){ /* right */
+			e->type = down ? SDL_MOUSEBUTTONDOWN : SDL_MOUSEBUTTONUP;
+			e->button.button = SDL_BUTTON_RIGHT;
+			oldmouse.buttons = (oldmouse.buttons & ~4) | (mouse.buttons & 4);
+			return 1;
+		}
+		if(mouse.buttons & (8|16)){
+			e->type = SDL_MOUSEWHEEL;
+			e->wheel.x = 0;
+			e->wheel.y = (mouse.buttons & 8) ? -1 : 1;
+			return 1;
+		}
+		break;
+
+	case Cresize:
+		forceredraw = 1;
+		if(getwindow(display, Refnone) < 0){
+			fprint(2, "%r\n");
+			/* FIXME do something here? */
+			//threadexitsall(nil);
+		}
+		e->type = SDL_WINDOWEVENT;
+		e->window.event = SDL_WINDOWEVENT_EXPOSED;
+		return 1;
+	}
+
+	return 0;
+}
+
+int
+SDL_PushEvent(SDL_Event *event)
+{
+	/* FIXME does it matter? */
+	USED(event);
+	return -1;
+}
+
+void
+SDL_WarpMouseInWindow(SDL_Window *, int x, int y)
+{
+	moveto(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 = mouse.xy.x;
+	if(y != nil)
+		*y = mouse.xy.y;
+
+	b = 0;
+	if(mouse.buttons & 1)
+		b |= SDL_BUTTON_LMASK;
+	if(mouse.buttons & 4)
+		b |= SDL_BUTTON_RMASK;
+	/* FIXME no middle button use AT ALL? */
+
+	return b;
+}
+
+Uint32
+SDL_GetMouseState(int *x, int *y)
+{
+	Uint32 b;
+
+	b = SDL_GetGlobalMouseState(nil, nil);
+	if(x != nil)
+		*x = (mouse.xy.x - screen->r.min.x) * logiw / physw;
+	if(y != nil)
+		*y = (mouse.xy.y - screen->r.min.y) * logih / physh;
+
+	return b;
+}
+
+void
+SDL_RenderGetScale(SDL_Renderer *, float *scaleX, float *scaleY)
+{
+	*scaleX = 1.0;
+	*scaleY = 1.0;
+}
+
+void
+SDL_GetWindowSize(SDL_Window *, int *w, int *h)
+{
+	/* no matter what rio decides */
+	*w = physw;
+	*h = physh;
+}
+
+void
+SDL_GetWindowPosition(SDL_Window *, int *x, int *y)
+{
+	*x = screen->r.min.x;
+	*y = screen->r.min.y;
+}
+
+SDL_bool
+SDL_IsTextInputActive(void)
+{
+	return textinput;
+}
+
+void
+SDL_StartTextInput(void)
+{
+	textinput = SDL_TRUE;
+}
+
+void
+SDL_StopTextInput(void)
+{
+	textinput = SDL_FALSE;
+}
+
+void
+SDL_Delay(Uint32 ms)
+{
+	nsleep((uvlong)ms*1000000ULL);
+}
+
+static void *
+resize(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;
+}
+
+int
+SDL_RenderCopy(SDL_Renderer *, SDL_Texture *t, SDL_Rect *sre, SDL_Rect *dre)
+{
+	Rectangle sr, dr;
+
+	if(sre != nil){
+		sr.min = Pt(sre->x, sre->y);
+		sr.max = addpt(sr.min, Pt(sre->w, sre->h));
+	}else
+		sr = t->r;
+
+	if(dre != nil){
+		dr.min = Pt(dre->x, dre->y);
+		dr.max = addpt(dr.min, Pt(dre->w, dre->h));
+	}else /* stretch */
+		dr = Rect(0, 0, logiw, logih);
+
+	if(back == nil || Dx(back->r) != logiw || Dy(back->r) != logih){
+		freememimage(back);
+		back = allocmemimage(Rect(0, 0, logiw, logih), XRGB32);
+		if(back == nil){
+			werrstr("SDL_RenderCopy: %r");
+			return -1;
+		}
+		free(backcopy);
+		backcopy = malloc(logiw*logih*4);
+	}
+
+	if(dre == nil)
+		resize((u32int*)byteaddr(t, ZP), Dx(sr), Dy(sr), (u32int*)byteaddr(back, ZP), logiw, logih);
+	else
+		memimagedraw(back, dr, t, sr.min, nil, ZP, S);
+
+	return 0;
+}
+
+void
+SDL_RenderPresent(SDL_Renderer *)
+{
+	Rectangle r, clipr;
+	static u32int *b;
+	uchar *rb;
+
+	if(!forceredraw && (forceredraw = memcmp(backcopy, byteaddr(back, ZP), logiw*logih*4)) == 0 && !mouseredraw)
+		return;
+
+	r = Rect(0, 0, physw, physh);
+	if(front != nil && (Dx(front->r) != physw || Dy(front->r) != physh)){
+		freeimage(front);
+		front = nil;
+		free(b);
+		b = nil;
+	}
+	if(b == nil && (b = realloc(b, physw*physh*4)) == nil){
+		fprint(2, "SDL_RenderPresent: %r\n");
+		return;
+	}
+	if(forceredraw || front == nil){
+		rb = resize((u32int*)byteaddr(back, ZP), Dx(back->r), Dy(back->r), b, physw, physh);
+		if(front == nil && (front = allocimage(display, r, XRGB32, 0, DNofill)) == nil){
+			fprint(2, "SDL_RenderPresent: %r\n");
+			return;
+		}
+		if(loadimage(front, r, rb, Dx(r)*Dy(r)*4) < 0){
+			fprint(2, "SDL_RenderPresent: %r\n");
+			return;
+		}
+	}
+
+	if(cursor != nil && showcursor){
+		r.min = subpt(mouse.xy, cursor->hot);
+		r.max = addpt(r.min, cursor->i->r.max);
+		if(!forceredraw && oldcursor != nil){
+			clipr.min = subpt(oldmouse.xy, oldcursor->hot);
+			clipr.max = addpt(clipr.min, oldcursor->i->r.max);
+			combinerect(&clipr, r);
+			replclipr(screen, 0, clipr);
+		}
+	}
+	draw(screen, screen->r, front, nil, ZP);
+	if(cursor != nil && showcursor)
+		draw(screen, r, cursor->i, cursor->m, ZP);
+	mouseredraw = 0;
+	oldmouse.xy = mouse.xy;
+	oldcursor = cursor;
+
+	flushimage(display, 1);
+
+	if(forceredraw)
+		memmove(backcopy, byteaddr(back, ZP), logiw*logih*4);
+	else
+		replclipr(screen, 0, screen->r);
+	forceredraw = 0;
+}
+
+Uint32
+SDL_GetWindowFlags(SDL_Window *)
+{
+	/* FIXME is this correct? */
+	return SDL_WINDOW_INPUT_FOCUS;
+}
+
+int
+SDL_RenderSetLogicalSize(SDL_Renderer *, int w, int h)
+{
+	if(logiw != w || logih != h){
+		logiw = w;
+		logih = h;
+		forceredraw = 1;
+	}
+
+	return 0;
+}
+
+int
+SDL_GetRendererOutputSize(SDL_Renderer *, int *w, int *h)
+{
+	if(w != nil)
+		*w = logiw;
+	if(h != nil)
+		*h = logih;
+
+	return 0;
+}
+
+void
+SDL_SetWindowSize(SDL_Window *, int w, int h)
+{
+	int f, n;
+
+	if(physw != w || physh != h){
+		physw = w;
+		physh = h;
+		if((f = open("/dev/wctl", OWRITE|OCEXEC)) >= 0){
+			n = fprint(f, "resize -dx %d -dy %d", w+Borderwidth*2, h+Borderwidth*2);
+			close(f);
+			if(n > 0){
+				getwindow(display, Refnone);
+				forceredraw = 1;
+			}
+		}
+	}
+}
+
+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){
+			getwindow(display, Refnone);
+			forceredraw = 1;
+		}
+	}
+}
+
+int
+SDL_GetDesktopDisplayMode(int displayIndex, SDL_DisplayMode *mode)
+{
+	if(displayIndex != 0)
+		return -1;
+
+	mode->w = physw;
+	mode->h = physh;
+	mode->format = SDL_PIXELFORMAT_ARGB8888;
+	mode->refresh_rate = 0;
+
+	return 0;
+}
+
+void
+SDL_SetWindowTitle(SDL_Window *, char *title)
+{
+	int f;
+
+	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);
+	}
+}
+
+void
+SDL_DestroyTexture(SDL_Texture *t)
+{
+	freememimage(t);
+}
+
+int
+SDL_SetTextureBlendMode(SDL_Texture *, SDL_BlendMode blendMode)
+{
+	if(blendMode != SDL_BLENDMODE_NONE){
+		werrstr("SDL_SetTextureBlendMode: only SDL_BLENDMODE_NONE is supported");
+		return -1;
+	}
+
+	return 0;
+}
+
+SDL_bool
+SDL_SetHint(char *name, char *value)
+{
+	/* FIXME anyone cares about name="SDL_RENDER_SCALE_QUALITY" value="(best|nearest)"? */
+	USED(name);
+	USED(value);
+
+	return SDL_FALSE;
+}
+
+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 + (Dx(display->image->r) - w)/2;
+		SDL_SetWindowPosition(&onewin, x, y);
+	}
+
+	return &onewin;
+}
+
+SDL_Renderer *
+SDL_CreateRenderer(SDL_Window *, int, Uint32)
+{
+	SDL_RenderSetLogicalSize(nil, physw, physh);
+	return &oneren;
+}
+
+int
+SDL_SetRenderDrawBlendMode(SDL_Renderer *, SDL_BlendMode blendMode)
+{
+	if(blendMode != SDL_BLENDMODE_NONE){
+		werrstr("SDL_SetRenderDrawBlendMode: only SDL_BLENDMODE_NONE is supported");
+		return -1;
+	}
+
+	return 0;
+}
+
+char *
+SDL_GetCurrentVideoDriver(void)
+{
+	return "/dev/draw";
+}
+
+Uint32
+SDL_GetTicks(void)
+{
+	return nanosec()/1000000ULL;
+}
+
+SDL_bool
+SDL_HasClipboardText(void)
+{
+	/* most def */
+	return SDL_TRUE;
+}
+
+void
+SDL_SetThreadPriority(int)
+{
+	/* nothing to do here */
+}
+
+void
+SDL_RestoreWindow(SDL_Window *)
+{
+	/* nothing to do here */
+}
+
+void
+SDL_RaiseWindow(SDL_Window *)
+{
+	/* nothing to do here */
+}
+
+int
+SDL_SetSurfaceRLE(SDL_Surface *, int)
+{
+	/* nothing to do here */
+	return 0;
+}
+
+int
+SDL_SetSurfaceBlendMode(SDL_Surface *, SDL_BlendMode blendMode)
+{
+	if(blendMode != SDL_BLENDMODE_NONE){
+		werrstr("SDL_SetSurfaceBlendMode: only SDL_BLENDMODE_NONE is supported");
+		return -1;
+	}
+
+	return 0;
+}
+
+int
+SDL_LockSurface(SDL_Surface *)
+{
+	/* nothing to do here */
+	return 0;
+}
+
+int
+SDL_UnlockSurface(SDL_Surface *)
+{
+	/* nothing to do here */
+	return 0;
+}
+
+void
+SDL_DestroyRenderer(SDL_Renderer *)
+{
+	/* nothing to do here */
+}
+
+void
+SDL_DestroyWindow(SDL_Window *)
+{
+}
+
+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)
+{
+}
+
+int
+SDL_SaveBMP(SDL_Surface *s, const char *file)
+{
+	/* FIXME implement this */
+	USED(s, file);
+
+	return -1;
+}
--- a/limits.h
+++ /dev/null
@@ -1,18 +1,0 @@
-#ifndef _limits_h_
-#define _limits_h_
-
-#define INT16_MAX 0x7fff
-#define INT16_MIN ((int16_t)0x8000)
-#define INT64_MIN ((int64_t)0x8000000000000000ULL)
-#define INT32_MAX 0x7fffffff
-#define INT_MAX INT32_MAX
-#define INT32_MIN (-INT32_MAX-1)
-#define INT_MIN INT32_MIN
-#define SHRT_MAX 0x7fff
-#define SHRT_MIN (-SHRT_MAX-1)
-#define SIZE_MAX 0xffffffffU
-#define UINT32_MAX 0xffffffffU
-#define UINT_MAX UINT32_MAX
-#define UINT64_MAX 0xffffffffffffffffULL
-
-#endif
--- a/malloc.h
+++ /dev/null
@@ -1,1 +1,0 @@
-#include "plan9.h"
--- a/math.c
+++ /dev/null
@@ -1,71 +1,0 @@
-#include <math.h>
-
-/* some of these were taken from musl */
-
-#define ln2o1 1.4426950408889634073599
-#define dbleps 2.2204460492503131e-16
-#define huge 1.79769313486231e+308
-
-static double toint = 1.0/dbleps;
-
-double
-round(double x)
-{
-	union {double f; u64int i;} u = {x};
-	int e = u.i >> 52 & 0x7ff;
-	double y;
-
-	if(e >= 0x3ff+52)
-		return x;
-	if(u.i >> 63)
-		x = -x;
-	if(e < 0x3ff-1)
-		return 0*u.f;
-	y = x + toint - toint - x;
-	if(y > 0.5)
-		y = y + x - 1;
-	else if(y <= -0.5)
-		y = y + x + 1;
-	else
-		y = y + x;
-	if(u.i >> 63)
-		y = -y;
-
-	return y;
-}
-
-double
-log2(double x)
-{
-	if(x == 0)
-		return -huge;
-	if(x < 0 || isNaN(x))
-		return NaN();
-
-	return log(x)*ln2o1;
-}
-
-double
-trunc(double x)
-{
-	union {double f; u64int i;} u = {x};
-	int e = (int)(u.i >> 52 & 0x7ff) - 0x3ff + 12;
-	u64int m;
-
-	if(e >= 52 + 12)
-		return x;
-	if(e < 12)
-		e = 1;
-	m = -1ULL >> e;
-	if((u.i & m) == 0)
-		return x;
-	u.i &= ~m;
-
-	return u.f;
-}
-
-double
-exp2(double x)
-{
-	return exp(x/ln2o1);
-}
--- a/math.h
+++ /dev/null
@@ -1,18 +1,0 @@
-#ifndef _math_h_
-#define _math_h_
-
-#include "plan9.h"
-
-#define sinf sin
-#define cosf cos
-#define fabsf fabs
-#define fmodf fmod
-#define ceilf ceil
-#define floorf floor
-
-double round(double);
-double log2(double);
-double trunc(double);
-double exp2(double);
-
-#endif
--- a/plan9.c
+++ /dev/null
@@ -1,86 +1,0 @@
-#include "plan9.h"
-#include <thread.h>
-#include <tos.h>
-#include <pool.h>
-
-int mainstacksize = 32*1024;
-
-int
-rm_r(char *dir)
-{
-	int pid;
-
-	if((pid = rfork(RFPROC|RFFDG|RFNOTEG|RFCENVG|RFNOWAIT)) == 0){
-		execl("/bin/rm", "rm", "-r", dir, nil);
-		sysfatal("execl: %r");
-	}
-
-	return pid < 0 ? -1 : 0;
-}
-
-/*
- * nsec() is wallclock and can be adjusted by timesync
- * so need to use cycles() instead, but fall back to
- * nsec() in case we can't
- */
-uvlong
-nanosec(void)
-{
-	static uvlong fasthz, xstart;
-	uvlong x, div;
-
-	if(fasthz == ~0ULL)
-		return nsec() - xstart;
-
-	if(fasthz == 0){
-		if(_tos->cyclefreq){
-			cycles(&xstart);
-			fasthz = _tos->cyclefreq;
-		} else {
-			xstart = nsec();
-			fasthz = ~0ULL;
-			fprint(2, "cyclefreq not available, falling back to nsec()\n");
-			fprint(2, "you might want to disable aux/timesync\n");
-			return 0;
-		}
-	}
-	cycles(&x);
-	x -= xstart;
-
-	/* this is ugly */
-	for(div = NSEC; x < 0x1999999999999999ULL && div > 1 ; div /= 10ULL, x *= 10ULL);
-
-	return x / (fasthz / div);
-}
-
-void
-nsleep(uvlong ns)
-{
-	uvlong start, end;
-
-	start = nanosec();
-	end = start + ns;
-	ns = start;
-	do{
-		if(end - ns > 750000000ULL)
-			sleep(70);
-		else if (end - ns > 25000000ULL)
-			sleep(20);
-		else if (end - ns > 10000000ULL)
-			sleep(1);
-		else
-			break;
-		ns = nanosec();
-	}while(ns < end);
-}
-
-void
-threadmain(int argc, char **argv)
-{
-	int __main(int argc, char *argv[]);
-
-	setfcr(getfcr() & ~(FPINVAL|FPOVFL));
-	//mainmem->flags |= POOL_PARANOIA | POOL_ANTAGONISM;
-
-	threadexitsall(__main(argc, argv) == 0 ? nil : "error");
-}
--- a/plan9.h
+++ /dev/null
@@ -1,18 +1,0 @@
-#ifndef __plan9_h__
-#define __plan9_h__
-
-#include <u.h>
-#include <libc.h>
-#include </sys/include/stdio.h>
-
-#define PATH_MAX 256
-
-#define __attribute__(a)
-#define main __main
-#define NSEC 1000000000ULL
-
-int rm_r(char *dir);
-uvlong nanosec(void);
-void nsleep(uvlong ns);
-
-#endif
--- a/sdl2.c
+++ /dev/null
@@ -1,1335 +1,0 @@
-#include "plan9.h"
-#include <stdint.h>
-#include <tos.h>
-#include <draw.h>
-#include <memdraw.h>
-#include <mouse.h>
-#include <cursor.h>
-#include <thread.h>
-#include <SDL2/SDL.h>
-#include <pool.h>
-
-typedef struct Audiodev Audiodev;
-
-enum {
-	Aout = 2,
-	Arec,
-
-	/* FIXME missing plumber→dropfile and enter()→text */
-	Ckey = 0,
-	Ckeytype,
-	Cmouse,
-	Cresize,
-	Numchan,
-
-	Rdown = 0,
-	Rup,
-	Rrepeat,
-
-	Audiobufsz = 16384,
-};
-
-struct SDL_Window {
-	int dummy;
-};
-
-struct SDL_Renderer {
-	int dummy;
-};
-
-struct SDL_Texture {
-	Memimage;
-};
-
-struct SDL_PixelFormat {
-	int format;
-};
-
-struct SDL_Cursor {
-	Image *i;
-	Image *m;
-	Point hot;
-};
-
-struct SDL_Thread {
-	SDL_ThreadFunction f;
-	char *name;
-	void *userdata;
-};
-
-static SDL_PixelFormat argb8888 = {
-	.format = SDL_PIXELFORMAT_ARGB8888,
-};
-
-struct Audiodev {
-	QLock;
-	int paused;
-	int fd;
-	int pid;
-	int mode;
-	Uint8 buf[Audiobufsz];
-	void *userdata;
-	void (*cb)(void *, Uint8 *, int);
-};
-
-/* FIXME extra USB audio devices? */
-static Audiodev au[4] = {
-	[0] = {.fd = -1, .mode = -1},
-	[1] = {.fd = -1, .mode = -1},
-	[Aout] = {.fd = -1, .pid = -1, .mode = OWRITE},
-	[Arec] = {.fd = -1, .pid = -1, .mode = OREAD},
-};
-
-static SDL_Window onewin;
-static SDL_Renderer oneren;
-static int kmod;
-static Mouse mouse, oldmouse;
-static Rune rune;
-static Mousectl *mctl;
-static Keyboardctl kctl;
-static Memimage *back;
-static u8int *backcopy;
-static Image *front;
-static int logiw, logih;
-static int physw, physh;
-static int forceredraw = 1;
-static SDL_Cursor *oldcursor, *cursor;
-static int mouseredraw = 0;
-static int showcursor = SDL_ENABLE;
-static int textinput;
-
-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 Alt salt[Numchan+1] = {
-	[Ckey] = { nil, &rune, CHANRCV },
-	[Ckeytype] = { nil, nil, CHANNOP },
-	[Cmouse] = { nil, &mouse, CHANRCV },
-	[Cresize] = { nil, nil, CHANRCV },
-	[Numchan] = { nil, nil, CHANNOBLK },
-};
-
-static void
-kbdproc(void *)
-{
-	char buf[128], buf2[128], *s;
-	int kfd, n, kbin, t;
-	Rune r, o;
-
-	threadsetname("kbdproc");
-	if((kfd = open("/dev/kbd", OREAD|OCEXEC)) < 0)
-		sysfatal("/dev/kbd: %r");
-	kbin = open("/dev/kbin", OWRITE|OCEXEC);
-
-	buf2[0] = 0;
-	buf2[1] = 0;
-	buf[0] = 0;
-	kmod = 0;
-	o = 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){
-				t = Rrepeat;
-				if(!textinput)
-					r = tolowerrune(r);
-				if(textinput || o != r){
-					send(salt[Ckey].c, &r);
-					send(salt[Ckeytype].c, &t);
-				}
-				o = 0;
-			}
-		default:
-			continue;
-
-		case 'k':
-			s = buf+1;
-			while(*s){
-				s += chartorune(&r, s);
-				if(utfrune(buf2+1, r) == nil){
-					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 |= KMOD_LALT;
-					}else if (r == Kshift)
-						kmod |= KMOD_LSHIFT;
-					else if(r == Kctl)
-						kmod |= KMOD_LCTRL;
-					else{
-						t = Rdown;
-						send(salt[Ckey].c, &r);
-						send(salt[Ckeytype].c, &t);
-						o = r;
-					}
-				}
-			}
-			break;
-
-		case 'K':
-			s = buf2+1;
-			while(*s){
-				s += chartorune(&r, s);
-				if(utfrune(buf+1, r) == nil){
-					if(r == Kalt)
-						kmod &= ~KMOD_LALT;
-					else if(r == Kshift)
-						kmod &= ~KMOD_LSHIFT;
-					else if(r == Kctl)
-						kmod &= ~KMOD_LCTRL;
-					else{
-						t = Rup;
-						send(salt[Ckey].c, &r);
-						send(salt[Ckeytype].c, &t);
-					}
-				}
-			}
-			break;
-		}
-		strcpy(buf2, buf);
-	}
-
-	threadexits(nil);
-}
-
-int
-SDL_Init(int mask)
-{
-	/* FIXME actually use the mask? */
-	USED(mask);
-
-	if(memimageinit() < 0)
-		goto err;
-	if(initdraw(nil, nil, "ft2-clone") < 0)
-		goto err;
-	if((mctl = initmouse(nil, screen)) == nil)
-		goto err;
-
-	salt[Ckey].c = chancreate(sizeof(Rune), 20);
-	salt[Ckeytype].c = chancreate(sizeof(int), 20);
-	salt[Cmouse].c = mctl->c;
-	salt[Cresize].c = mctl->resizec;
-	kctl.c = salt[Ckey].c; /* for enter() */
-
-	if(salt[Ckey].c == nil || salt[Ckeytype].c == nil || proccreate(kbdproc, nil, 4096) < 0)
-		goto err;
-
-	return 0;
-err:
-	werrstr("SDL_Init: %r");
-	return -1;
-}
-
-int
-SDL_EventState(Uint32, int)
-{
-	return 0;
-}
-
-SDL_Keymod
-SDL_GetModState(void)
-{
-	return kmod;
-}
-
-int
-SDL_ShowCursor(int toggle)
-{
-	if(toggle == SDL_QUERY)
-		return showcursor;
-
-	showcursor = toggle == SDL_ENABLE;
-	setcursor(mctl, (cursor == nil && showcursor) ? nil : &nocursor);
-
-	return showcursor;
-}
-
-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); USED(iscapture);
-	return "/dev/audio";
-}
-
-void
-SDL_LockAudioDevice(SDL_AudioDeviceID id)
-{
-	qlock(&au[id]);
-}
-
-void
-SDL_UnlockAudioDevice(SDL_AudioDeviceID id)
-{
-	qunlock(&au[id]);
-}
-
-static void
-audiothread(void *p)
-{
-	Audiodev *d;
-
-	d = p;
-	threadsetname("audio %s", d == &au[Aout] ? "out" : "in");
-
-	for(;;){
-		qlock(d);
-		if(d->paused)
-			memset(d->buf, 0, sizeof(d->buf));
-		else
-			d->cb(d->userdata, d->buf, sizeof(d->buf));
-		qunlock(d);
-
-		if(write(d->fd, d->buf, sizeof(d->buf)) != sizeof(d->buf))
-			break;
-	}
-
-	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, mainstacksize);
-		a->paused = 0;
-	}else if(!a->paused && pause){
-		a->paused = 1;
-	}
-}
-
-SDL_AudioDeviceID
-SDL_OpenAudioDevice(char *dev, int rec, SDL_AudioSpec *want, SDL_AudioSpec *have, u32int change)
-{
-	Audiodev *a;
-	SDL_AudioDeviceID id;
-
-	/* FIXME look for extra USB devices? */
-	USED(dev);
-
-	if(change != SDL_AUDIO_ALLOW_ANY_CHANGE){ /* FIXME sampling in mono */
-		werrstr("SDL_OpenAudioDevice: changes not implemented");
-		return 0;
-	}
-
-	have->freq = 44100;
-	have->format = AUDIO_S16;
-	have->channels = 2;
-	have->samples = want->samples;
-
-	id = rec ? Arec : Aout;
-	a = &au[id];
-
-	if(a->fd < 0 && (a->fd = open("/dev/audio", a->mode|OCEXEC)) < 0){
-		werrstr("SDL_OpenAudioDevice: %r");
-		return 0;
-	}
-
-	a->userdata = want->userdata;
-	a->cb = want->callback;
-	a->paused = 1;
-
-	return id;
-}
-
-void
-SDL_CloseAudioDevice(SDL_AudioDeviceID id)
-{
-	Audiodev *a;
-
-	a = &au[id];
-	qlock(a);
-	close(a->fd);
-	a->fd = -1;
-	a->pid = -1;
-	a->userdata = nil;
-	a->cb = nil;
-	qunlock(a);
-}
-
-u64int
-SDL_GetPerformanceFrequency(void)
-{
-	return _tos->cyclefreq;
-}
-
-u64int
-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
-sdlthread(void *p)
-{
-	SDL_Thread t;
-
-	t = *(SDL_Thread*)p;
-	if(t.name != nil)
-		threadsetname(t.name);
-	free(p);
-
-	threadexits(t.f(t.userdata) == 0 ? nil : "error");
-}
-
-SDL_Thread *
-SDL_CreateThread(SDL_ThreadFunction f, char *name, void *userdata)
-{
-	SDL_Thread *t;
-
-	if((t = calloc(1, sizeof(*t))) == nil)
-		return nil;
-
-	t->f = f;
-	t->name = name;
-	t->userdata = userdata;
-
-	if(proccreate(sdlthread, t, mainstacksize) < 0){
-		free(t);
-		t = nil;
-	}
-
-	return t;
-}
-
-void
-SDL_DetachThread(SDL_Thread *)
-{
-}
-
-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;
-}
-
-SDL_Texture *
-SDL_CreateTexture(SDL_Renderer *, Uint32 format, int, int w, int h)
-{
-	SDL_Texture *t;
-
-	if(format != SDL_PIXELFORMAT_ARGB8888){
-		werrstr("SDL_CreateTexture: only SDL_PIXELFORMAT_ARGB8888 is supported");
-		goto err;
-	}
-	if((t = (SDL_Texture*)allocmemimage(Rect(0, 0, w, h), XRGB32)) == nil) /* FIXME ARGB32? */
-		goto err;
-	memfillcolor(t, DTransparent);
-
-	return t;
-err:
-	werrstr("SDL_CreateTexture: %r");
-	return nil;
-}
-
-int
-SDL_UpdateTexture(SDL_Texture *t, 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->r;
-	/* FIXME non-ARGB8888 */
-	/* FIXME pitch */ USED(pitch);
-	if(loadmemimage(t, r, pixels, Dx(r)*Dy(r)*4) < 0){
-		werrstr("SDL_UpdateTexture: %r");
-		return -1;
-	}
-
-	return 0;
-}
-
-int
-SDL_RenderClear(SDL_Renderer *)
-{
-	if(back != nil)
-		memfillcolor(back, DBlack);
-
-	return 0;
-}
-
-int
-SDL_GetWindowDisplayIndex(SDL_Window *)
-{
-	return 0;
-}
-
-void
-SDL_Quit(void)
-{
-	threadexitsall(nil);
-}
-
-void
-SDL_free(void *p)
-{
-	free(p);
-}
-
-SDL_Surface *
-SDL_CreateRGBSurface(Uint32, int w, int h, int bpp, Uint32 rm, Uint32 gm, Uint32 bm, Uint32 am)
-{
-	SDL_Surface *s;
-	int n;
-
-	USED(rm, gm, bm, am); /* FIXME masks */
-
-	n = w*h*bpp/8;
-	if((s = calloc(1, sizeof(*s)+n)) == nil){
-		werrstr("SDL_CreateRGBSurface: memory");
-		return nil;
-	}
-	s->format = &argb8888;
-	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;
-
-	return s;
-}
-
-SDL_Surface *
-SDL_CreateRGBSurfaceFrom(Uint32 *pixels, int w, int h, int bpp, int pitch, Uint32 rm, Uint32 gm, Uint32 bm, Uint32 am)
-{
-	SDL_Surface *s;
-	int n;
-
-	USED(pitch); /* FIXME pitch */
-
-	if((s = SDL_CreateRGBSurface(0, w, h, bpp, rm, gm, bm, am)) == nil)
-		return nil;
-
-	n = w*h*bpp/8;
-	memmove(s->pixels, pixels, n);
-
-	return s;
-}
-
-void
-SDL_FreeSurface(SDL_Surface *surface)
-{
-	memset(surface, 0, sizeof(surface));
-	free(surface);
-}
-
-Uint32
-SDL_MapRGB(SDL_PixelFormat *, Uint8 r, Uint8 g, Uint8 b)
-{
-	return r<<24 | g<<16 | b<<8 | 0xff;
-}
-
-int
-SDL_SetColorKey(SDL_Surface *s, int flag, Uint32 key)
-{
-	s->keyset = flag;
-	s->key = key;
-	return 0;
-}
-
-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;
-}
-
-void
-SDL_SetCursor(SDL_Cursor *c)
-{
-	if(cursor != c){
-		cursor = c;
-		mouseredraw = 1;
-		setcursor(mctl, (cursor == nil && showcursor) ? nil : &nocursor);
-	}
-}
-
-void
-SDL_FreeCursor(SDL_Cursor *c)
-{
-	freeimage(c->i);
-	free(c);
-	if(cursor == c){
-		oldcursor = nil;
-		cursor = nil;
-	}
-}
-
-static int
-rune2scancode(Rune 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_ENTER;
-	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;
-
-/* FIXME
-	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;
-}
-
-int
-SDL_PollEvent(SDL_Event *e)
-{
-	int t, down;
-
-	if(e == nil) /* FIXME need to buffer the event so it won't get lost */
-		return 0;
-
-	switch(alt(salt)){
-	case Ckey:
-		recv(salt[Ckeytype].c, &t);
-		if(textinput && rune >= 0x20 && rune <= 0x7f){
-			if(t != Rrepeat)
-				break;
-			e->type = SDL_TEXTINPUT;
-			e->text.text[runetochar(e->text.text, &rune)] = 0;
-		}else if(textinput && t != Rrepeat){
-			break;
-		}else{
-			e->type = (t == Rup) ? SDL_KEYUP : SDL_KEYDOWN;
-			e->key.repeat = !textinput && t == Rrepeat;
-			e->key.keysym.scancode = rune2scancode(rune);
-			e->key.keysym.sym = rune;
-		}
-		return 1;
-
-	case Cmouse:
-		e->motion.x = mouse.xy.x - screen->r.min.x;
-		e->motion.y = mouse.xy.y - screen->r.min.y;
-		if(!eqpt(mouse.xy, oldmouse.xy)){
-			mouseredraw = 1;
-			if(mouse.buttons == oldmouse.buttons){
-				e->type = SDL_MOUSEMOTION;
-				return 1;
-			}
-		}
-		if(mouse.buttons == oldmouse.buttons)
-			break;
-		/* FIXME there is a lot of hope for both buttons to never change at the same time */
-		if((down = (mouse.buttons & 1)) != (oldmouse.buttons & 1)){ /* left */
-			e->type = down ? SDL_MOUSEBUTTONDOWN : SDL_MOUSEBUTTONUP;
-			e->button.button = SDL_BUTTON_LEFT;
-			oldmouse.buttons = (oldmouse.buttons & ~1) | (mouse.buttons & 1);
-			return 1;
-		}
-		if((down = (mouse.buttons & 4)) != (oldmouse.buttons & 4)){ /* right */
-			e->type = down ? SDL_MOUSEBUTTONDOWN : SDL_MOUSEBUTTONUP;
-			e->button.button = SDL_BUTTON_RIGHT;
-			oldmouse.buttons = (oldmouse.buttons & ~4) | (mouse.buttons & 4);
-			return 1;
-		}
-		if(mouse.buttons & (8|16)){
-			e->type = SDL_MOUSEWHEEL;
-			e->wheel.x = 0;
-			e->wheel.y = (mouse.buttons & 8) ? -1 : 1;
-			return 1;
-		}
-		break;
-
-	case Cresize:
-		forceredraw = 1;
-		if(getwindow(display, Refnone) < 0){
-			fprint(2, "%r\n");
-			/* FIXME do something here? */
-			//threadexitsall(nil);
-		}
-		e->type = SDL_WINDOWEVENT;
-		e->window.event = SDL_WINDOWEVENT_EXPOSED;
-		return 1;
-	}
-
-	return 0;
-}
-
-int
-SDL_PushEvent(SDL_Event *event)
-{
-	/* FIXME does it matter? */
-	USED(event);
-	return -1;
-}
-
-void
-SDL_WarpMouseInWindow(SDL_Window *, int x, int y)
-{
-	moveto(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 = mouse.xy.x;
-	if(y != nil)
-		*y = mouse.xy.y;
-
-	b = 0;
-	if(mouse.buttons & 1)
-		b |= SDL_BUTTON_LMASK;
-	if(mouse.buttons & 4)
-		b |= SDL_BUTTON_RMASK;
-	/* FIXME no middle button use AT ALL? */
-
-	return b;
-}
-
-Uint32
-SDL_GetMouseState(int *x, int *y)
-{
-	Uint32 b;
-
-	b = SDL_GetGlobalMouseState(nil, nil);
-	if(x != nil)
-		*x = (mouse.xy.x - screen->r.min.x) * logiw / physw;
-	if(y != nil)
-		*y = (mouse.xy.y - screen->r.min.y) * logih / physh;
-
-	return b;
-}
-
-void
-SDL_RenderGetScale(SDL_Renderer *, float *scaleX, float *scaleY)
-{
-	*scaleX = 1.0;
-	*scaleY = 1.0;
-}
-
-void
-SDL_GetWindowSize(SDL_Window *, int *w, int *h)
-{
-	/* no matter what rio decides */
-	*w = physw;
-	*h = physh;
-}
-
-void
-SDL_GetWindowPosition(SDL_Window *, int *x, int *y)
-{
-	*x = screen->r.min.x;
-	*y = screen->r.min.y;
-}
-
-SDL_bool
-SDL_IsTextInputActive(void)
-{
-	return textinput;
-}
-
-void
-SDL_StartTextInput(void)
-{
-	textinput = SDL_TRUE;
-}
-
-void
-SDL_StopTextInput(void)
-{
-	textinput = SDL_FALSE;
-}
-
-void
-SDL_Delay(Uint32 ms)
-{
-	nsleep((uvlong)ms*1000000ULL);
-}
-
-static void *
-resize(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;
-}
-
-int
-SDL_RenderCopy(SDL_Renderer *, SDL_Texture *t, SDL_Rect *sre, SDL_Rect *dre)
-{
-	Rectangle sr, dr;
-
-	if(sre != nil){
-		sr.min = Pt(sre->x, sre->y);
-		sr.max = addpt(sr.min, Pt(sre->w, sre->h));
-	}else
-		sr = t->r;
-
-	if(dre != nil){
-		dr.min = Pt(dre->x, dre->y);
-		dr.max = addpt(dr.min, Pt(dre->w, dre->h));
-	}else /* stretch */
-		dr = Rect(0, 0, logiw, logih);
-
-	if(back == nil || Dx(back->r) != logiw || Dy(back->r) != logih){
-		freememimage(back);
-		back = allocmemimage(Rect(0, 0, logiw, logih), XRGB32);
-		if(back == nil){
-			werrstr("SDL_RenderCopy: %r");
-			return -1;
-		}
-		free(backcopy);
-		backcopy = malloc(logiw*logih*4);
-	}
-
-	if(dre == nil)
-		resize((u32int*)byteaddr(t, ZP), Dx(sr), Dy(sr), (u32int*)byteaddr(back, ZP), logiw, logih);
-	else
-		memimagedraw(back, dr, t, sr.min, nil, ZP, S);
-
-	return 0;
-}
-
-void
-SDL_RenderPresent(SDL_Renderer *)
-{
-	Rectangle r, clipr;
-	static u32int *b;
-	uchar *rb;
-
-	if(!forceredraw && (forceredraw = memcmp(backcopy, byteaddr(back, ZP), logiw*logih*4)) == 0 && !mouseredraw)
-		return;
-
-	r = Rect(0, 0, physw, physh);
-	if(front != nil && (Dx(front->r) != physw || Dy(front->r) != physh)){
-		freeimage(front);
-		front = nil;
-		free(b);
-		b = nil;
-	}
-	if(b == nil && (b = realloc(b, physw*physh*4)) == nil){
-		fprint(2, "SDL_RenderPresent: %r\n");
-		return;
-	}
-	if(forceredraw || front == nil){
-		rb = resize((u32int*)byteaddr(back, ZP), Dx(back->r), Dy(back->r), b, physw, physh);
-		if(front == nil && (front = allocimage(display, r, XRGB32, 0, DNofill)) == nil){
-			fprint(2, "SDL_RenderPresent: %r\n");
-			return;
-		}
-		if(loadimage(front, r, rb, Dx(r)*Dy(r)*4) < 0){
-			fprint(2, "SDL_RenderPresent: %r\n");
-			return;
-		}
-	}
-
-	if(cursor != nil && showcursor){
-		r.min = subpt(mouse.xy, cursor->hot);
-		r.max = addpt(r.min, cursor->i->r.max);
-		if(!forceredraw && oldcursor != nil){
-			clipr.min = subpt(oldmouse.xy, oldcursor->hot);
-			clipr.max = addpt(clipr.min, oldcursor->i->r.max);
-			combinerect(&clipr, r);
-			replclipr(screen, 0, clipr);
-		}
-	}
-	draw(screen, screen->r, front, nil, ZP);
-	if(cursor != nil && showcursor)
-		draw(screen, r, cursor->i, cursor->m, ZP);
-	mouseredraw = 0;
-	oldmouse.xy = mouse.xy;
-	oldcursor = cursor;
-
-	flushimage(display, 1);
-
-	if(forceredraw)
-		memmove(backcopy, byteaddr(back, ZP), logiw*logih*4);
-	else
-		replclipr(screen, 0, screen->r);
-	forceredraw = 0;
-}
-
-Uint32
-SDL_GetWindowFlags(SDL_Window *)
-{
-	/* FIXME is this correct? */
-	return SDL_WINDOW_INPUT_FOCUS;
-}
-
-int
-SDL_RenderSetLogicalSize(SDL_Renderer *, int w, int h)
-{
-	if(logiw != w || logih != h){
-		logiw = w;
-		logih = h;
-		forceredraw = 1;
-	}
-
-	return 0;
-}
-
-int
-SDL_GetRendererOutputSize(SDL_Renderer *, int *w, int *h)
-{
-	if(w != nil)
-		*w = logiw;
-	if(h != nil)
-		*h = logih;
-
-	return 0;
-}
-
-void
-SDL_SetWindowSize(SDL_Window *, int w, int h)
-{
-	int f, n;
-
-	if(physw != w || physh != h){
-		physw = w;
-		physh = h;
-		if((f = open("/dev/wctl", OWRITE|OCEXEC)) >= 0){
-			n = fprint(f, "resize -dx %d -dy %d", w+Borderwidth*2, h+Borderwidth*2);
-			close(f);
-			if(n > 0){
-				getwindow(display, Refnone);
-				forceredraw = 1;
-			}
-		}
-	}
-}
-
-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){
-			getwindow(display, Refnone);
-			forceredraw = 1;
-		}
-	}
-}
-
-int
-SDL_GetDesktopDisplayMode(int displayIndex, SDL_DisplayMode *mode)
-{
-	if(displayIndex != 0)
-		return -1;
-
-	mode->w = physw;
-	mode->h = physh;
-	mode->format = SDL_PIXELFORMAT_ARGB8888;
-	mode->refresh_rate = 0;
-
-	return 0;
-}
-
-void
-SDL_SetWindowTitle(SDL_Window *, char *title)
-{
-	int f;
-
-	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);
-	}
-}
-
-void
-SDL_DestroyTexture(SDL_Texture *t)
-{
-	freememimage(t);
-}
-
-int
-SDL_SetTextureBlendMode(SDL_Texture *, SDL_BlendMode blendMode)
-{
-	if(blendMode != SDL_BLENDMODE_NONE){
-		werrstr("SDL_SetTextureBlendMode: only SDL_BLENDMODE_NONE is supported");
-		return -1;
-	}
-
-	return 0;
-}
-
-SDL_bool
-SDL_SetHint(char *name, char *value)
-{
-	/* FIXME anyone cares about name="SDL_RENDER_SCALE_QUALITY" value="(best|nearest)"? */
-	USED(name);
-	USED(value);
-
-	return SDL_FALSE;
-}
-
-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 + (Dx(display->image->r) - w)/2;
-		SDL_SetWindowPosition(&onewin, x, y);
-	}
-
-	return &onewin;
-}
-
-SDL_Renderer *
-SDL_CreateRenderer(SDL_Window *, int, Uint32)
-{
-	SDL_RenderSetLogicalSize(nil, physw, physh);
-	return &oneren;
-}
-
-int
-SDL_SetRenderDrawBlendMode(SDL_Renderer *, SDL_BlendMode blendMode)
-{
-	if(blendMode != SDL_BLENDMODE_NONE){
-		werrstr("SDL_SetRenderDrawBlendMode: only SDL_BLENDMODE_NONE is supported");
-		return -1;
-	}
-
-	return 0;
-}
-
-char *
-SDL_GetCurrentVideoDriver(void)
-{
-	return "/dev/draw";
-}
-
-Uint32
-SDL_GetTicks(void)
-{
-	return nanosec()/1000000ULL;
-}
-
-SDL_bool
-SDL_HasClipboardText(void)
-{
-	/* most def */
-	return SDL_TRUE;
-}
-
-void
-SDL_SetThreadPriority(int)
-{
-	/* nothing to do here */
-}
-
-void
-SDL_RestoreWindow(SDL_Window *)
-{
-	/* nothing to do here */
-}
-
-void
-SDL_RaiseWindow(SDL_Window *)
-{
-	/* nothing to do here */
-}
-
-int
-SDL_SetSurfaceRLE(SDL_Surface *, int)
-{
-	/* nothing to do here */
-	return 0;
-}
-
-int
-SDL_SetSurfaceBlendMode(SDL_Surface *, SDL_BlendMode blendMode)
-{
-	if(blendMode != SDL_BLENDMODE_NONE){
-		werrstr("SDL_SetSurfaceBlendMode: only SDL_BLENDMODE_NONE is supported");
-		return -1;
-	}
-
-	return 0;
-}
-
-int
-SDL_LockSurface(SDL_Surface *)
-{
-	/* nothing to do here */
-	return 0;
-}
-
-int
-SDL_UnlockSurface(SDL_Surface *)
-{
-	/* nothing to do here */
-	return 0;
-}
-
-void
-SDL_DestroyRenderer(SDL_Renderer *)
-{
-	/* nothing to do here */
-}
-
-void
-SDL_DestroyWindow(SDL_Window *)
-{
-}
-
-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)
-{
-}
-
-int
-SDL_SaveBMP(SDL_Surface *s, const char *file)
-{
-	/* FIXME implement this */
-	USED(s, file);
-
-	return -1;
-}
--- a/signal.h
+++ /dev/null
@@ -1,25 +1,0 @@
-#ifndef _signal_h_
-#define _signal_h_
-
-#include "plan9.h"
-
-enum {
-	SA_RESETHAND = 1<<0, /* not used */
-
-	/* all are treated as SIGSEGV */
-	SIGILL = 4,
-	SIGABRT = 6,
-	SIGFPE = 8,
-	SIGSEGV = 11,
-};
-
-typedef void (*sa_handler)(int);
-
-struct sigaction {
-	sa_handler sa_handler;
-	int sa_flags;
-};
-
-void sigaction(int, struct sigaction *, struct sigaction *);
-
-#endif
--- a/stdarg.h
+++ /dev/null
@@ -1,1 +1,0 @@
-#include "plan9.h"
--- a/stdbool.h
+++ /dev/null
@@ -1,6 +1,0 @@
-#ifndef _stdbool_h_
-#define _stdbool_h_
-
-typedef enum { false, true } bool;
-
-#endif
--- a/stddef.h
+++ /dev/null
@@ -1,1 +1,0 @@
-#include "plan9.h"
--- a/stdint.h
+++ /dev/null
@@ -1,21 +1,0 @@
-#ifndef _stdint_h_
-#define _stdint_h_
-
-#include "plan9.h"
-#include <limits.h>
-
-typedef s8int int8_t;
-typedef u8int uint8_t;
-typedef s16int int16_t;
-typedef u16int uint16_t;
-typedef s32int int32_t;
-typedef u32int uint32_t;
-typedef s64int int64_t;
-typedef u64int uint64_t;
-typedef long ssize_t;
-typedef ulong size_t;
-typedef intptr ptrdiff_t;
-typedef intptr intptr_t;
-typedef uintptr uintptr_t;
-
-#endif
--- a/stdio.h
+++ /dev/null
@@ -1,1 +1,0 @@
-#include "plan9.h"
--- a/stdlib.h
+++ /dev/null
@@ -1,8 +1,0 @@
-#ifndef _stdlib_h_
-#define _stdlib_h_
-
-#include "plan9.h"
-
-#define exit(x) exits(x == 0 ? nil : "error")
-
-#endif
--- a/string.h
+++ /dev/null
@@ -1,1 +1,0 @@
-#include "plan9.h"
--- a/strings.h
+++ /dev/null
@@ -1,7 +1,0 @@
-#ifndef _strings_h_
-#define _strings_h_
-
-#define strcasecmp cistrcmp
-#define strncasecmp cistrncmp
-
-#endif
--- a/sys/stat.h
+++ /dev/null
@@ -1,1 +1,0 @@
-#include "plan9.h"
--- a/sys/time.h
+++ /dev/null
@@ -1,1 +1,0 @@
-#include "plan9.h"
--- a/sys/types.h
+++ /dev/null
@@ -1,1 +1,0 @@
-#include "plan9.h"
--- a/time.h
+++ /dev/null
@@ -1,1 +1,0 @@
-#include "plan9.h"
--- a/unistd.c
+++ /dev/null
@@ -1,65 +1,0 @@
-#include <unistd.h>
-
-#undef getenv
-#undef stat
-
-char *
-getenv9(char *s)
-{
-	static char t[1024];
-
-	snprint(t, sizeof(t), "%s", s = getenv(strcmp(s, "HOME") == 0 ? "home" : s));
-	free(s);
-
-	return t;
-}
-
-int
-stat9(char *filename, struct stat9 *buf)
-{
-	Dir *d;
-
-	if((d = dirstat(filename)) == nil)
-		return -1;
-
-	buf->st_size = d->length;
-	buf->st_mode = d->mode;
-	free(d);
-
-	return 0;
-}
-
-int
-mkdir(char *path, int perm)
-{
-	int f;
-
-	if(access(path, AEXIST) == 0){
-		werrstr("%s: already exists", path);
-		return -1;
-	}
-	if((f = create(path, OREAD, DMDIR|perm)) < 0){
-		werrstr("%s: can't create: %r", path);
-		return -1;
-	}
-	close(f);
-
-	return 0;
-}
-
-int
-rename(char *old, char *new)
-{
-	Dir d;
-
-	nulldir(&d);
-	d.name = new;
-
-	return dirwstat(old, &d) > 0 ? 0 : -1;
-}
-
-void
-usleep(unsigned us)
-{
-	nsleep((uvlong)us*1000ULL);
-}
--- a/unistd.h
+++ /dev/null
@@ -1,47 +1,0 @@
-#ifndef _unistd_h_
-#define _unistd_h_
-
-#include "plan9.h"
-
-enum {
-	S_IFMT = 0xff,
-	S_IFDIR = 1<<0,
-
-	F_OK = AEXIST,
-	R_OK = AREAD,
-	W_OK = AWRITE,
-	X_OK = AEXEC,
-
-	S_ISUID = 04000,
-	S_ISGID = 02000,
-	S_IRWXU = 00700,
-	S_IRUSR = 00400,
-	S_IWUSR = 00200,
-	S_IXUSR = 00100,
-	S_IRWXG = 00070,
-	S_IRGRP = 00040,
-	S_IWGRP = 00020,
-	S_IXGRP = 00010,
-	S_IRWXO = 00007,
-	S_IROTH = 00004,
-	S_IWOTH = 00002,
-	S_IXOTH = 00001,
-};
-
-#define getcwd getwd
-#define getenv getenv9
-#define stat stat9
-
-struct stat9 {
-	uvlong st_size;
-	int st_mode;
-};
-
-char *getenv9(char *s);
-int stat9(char *filename, struct stat9 *buf);
-int mkdir(char *path, int perm);
-int access(char *name, int mode);
-int rename(char *old, char *new);
-void usleep(unsigned us);
-
-#endif
--- a/wchar.h
+++ /dev/null
@@ -1,1 +1,0 @@
-#include "plan9.h"