From 7ab6170ca3d607c9e352bdfa54dd36adccf8f1cc Mon Sep 17 00:00:00 2001
From: lephe <sebastien.mld@numericable.fr>
Date: Sat, 25 Feb 2017 23:19:35 +0100
Subject: [PATCH] Key repeat events & Timer callbacks with arguments. Fixed
 sleep_us().

---
 TODO                              |  19 +++--
 demo/gintdemo.c                   |   6 +-
 demo/gintdemo.h                   |   6 +-
 demo/test_keyboard.c              | 111 +++++++++++++++++++++++-------
 demo/test_timer.c                 |   2 +-
 include/events.h                  |  35 +++++-----
 include/internals/events.h        |   2 +-
 include/internals/keyboard.h      |  14 ++--
 include/internals/timer.h         |   3 +-
 include/keyboard.h                |  36 +---------
 include/timer.h                   |  23 ++++---
 src/clock/clock.c                 |  38 ++++++----
 src/events/event_get.c            |  10 +--
 src/events/event_push.c           |   4 +-
 src/gray/gray_engine.c            |   3 +-
 src/keyboard/getPressedKey.c      |   2 +-
 src/keyboard/getPressedKeys.c     |   3 +-
 src/keyboard/getkey.c             |  20 +-----
 src/keyboard/keyboard_interrupt.c |  38 ++++++----
 src/keyboard/keyboard_misc.c      |   2 +-
 src/keyboard/keyboard_sh7305.c    |   2 +-
 src/keyboard/keyboard_sh7705.c    |   2 +-
 src/keyboard/multigetkey.c        |  36 ++++++----
 src/timer/timer_interrupt.c       |  21 +++++-
 src/timer/timer_start.c           |  15 ++--
 25 files changed, 264 insertions(+), 189 deletions(-)

diff --git a/TODO b/TODO
index 3f7b8de..5be8293 100644
--- a/TODO
+++ b/TODO
@@ -9,22 +9,21 @@ Simple improvements:
 -  demo:	Try 284x124 at (-60, -28) (all disadvantages)
 -  display:	Rectangle-based drawing functions
 -  time:	Compute CLOCKS_PER_SEC
--  events:	Introduce KeyRepeat events
 -  string:	Use cmp/str to implement memchr() (assembler examples)
 -  string:	Do some tests for memcmp()
--  core:        Register more interrupts (and understand their parameters)
--  rtc:         Take care of carry when reading time
+-  core:	Register more interrupts (and understand their parameters)
+-  rtc:		Take care of carry when reading time
 Larger improvements:
--  errno:       Introduce errno and use it more or less everywhere
+-  errno:	Introduce errno and use it more or less everywhere
 -  bopti:	Monochrome bitmaps blending modes
 -  bopti:	Handle partial transparency
 -  core:	Implement all callbacks and a complete user API
-*  core:        Better save registers
-*  core:        Allow return to menu
--  serial:      Implement a driver
--  usb:         Implement a driver
--  esper:       Cleaner playback, synthetizing
--  clock:       Handle overclocking (relaunch clocks when overclocking)
+*  core:	Better save registers
+*  core:	Allow return to menu
+-  serial:	Implement a driver
+-  usb:		Implement a driver
+-  esper:	Cleaner playback, synthetizing
+-  clock:	Handle overclocking (relaunch clocks when overclocking)
 
 Things to investigate:
 -  Packed bit fields alignment
diff --git a/demo/gintdemo.c b/demo/gintdemo.c
index b9b92f6..a65dad6 100644
--- a/demo/gintdemo.c
+++ b/demo/gintdemo.c
@@ -300,7 +300,7 @@ void main_menu(int *category, int *app)
 	};
 
 	const char *list_tests[] = {
-		"Keyboard",
+		"Keyboard and events",
 		"Gray engine",
 		"Image rendering",
 		"Text rendering",
@@ -495,6 +495,8 @@ int main(void)
 {
 	int category, app;
 
+	sleep_ms(2000);
+
 	while(1)
 	{
 		main_menu(&category, &app);
@@ -502,7 +504,7 @@ int main(void)
 
 		switch((category << 8) | app)
 		{
-		case 0x0101: test_keyboard();			break;
+		case 0x0101: test_keyboard_events();		break;
 		case 0x0102: test_gray();			break;
 		case 0x0103: test_bopti();			break;
 		case 0x0104: test_tales();			break;
diff --git a/demo/gintdemo.h b/demo/gintdemo.h
index 1d32bcd..2b4fc24 100644
--- a/demo/gintdemo.h
+++ b/demo/gintdemo.h
@@ -46,10 +46,10 @@ void print(int x, int y, const char *format, ...);
 //---
 
 /*
-	test_keyboard()
-	Displays a real-time multigetkey() and the keyboard state.
+	test_keyboard_events()
+	Real-time keyboard management with events.
 */
-void test_keyboard(void);
+void test_keyboard_events(void);
 
 /*
 	test_gray()
diff --git a/demo/test_keyboard.c b/demo/test_keyboard.c
index ab90268..0ab410d 100644
--- a/demo/test_keyboard.c
+++ b/demo/test_keyboard.c
@@ -2,13 +2,9 @@
 #include <display.h>
 #include <keyboard.h>
 #include <stdlib.h>
+#include <events.h>
 
-/*
-	test_keyboard()
-	Displays a real-time multigetkey() and the keyboard state.
-*/
-
-static void draw(volatile unsigned char *state)
+static void draw_keyboard(volatile uint8_t *state)
 {
 	int i, j, k, l;
 	int x, y;
@@ -22,12 +18,16 @@ static void draw(volatile unsigned char *state)
 		if(i == 4 && j == 5) continue;
 
 		x = 5 * j + 1;
-		y = 61 - 5 * i;
+		y = 59 - 5 * i;
+
+		// Space for the horizontal line.
+		y += 3 * (i < 7);
+
 		// Moving the [AC/ON] key.
-		if(!i) x = 5 * (5) + 1, y = 61 - 5 * (4);
+		if(!i) x = 5 * (5) + 1, y = 61 - 5 * (4) + 1;
 
 		// Drawing a filled shape when the key is pressed.
-		if(state[i] & (128 >> j))
+		if(state[i] & (0x80 >> j))
 		{
 			for(k = -2; k <= 2; k++) for(l = -2; l <= 2; l++)
 				if(abs(k) + abs(l) <= 2)
@@ -40,9 +40,55 @@ static void draw(volatile unsigned char *state)
 				if(k || l) dpixel(x + k, y + l, Color_Black);
 		}
 	}
+
+	// Binding the arrow keys together for a more visual thing.
+	dpixel(28, 19, Color_Black); dpixel(29, 19, Color_Black);
+	dpixel(28, 24, Color_Black); dpixel(29, 24, Color_Black);
+	dpixel(26, 21, Color_Black); dpixel(26, 22, Color_Black);
+	dpixel(31, 21, Color_Black); dpixel(31, 22, Color_Black);
+
+	// An horizontal line to separate parts of the keyboard.
+	dline(5, 28, 32, 28, Color_Black);
 }
 
-void test_keyboard(void)
+typedef struct {
+	event_type_t type;
+	int key;
+	int repeats;
+
+} enhanced_event_t;
+
+static void push_history(enhanced_event_t *history, int size, event_t event)
+{
+	#define	event_eq(x, y) ((x).type == (y).type && (x).key == (y).key)
+
+	// Determining where the history ends.
+	int length = 0;
+	while(length < size && history[length].type != ET_None) length++;
+
+	// Checking if the previous event is being repeated.
+	if(length > 0 && event_eq(history[length - 1], event))
+	{
+		history[length - 1].repeats++;
+		return;
+	}
+
+	// Making up some space if required.
+	if(length == size)
+	{
+		for(int i = 0; i < size - 1; i++) history[i] = history[i + 1];
+		length = size - 1;
+	}
+
+	// Adding a new entry to the history.
+	history[length].type	= event.type;
+	history[length].key	= event.key;
+	history[length].repeats	= 1;
+
+	#undef	event_eq
+}
+
+static void draw_events(enhanced_event_t *history, int size)
 {
 	const char *key_names[] = {
 		"F1",       "F2",            "F3",   "F4",   "F5",    "F6",
@@ -55,28 +101,41 @@ void test_keyboard(void)
 		"1",        "2",             "3",    "+",    "-",     NULL,
 		"0",        ".",             "\x08", "(-)",  "EXE",   NULL
 	};
+	const char *event_names[] = {
+		"None ", "User ", "Press", "Rept.", "Rel. "
+	};
 
-	volatile unsigned char *state = keystate();
-	int keys[4] = { 0 };
-	int i;
+	for(int i = 0; i < size && history[i].type != ET_None; i++)
+	{
+		print(8, 3 + i, "%s %s", event_names[history[i].type],
+			key_names[keyid(history[i].key)]);
+		if(history[i].repeats > 1)
+			print(19, 3 + i, "%d", history[i].repeats);
+	}
+}
+
+/*
+	test_keyboard_events()
+	Real-time keyboard management with events.
+*/
+void test_keyboard_events(void)
+{
+	enhanced_event_t history[5];
+	int history_size = 5;
+	event_t event;
+
+	for(int i = 0; i < history_size; i++) history[i].type = ET_None;
 
 	while(1)
 	{
 		dclear();
-		locate(1, 1, "Keyboard driver");
-		locate(8, 3, "Pressed keys:");
-		draw(state);
-
-		if(keys[0] == KEY_NONE) locate(9, 4, ":None");
-		else for(i = 0; i < 4 && keys[i] != KEY_NONE; i++)
-		{
-			locate( 9, i + 4, ":");
-			locate(10, i + 4, key_names[keyid(keys[i])]);
-		}
-
+		locate(1, 1, "Keyboard and events");
+		draw_keyboard(keystate());
+		draw_events(history, history_size);
 		dupdate();
 
-		multigetkey(keys, 4, 1);
-		if(keys[0] == KEY_EXIT && keys[1] == KEY_NONE) break;
+		event = waitevent();
+		if(event.type == ET_KeyPress && event.key == KEY_EXIT) break;
+		push_history(history, history_size, event);
 	}
 }
diff --git a/demo/test_timer.c b/demo/test_timer.c
index d8158b5..53e8bd0 100644
--- a/demo/test_timer.c
+++ b/demo/test_timer.c
@@ -35,7 +35,7 @@ static void timing_timer(void)
 
 static void timing_start(void)
 {
-	timer_start(TIMER_USER, 64, Clock_Hz, timing_timer, 0);
+	timer_start(TIMER_USER, 64, Clock_Hz, timing_timer, NULL, 0);
 	rtc_cb_edit(cb_id, RTCFreq_64Hz, timing_rtc);
 
 	elapsed_timer = 0;
diff --git a/include/events.h b/include/events.h
index 01e5277..b1832a4 100644
--- a/include/events.h
+++ b/include/events.h
@@ -9,15 +9,11 @@
 #ifndef	_EVENTS_H
 #define	_EVENTS_H
 
-//---
-//	Type definitions.
-//---
-
 /*
-	enum EventType
+	event_type_t
 	Something user programs will surely use most often.
 */
-enum EventType
+typedef enum
 {
 	EventType_None		= 0,
 	ET_None			= EventType_None,
@@ -28,27 +24,32 @@ enum EventType
 	EventType_KeyPressed	= 2,
 	ET_KeyPress		= EventType_KeyPressed,
 
-	EventType_KeyReleased	= 3,
+	EventType_KeyRepeated	= 3,
+	ET_KeyRepeat		= EventType_KeyRepeated,
+
+	EventType_KeyReleased	= 4,
 	ET_KeyRel		= EventType_KeyReleased,
-};
+
+} event_type_t;
 
 /*
-	struct Event
+	event_t
 	Wake up, something's going on. The union member that holds information
 	about the event is implicitly defined by the type attribute.
 */
-struct Event
+typedef struct
 {
-	enum EventType type;
+	event_type_t type;
 
 	union
 	{
 		// For ET_User.
 		void *data;
-		// For ET_KeyPress and ET_KeyRel.
+		// For ET_KeyPress, ET_KeyRepeat and ET_KeyRel.
 		int key;
 	};
-};
+
+} event_t;
 
 
 
@@ -64,21 +65,21 @@ struct Event
 	allowed.
 	Returns non-zero on error.
 */
-int event_push(struct Event event);
+int event_push(event_t event);
 
 /*
-	getevent()
+	waitevent()
 	Returns the next event. If no one is available, waits for something to
 	happen. This function uses low-level sleep and should be preferred to
 	active waiting using loops.
 */
-struct Event getevent(void);
+event_t waitevent(void);
 
 /*
 	pollevent()
 	Returns the next event. If no one is available, returns an event whose
 	type is ET_None. This function always returns immediately.
 */
-struct Event pollevent(void);
+event_t pollevent(void);
 
 #endif	// _EVENTS_H
diff --git a/include/internals/events.h b/include/internals/events.h
index 976e352..ac8b7ee 100644
--- a/include/internals/events.h
+++ b/include/internals/events.h
@@ -12,7 +12,7 @@
 	like any other queue. Trying to add an event when the queue is full
 	fails, and the operation is ignored.
 */
-extern volatile struct Event event_queue[];
+extern volatile event_t event_queue[];
 extern volatile int queue_start;
 extern volatile int queue_size;
 
diff --git a/include/internals/keyboard.h b/include/internals/keyboard.h
index 2824893..10bd564 100644
--- a/include/internals/keyboard.h
+++ b/include/internals/keyboard.h
@@ -5,7 +5,7 @@
 #include <clock.h>
 
 // Keyboard variables.
-extern volatile unsigned char keyboard_state[10];
+extern volatile uint8_t keyboard_state[10];
 extern volatile int interrupt_flag;
 
 // Key statistics.
@@ -19,7 +19,7 @@ extern unsigned cb_id;
 	getPressedKey()
 	Finds a pressed key in the keyboard state and returns it.
 */
-int getPressedKey(volatile unsigned char *keyboard_state);
+int getPressedKey(volatile uint8_t *keyboard_state);
 
 /*
 	getPressedKeys()
@@ -28,7 +28,13 @@ int getPressedKey(volatile unsigned char *keyboard_state);
 	WARNING: keyboard artifacts make this function read as pressed keys
 	that aren't (typically, LEFT + DOWN + SHIFT => ALPHA).
 */
-int getPressedKeys(volatile unsigned char *keyboard_state, int *keys,
-	int count);
+int getPressedKeys(volatile uint8_t *keyboard_state, int *keys, int count);
+
+/*
+	keyboard_updateState()
+	Updates the keyboard state.
+*/
+void keyboard_updateState_7705(volatile uint8_t *state);
+void keyboard_updateState_7305(volatile uint8_t *state);
 
 #endif	// _INTERNALS_KEYBOARD_H
diff --git a/include/internals/timer.h b/include/internals/timer.h
index f15abc1..19b4a4e 100644
--- a/include/internals/timer.h
+++ b/include/internals/timer.h
@@ -7,7 +7,8 @@
 */
 struct Timer
 {
-	void (*callback)(void);
+	void *callback;
+	void *data;
 	int repeats;
 };
 
diff --git a/include/keyboard.h b/include/keyboard.h
index 4bb2483..ea27483 100644
--- a/include/keyboard.h
+++ b/include/keyboard.h
@@ -13,6 +13,7 @@
 #ifndef	_KEYBOARD_H
 #define	_KEYBOARD_H	1
 
+#include <stdint.h>
 #include <rtc.h>
 
 //---
@@ -232,7 +233,7 @@ int keylast(int *repeat_count);
 	functions, but the buffer data is very volatile. Therefore, data
 	written to the buffer could be replaced anytime.
 */
-volatile unsigned char *keystate(void);
+volatile uint8_t *keystate(void);
 
 
 
@@ -268,37 +269,4 @@ int keychar(int key);
 */
 enum KeyType keytype(int key);
 
-
-
-//---
-//	Internal API.
-//	Reference here for documentation purposes only. Do not call.
-//---
-
-/*
-	keyboard_interrupt()
-	Notifies the keyboard module that an interrupt request has been issued,
-	and updates the keyboard state.
-*/
-void keyboard_interrupt(void);
-
-/*
-	keyboard_updateState()
-	Updates the keyboard state.
-*/
-void keyboard_updateState_7705(volatile unsigned char *state);
-void keyboard_updateState_7305(volatile unsigned char *state);
-
-/*
-	keyboard_init()
-	Starts the keyboard timer.
-*/
-void keyboard_init(void)		__attribute__((constructor));
-
-/*
-	keyboard_quit()
-	Stops the keyboard timer.
-*/
-void keyboard_quit(void)		__attribute__((destructor));
-
 #endif	// _KEYBOARD_H
diff --git a/include/timer.h b/include/timer.h
index 1b5cb7d..e231cf8 100644
--- a/include/timer.h
+++ b/include/timer.h
@@ -48,17 +48,22 @@
 	not running the gray engine.
 	Unit names are defined in the clock.h header and must be one of the
 	following:
-	- Clock_us	(microseconds)
-	- Clock_ms	(milliseconds)
-	- Clock_s	(seconds)
-	- Clock_Hz	(hertz)
-	- Clock_kHz	(kilohertz)
-	- Clock_MHz	(megahertz)
+	  - Clock_us	(microseconds)
+	  - Clock_ms	(milliseconds)
+	  - Clock_s	(seconds)
+	  - Clock_Hz	(hertz)
+	  - Clock_kHz	(kilohertz)
+	  - Clock_MHz	(megahertz)
 	The number of repeats may to set to 0. In this case, the timer will not
 	stop until timer_stop() is explicitly called.
+	The callback is expected to be a function of the following type:
+	  - void callback(void)        if data == NULL
+	  - void callback(void *data)  if data is non-NULL
+	In the latter case, the data pointer will be passed as argument to the
+	callback function.
 */
 void timer_start(int timer, int delay_or_frequency, enum ClockUnit unit,
-	void (*callback)(void), int repeats);
+	void *callback, void *data, int repeats);
 
 /*
 	timer_start2()
@@ -71,8 +76,8 @@ void timer_start(int timer, int delay_or_frequency, enum ClockUnit unit,
 	- TIMER_Po_256
 	- TIMER_TCLK
 */
-void timer_start2(int timer, int delay, int prescaler, void (*callback)(void),
-	int repeats);
+void timer_start2(int timer, int delay, int prescaler, void *callback,
+	void *data, int repeats);
 
 /*
 	timer_stop()
diff --git a/src/clock/clock.c b/src/clock/clock.c
index a88a216..06426eb 100644
--- a/src/clock/clock.c
+++ b/src/clock/clock.c
@@ -4,6 +4,7 @@
 #include <rtc.h>
 #include <stddef.h>
 #include <mpu.h>
+#include <stdint.h>
 
 static clock_config_t conf = {
 	.FLL = -1, .PLL = -1,
@@ -21,27 +22,36 @@ static clock_config_t conf = {
 int clock_setting(int duration, enum ClockUnit unit)
 {
 	if(conf.Pphi_f <= 0) return -1;
-	int f = conf.Pphi_f >> 2;
+	uint64_t f = conf.Pphi_f >> 2;
+	uint64_t result;
 
 	switch(unit)
 	{
 	case Clock_us:
-		return (duration * f) / 1000000;
+		result = (duration * f) / 1000000;
+		break;
 	case Clock_ms:
-		return (duration * f) / 1000;
+		result = (duration * f) / 1000;
+		break;
 	case Clock_s:
-		return (duration * f);
+		result = (duration * f);
+		break;
 
 	case Clock_Hz:
-		return f / duration;
+		result = f / duration;
+		break;
 	case Clock_kHz:
-		return f / (duration * 1000);
+		result = f / (duration * 1000);
+		break;
 	case Clock_MHz:
-		return f / (duration * 1000000);
+		result = f / (duration * 1000000);
+		break;
 
 	default:
 		return -1;
 	}
+
+	return (result > 0xffffffff) ? (0xffffffff) : (result);
 }
 
 /*
@@ -60,7 +70,7 @@ clock_config_t clock_config(void)
 void sleep(void)
 {
 	__asm__(
-		"sleep\n\t"
+		"sleep"
 	);
 }
 
@@ -71,10 +81,9 @@ void sleep(void)
 	than requested.
 */
 
-static volatile int sleep_done = 0;
-static void sleep_callback(void)
+static void sleep_callback(void *flag)
 {
-	sleep_done = 1;
+	*((int *)flag) = 1;
 }
 
 void sleep_ms(int ms_delay)
@@ -83,8 +92,10 @@ void sleep_ms(int ms_delay)
 }
 void sleep_us(int us_delay)
 {
-	sleep_done = 0;
-	timer_start(TIMER_USER, us_delay, Clock_us, sleep_callback, 1);
+	volatile int sleep_done = 0;
+	timer_start(TIMER_USER, us_delay, Clock_us, sleep_callback,
+		(void *)&sleep_done, 1);
+
 	do sleep();
 	while(!sleep_done);
 }
@@ -133,6 +144,7 @@ void clock_measure(void)
 		tmu->TCR.CKEG	= 0;
 
 		timers[TIMER_USER].callback	= NULL;
+		timers[TIMER_USER].data		= NULL;
 		timers[TIMER_USER].repeats	= 0;
 
 		cb_id_7705 = rtc_cb_add(RTCFreq_256Hz, clock_measure_7705, 0);
diff --git a/src/events/event_get.c b/src/events/event_get.c
index eceb1ff..5b2e707 100644
--- a/src/events/event_get.c
+++ b/src/events/event_get.c
@@ -7,9 +7,9 @@
 	Returns the next event. If no one is available, returns an event whose
 	type is ET_None. This function always returns immediately.
 */
-struct Event pollevent(void)
+event_t pollevent(void)
 {
-	struct Event event = {
+	event_t event = {
 		.type	= ET_None
 	};
 	if(queue_size <= 0) return event;
@@ -24,14 +24,14 @@ struct Event pollevent(void)
 }
 
 /*
-	getevent()
+	waitevent()
 	Returns the next event. If no one is available, waits for something to
 	happen. This function uses low-level sleep and should be preferred to
 	active waiting using loops.
 */
-struct Event getevent(void)
+event_t waitevent(void)
 {
-	struct Event event;
+	event_t event;
 
 	while((event = pollevent()).type == ET_None) sleep();
 	return event;
diff --git a/src/events/event_push.c b/src/events/event_push.c
index 3c47d09..0e39b2c 100644
--- a/src/events/event_push.c
+++ b/src/events/event_push.c
@@ -1,7 +1,7 @@
 #include <internals/events.h>
 #include <events.h>
 
-volatile struct Event event_queue[EVENTS_QUEUE_SIZE];
+volatile event_t event_queue[EVENTS_QUEUE_SIZE];
 volatile int queue_start = 0;
 volatile int queue_size = 0;
 
@@ -11,7 +11,7 @@ volatile int queue_size = 0;
 	or pollevent() later. Pushing ET_None events is not allowed.
 	Returns non-zero on error.
 */
-int event_push(struct Event event)
+int event_push(event_t event)
 {
 	if(queue_size >= EVENTS_QUEUE_SIZE) return 1;
 	if(event.type == ET_None) return 2;
diff --git a/src/gray/gray_engine.c b/src/gray/gray_engine.c
index 9c1071f..fab319e 100644
--- a/src/gray/gray_engine.c
+++ b/src/gray/gray_engine.c
@@ -68,7 +68,8 @@ void gray_start(void)
 {
 	if(runs) return;
 
-	timer_start2(TIMER_GRAY, delays[0], GRAY_PRESCALER, gray_interrupt, 0);
+	timer_start2(TIMER_GRAY, delays[0], GRAY_PRESCALER, gray_interrupt,
+		NULL, 0);
 	current &= 1;
 	runs = 1;
 }
diff --git a/src/keyboard/getPressedKey.c b/src/keyboard/getPressedKey.c
index ca8ba32..f7d7d06 100644
--- a/src/keyboard/getPressedKey.c
+++ b/src/keyboard/getPressedKey.c
@@ -4,7 +4,7 @@
 	getPressedKey()
 	Finds a pressed key in the keyboard state and returns it.
 */
-int getPressedKey(volatile unsigned char *keyboard_state)
+int getPressedKey(volatile uint8_t *keyboard_state)
 {
 	int row = 1, column = 0;
 	int state;
diff --git a/src/keyboard/getPressedKeys.c b/src/keyboard/getPressedKeys.c
index b6dd739..c3b21f1 100644
--- a/src/keyboard/getPressedKeys.c
+++ b/src/keyboard/getPressedKeys.c
@@ -5,8 +5,7 @@
 	Find 'count' pressed keys in the keyboard state and fills the 'keys'
 	array. Returns the number of actually-pressed keys found.
 */
-int getPressedKeys(volatile unsigned char *keyboard_state, int *keys,
-	int count)
+int getPressedKeys(volatile uint8_t *keyboard_state, int *keys, int count)
 {
 	int row = 1, column;
 	int found = 0, actually_pressed;
diff --git a/src/keyboard/getkey.c b/src/keyboard/getkey.c
index 89e2bc5..1cbfcad 100644
--- a/src/keyboard/getkey.c
+++ b/src/keyboard/getkey.c
@@ -25,7 +25,7 @@ int getkey(void)
 	If max_cycles is non-zero and positive, getkey_opt() will return
 	KEY_NOEVENT if no event occurs during max_cycle analysis.
 */
-void getkey_opt_wait(int *cycles)
+static void getkey_opt_wait(int *cycles)
 {
 	while(!interrupt_flag) sleep();
 	interrupt_flag = 0;
@@ -34,7 +34,7 @@ void getkey_opt_wait(int *cycles)
 }
 int getkey_opt(enum GetkeyOpt options, int cycles)
 {
-	struct Event event;
+	event_t event;
 	int modifier = 0;
 	static int event_ref = 0;
 
@@ -120,19 +120,3 @@ int getkey_opt(enum GetkeyOpt options, int cycles)
 	event_ref	= 0;
 	return KEY_NONE;
 }
-
-/*
-int getkey_opt(enum GetkeyOpt options, int max_cycles)
-{
-	while(max_cycles != 0)
-	{
-		// Handling "new key" events.
-
-		// Handling key repetitions.
-
-	}
-
-	// When no key was pressed during the given delay...
-	return KEY_NOEVENT;
-}
-*/
diff --git a/src/keyboard/keyboard_interrupt.c b/src/keyboard/keyboard_interrupt.c
index e98dc51..9cf854a 100644
--- a/src/keyboard/keyboard_interrupt.c
+++ b/src/keyboard/keyboard_interrupt.c
@@ -10,7 +10,7 @@
 //---
 
 // These ones get modified by interrupts.
-volatile unsigned char keyboard_state[10] = { 0 };
+volatile uint8_t keyboard_state[10] = { 0 };
 volatile int interrupt_flag = 0;
 
 // Key statistics.
@@ -28,16 +28,25 @@ unsigned cb_id;
 
 static void push_press(int keycode)
 {
-	struct Event event = {
+	event_t event = {
 		.type	= ET_KeyPress,
 		.key	= keycode,
 	};
 	event_push(event);
 }
 
+static void push_repeat(int keycode)
+{
+	event_t event = {
+		.type	= ET_KeyRepeat,
+		.key	= keycode,
+	};
+	event_push(event);
+}
+
 static void push_release(int keycode)
 {
-	struct Event event = {
+	event_t event = {
 		.type	= ET_KeyRel,
 		.key	= keycode,
 	};
@@ -51,7 +60,7 @@ static void push_release(int keycode)
 */
 void keyboard_interrupt(void)
 {
-	unsigned char state[10] = { 0 };
+	uint8_t state[10] = { 0 };
 
 	isSH3()	? keyboard_updateState_7705(state)
 		: keyboard_updateState_7305(state)
@@ -61,8 +70,8 @@ void keyboard_interrupt(void)
 	// AC/ON.
 	if(keyboard_state[0] ^ state[0])
 	{
-		unsigned char pressed	= ~keyboard_state[0] &  state[0];
-		unsigned char released	=  keyboard_state[0] & ~state[0];
+		uint8_t pressed		= ~keyboard_state[0] &  state[0];
+		uint8_t released	=  keyboard_state[0] & ~state[0];
 
 		if(pressed & 1)  push_press(KEY_AC_ON);
 		if(released & 1) push_release(KEY_AC_ON);
@@ -71,19 +80,22 @@ void keyboard_interrupt(void)
 
 	for(int row = 1; row <= 9; row++)
 	{
-		unsigned char pressed	= ~keyboard_state[row] &  state[row];
-		unsigned char released	=  keyboard_state[row] & ~state[row];
+		uint8_t pressed		= ~keyboard_state[row] &  state[row];
+		uint8_t repeated	=  keyboard_state[row] &  state[row];
+		uint8_t released	=  keyboard_state[row] & ~state[row];
 		keyboard_state[row] = state[row];
 
 		// Fasten this a bit.
-		if(!pressed && !released) continue;
+		if(!(pressed | repeated | released)) continue;
 
 		for(int column = 0; column < 8; column++)
 		{
-			if(pressed & 1)  push_press  ((column << 4) | row);
+			if(pressed  & 1) push_press  ((column << 4) | row);
+			if(repeated & 1) push_repeat ((column << 4) | row);
 			if(released & 1) push_release((column << 4) | row);
 
-			pressed >>= 1;
+			pressed  >>= 1;
+			repeated >>= 1;
 			released >>= 1;
 		}
 	}
@@ -95,7 +107,7 @@ void keyboard_interrupt(void)
 	keyboard_init()
 	Starts the keyboard timer.
 */
-void keyboard_init(void)
+__attribute__((constructor)) void keyboard_init(void)
 {
 	cb_id = rtc_cb_add(RTCFreq_16Hz, keyboard_interrupt, 0);
 }
@@ -114,7 +126,7 @@ void keyboard_setFrequency(enum KeyboardFrequency frequency)
 	keyboard_quit()
 	Stops the keyboard timer.
 */
-void keyboard_quit(void)
+__attribute__((destructor)) void keyboard_quit(void)
 {
 	rtc_cb_end(cb_id);
 }
diff --git a/src/keyboard/keyboard_misc.c b/src/keyboard/keyboard_misc.c
index abdf59c..6248a3d 100644
--- a/src/keyboard/keyboard_misc.c
+++ b/src/keyboard/keyboard_misc.c
@@ -19,7 +19,7 @@ int keylast(int *repeat_count)
 	Returns the address of the keyboard state array. The returned address
 	is the handler's buffer, therefore it contains volatile data.
 */
-volatile unsigned char *keystate(void)
+volatile uint8_t *keystate(void)
 {
 	return keyboard_state;
 }
diff --git a/src/keyboard/keyboard_sh7305.c b/src/keyboard/keyboard_sh7305.c
index ab515ed..8eb0bca 100644
--- a/src/keyboard/keyboard_sh7305.c
+++ b/src/keyboard/keyboard_sh7305.c
@@ -136,7 +136,7 @@ static int krow(int row)
 	keyboard_updateState()
 	Updates the keyboard state.
 */
-void keyboard_updateState_7305(volatile unsigned char *keyboard_state)
+void keyboard_updateState_7305(volatile uint8_t *keyboard_state)
 {
 	for(int i = 0; i < 10; i++) keyboard_state[i] = krow(i);
 }
diff --git a/src/keyboard/keyboard_sh7705.c b/src/keyboard/keyboard_sh7705.c
index 1ab1800..20cd32c 100644
--- a/src/keyboard/keyboard_sh7705.c
+++ b/src/keyboard/keyboard_sh7705.c
@@ -129,7 +129,7 @@ static int krow(int row)
 	keyboard_updateState()
 	Updates the keyboard state.
 */
-void keyboard_updateState_7705(volatile unsigned char *keyboard_state)
+void keyboard_updateState_7705(volatile uint8_t *keyboard_state)
 {
 	for(int i = 0; i < 10; i++) keyboard_state[i] = krow(i);
 }
diff --git a/src/keyboard/multigetkey.c b/src/keyboard/multigetkey.c
index dbe8a44..b1bc5e5 100644
--- a/src/keyboard/multigetkey.c
+++ b/src/keyboard/multigetkey.c
@@ -1,5 +1,6 @@
 #include <keyboard.h>
 #include <internals/keyboard.h>
+#include <events.h>
 
 /*
 	multigetkey()
@@ -12,23 +13,28 @@
 	Setting count = 3 is generally safe.
 	The function returns after 'max_cycles' if no key is pressed.
 */
-void multigetkey(int *keys, int count, int max_cycles)
+static void multigetkey_wait(int *cycles)
 {
-	int number;
+	while(!interrupt_flag) sleep();
+	interrupt_flag = 0;
 
-	if(!max_cycles) max_cycles = -1;
+	if(*cycles > 0) (*cycles)--;
+}
+void multigetkey(int *keys, int count, int cycles)
+{
+	event_t event;
+	int number = 0;
 
-	while(max_cycles != 0)
+	if(count <= 0) return;
+	if(cycles <= 0) cycles = -1;
+
+	while(cycles != 0)
 	{
-		while(!interrupt_flag) sleep();
-		interrupt_flag = 0;
-		if(max_cycles > 0) max_cycles--;
-
 		number = getPressedKeys(keyboard_state, keys, count);
 
-		// We need to update the last key data, in case multigetkey()
-		// returns a single key, and getkey() is called a short time
-		// after. Otherwise getkey() could re-send an event for this
+		// We want to update the last key data when multigetkey()
+		// returns a single key, because getkey() could be called a
+		// short time after we return, and send a new event for this
 		// key.
 		if(number == 1)
 		{
@@ -37,10 +43,12 @@ void multigetkey(int *keys, int count, int max_cycles)
 			last_events = 0;
 		}
 
-		if(number) return;
+		if(number) break;
+		multigetkey_wait(&cycles);
 	}
 
-	// When no key was pressed during the given delay... (no need to fill
-	// the array, it has already been done by getPressedKeys()).
+	do event = pollevent();
+	while(event.type != ET_None);
+
 	return;
 }
diff --git a/src/timer/timer_interrupt.c b/src/timer/timer_interrupt.c
index 85f4427..5b183f4 100644
--- a/src/timer/timer_interrupt.c
+++ b/src/timer/timer_interrupt.c
@@ -3,7 +3,11 @@
 
 #include <stddef.h>
 
-struct Timer timers[3] = { { NULL, 0 }, { NULL, 0 }, { NULL, 0 } };
+struct Timer timers[3] = {
+	{ .callback = NULL, .data = NULL, .repeats = 0 },
+	{ .callback = NULL, .data = NULL, .repeats = 0 },
+	{ .callback = NULL, .data = NULL, .repeats = 0 },
+};
 
 /*
 	timer_interrupt()
@@ -15,7 +19,20 @@ void timer_interrupt(int timer)
 	timer_get(timer, &tmu, NULL);
 
 	tmu->TCR.UNF = 0;
-	if(timers[timer].callback) timers[timer].callback();
+
+	if(timers[timer].callback)
+	{
+		if(timers[timer].data)
+		{
+			void (*fun)(void *data) = timers[timer].callback;
+			fun(timers[timer].data);
+		}
+		else
+		{
+			void (*fun)(void) = timers[timer].callback;
+			fun();
+		}
+	}
 
 	// Reducing the number of repetitions left, if not infinite.
 	if(!timers[timer].repeats) return;
diff --git a/src/timer/timer_start.c b/src/timer/timer_start.c
index 7e7c9d6..0792702 100644
--- a/src/timer/timer_start.c
+++ b/src/timer/timer_start.c
@@ -5,8 +5,8 @@
 	timer_start2()
 	Configures and starts a time using a clock count and a prescaler.
 */
-void timer_start2(int timer, int delay, int prescaler, void (*callback)(void),
-	int repeats)
+void timer_start2(int timer, int delay, int prescaler, void *callback,
+	void *data, int repeats)
 {
 	volatile struct mod_tmu *tmu;
 	volatile unsigned char *tstr;
@@ -27,8 +27,9 @@ void timer_start2(int timer, int delay, int prescaler, void (*callback)(void),
 	tmu->TCR.CKEG = 0;
 
 	// Loading the structure information.
-	timers[timer].callback = callback;
-	timers[timer].repeats = repeats;
+	timers[timer].callback	= callback;
+	timers[timer].data	= data;
+	timers[timer].repeats	= repeats;
 
 	// Starting the timer.
 	*tstr |= byte;
@@ -39,9 +40,9 @@ void timer_start2(int timer, int delay, int prescaler, void (*callback)(void),
 	Configures and starts a timer using a delay, or a frequency, and the
 	associated unit.
 */
-void timer_start(int timer, int delay, enum ClockUnit unit,
-	void (*callback)(void), int repeats)
+void timer_start(int timer, int delay, enum ClockUnit unit, void *callback,
+	void *data, int repeats)
 {
 	timer_start2(timer, clock_setting(delay, unit), TIMER_Po_4, callback,
-		repeats);
+		data, repeats);
 }