window.c: Add touch handlers

This adds basic infrastructure for handling touch events in the toytoolkit.
diff --git a/clients/window.c b/clients/window.c
index cbf8bf5..ae1bc6a 100644
--- a/clients/window.c
+++ b/clients/window.c
@@ -274,6 +274,11 @@
 	widget_leave_handler_t leave_handler;
 	widget_motion_handler_t motion_handler;
 	widget_button_handler_t button_handler;
+	widget_touch_down_handler_t touch_down_handler;
+	widget_touch_up_handler_t touch_up_handler;
+	widget_touch_motion_handler_t touch_motion_handler;
+	widget_touch_frame_handler_t touch_frame_handler;
+	widget_touch_cancel_handler_t touch_cancel_handler;
 	widget_axis_handler_t axis_handler;
 	void *user_data;
 	int opaque;
@@ -281,13 +286,22 @@
 	int default_cursor;
 };
 
+struct touch_point {
+	int32_t id;
+	struct widget *widget;
+	struct wl_list link;
+};
+
 struct input {
 	struct display *display;
 	struct wl_seat *seat;
 	struct wl_pointer *pointer;
 	struct wl_keyboard *keyboard;
+	struct wl_touch *touch;
+	struct wl_list touch_point_list;
 	struct window *pointer_focus;
 	struct window *keyboard_focus;
+	struct window *touch_focus;
 	int current_cursor;
 	uint32_t cursor_anim_start;
 	struct wl_callback *cursor_frame_cb;
@@ -1890,6 +1904,41 @@
 }
 
 void
+widget_set_touch_up_handler(struct widget *widget,
+			    widget_touch_up_handler_t handler)
+{
+	widget->touch_up_handler = handler;
+}
+
+void
+widget_set_touch_down_handler(struct widget *widget,
+			      widget_touch_down_handler_t handler)
+{
+	widget->touch_down_handler = handler;
+}
+
+void
+widget_set_touch_motion_handler(struct widget *widget,
+				widget_touch_motion_handler_t handler)
+{
+	widget->touch_motion_handler = handler;
+}
+
+void
+widget_set_touch_frame_handler(struct widget *widget,
+			       widget_touch_frame_handler_t handler)
+{
+	widget->touch_frame_handler = handler;
+}
+
+void
+widget_set_touch_cancel_handler(struct widget *widget,
+				widget_touch_cancel_handler_t handler)
+{
+	widget->touch_cancel_handler = handler;
+}
+
+void
 widget_set_axis_handler(struct widget *widget,
 			widget_axis_handler_t handler)
 {
@@ -2303,6 +2352,35 @@
 	}
 }
 
+static void
+frame_button_touch_down_handler(struct widget *widget, uint32_t serial,
+				uint32_t time, int32_t id,
+				float x, float y, void *data)
+{
+	struct frame_button *frame_button = data;
+	struct window *window = widget->window;
+
+	switch (frame_button->type) {
+	case FRAME_BUTTON_CLOSE:
+		if (window->close_handler)
+			window->close_handler(window->parent,
+					      window->user_data);
+		else
+			display_exit(window->display);
+		break;
+	case FRAME_BUTTON_MINIMIZE:
+		fprintf(stderr,"Minimize stub\n");
+		break;
+	case FRAME_BUTTON_MAXIMIZE:
+		window_set_maximized(window, window->type != TYPE_MAXIMIZED);
+		break;
+	default:
+		/* Unknown operation */
+		break;
+	}
+}
+
+
 static int
 frame_button_motion_handler(struct widget *widget,
                             struct input *input, uint32_t time,
@@ -2402,6 +2480,7 @@
 	widget_set_redraw_handler(frame_button->widget, frame_button_redraw_handler);
 	widget_set_enter_handler(frame_button->widget, frame_button_enter_handler);
 	widget_set_leave_handler(frame_button->widget, frame_button_leave_handler);
+	widget_set_touch_down_handler(frame_button->widget, frame_button_touch_down_handler);
 	widget_set_button_handler(frame_button->widget, frame_button_button_handler);
 	widget_set_motion_handler(frame_button->widget, frame_button_motion_handler);
 	return frame_button->widget;
@@ -3116,6 +3195,153 @@
 };
 
 static void
+touch_handle_down(void *data, struct wl_touch *wl_touch,
+		  uint32_t serial, uint32_t time, struct wl_surface *surface,
+		  int32_t id, wl_fixed_t x_w, wl_fixed_t y_w)
+{
+	struct input *input = data;
+	struct widget *widget;
+	float sx = wl_fixed_to_double(x_w);
+	float sy = wl_fixed_to_double(y_w);
+
+	DBG("touch_handle_down: %i %i\n", id, wl_list_length(&input->touch_point_list));
+
+	input->touch_focus = wl_surface_get_user_data(surface);
+	if (!input->touch_focus) {
+		DBG("Failed to find to touch focus for surface %p\n", surface);
+		return;
+	}
+
+	widget = window_find_widget(input->touch_focus,
+				    wl_fixed_to_double(x_w),
+				    wl_fixed_to_double(y_w));
+	if (widget) {
+		struct touch_point *tp = xmalloc(sizeof *tp);
+		if (tp) {
+			tp->id = id;
+			tp->widget = widget;
+			wl_list_insert(&input->touch_point_list, &tp->link);
+
+			if (widget->touch_down_handler)
+				(*widget->touch_down_handler)(widget, serial,
+							      time, id,
+							      sx, sy,
+							      widget->user_data);
+		}
+	}
+}
+
+static void
+touch_handle_up(void *data, struct wl_touch *wl_touch,
+		uint32_t serial, uint32_t time, int32_t id)
+{
+	struct input *input = data;
+	struct touch_point *tp, *tmp;
+
+	DBG("touch_handle_up: %i %i\n", id, wl_list_length(&input->touch_point_list));
+
+	if (!input->touch_focus) {
+		DBG("No touch focus found for touch up event!\n");
+		return;
+	}
+
+	wl_list_for_each_safe(tp, tmp, &input->touch_point_list, link) {
+		if (tp->id != id)
+			continue;
+
+		if (tp->widget->touch_up_handler)
+			(*tp->widget->touch_up_handler)(tp->widget, serial,
+							time, id,
+							tp->widget->user_data);
+
+		wl_list_remove(&tp->link);
+		free(tp);
+
+		return;
+	}
+}
+
+static void
+touch_handle_motion(void *data, struct wl_touch *wl_touch,
+		    uint32_t time, int32_t id, wl_fixed_t x_w, wl_fixed_t y_w)
+{
+	struct input *input = data;
+	struct touch_point *tp;
+	float sx = wl_fixed_to_double(x_w);
+	float sy = wl_fixed_to_double(y_w);
+
+	DBG("touch_handle_motion: %i %i\n", id, wl_list_length(&input->touch_point_list));
+
+	if (!input->touch_focus) {
+		DBG("No touch focus found for touch motion event!\n");
+		return;
+	}
+
+	wl_list_for_each(tp, &input->touch_point_list, link) {
+		if (tp->id != id)
+			continue;
+
+		if (tp->widget->touch_motion_handler)
+			(*tp->widget->touch_motion_handler)(tp->widget, time,
+							    id, sx, sy,
+							    tp->widget->user_data);
+		return;
+	}
+}
+
+static void
+touch_handle_frame(void *data, struct wl_touch *wl_touch)
+{
+	struct input *input = data;
+	struct touch_point *tp, *tmp;
+
+	DBG("touch_handle_frame\n");
+
+	if (!input->touch_focus) {
+		DBG("No touch focus found for touch frame event!\n");
+		return;
+	}
+
+	wl_list_for_each_safe(tp, tmp, &input->touch_point_list, link) {
+		if (tp->widget->touch_frame_handler)
+			(*tp->widget->touch_frame_handler)(tp->widget, tp->widget->user_data);
+
+		wl_list_remove(&tp->link);
+		free(tp);
+	}
+}
+
+static void
+touch_handle_cancel(void *data, struct wl_touch *wl_touch)
+{
+	struct input *input = data;
+	struct touch_point *tp, *tmp;
+
+	DBG("touch_handle_cancel\n");
+
+	if (!input->touch_focus) {
+		DBG("No touch focus found for touch cancel event!\n");
+		return;
+	}
+
+	wl_list_for_each_safe(tp, tmp, &input->touch_point_list, link) {
+		if (tp->widget->touch_cancel_handler)
+			(*tp->widget->touch_cancel_handler)(tp->widget, tp->widget->user_data);
+
+		wl_list_remove(&tp->link);
+		free(tp);
+	}
+}
+
+static const struct wl_touch_listener touch_listener = {
+	touch_handle_down,
+	touch_handle_up,
+	touch_handle_motion,
+	touch_handle_frame,
+	touch_handle_cancel,
+};
+
+static void
 seat_handle_capabilities(void *data, struct wl_seat *seat,
 			 enum wl_seat_capability caps)
 {
@@ -3140,6 +3366,15 @@
 		wl_keyboard_destroy(input->keyboard);
 		input->keyboard = NULL;
 	}
+
+	if ((caps & WL_SEAT_CAPABILITY_TOUCH) && !input->touch) {
+		input->touch = wl_seat_get_touch(seat);
+		wl_touch_set_user_data(input->touch, input);
+		wl_touch_add_listener(input->touch, &touch_listener, input);
+	} else if (!(caps & WL_SEAT_CAPABILITY_TOUCH) && input->touch) {
+		wl_touch_destroy(input->touch);
+		input->touch = NULL;
+	}
 }
 
 static const struct wl_seat_listener seat_listener = {
@@ -4693,6 +4928,7 @@
 	input->seat = wl_registry_bind(d->registry, id, &wl_seat_interface, 1);
 	input->pointer_focus = NULL;
 	input->keyboard_focus = NULL;
+	wl_list_init(&input->touch_point_list);
 	wl_list_insert(d->input_list.prev, &input->link);
 
 	wl_seat_add_listener(input->seat, &seat_listener, input);