shithub: npe

Download patch

ref: 10d8d4ff5dad3dc3a62eefba9d6abfa8243fb348
parent: 623f5427f2e5d9cbc7540e8e2a72981c973e4cac
author: Sigrid Solveig Haflínudóttir <ftrvxmtrx@gmail.com>
date: Thu Mar 18 10:13:45 EDT 2021

sdl2: more enhancements

* set SDL version definitions
* SDL_RenderSetIntegerScale nop stub
* support SDL_HINT_WINDOWS_NO_CLOSE_ON_ALT_F4 hint, bring back Alt+F4 = SDL_QUIT by default
* audio format changes
* audio input

--- a/README.md
+++ b/README.md
@@ -27,8 +27,6 @@
 
 Stack size is set to 256k.
 
-With SDL applications Alt+Delete is used to send `SDL_QUIT` event.
-
 [General porting guide](http://docs.9front.org/porting).
 
 ## Helping out
--- a/include/npe/SDL2/SDL.h
+++ b/include/npe/SDL2/SDL.h
@@ -6,6 +6,10 @@
 
 #pragma lib "libnpe_sdl2.a"
 
+#define SDL_MAJOR_VERSION 2
+#define SDL_MINOR_VERSION 0
+#define SDL_PATCHLEVEL 14
+
 typedef int SDL_AudioDeviceID;
 typedef struct SDL_AudioSpec SDL_AudioSpec;
 typedef struct SDL_Window SDL_Window;
@@ -132,11 +136,34 @@
 int SDL_GetCurrentDisplayMode(int displayIndex, SDL_DisplayMode *mode);
 void SDL_WaitThread(SDL_Thread *thread, int *status);
 void SDL_ShowWindow(SDL_Window *w);
+int SDL_RenderSetIntegerScale(SDL_Renderer *r, SDL_bool enable);
 
 enum {
-	AUDIO_S16,
-	AUDIO_F32,
+	AUDIO_U8 = 1,
+	AUDIO_S8,
+	AUDIO_U16LSB,
+	AUDIO_S16LSB,
+	AUDIO_U16MSB,
+	AUDIO_S16MSB,
+	AUDIO_S32LSB,
+	AUDIO_S32MSB,
+	AUDIO_F32LSB,
+	AUDIO_F32MSB,
+	AUDIO_NUM_FORMATS,
+	/* show me that BIG endian device of yours */
+	AUDIO_U16 = AUDIO_U16LSB,
+	AUDIO_S16 = AUDIO_S16LSB,
+	AUDIO_S32 = AUDIO_S32LSB,
+	AUDIO_F32 = AUDIO_F32LSB,
+	AUDIO_U16SYS = AUDIO_U16,
+	AUDIO_S16SYS = AUDIO_S16,
+	AUDIO_S32SYS = AUDIO_S32,
+	AUDIO_F32SYS = AUDIO_F32,
 
+	SDL_AUDIO_ALLOW_FREQUENCY_CHANGE = 1<<0,
+	SDL_AUDIO_ALLOW_FORMAT_CHANGE = 1<<1,
+	SDL_AUDIO_ALLOW_CHANNELS_CHANGE = 1<<2,
+	SDL_AUDIO_ALLOW_SAMPLES_CHANGE = 1<<3,
 	SDL_AUDIO_ALLOW_ANY_CHANGE = ~0,
 
 	SDL_THREAD_PRIORITY_HIGH = 1,
@@ -428,6 +455,8 @@
 };
 
 #define SDL_HINT_RENDER_SCALE_QUALITY "SDL_HINT_RENDER_SCALE_QUALITY"
+#define SDL_HINT_WINDOWS_NO_CLOSE_ON_ALT_F4 "SDL_WINDOWS_NO_CLOSE_ON_ALT_F4"
+#define SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH "SDL_MOUSE_FOCUS_CLICKTHROUGH"
 
 struct SDL_AudioSpec {
 	void (*callback)(void *, Uint8 *, int);
--- a/libnpe_sdl2/audio.c
+++ b/libnpe_sdl2/audio.c
@@ -4,22 +4,24 @@
 	Aout = 2,
 	Arec,
 
-	Audiobufsz = 8192,
+	Audiosamples = 8192,
 };
 
 typedef struct Audiodev Audiodev;
 
 struct Audiodev {
-	QLock;
+	Lock;
 	void (*cb)(void *, Uint8 *, int);
 	void *userdata;
 	char *name;
 	Channel *wait;
+	Uint8 *buf;
+	int bufsz;
 	int paused;
 	int fd;
 	int pid;
+	int pidconv;
 	int mode;
-	Uint8 buf[Audiobufsz];
 };
 
 /* FIXME extra USB audio devices? */
@@ -30,6 +32,22 @@
 	[Arec] = {.name = "/dev/audio", .fd = -1, .pid = -1, .mode = OREAD},
 };
 
+static struct {
+	char *spec;
+	int ssz; /* samples size */
+}fmts[AUDIO_NUM_FORMATS] = {
+	[AUDIO_U8] = {"u8", 1},
+	[AUDIO_S8] = {"s8", 1},
+	[AUDIO_U16LSB] = {"u16", 2},
+	[AUDIO_S16LSB] = {"s16", 2},
+	[AUDIO_U16MSB] = {"U16", 2},
+	[AUDIO_S16MSB] = {"S16", 2},
+	[AUDIO_S32LSB] = {"s32", 4},
+	[AUDIO_S32MSB] = {"S32", 4},
+	[AUDIO_F32LSB] = {"f32", 4},
+	[AUDIO_F32MSB] = {"F32", -1}, /* FIXME big endian f32 not supported by pcmconv */
+};
+
 int
 SDL_GetNumAudioDevices(int iscapture)
 {
@@ -49,13 +67,13 @@
 void
 SDL_LockAudioDevice(SDL_AudioDeviceID id)
 {
-	qlock(&au[id]);
+	lock(&au[id]);
 }
 
 void
 SDL_UnlockAudioDevice(SDL_AudioDeviceID id)
 {
-	qunlock(&au[id]);
+	unlock(&au[id]);
 }
 
 static void
@@ -67,18 +85,22 @@
 	threadsetname("%s (%s)", a->name, a->mode == OREAD ? "out" : "in");
 
 	for(;;){
-		qlock(a);
-		if(a->paused)
-			memset(a->buf, 0, sizeof(a->buf));
+		if(a->mode == OREAD && readn(a->fd, a->buf, a->bufsz) != a->bufsz)
+			break;
+
+		lock(a);
+		if(a->mode == OWRITE && a->paused)
+			memset(a->buf, 0, a->bufsz);
 		else
-			a->cb(a->userdata, a->buf, sizeof(a->buf));
-		qunlock(a);
+			a->cb(a->userdata, a->buf, a->bufsz);
+		unlock(a);
 
-		if(write(a->fd, a->buf, sizeof(a->buf)) != sizeof(a->buf))
+		if(a->mode == OWRITE && write(a->fd, a->buf, a->bufsz) != a->bufsz)
 			break;
 	}
 
-	sendul(a->wait, 0);
+	(a->mode == OWRITE ? write : read)(a->fd, a->buf, 0);
+	chanclose(a->wait);
 
 	threadexits(nil);
 }
@@ -91,7 +113,7 @@
 	a = &au[id];
 	if(a->paused && !pause){
 		if(a->pid < 0)
-			a->pid = proccreate(audiothread, a, mainstacksize);
+			a->pid = proccreate(audiothread, a, 4096);
 		a->paused = 0;
 	}else if(!a->paused && pause){
 		a->paused = 1;
@@ -98,39 +120,107 @@
 	}
 }
 
+static int
+convspec(SDL_AudioSpec *s, char *spec, int n)
+{
+	int ssz;
+
+	ssz = -1;
+	if(s->format < 0 || s->format >= nelem(fmts))
+		werrstr("invalid audio format: #%d", s->format);
+	else if(fmts[s->format].ssz < 1)
+		werrstr("unsupported audio format: #%d", s->format);
+	else if(s->channels < 1)
+		werrstr("invalid number of channels: %d", s->channels);
+	else if(s->freq < 1)
+		werrstr("invalid sampling rate: %d", s->freq);
+	else if(snprint(spec, n, "%sc%dr%d", fmts[s->format].spec, s->channels, s->freq) >= n)
+		werrstr("audio spec does not fit");
+	else
+		ssz = fmts[s->format].ssz;
+
+	return ssz;
+}
+
 SDL_AudioDeviceID
 SDL_OpenAudioDevice(char *dev, int rec, SDL_AudioSpec *want, SDL_AudioSpec *have, u32int change)
 {
-	Audiodev *a;
 	SDL_AudioDeviceID id;
+	int p[2], ssz, fd;
+	char spec[16];
+	Audiodev *a;
 
 	/* 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;
+	if(have == nil)
+		have = want;
+	*have = *want;
+	if(have->freq < 44100 && (change & SDL_AUDIO_ALLOW_FREQUENCY_CHANGE) != 0)
+		have->freq = 44100;
+	if(have->format <= 0 || have->format >= nelem(fmts) || fmts[have->format].ssz < 1 && (change & SDL_AUDIO_ALLOW_FORMAT_CHANGE) != 0)
+		have->format = AUDIO_S16;
+	if(have->channels < 1 && (change & SDL_AUDIO_ALLOW_CHANNELS_CHANGE) != 0)
+		have->channels = 2;
+	if(have->samples < 2 || (have->samples & (have->samples-1)) != 0){
+		if(change & SDL_AUDIO_ALLOW_SAMPLES_CHANGE)
+			have->samples = Audiosamples;
+		else{
+			werrstr("invalid number of samples: %d", have->samples);
+			goto err;
+		}
 	}
 
-	a->userdata = want->userdata;
-	a->cb = want->callback;
-	a->paused = 1;
+	if((ssz = convspec(have, spec, sizeof(spec))) < 1)
+		goto err;
+
+	a->userdata = have->userdata;
+	a->cb = have->callback;
 	a->wait = chancreate(sizeof(ulong), 0);
+	a->bufsz = have->samples * ssz * have->channels;
+	a->buf = malloc(a->bufsz);
+	if(a->wait == nil || a->buf == nil){
+		werrstr("memory");
+		goto err;
+	}
 
+	a->paused = 1;
+	a->pidconv = -1;
+	if(have->freq != 44100 || have->format != AUDIO_S16 || have->channels != 2){
+		pipe(p);
+		if((a->pidconv = rfork(RFPROC|RFFDG|RFNOTEG|RFCENVG)) == 0){
+			if((fd = open("/dev/audio", a->mode)) < 0)
+				exits("%r");
+			dup(fd, rec ? 0 : 1); close(fd);
+			dup(p[0], rec ? 1 : 0); close(p[0]);
+			close(p[1]);
+			//close(2);
+			setfcr(FPPDBL|FPRNR|FPINVAL|FPZDIV|FPOVFL);
+			if(execl("/bin/audio/pcmconv", "pcmconv", rec ? "-o" : "-i", spec, nil) != 0)
+				exits("%r");
+		}else if(a->pidconv < 0){
+			werrstr("pcmconv: %r");
+			goto err;
+		}
+		a->fd = p[1];
+		close(p[0]);
+	}else if(a->fd < 0 && (a->fd = open("/dev/audio", a->mode|OCEXEC)) < 0)
+		goto err;
+
 	return id;
+err:
+	werrstr("SDL_OpenAudioDevice: %r");
+	close(a->fd);
+	a->fd = -1;
+	free(a->buf);
+	a->buf = nil;
+	chanfree(a->wait);
+	a->wait = nil;
+
+	return 0;
 }
 
 void
@@ -137,17 +227,32 @@
 SDL_CloseAudioDevice(SDL_AudioDeviceID id)
 {
 	Audiodev *a;
+	Waitmsg *w;
+	int pid;
 
 	a = &au[id];
 
-	qlock(a);
-	close(a->fd);
-	a->fd = -1;
-	a->pid = -1;
-	a->userdata = nil;
-	a->cb = nil;
-	qunlock(a);
+	if(a->fd < 0)
+		return;
 
-	recvul(a->wait);
+	lock(a);
+		close(a->fd);
+	unlock(a);
+
+	if(a->pid >= 0)
+		recvul(a->wait);
 	chanfree(a->wait);
+
+	free(a->buf);
+	a->fd = -1;
+	a->pid = -1;
+again:
+	if(a->pidconv >= 0 && (w = wait()) != nil){
+		if(w->msg[0])
+			fprint(2, "SDL_CloseAudioDevice: %s: %s\n", a->name, w->msg);
+		pid = w->pid;
+		free(w);
+		if(pid != a->pidconv)
+			goto again;
+	}
 }
--- a/libnpe_sdl2/sdl2.c
+++ b/libnpe_sdl2/sdl2.c
@@ -12,7 +12,7 @@
 
 enum {
 	/* FIXME missing plumber→dropfile */
-	Ckey = 0,
+	Ckey,
 	Ckeytype,
 	Cmouse,
 	Cresize,
@@ -21,6 +21,8 @@
 	Rdown = 0,
 	Rup,
 	Rrepeat,
+
+	Altf4noclose = 1<<0,
 };
 
 struct SDL_Window {
@@ -75,6 +77,7 @@
 static int textinput;
 static char basepath[PATH_MAX];
 static u32int renddrawcol = DBlack;
+static int hints;
 
 static Cursor nocursor = {
 	{0, 0},
@@ -733,7 +736,7 @@
 				break;
 			e->type = SDL_TEXTINPUT;
 			e->text.text[runetochar(e->text.text, &rune)] = 0;
-		}else if((kmod & KMOD_LALT) != 0 && rune == Kdel){ /* alt+del = quit */
+		}else if((hints & Altf4noclose) == 0 && (kmod & KMOD_LALT) != 0 && rune == (KF|4)){
 			e->type = SDL_QUIT;
 			return 1;
 		}else if(textinput && t == Rdown){
@@ -856,6 +859,14 @@
 	*scaleY = 1.0;
 }
 
+int
+SDL_RenderSetIntegerScale(SDL_Renderer *, SDL_bool enable)
+{
+	/* FIXME */
+	USED(enable);
+	return 0;
+}
+
 void
 SDL_GetWindowSize(SDL_Window *, int *w, int *h)
 {
@@ -1155,8 +1166,10 @@
 SDL_SetHint(char *name, char *value)
 {
 	/* FIXME anyone cares about name="SDL_RENDER_SCALE_QUALITY" value="(best|nearest)"? */
-	USED(name);
-	USED(value);
+	if(strcmp(name, SDL_HINT_WINDOWS_NO_CLOSE_ON_ALT_F4) == 0){
+		hints = (hints & ~Altf4noclose) | (atoi(value) ? Altf4noclose : 0);
+		return SDL_TRUE;
+	}
 
 	return SDL_FALSE;
 }