gui: move input handling into a class

Change-Id: I97e08a23369af0112875af84b3fb529cf42e929e
diff --git a/gui/gui.cpp b/gui/gui.cpp
index da3e5e5..4bf01ec 100644
--- a/gui/gui.cpp
+++ b/gui/gui.cpp
@@ -56,6 +56,12 @@
 // Enable to print render time of each frame to the log file
 //#define PRINT_RENDER_TIME 1
 
+#ifdef _EVENT_LOGGING
+#define LOGEVENT(...) LOGERR(__VA_ARGS)
+#else
+#define LOGEVENT(...) do {} while (0)
+#endif
+
 const static int CURTAIN_FADE = 32;
 
 using namespace rapidxml;
@@ -67,7 +73,6 @@
 static TWAtomicInt gGuiConsoleTerminate;
 static TWAtomicInt gForceRender;
 const int gNoAnimation = 1;
-static int gGuiInputInit = 0;
 blanktimer blankTimer;
 int ors_read_fd = -1;
 
@@ -172,259 +177,283 @@
 #endif
 }
 
-void input_init(void)
+class InputHandler
 {
-#ifndef TW_NO_SCREEN_TIMEOUT
+public:
+	void init()
 	{
-		string seconds;
-		DataManager::GetValue("tw_screen_timeout_secs", seconds);
-		blankTimer.setTime(atoi(seconds.c_str()));
-		blankTimer.resetTimerAndUnblank();
-	}
-#else
-	LOGINFO("Skipping screen timeout: TW_NO_SCREEN_TIMEOUT is set\n");
-#endif
-	gGuiInputInit = 1;
-}
+		// these might be read from DataManager in the future
+		touch_hold_ms = 500;
+		touch_repeat_ms = 100;
+		key_hold_ms = 500;
+		key_repeat_ms = 100;
+		touch_status = TS_NONE;
+		key_status = KS_NONE;
+		state = AS_NO_ACTION;
+		x = y = 0;
 
-enum touch_status_enum {
-	TS_NONE = 0,
-	TS_TOUCH_AND_HOLD = 1,
-	TS_TOUCH_REPEAT = 2,
-};
-
-enum key_status_enum {
-	KS_NONE = 0,
-	KS_KEY_PRESSED = 1,
-	KS_KEY_REPEAT = 2,
-};
-
-enum action_state_enum {
-	AS_IN_ACTION_AREA = 0,
-	AS_NO_ACTION = 1,
-};
-
-void get_input(int send_drag)
-{
-	static touch_status_enum touch_status = TS_NONE;
-	static key_status_enum key_status = KS_NONE;
-	static int x = 0, y = 0; // x and y coordinates
-	static struct timeval touchStart; // used to track time for long press / key repeat
-	static HardwareKeyboard *kb = PageManager::GetHardwareKeyboard();
-	static MouseCursor *cursor = PageManager::GetMouseCursor();
-	struct input_event ev;
-	static action_state_enum state = AS_NO_ACTION; // 1 means that we've touched in an empty area (no action) while 0 means we've touched a spot with an action
-	int ret = 0; // return value from ev_get
-
-	if (send_drag) {
-		// This allows us to only send one NotifyTouch event per render
-		// cycle to reduce overhead and perceived input latency.
-		static int prevx = 0, prevy = 0; // these track where the last drag notice was so that we don't send duplicate drag notices
-		if (touch_status && (x != prevx || y != prevy)) {
-			prevx = x;
-			prevy = y;
-			if (PageManager::NotifyTouch(TOUCH_DRAG, x, y) > 0)
-				state = AS_NO_ACTION;
-			else
-				state = AS_IN_ACTION_AREA;
+#ifndef TW_NO_SCREEN_TIMEOUT
+		{
+			string seconds;
+			DataManager::GetValue("tw_screen_timeout_secs", seconds);
+			blankTimer.setTime(atoi(seconds.c_str()));
+			blankTimer.resetTimerAndUnblank();
 		}
-		return;
+#else
+		LOGINFO("Skipping screen timeout: TW_NO_SCREEN_TIMEOUT is set\n");
+#endif
 	}
 
-	ret = ev_get(&ev);
+	void processInput();
+	void handleDrag();
+
+private:
+	// timeouts for touch/key hold and repeat
+	int touch_hold_ms;
+	int touch_repeat_ms;
+	int key_hold_ms;
+	int key_repeat_ms;
+
+	enum touch_status_enum {
+		TS_NONE = 0,
+		TS_TOUCH_AND_HOLD = 1,
+		TS_TOUCH_REPEAT = 2,
+	};
+
+	enum key_status_enum {
+		KS_NONE = 0,
+		KS_KEY_PRESSED = 1,
+		KS_KEY_REPEAT = 2,
+	};
+
+	enum action_state_enum {
+		AS_IN_ACTION_AREA = 0, // we've touched a spot with an action
+		AS_NO_ACTION = 1,    // we've touched in an empty area (no action) and ignore remaining events until touch release
+	};
+	touch_status_enum touch_status;
+	key_status_enum key_status;
+	action_state_enum state;
+	int x, y; // x and y coordinates of last touch
+	struct timeval touchStart; // used to track time for long press / key repeat
+
+	void processHoldAndRepeat();
+	void process_EV_REL(input_event& ev);
+	void process_EV_ABS(input_event& ev);
+	void process_EV_KEY(input_event& ev);
+
+	void doTouchStart();
+};
+
+InputHandler input_handler;
+
+
+void InputHandler::processInput()
+{
+	input_event ev;
+	int ret = ev_get(&ev);
 
 	if (ret < 0)
 	{
 		// This path means that we did not get any new touch data, but
 		// we do not get new touch data if you press and hold on either
 		// the screen or on a keyboard key or mouse button
-		if (touch_status || key_status) {
-			// touch and key repeat section
-			struct timeval curTime;
-			long mtime, seconds, useconds;
-			gettimeofday(&curTime, NULL);
-			seconds = curTime.tv_sec - touchStart.tv_sec;
-			useconds = curTime.tv_usec - touchStart.tv_usec;
-			mtime = ((seconds) * 1000 + useconds / 1000.0) + 0.5;
-
-			if (touch_status == TS_TOUCH_AND_HOLD && mtime > 500)
-			{
-				touch_status = TS_TOUCH_REPEAT;
-				gettimeofday(&touchStart, NULL);
-#ifdef _EVENT_LOGGING
-				LOGERR("TOUCH_HOLD: %d,%d\n", x, y);
-#endif
-				PageManager::NotifyTouch(TOUCH_HOLD, x, y);
-				blankTimer.resetTimerAndUnblank();
-			}
-			else if (touch_status == TS_TOUCH_REPEAT && mtime > 100)
-			{
-#ifdef _EVENT_LOGGING
-				LOGERR("TOUCH_REPEAT: %d,%d\n", x, y);
-#endif
-				gettimeofday(&touchStart, NULL);
-				PageManager::NotifyTouch(TOUCH_REPEAT, x, y);
-				blankTimer.resetTimerAndUnblank();
-			}
-			else if (key_status == KS_KEY_PRESSED && mtime > 500)
-			{
-#ifdef _EVENT_LOGGING
-				LOGERR("KEY_HOLD: %d,%d\n", x, y);
-#endif
-				gettimeofday(&touchStart, NULL);
-				key_status = KS_KEY_REPEAT;
-				kb->KeyRepeat();
-				blankTimer.resetTimerAndUnblank();
-
-			}
-			else if (key_status == KS_KEY_REPEAT && mtime > 100)
-			{
-#ifdef _EVENT_LOGGING
-				LOGERR("KEY_REPEAT: %d,%d\n", x, y);
-#endif
-				gettimeofday(&touchStart, NULL);
-				kb->KeyRepeat();
-				blankTimer.resetTimerAndUnblank();
-			}
-		} else {
-			return; // nothing is pressed so do nothing and exit
-		}
+		if (touch_status || key_status)
+			processHoldAndRepeat();
+		return;
 	}
-	else if (ev.type == EV_ABS)
+
+	switch (ev.type)
 	{
+	case EV_ABS:
+		process_EV_ABS(ev);
+		break;
 
-		x = ev.value >> 16;
-		y = ev.value & 0xFFFF;
+	case EV_REL:
+		process_EV_REL(ev);
+		break;
 
-		if (ev.code == 0)
+	case EV_KEY:
+		process_EV_KEY(ev);
+		break;
+	}
+}
+
+void InputHandler::processHoldAndRepeat()
+{
+	HardwareKeyboard *kb = PageManager::GetHardwareKeyboard();
+
+	// touch and key repeat section
+	struct timeval curTime;
+	gettimeofday(&curTime, NULL);
+	long seconds = curTime.tv_sec - touchStart.tv_sec;
+	long useconds = curTime.tv_usec - touchStart.tv_usec;
+	long mtime = ((seconds) * 1000 + useconds / 1000.0) + 0.5;
+
+	if (touch_status == TS_TOUCH_AND_HOLD && mtime > touch_hold_ms)
+	{
+		touch_status = TS_TOUCH_REPEAT;
+		gettimeofday(&touchStart, NULL);
+		LOGEVENT("TOUCH_HOLD: %d,%d\n", x, y);
+		PageManager::NotifyTouch(TOUCH_HOLD, x, y);
+		blankTimer.resetTimerAndUnblank();
+	}
+	else if (touch_status == TS_TOUCH_REPEAT && mtime > touch_repeat_ms)
+	{
+		LOGEVENT("TOUCH_REPEAT: %d,%d\n", x, y);
+		gettimeofday(&touchStart, NULL);
+		PageManager::NotifyTouch(TOUCH_REPEAT, x, y);
+		blankTimer.resetTimerAndUnblank();
+	}
+	else if (key_status == KS_KEY_PRESSED && mtime > key_hold_ms)
+	{
+		LOGEVENT("KEY_HOLD: %d,%d\n", x, y);
+		gettimeofday(&touchStart, NULL);
+		key_status = KS_KEY_REPEAT;
+		kb->KeyRepeat();
+		blankTimer.resetTimerAndUnblank();
+
+	}
+	else if (key_status == KS_KEY_REPEAT && mtime > key_repeat_ms)
+	{
+		LOGEVENT("KEY_REPEAT: %d,%d\n", x, y);
+		gettimeofday(&touchStart, NULL);
+		kb->KeyRepeat();
+		blankTimer.resetTimerAndUnblank();
+	}
+}
+
+void InputHandler::doTouchStart()
+{
+	LOGEVENT("TOUCH_START: %d,%d\n", x, y);
+	if (PageManager::NotifyTouch(TOUCH_START, x, y) > 0)
+		state = AS_NO_ACTION;
+	else
+		state = AS_IN_ACTION_AREA;
+	touch_status = TS_TOUCH_AND_HOLD;
+	gettimeofday(&touchStart, NULL);
+	blankTimer.resetTimerAndUnblank();
+}
+
+void InputHandler::process_EV_ABS(input_event& ev)
+{
+	x = ev.value >> 16;
+	y = ev.value & 0xFFFF;
+
+	if (ev.code == 0)
+	{
+		if (state == AS_IN_ACTION_AREA)
 		{
-			if (state == AS_IN_ACTION_AREA)
-			{
-#ifdef _EVENT_LOGGING
-				LOGERR("TOUCH_RELEASE: %d,%d\n", x, y);
-#endif
-				PageManager::NotifyTouch(TOUCH_RELEASE, x, y);
-				blankTimer.resetTimerAndUnblank();
-			}
-			touch_status = TS_NONE;
+			LOGEVENT("TOUCH_RELEASE: %d,%d\n", x, y);
+			PageManager::NotifyTouch(TOUCH_RELEASE, x, y);
+			blankTimer.resetTimerAndUnblank();
+		}
+		touch_status = TS_NONE;
+	}
+	else
+	{
+		if (!touch_status)
+		{
+			doTouchStart();
 		}
 		else
 		{
-			if (!touch_status)
+			if (state == AS_IN_ACTION_AREA)
 			{
-				if (x != 0 && y != 0) {
-#ifdef _EVENT_LOGGING
-					LOGERR("TOUCH_START: %d,%d\n", x, y);
-#endif
-					if (PageManager::NotifyTouch(TOUCH_START, x, y) > 0)
-						state = AS_NO_ACTION;
-					else
-						state = AS_IN_ACTION_AREA;
-					touch_status = TS_TOUCH_AND_HOLD;
-					gettimeofday(&touchStart, NULL);
-				}
+				LOGEVENT("TOUCH_DRAG: %d,%d\n", x, y);
 				blankTimer.resetTimerAndUnblank();
 			}
-			else
-			{
-				if (state == AS_IN_ACTION_AREA)
-				{
-#ifdef _EVENT_LOGGING
-					LOGERR("TOUCH_DRAG: %d,%d\n", x, y);
-#endif
-					blankTimer.resetTimerAndUnblank();
-				}
-			}
 		}
 	}
-	else if (ev.type == EV_KEY)
+}
+
+void InputHandler::process_EV_KEY(input_event& ev)
+{
+	HardwareKeyboard *kb = PageManager::GetHardwareKeyboard();
+
+	// Handle key-press here
+	LOGEVENT("TOUCH_KEY: %d\n", ev.code);
+	// Left mouse button is treated as a touch
+	if(ev.code == BTN_LEFT)
 	{
-		// Handle key-press here
-#ifdef _EVENT_LOGGING
-		LOGERR("TOUCH_KEY: %d\n", ev.code);
-#endif
-		// Left mouse button
-		if(ev.code == BTN_LEFT)
+		MouseCursor *cursor = PageManager::GetMouseCursor();
+		if(ev.value == 1)
 		{
-			// Left mouse button is treated as a touch
-			if(ev.value == 1)
+			cursor->GetPos(x, y);
+			doTouchStart();
+		}
+		else if(touch_status)
+		{
+			// Left mouse button was previously pressed and now is
+			// being released so send a TOUCH_RELEASE
+			if (state == AS_IN_ACTION_AREA)
 			{
 				cursor->GetPos(x, y);
-#ifdef _EVENT_LOGGING
-				LOGERR("Mouse TOUCH_START: %d,%d\n", x, y);
-#endif
-				if (PageManager::NotifyTouch(TOUCH_START, x, y) > 0)
-					state = AS_NO_ACTION;
-				else
-					state = AS_IN_ACTION_AREA;
-				touch_status = TS_TOUCH_AND_HOLD;
-				gettimeofday(&touchStart, NULL);
-			}
-			else if(touch_status)
-			{
-				// Left mouse button was previously pressed and now is
-				// being released so send a TOUCH_RELEASE
-				if (state == AS_IN_ACTION_AREA)
-				{
-					cursor->GetPos(x, y);
 
-#ifdef _EVENT_LOGGING
-					LOGERR("Mouse TOUCH_RELEASE: %d,%d\n", x, y);
-#endif
-					PageManager::NotifyTouch(TOUCH_RELEASE, x, y);
-
-				}
-				touch_status = TS_NONE;
+				LOGEVENT("Mouse TOUCH_RELEASE: %d,%d\n", x, y);
+				PageManager::NotifyTouch(TOUCH_RELEASE, x, y);
 			}
+			touch_status = TS_NONE;
 		}
-		// side mouse button, often used for "back" function
-		else if(ev.code == BTN_SIDE)
-		{
-			if(ev.value == 1)
-				kb->KeyDown(KEY_BACK);
-			else
-				kb->KeyUp(KEY_BACK);
-		} else if (ev.value != 0) {
-			// This is a key press
-			if (kb->KeyDown(ev.code)) {
-				// Key repeat is enabled for this key
-				key_status = KS_KEY_PRESSED;
-				touch_status = TS_NONE;
-				gettimeofday(&touchStart, NULL);
-				blankTimer.resetTimerAndUnblank();
-			} else {
-				key_status = KS_NONE;
-				touch_status = TS_NONE;
-				blankTimer.resetTimerAndUnblank();
-			}
+	}
+	// side mouse button, often used for "back" function
+	else if(ev.code == BTN_SIDE)
+	{
+		if(ev.value == 1)
+			kb->KeyDown(KEY_BACK);
+		else
+			kb->KeyUp(KEY_BACK);
+	} else if (ev.value != 0) {
+		// This is a key press
+		if (kb->KeyDown(ev.code)) {
+			// Key repeat is enabled for this key
+			key_status = KS_KEY_PRESSED;
+			touch_status = TS_NONE;
+			gettimeofday(&touchStart, NULL);
+			blankTimer.resetTimerAndUnblank();
 		} else {
-			// This is a key release
-			kb->KeyUp(ev.code);
 			key_status = KS_NONE;
 			touch_status = TS_NONE;
 			blankTimer.resetTimerAndUnblank();
 		}
+	} else {
+		// This is a key release
+		kb->KeyUp(ev.code);
+		key_status = KS_NONE;
+		touch_status = TS_NONE;
+		blankTimer.resetTimerAndUnblank();
 	}
-	else if(ev.type == EV_REL)
-	{
-		// Mouse movement
-#ifdef _EVENT_LOGGING
-		LOGERR("EV_REL %d %d\n", ev.code, ev.value);
-#endif
-		if(ev.code == REL_X)
-			cursor->Move(ev.value, 0);
-		else if(ev.code == REL_Y)
-			cursor->Move(0, ev.value);
+}
 
-		if(touch_status) {
-			cursor->GetPos(x, y);
-#ifdef _EVENT_LOGGING
-			LOGERR("Mouse TOUCH_DRAG: %d, %d\n", x, y);
-#endif
-			key_status = KS_NONE;
-		}
+void InputHandler::process_EV_REL(input_event& ev)
+{
+	// Mouse movement
+	MouseCursor *cursor = PageManager::GetMouseCursor();
+	LOGEVENT("EV_REL %d %d\n", ev.code, ev.value);
+	if(ev.code == REL_X)
+		cursor->Move(ev.value, 0);
+	else if(ev.code == REL_Y)
+		cursor->Move(0, ev.value);
+
+	if(touch_status) {
+		cursor->GetPos(x, y);
+		LOGEVENT("Mouse TOUCH_DRAG: %d, %d\n", x, y);
+		key_status = KS_NONE;
 	}
-	return;
+}
+
+void InputHandler::handleDrag()
+{
+	// This allows us to only send one NotifyTouch event per render
+	// cycle to reduce overhead and perceived input latency.
+	static int prevx = 0, prevy = 0; // these track where the last drag notice was so that we don't send duplicate drag notices
+	if (touch_status && (x != prevx || y != prevy)) {
+		prevx = x;
+		prevy = y;
+		if (PageManager::NotifyTouch(TOUCH_DRAG, x, y) > 0)
+			state = AS_NO_ACTION;
+		else
+			state = AS_IN_ACTION_AREA;
+	}
 }
 
 static void setup_ors_command()
@@ -537,7 +566,7 @@
 
 	do
 	{
-		get_input(0); // get inputs but don't send drag notices
+		input_handler.processInput(); // get inputs but don't send drag notices
 		timespec curTime;
 		clock_gettime(CLOCK_MONOTONIC, &curTime);
 
@@ -547,7 +576,7 @@
 		if (diff.tv_sec || diff.tv_nsec > 33333333)
 		{
 			lastCall = curTime;
-			get_input(1); // send only drag notices if needed
+			input_handler.handleDrag(); // send only drag notices if needed
 			return;
 		}
 
@@ -577,10 +606,6 @@
 
 	DataManager::SetValue("tw_loaded", 1);
 
-#ifdef PRINT_RENDER_TIME
-	timespec start, end;
-	int32_t render_t, flip_t;
-#endif
 #ifndef TW_OEM_BUILD
 	struct timeval timeout;
 	fd_set fdset;
@@ -622,6 +647,8 @@
 #else
 			if (ret > 1)
 			{
+				timespec start, end;
+				int32_t render_t, flip_t;
 				clock_gettime(CLOCK_MONOTONIC, &start);
 				PageManager::Render();
 				clock_gettime(CLOCK_MONOTONIC, &end);
@@ -856,10 +883,7 @@
 	// Set the default package
 	PageManager::SelectPackage("TWRP");
 
-	if (!gGuiInputInit)
-	{
-		input_init();
-	}
+	input_handler.init();
 #ifndef TW_OEM_BUILD
 	if (allow_commands)
 	{