shithub: qk1

Download patch

ref: 0d4b3504cc557b5ded1180e2fd00cd210c1024d7
parent: 99d14fa43f2ee64ebf542ab5b7e87e77939c0cdb
author: Konstantinn Bonnet <qu7uux@gmail.com>
date: Thu Feb 26 18:10:02 EST 2015

fix buggy linux event handling bullshit

this hopefully fully resolves the issues with abnormal exits when entering a new
game while already connected, or loading one, etc.
afaict, Sys_SendKeyEvents() is called from _Host_Frame(); it in turn calls Key_Event,
which, and since we're in the SP menu, enter M_Keydown() → M_SinglePlayer_Key()
now, to confirm entering a new game, SCR_ModalMessage is called, but it in turn calls
Sys_SendKeyEvents in a loop, which calls Key_Event → M_Keydown → M_SinglePlayerKey, etc.
somewhere before or during this mess, the key queue gets horribly disfigured, and the
tail and head are either not synced correctly or not pointing to the correct node,
causing Sys_SendKeyEvents() to go through almost the whole queue, and shit hits the fan.
or race conditions with kproc, or something.
Cbuf overflows happen when you mash keys before during the SCR_ModalMessage loop, and
then pressing 'y', thus, for each key press, adding the corresponding command, then
the ones added from M_SinglePlayer_Key. if you haven't mashed too many keys, Cbuf is
big enough to contain all keys. game is reset several times because of this.
because of the crazy loop, the stack grows larger and larger, then R_RenderView() exits
with 'called without enough stack'.
this also happens in the original release on linux/x11, on which this code is based.
a quick workaround was to keyq_tail = keyq_head = 0 after Sys_SendKeyEvents' loop, but
it had race conditions with kproc or whatever and some keys were sometimes ignored.
instead, rewrite the shit and use a channel like the gods intended. if I understood the
issue correctly, that's the end of that.

--- a/README
+++ b/README
@@ -48,11 +48,8 @@
 -----------
 	- compile with PARANOID -> MSG_WriteByte complains about range error and
 	  exits. no idea why.
-	- new game while already ingame -> front falls off
-		. most of the time: exits with R_RenderView: called without enough stack
-		. sometimes: goes batshit on the console (->press y again and quit normally, etc.)
 	- leave running for a couple of hours -> sound dies and eventually, invalid
-	write/reads -> crash
+	  write/reads -> crash
 	- compile with BAN_TEST -> net_dgrm build fails
 	- sprites/particles not scaled correctly on high resolutions
 	- m_windowed 1: can still look around in frozen world when paused
--- a/in_9.c
+++ b/in_9.c
@@ -7,6 +7,15 @@
 #include <keyboard.h>
 #include "quakedef.h"
 
+typedef struct Kev Kev;
+struct Kev{
+	int key;
+	int down;
+};
+enum{
+	Nbuf	= 64
+};
+
 cvar_t m_windowed = {"m_windowed","0", true};
 cvar_t m_filter = {"m_filter","0", true};
 float oldm_windowed;
@@ -14,15 +23,9 @@
 int mouse_buttons = 3;
 int mouse_buttonstate, mouse_oldbuttonstate;
 float mouse_x, mouse_y, old_mouse_x, old_mouse_y;
-
-struct {
-	int key;
-	int down;
-} keyq[64];
-int keyq_head = 0;
-int keyq_tail = 0;
 int mouseactive;
 int ktid = -1, mtid = -1;
+Channel *kchan;
 
 /* vid_9.c */
 extern int config_notify;
@@ -32,6 +35,9 @@
 
 void Sys_SendKeyEvents(void)
 {
+	Kev ev;
+	int r;
+
 	/* FIXME: sloppy */
 	if(oldm_windowed != m_windowed.value){
 		oldm_windowed = m_windowed.value;
@@ -41,10 +47,10 @@
 			IN_Grabm(1);
 	}
 
-	while(keyq_head != keyq_tail){
-		Key_Event(keyq[keyq_tail].key, keyq[keyq_tail].down);
-		keyq_tail = keyq_tail+1 & 63;
-	}
+	while((r = nbrecv(kchan, &ev)) > 0)
+		Key_Event(ev.key, ev.down);
+	if(r < 0)
+		Con_Printf("Sys_SendKeyEvents:nbrecv: %r\n");
 }
 
 void IN_Commands (void)
@@ -148,6 +154,7 @@
 	int n, k, fd;
 	char buf[128], kdown[128] = {0}, *s;
 	Rune r;
+	Kev ev;
 
 	if((fd = open("/dev/kbd", OREAD)) < 0)
 		sysfatal("open /dev/kbd: %r");
@@ -164,9 +171,10 @@
 				s += chartorune(&r, s);
 				if(utfrune(kdown+1, r) == nil){
 					if(k = runetokey(r)){
-						keyq[keyq_head].key = k;
-						keyq[keyq_head].down = true;
-						keyq_head = keyq_head+1 & 63;
+						ev.key = k;
+						ev.down = true;
+						if(nbsend(kchan, &ev) < 0)
+							Con_Printf("kproc:nbsend: %r\n");
 					}
 				}
 			}
@@ -177,9 +185,10 @@
 				s += chartorune(&r, s);
 				if(utfrune(buf+1, r) == nil){
 					if(k = runetokey(r)){
-						keyq[keyq_head].key = k;
-						keyq[keyq_head].down = false;
-						keyq_head = keyq_head+1 & 63;
+						ev.key = k;
+						ev.down = false;
+						if(nbsend(kchan, &ev) < 0)
+							Con_Printf("kproc:nbsend: %r\n");
 					}
 				}
 			}
@@ -262,6 +271,8 @@
 		threadkill(mtid);
 		mtid = -1;
 	}
+	chanclose(kchan);
+	chanfree(kchan);
 	mouse_avail = 0;
 }
 
@@ -277,6 +288,7 @@
 	Cvar_RegisterVariable(&m_windowed);
 	Cvar_RegisterVariable(&m_filter);
 	notify(sucks);
+	kchan = chancreate(sizeof(Kev), Nbuf);
 	if((ktid = proccreate(kproc, nil, mainstacksize)) < 0)
 		sysfatal("proccreate kproc: %r");
 	if(COM_CheckParm("-nomouse"))
--- a/snd_9.c
+++ b/snd_9.c
@@ -103,7 +103,7 @@
 void SNDDMA_Submit(void)
 {
 	if(nbsend(schan, nil) < 0){
-		Con_Printf("SNDDMA_Submit:send: %r\n");
+		Con_Printf("SNDDMA_Submit:nbsend: %r\n");
 		SNDDMA_Shutdown();
 	}
 }