about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorMagnus Auvinen <magnus.auvinen@gmail.com>2008-03-21 16:45:26 +0000
committerMagnus Auvinen <magnus.auvinen@gmail.com>2008-03-21 16:45:26 +0000
commitfaa13fe70371834c3e7e234875d32bb95d278c54 (patch)
tree2a1e52d2fde94993a9ea787dadeaec8416912f49 /src
parenta339dcf731278656a57aceec02696b752e85f7b6 (diff)
downloadzcatch-faa13fe70371834c3e7e234875d32bb95d278c54.tar.gz
zcatch-faa13fe70371834c3e7e234875d32bb95d278c54.zip
fixed input problems. made better input in console and chat
Diffstat (limited to 'src')
-rw-r--r--src/engine/client/ec_gfx.c232
-rw-r--r--src/engine/e_if_gfx.h28
-rw-r--r--src/game/client/gc_client.cpp217
-rw-r--r--src/game/client/gc_client.h47
-rw-r--r--src/game/client/gc_console.cpp161
-rw-r--r--src/game/client/gc_console.h9
-rw-r--r--src/game/client/gc_hooks.cpp22
-rw-r--r--src/game/client/gc_menu.cpp23
-rw-r--r--src/game/client/gc_ui.cpp4
9 files changed, 455 insertions, 288 deletions
diff --git a/src/engine/client/ec_gfx.c b/src/engine/client/ec_gfx.c
index c1bf4112..2de9ca23 100644
--- a/src/engine/client/ec_gfx.c
+++ b/src/engine/client/ec_gfx.c
@@ -935,129 +935,159 @@ void gfx_text_set_default_font(void *font)
 	default_font_set = (FONT_SET *)font;
 }
 
-float gfx_text_raw(void *font_set_v, float x, float y, float size, const char *text, int length)
+
+void gfx_text_set_cursor(TEXT_CURSOR *cursor, float x, float y, float font_size, int flags)
 {
-    FONT_SET *font_set = font_set_v;
-    float fake_to_screen_x = (screen_width/(screen_x1-screen_x0));
-    float fake_to_screen_y = (screen_height/(screen_y1-screen_y0));
-
-    FONT *font;
-    int actual_size;
-    int i;
-    float draw_x, draw_y;
-    
-    /* to correct coords, convert to screen coords, round, and convert back */
-    int actual_x = x * fake_to_screen_x;
-    int actual_y = y * fake_to_screen_y;
-    x = actual_x / fake_to_screen_x;
-    y = actual_y / fake_to_screen_y;
-
-    /* same with size */
-    actual_size = size * fake_to_screen_y;
-    size = actual_size / fake_to_screen_y;
-
-    if(!font_set)
-    	font_set = default_font_set;
-
-    font = font_set_pick(font_set, actual_size);
-
-    if (length < 0)
-        length = strlen(text);
-
-    for (i = 0; i < 2; i++)
-    {
-        const unsigned char *c = (unsigned char *)text;
-        int to_render = length;
-        draw_x = x;
-        draw_y = y;
-
-        if (i == 0)
-            gfx_texture_set(font->outline_texture);
-        else
-            gfx_texture_set(font->text_texture);
-
-        gfx_quads_begin();
-        if (i == 0)
-            gfx_setcolor(0.0f, 0.0f, 0.0f, 0.3f*text_a);
-        else
-            gfx_setcolor(text_r, text_g, text_b, text_a);
-        
-        while (to_render--)
-        {
-            float tex_x0, tex_y0, tex_x1, tex_y1;
-            float width, height;
-            float x_offset, y_offset, x_advance;
-
-            float advance;
-            
-            if(*c == '\n')
-            {
-            	draw_x = x;
-            	draw_y += size; /* is this correct? -kma */
-            	c++;
-            	continue;
-			}
+	mem_zero(cursor, sizeof(*cursor));
+	cursor->font_size = font_size;
+	cursor->start_x = x;
+	cursor->start_y = y;
+	cursor->x = x;
+	cursor->y = y;
+	cursor->line_count = 1;
+	cursor->line_width = -1;
+	cursor->flags = flags;
+}
 
-            font_character_info(font, *c, &tex_x0, &tex_y0, &tex_x1, &tex_y1, &width, &height, &x_offset, &y_offset, &x_advance);
+void gfx_text_ex(TEXT_CURSOR *cursor, const char *text, int length)
+{
+	FONT_SET *font_set = cursor->font_set;
+	float fake_to_screen_x = (screen_width/(screen_x1-screen_x0));
+	float fake_to_screen_y = (screen_height/(screen_y1-screen_y0));
 
-            gfx_quads_setsubset(tex_x0, tex_y0, tex_x1, tex_y1);
+	FONT *font;
+	int actual_size;
+	int i;
+	float draw_x, draw_y;
+	const char *end;
 
-            gfx_quads_drawTL(draw_x+x_offset*size, draw_y+y_offset*size, width*size, height*size);
+	float size = cursor->font_size;
 
-            advance = x_advance + font_kerning(font, *c, *(c+1));
+	/* to correct coords, convert to screen coords, round, and convert back */
+	int actual_x = cursor->x * fake_to_screen_x;
+	int actual_y = cursor->y * fake_to_screen_y;
+	cursor->x = actual_x / fake_to_screen_x;
+	cursor->y = actual_y / fake_to_screen_y;
 
-            draw_x += advance*size;
+	/* same with size */
+	actual_size = size * fake_to_screen_y;
+	size = actual_size / fake_to_screen_y;
 
-            c++;
-        }
+	if(!font_set)
+		font_set = default_font_set;
 
-        gfx_quads_end();
-    }
+	font = font_set_pick(font_set, actual_size);
 
-    return draw_x;
-}
+	if (length < 0)
+		length = strlen(text);
+		
+	end = text + length;
 
-int gfx_text(void *font_set_v, float x, float y, float size, const char *text, int max_width)
-{
-	int lines = 1;
-	if(max_width == -1)
-		gfx_text_raw(font_set_v, x, y, size, text, -1);
-	else
+	/* if we don't want to render, we can just skip the first outline pass */
+	i = 1;
+	if(cursor->flags&TEXTFLAG_RENDER)
+		i = 0;
+
+	for (i = 0; i < 2; i++)
 	{
-		float startx = x;
-		while(*text)
+		const unsigned char *current = (unsigned char *)text;
+		int to_render = length;
+		draw_x = cursor->x;
+		draw_y = cursor->y;
+
+		if(cursor->flags&TEXTFLAG_RENDER)
+		{
+			if (i == 0)
+				gfx_texture_set(font->outline_texture);
+			else
+				gfx_texture_set(font->text_texture);
+
+			gfx_quads_begin();
+			if (i == 0)
+				gfx_setcolor(0.0f, 0.0f, 0.0f, 0.3f*text_a);
+			else
+				gfx_setcolor(text_r, text_g, text_b, text_a);
+		}
+
+		while(to_render > 0)
 		{
-			int wlen = word_length(text);
-			float w = gfx_text_width(font_set_v, size, text, wlen);
-			if(x+w-startx > max_width)
+			int this_batch = to_render;
+			if(cursor->line_width > 0)
 			{
-				lines++;
-				y += size;
-				x = startx;
+				int wlen = word_length((char *)current);
+				TEXT_CURSOR compare = *cursor;
+				compare.x = draw_x;
+				compare.y = draw_y;
+				compare.flags &= ~TEXTFLAG_RENDER;
+				compare.line_width = -1;
+				gfx_text_ex(&compare, text, wlen);
+				
+				if(compare.x-cursor->start_x > cursor->line_width)
+				{
+					draw_x = cursor->start_x;
+					draw_y += size; /* is this correct? -kma */
+				}
+				
+				this_batch = wlen;
 			}
 			
-			x = gfx_text_raw(font_set_v, x, y, size, text, wlen);
+			if((const char *)current+this_batch > end)
+				this_batch = (const char *)end-(const char *)current;
 			
-			text += wlen;
+			to_render -= this_batch;
+
+			while(this_batch > 0)
+			{
+				float tex_x0, tex_y0, tex_x1, tex_y1;
+				float width, height;
+				float x_offset, y_offset, x_advance;
+				float advance;
+
+				if(*current == '\n')
+				{
+					draw_x = cursor->start_x;
+					draw_y += size; /* is this correct? -kma */
+					current++;
+					continue;
+				}
+
+				font_character_info(font, *current, &tex_x0, &tex_y0, &tex_x1, &tex_y1, &width, &height, &x_offset, &y_offset, &x_advance);
+
+				if(cursor->flags&TEXTFLAG_RENDER)
+				{
+					gfx_quads_setsubset(tex_x0, tex_y0, tex_x1, tex_y1);
+					gfx_quads_drawTL(draw_x+x_offset*size, draw_y+y_offset*size, width*size, height*size);
+				}
+
+				advance = x_advance + font_kerning(font, *current, *(current+1));
+				draw_x += advance*size;
+				current++;
+				this_batch--;
+			}
 		}
+
+		if(cursor->flags&TEXTFLAG_RENDER)
+			gfx_quads_end();
 	}
-    gfx_text_raw(font_set_v, x, y, size, text, -1);
-    return lines;
+
+	cursor->x = draw_x;
+	cursor->y = draw_y;
+}
+
+void gfx_text(void *font_set_v, float x, float y, float size, const char *text, int max_width)
+{
+	TEXT_CURSOR cursor;
+	gfx_text_set_cursor(&cursor, x, y, size, TEXTFLAG_RENDER);
+	cursor.line_width = max_width;
+	gfx_text_ex(&cursor, text, -1);
 }
 
 float gfx_text_width(void *font_set_v, float size, const char *text, int length)
 {
-    FONT_SET *font_set = font_set_v;
-    FONT *font;
-    float fake_to_screen_y = (screen_height/(screen_y1-screen_y0));
-    int actual_size = size * fake_to_screen_y;
-    size = actual_size / fake_to_screen_y;
-    
-    if(!font_set)
-    	font_set = default_font_set;
-
-    font = font_set_pick(font_set, actual_size);
-    return font_text_width(font, text, size, length);
+	TEXT_CURSOR cursor;
+	gfx_text_set_cursor(&cursor, 0, 0, size, 0);
+	gfx_text_ex(&cursor, text, length);
+	return cursor.x;
 }
 
 void gfx_text_color(float r, float g, float b, float a)
diff --git a/src/engine/e_if_gfx.h b/src/engine/e_if_gfx.h
index 71a9628e..265d41d3 100644
--- a/src/engine/e_if_gfx.h
+++ b/src/engine/e_if_gfx.h
@@ -247,7 +247,7 @@ void gfx_lines_end();
 	See Also:
 		<other_func>
 */
-int gfx_text(void *font, float x, float y, float size, const char *text, int max_width);
+void gfx_text(void *font, float x, float y, float size, const char *text, int max_width);
 
 /*
 	Function: gfx_text_width
@@ -575,4 +575,30 @@ void gfx_clip_enable(int x, int y, int w, int h);
 */
 void gfx_clip_disable();
 
+
+enum
+{
+	TEXTFLAG_RENDER=1,
+	TEXTFLAG_ALLOW_NEWLINE=2
+};
+
+typedef struct
+{
+	int flags;
+	int line_count;
+	
+	float start_x;
+	float start_y;
+	float line_width;
+	float x, y;
+	void *font_set;
+	float font_size;
+} TEXT_CURSOR;
+
+void gfx_text_set_cursor(TEXT_CURSOR *cursor, float x, float y, float font_size, int flags);
+void gfx_text_ex(TEXT_CURSOR *cursor, const char *text, int length);
+
+
+
+
 #endif
diff --git a/src/game/client/gc_client.cpp b/src/game/client/gc_client.cpp
index 80e7bd40..552ca252 100644
--- a/src/game/client/gc_client.cpp
+++ b/src/game/client/gc_client.cpp
@@ -197,10 +197,26 @@ void render_damage_indicators()
 	dmgind.render();
 }
 
-static char chat_input[512];
-static unsigned chat_input_len;
+static line_input chat_input;
 static const int chat_max_lines = 10;
 
+bool chat_input_handle(INPUT_EVENT e, void *user_data)
+{
+	if(chat_mode == CHATMODE_NONE)
+		return false;
+
+	if(e.flags&INPFLAG_PRESS && (e.key == KEY_ENTER || e.key == KEY_KP_ENTER))
+	{
+		if(chat_input.get_string()[0])
+			chat_say(chat_mode == CHATMODE_ALL ? 0 : 1, chat_input.get_string());
+		chat_mode = CHATMODE_NONE;
+	}
+	else
+		chat_input.process_input(e);
+	
+	return true;
+}
+
 struct chatline
 {
 	int tick;
@@ -263,6 +279,101 @@ int killmsg_current = 0;
 
 //bool add_trail = false;
 
+line_input::line_input()
+{
+	clear();
+}
+
+void line_input::clear()
+{
+	mem_zero(str, sizeof(str));
+	len = 0;
+	cursor_pos = 0;
+}
+
+void line_input::set(const char *string)
+{
+	str_copy(str, string, sizeof(str));
+	len = strlen(str);
+	cursor_pos = len;
+}
+
+void line_input::process_input(INPUT_EVENT e)
+{
+	if(cursor_pos > len)
+		cursor_pos = len;
+	
+	char c = e.ch;
+	int k = e.key;
+	
+	if (!(c >= 0 && c < 32))
+	{
+		if (len < sizeof(str) - 1 && cursor_pos < sizeof(str) - 1)
+		{
+			memmove(str + cursor_pos + 1, str + cursor_pos, len - cursor_pos + 1);
+			str[cursor_pos] = c;
+			cursor_pos++;
+			len++;
+		}
+	}
+	
+	if(e.flags&INPFLAG_PRESS)
+	{
+		if (k == KEY_BACKSPACE && cursor_pos > 0)
+		{
+			memmove(str + cursor_pos - 1, str + cursor_pos, len - cursor_pos + 1);
+			cursor_pos--;
+			len--;
+		}
+		else if (k == KEY_DEL && cursor_pos < len)
+		{
+			memmove(str + cursor_pos, str + cursor_pos + 1, len - cursor_pos);
+			len--;
+		}
+		else if (k == KEY_LEFT && cursor_pos > 0)
+			cursor_pos--;
+		else if (k == KEY_RIGHT && cursor_pos < len)
+			cursor_pos++;
+		else if (k == KEY_HOME)
+			cursor_pos = 0;
+		else if (k == KEY_END)
+			cursor_pos = len;
+	}
+}
+
+input_stack_handler::input_stack_handler()
+{
+	num_handlers = 0;
+}
+
+void input_stack_handler::add_handler(callback cb, void *user)
+{
+	user_data[num_handlers] = user;
+	handlers[num_handlers++] = cb;
+}
+
+void input_stack_handler::dispatch_input()
+{
+	for(int i = 0; i < inp_num_events(); i++)
+	{
+		INPUT_EVENT e = inp_get_event(i);
+		
+		for(int h = 0; h < num_handlers; h++)
+		{
+			if(handlers[h](e, user_data[h]))
+			{
+				//dbg_msg("", "%d char=%d key=%d flags=%d", h, e.ch, e.key, e.flags);
+				break;
+			}
+		}
+	}
+	
+	inp_clear_events();
+}
+
+
+input_stack_handler input_stack;
+
 extern int render_popup(const char *caption, const char *text, const char *button_text);
 
 void process_events(int snaptype)
@@ -738,12 +849,8 @@ void chat_enable_mode(int team)
 			chat_mode = CHATMODE_TEAM;
 		else
 			chat_mode = CHATMODE_ALL;
-			
-		mem_zero(chat_input, sizeof(chat_input));
-		chat_input_len = 0;
 		
-		// make sure that we don't trigger something weird
-		inp_clear_events();
+		chat_input.clear();
 	}
 }
 
@@ -804,51 +911,6 @@ void render_game()
 		menu_was_active = false;
 	}
 
-	// handle chat input
-	if (!menu_active)
-	{
-		if(chat_mode != CHATMODE_NONE)
-		{
-			for(int i = 0; i < inp_num_events(); i++)
-			{
-				INPUT_EVENT e = inp_get_event(i);
-	
-				if(e.ch >= 32)
-				{
-					if (chat_input_len < sizeof(chat_input) - 1)
-					{
-						chat_input[chat_input_len] = e.ch;
-						chat_input[chat_input_len+1] = 0;
-						chat_input_len++;
-					}
-				}
-
-				if((e.key == KEY_ENTER || e.key == KEY_KP_ENTER) && (e.flags&INPFLAG_PRESS))
-				{
-					// send message
-					if(chat_input_len)
-						chat_say(chat_mode == CHATMODE_ALL ? 0 : 1, chat_input);
-
-					chat_mode = CHATMODE_NONE;
-					break;					
-				}
-				
-				if(e.key == KEY_BACKSPACE && (e.flags&INPFLAG_PRESS))
-				{
-					if(chat_input_len > 0)
-					{
-						chat_input[chat_input_len-1] = 0;
-						chat_input_len--;
-					}
-				}
-			}
-		}
-	}
-
-	if (!menu_active)
-		inp_clear_events();
-
-
 	//
 	float camera_max_distance = 250.0f;
 	float deadzone = config.cl_mouse_deadzone;
@@ -1191,19 +1253,24 @@ void render_game()
 		gfx_mapscreen(0,0,300*gfx_screenaspect(),300);
 		float x = 10.0f;
 		float y = 300.0f-30.0f;
-		float starty = -1;
 		if(chat_mode != CHATMODE_NONE)
 		{
 			// render chat input
-			char buf[sizeof(chat_input)+16];
+			TEXT_CURSOR cursor;
+			gfx_text_set_cursor(&cursor, x, y, 8.0f, TEXTFLAG_RENDER);
+			cursor.line_width = 300.0f;
+			
 			if(chat_mode == CHATMODE_ALL)
-				str_format(buf, sizeof(buf), "All: %s_", chat_input);
+				gfx_text_ex(&cursor, "All: ", -1);
 			else if(chat_mode == CHATMODE_TEAM)
-				str_format(buf, sizeof(buf), "Team: %s_", chat_input);
+				gfx_text_ex(&cursor, "Team: ", -1);
 			else
-				str_format(buf, sizeof(buf), "Chat: %s_", chat_input);
-			gfx_text(0, x, y, 8.0f, buf, 380);
-			starty = y;
+				gfx_text_ex(&cursor, "Chat: ", -1);
+				
+			gfx_text_ex(&cursor, chat_input.get_string(), chat_input.cursor_offset());
+			TEXT_CURSOR marker = cursor;
+			gfx_text_ex(&marker, "|", -1);
+			gfx_text_ex(&cursor, chat_input.get_string()+chat_input.cursor_offset(), -1);
 		}
 
 		y -= 8;
@@ -1216,16 +1283,19 @@ void render_game()
 				break;
 
 			float begin = x;
-
-
 			float fontsize = 8.0f;
 			
-			// turn of alpha so we can render once and just get the number of lines
-			// TODO: this is ugly, but have to do for now
-			gfx_text_color(1,1,1,0);
-			int lines = gfx_text(0, begin, y, fontsize, chat_lines[r].text, 300);
-
-			y -= fontsize * (lines);
+			// get the y offset
+			TEXT_CURSOR cursor;
+			gfx_text_set_cursor(&cursor, begin, 0, fontsize, 0);
+			cursor.line_width = 300.0f;
+			gfx_text_ex(&cursor, chat_lines[r].name, -1);
+			gfx_text_ex(&cursor, chat_lines[r].text, -1);
+			y -= cursor.y + cursor.font_size;
+
+			// reset the cursor
+			gfx_text_set_cursor(&cursor, begin, y, fontsize, TEXTFLAG_RENDER);
+			cursor.line_width = 300.0f;
 
 			// render name
 			gfx_text_color(0.8f,0.8f,0.8f,1);
@@ -1240,22 +1310,17 @@ void render_game()
 			else if(chat_lines[r].name_color == -1)
 				gfx_text_color(0.75f,0.5f,0.75f, 1); // spectator
 				
-			// render line
-			
-			//int lines = int(gfx_text_width(0, fontsize, chat_lines[r].text, -1)) / 300 + 1;
-
-			
-			gfx_text(0, begin, y, fontsize, chat_lines[r].name, -1);
-			begin += gfx_text_width(0, fontsize, chat_lines[r].name, -1);
-
+			// render name
+			gfx_text_ex(&cursor, chat_lines[r].name, -1);
 
+			// render line
 			gfx_text_color(1,1,1,1);
 			if(chat_lines[r].client_id == -1)
 				gfx_text_color(1,1,0.5f,1); // system
 			else if(chat_lines[r].team)
 				gfx_text_color(0.65f,1,0.65f,1); // team message
 
-			gfx_text(0, begin, y, fontsize, chat_lines[r].text, 300);
+			gfx_text_ex(&cursor, chat_lines[r].text, -1);
 		}
 
 		gfx_text_color(1,1,1,1);
diff --git a/src/game/client/gc_client.h b/src/game/client/gc_client.h
index 7ba8a32a..6e63ce6f 100644
--- a/src/game/client/gc_client.h
+++ b/src/game/client/gc_client.h
@@ -82,6 +82,53 @@ enum
 extern int chat_mode;
 void chat_add_line(int client_id, int team, const char *line);
 void chat_reset();
+bool chat_input_handle(INPUT_EVENT e, void *user_data);
+
+// line input helter
+class line_input
+{
+	char str[256];
+	unsigned len;
+	unsigned cursor_pos;
+public:
+	class callback
+	{
+	public:
+		virtual ~callback() {}
+		virtual bool event(INPUT_EVENT e) = 0;
+	};
+
+	line_input();
+	void clear();
+	void process_input(INPUT_EVENT e);
+	void set(const char *string);
+	const char *get_string() const { return str; }
+	int get_length() const { return len; }
+	unsigned cursor_offset() const { return cursor_pos; }
+};
+
+class input_stack_handler
+{
+public:
+	typedef bool (*callback)(INPUT_EVENT e, void *user);
+	
+	input_stack_handler();
+	void add_handler(callback cb, void *user_data);
+	void dispatch_input();
+	
+private:
+	enum
+	{
+		MAX_HANDLERS=16
+	};
+	
+	callback handlers[MAX_HANDLERS];
+	void *user_data[MAX_HANDLERS];
+	int num_handlers;
+};
+
+extern input_stack_handler input_stack;
+
 
 extern int emoticon_selector_active; // TODO: ugly
 extern int scoreboard_active; // TODO: ugly
diff --git a/src/game/client/gc_console.cpp b/src/game/client/gc_console.cpp
index ca1e20cb..19ad6d63 100644
--- a/src/game/client/gc_console.cpp
+++ b/src/game/client/gc_console.cpp
@@ -36,18 +36,14 @@ public:
 	char backlog_data[65536];
 	RINGBUFFER *backlog;
 
-	unsigned int input_len;
-	char input[256];
+
+	line_input input;
 	
 	int type;
 	
 public:
 	CONSOLE(int t)
 	{
-		// clear input
-		input_len = 0;
-		mem_zero(input, sizeof(input));
-	
 		// init ringbuffers
 		history = ringbuf_init(history_data, sizeof(history_data));
 		backlog = ringbuf_init(backlog_data, sizeof(backlog_data));
@@ -72,43 +68,23 @@ public:
 	
 	void handle_event(INPUT_EVENT e)
 	{
-		if (e.ch >= 32)
-		{
-			if (input_len < sizeof(input) - 1)
-			{
-				input[input_len] = e.ch;
-				input[input_len+1] = 0;
-				input_len++;
-
-				history_entry = 0x0;
-			}
-		}
-
+		bool handled = false;
+		
 		if(e.flags&INPFLAG_PRESS)
 		{
-			if(e.key == KEY_BACKSPACE)
-			{
-				if(input_len > 0)
-				{
-					input[input_len-1] = 0;
-					input_len--;
-
-					history_entry = 0x0;
-				}
-			}
-			else if(e.key == KEY_ENTER || e.key == KEY_KP_ENTER)
+			if(e.key == KEY_ENTER || e.key == KEY_KP_ENTER)
 			{
-				if (input_len)
+				if(input.get_string()[0])
 				{
-					char *entry = (char *)ringbuf_allocate(history, input_len+1);
-					mem_copy(entry, input, input_len+1);
+					char *entry = (char *)ringbuf_allocate(history, input.get_length()+1);
+					mem_copy(entry, input.get_string(), input.get_length()+1);
 					
-					execute_line(input);
-					input[0] = 0;
-					input_len = 0;
-
+					execute_line(input.get_string());
+					input.clear();
 					history_entry = 0x0;
 				}
+				
+				handled = true;
 			}
 			else if (e.key == KEY_UP)
 			{
@@ -126,12 +102,9 @@ public:
 				{
 					unsigned int len = strlen(history_entry);
 					if (len < sizeof(input) - 1)
-					{
-						mem_copy(input, history_entry, len+1);
-						input_len = len;
-					}
+						input.set(history_entry);
 				}
-
+				handled = true;
 			}
 			else if (e.key == KEY_DOWN)
 			{
@@ -142,19 +115,16 @@ public:
 				{
 					unsigned int len = strlen(history_entry);
 					if (len < sizeof(input) - 1)
-					{
-						mem_copy(input, history_entry, len+1);
-
-						input_len = len;
-					}
+						input.set(history_entry);
 				}
 				else
-				{
-					input[0] = 0;
-					input_len = 0;
-				}
+					input.clear();
+				handled = true;
 			}
 		}
+		
+		if(!handled)
+			input.process_input(e);
 	}
 	
 	void print_line(const char *line)
@@ -426,38 +396,37 @@ void client_console_init()
 	binds_set('Y', "chat team");
 }
 
-void console_handle_input()
+bool console_input_cli(INPUT_EVENT e, void *user_data)
 {
-	int was_active = console_active();
+	if(!console_active())
+		return false;
+	
+	if(e.key == KEY_ESC && (e.flags&INPFLAG_PRESS))
+		console_toggle(console_type);
+	else
+		current_console()->handle_event(e);
+	return true;
+}
 
-	for(int i = 0; i < inp_num_events(); i++)
-	{
-		INPUT_EVENT e = inp_get_event(i);
-		
-		if(console_active())
-		{
-			if(e.key == KEY_ESC && e.flags&INPFLAG_PRESS)
-				console_toggle(console_type);
-			else
-				current_console()->handle_event(e);
-		}
-		else
-		{
-			if(e.key > 0 && e.key < KEY_LAST && keybindings[e.key][0] != 0)
-			{
-				int stroke = 0;
-				if(e.flags&INPFLAG_PRESS)
-					stroke = 1;
-				console_execute_line_stroked(stroke, keybindings[e.key]);
-			}
-		}
-	}
+bool console_input_special_binds(INPUT_EVENT e, void *user_data)
+{
+	// only handle function keys
+	if(e.key < KEY_F1 || e.key > KEY_F25)
+		return false;
+	return console_input_normal_binds(e, user_data);
+}
 
-	if(was_active || console_active())
-	{
-		inp_clear_events();
-		inp_clear_key_states();
-	}
+bool console_input_normal_binds(INPUT_EVENT e, void *user_data)
+{
+	// don't handle invalid events and keys that arn't set to anything
+	if(e.key <= 0 || e.key >= KEY_LAST || keybindings[e.key][0] == 0)
+		return false;
+
+	int stroke = 0;
+	if(e.flags&INPFLAG_PRESS)
+		stroke = 1;
+	console_execute_line_stroked(stroke, keybindings[e.key]);
+	return true;
 }
 
 void console_toggle(int type)
@@ -575,30 +544,38 @@ void console_render()
 	{
 		float font_size = 10.0f;
 		float row_height = font_size*1.25f;
-		float width = gfx_text_width(0, font_size, console->input, -1);
-		float x = 3, y = console_height - row_height - 2;
-		const char *prompt = ">";
+		float x = 3;
+		float y = console_height - row_height - 2;
+
+		// render prompt		
+		TEXT_CURSOR cursor;
+		gfx_text_set_cursor(&cursor, x, y, font_size, TEXTFLAG_RENDER);
+
+		const char *prompt = "> ";
 		if(console_type)
 		{
 			if(client_rcon_authed())
-				prompt = "rcon>";
+				prompt = "rcon> ";
 			else
-				prompt = "rcon password>";
+				prompt = "rcon password> ";
 		}
-		
-		float prompt_width = gfx_text_width(0, font_size,prompt, -1)+2;
-
-		gfx_text(0, x, y, font_size, prompt, -1);
-		gfx_text(0, x+prompt_width, y, font_size, console->input, -1);
-		gfx_text(0, x+prompt_width+width+1, y, font_size, "_", -1);
 
+		gfx_text_ex(&cursor, prompt, -1);
+		
+		// render console input
+		gfx_text_ex(&cursor, console->input.get_string(), console->input.cursor_offset());
+		TEXT_CURSOR marker = cursor;
+		gfx_text_ex(&marker, "|", -1);
+		gfx_text_ex(&cursor, console->input.get_string()+console->input.cursor_offset(), -1);
+		
+		// render version
 		char buf[128];
-		str_format(buf, sizeof(buf), "Teewars v%s %s", TEEWARS_VERSION);
+		str_format(buf, sizeof(buf), "Teewars v%s", TEEWARS_VERSION);
 		float version_width = gfx_text_width(0, font_size, buf, -1);
 		gfx_text(0, screen.w-version_width-5, y, font_size, buf, -1);
 
+		// render log
 		y -= row_height;
-
 		char *entry = (char *)ringbuf_last(console->backlog);
 		while (y > 0.0f && entry)
 		{
diff --git a/src/game/client/gc_console.h b/src/game/client/gc_console.h
index 0d516820..b75d7646 100644
--- a/src/game/client/gc_console.h
+++ b/src/game/client/gc_console.h
@@ -1,7 +1,14 @@
 #ifndef _GC_CONSOLE_H
 #define _GC_CONSOLE_H
 
-void console_handle_input();
+#include <engine/e_client_interface.h>
+
+bool console_input_cli(INPUT_EVENT e, void *user_data);
+bool console_input_special_binds(INPUT_EVENT e, void *user_data);
+bool console_input_normal_binds(INPUT_EVENT e, void *user_data);
+
+//void console_handle_input();
+
 void console_toggle(int tpye);
 void console_render();
 int console_active();
diff --git a/src/game/client/gc_hooks.cpp b/src/game/client/gc_hooks.cpp
index 021228c1..2d9a1ba3 100644
--- a/src/game/client/gc_hooks.cpp
+++ b/src/game/client/gc_hooks.cpp
@@ -52,15 +52,14 @@ extern "C" void modc_init()
 	static FONT_SET default_font;
 	int64 start = time_get();
 	
-	vec2 v;
-	v = closest_point_on_line(vec2(0, 0), vec2(10, 0), vec2(5, 0));
-	dbg_msg("", "1: %f,%f", v.x, v.y);
-	v = closest_point_on_line(vec2(0, 0), vec2(20, 0), vec2(5, 0));
-	dbg_msg("", "2: %f,%f", v.x, v.y);
-	v = closest_point_on_line(vec2(0, 0), vec2(10, 0), vec2(20, 0));
-	dbg_msg("", "3: %f,%f", v.x, v.y);
+	// setup input stack
+	input_stack.add_handler(console_input_special_binds, 0); // F1-Fx binds
+	input_stack.add_handler(console_input_cli, 0); // console
+	input_stack.add_handler(chat_input_handle, 0); // chat
+	//input_stack.add_handler() // ui
+	input_stack.add_handler(console_input_normal_binds, 0); // binds
+	
 	
-
 	int before = gfx_memory_usage();
 	font_set_load(&default_font, "data/fonts/default_font%d.tfnt", "data/fonts/default_font%d.png", "data/fonts/default_font%d_b.png", 14, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 36);
 	dbg_msg("font", "gfx memory used for font textures: %d", gfx_memory_usage()-before);
@@ -352,18 +351,13 @@ extern "C" void modc_newsnapshot()
 
 extern "C" void modc_render()
 {
-	console_handle_input();
-
 	// this should be moved around abit
 	if(client_state() == CLIENTSTATE_ONLINE)
-	{
 		render_game();
-	}
 	else
-	{
 		menu_render();
-	}
 
+	input_stack.dispatch_input();
 	console_render();
 }
 
diff --git a/src/game/client/gc_menu.cpp b/src/game/client/gc_menu.cpp
index 82c17ab1..90a39491 100644
--- a/src/game/client/gc_menu.cpp
+++ b/src/game/client/gc_menu.cpp
@@ -2069,6 +2069,27 @@ void menu_render()
 	static int mouse_x = 0;
 	static int mouse_y = 0;
 
+	/*
+    RECT screen = *ui_screen();
+	gfx_mapscreen(screen.x, screen.y, screen.w, screen.h);
+	menu2_render_background();
+
+	TEXT_CURSOR cursor;
+	mem_zero(&cursor, sizeof(cursor));
+	cursor.font_size = 32.0f;
+	cursor.start_x = 10.0f;
+	cursor.start_y = 10.0f;
+	cursor.x = cursor.start_x;
+	cursor.y = cursor.start_y;
+	cursor.line_count = 1;
+	cursor.line_width = 200.0f;
+	cursor.flags = TEXTFLAG_RENDER;
+
+	gfx_text_new(&cursor, "The quick brown fox jumped over the lazy dog.", -1);
+	
+	DEBUG_text = 0;
+	return;*/
+
 	// update colors
 
 	vec3 rgb = hsl_to_rgb(vec3(config.ui_color_hue/255.0f, config.ui_color_sat/255.0f, config.ui_color_lht/255.0f));
@@ -2124,6 +2145,4 @@ void menu_render()
     gfx_setcolor(1,1,1,1);
     gfx_quads_drawTL(mx,my,24,24);
     gfx_quads_end();
-
-	inp_clear_events();
 }
diff --git a/src/game/client/gc_ui.cpp b/src/game/client/gc_ui.cpp
index a7580d5d..dc78c0ca 100644
--- a/src/game/client/gc_ui.cpp
+++ b/src/game/client/gc_ui.cpp
@@ -45,7 +45,9 @@ int ui_update(float mx, float my, float mwx, float mwy, int buttons)
     becomming_hot_item = 0;
     return 0;
 }
-
+/*
+bool ui_
+*/
 int ui_mouse_inside(const RECT *r)
 {
     if(mouse_x >= r->x && mouse_x <= r->x+r->w && mouse_y >= r->y && mouse_y <= r->y+r->h)