[PATCH weston 06/19] client: Add tablet cursor support into libtoytoolkit

Stephen Chandler Paul thatslyude at gmail.com
Wed Aug 6 16:07:56 PDT 2014


Again, a lot of this is code that has been reused from the cursor code
for pointers.

Signed-off-by: Stephen Chandler Paul <thatslyude at gmail.com>
---
 clients/window.c | 140 +++++++++++++++++++++++++++++++++++++++++++++++++++++--
 clients/window.h |  14 +++---
 2 files changed, 144 insertions(+), 10 deletions(-)

diff --git a/clients/window.c b/clients/window.c
index ab4ae85..325c3f2 100644
--- a/clients/window.c
+++ b/clients/window.c
@@ -143,6 +143,12 @@ struct tablet {
 
 	struct window *focus;
 	struct widget *focus_widget;
+	uint32_t enter_serial;
+	uint32_t cursor_serial;
+	int current_cursor;
+	struct wl_surface *cursor_surface;
+	uint32_t cursor_anim_start;
+	struct wl_callback *cursor_frame_cb;
 
 	char *name;
 	int32_t vid;
@@ -301,6 +307,7 @@ struct widget {
 	int opaque;
 	int tooltip_count;
 	int default_cursor;
+	int default_tablet_cursor;
 	/* If this is set to false then no cairo surface will be
 	 * created before redrawing the surface. This is useful if the
 	 * redraw handler is going to do completely custom rendering
@@ -1599,6 +1606,7 @@ widget_create(struct window *window, struct surface *surface, void *data)
 	widget->tooltip = NULL;
 	widget->tooltip_count = 0;
 	widget->default_cursor = CURSOR_LEFT_PTR;
+	widget->default_tablet_cursor = CURSOR_LEFT_PTR;
 	widget->use_cairo = 1;
 
 	return widget;
@@ -1657,6 +1665,12 @@ widget_set_default_cursor(struct widget *widget, int cursor)
 }
 
 void
+widget_set_default_tablet_cursor(struct widget *widget, int cursor)
+{
+	widget->default_tablet_cursor = cursor;
+}
+
+void
 widget_get_allocation(struct widget *widget, struct rectangle *allocation)
 {
 	*allocation = widget->allocation;
@@ -3128,6 +3142,116 @@ static const struct wl_touch_listener touch_listener = {
 };
 
 static void
+tablet_set_cursor_image_index(struct tablet *tablet, int index)
+{
+	struct wl_buffer *buffer;
+	struct wl_cursor *cursor;
+	struct wl_cursor_image *image;
+
+	cursor = tablet->input->display->cursors[tablet->current_cursor];
+
+	if (index >= (int)cursor->image_count) {
+		fprintf(stderr, "cursor index out of range\n");
+		return;
+	}
+
+	image = cursor->images[index];
+	buffer = wl_cursor_image_get_buffer(image);
+	if (!buffer)
+		return;
+
+	wl_surface_attach(tablet->cursor_surface, buffer, 0, 0);
+	wl_surface_damage(tablet->cursor_surface, 0, 0, image->width,
+			  image->height);
+	wl_surface_commit(tablet->cursor_surface);
+	wl_tablet_set_cursor(tablet->tablet, tablet->enter_serial,
+			     tablet->cursor_surface, image->hotspot_x,
+			     image->hotspot_y);
+}
+
+static const struct wl_callback_listener tablet_cursor_surface_listener;
+
+static void
+tablet_surface_frame_callback(void *data, struct wl_callback *callback,
+			      uint32_t time)
+{
+	struct tablet *tablet = data;
+	struct wl_cursor *cursor;
+	int i;
+
+	if (callback) {
+		assert(callback == tablet->cursor_frame_cb);
+		wl_callback_destroy(callback);
+		tablet->cursor_frame_cb = NULL;
+	}
+
+	if (tablet->current_cursor == CURSOR_BLANK) {
+		wl_tablet_set_cursor(tablet->tablet, tablet->enter_serial, NULL,
+				     0, 0);
+		return;
+	}
+
+	if (tablet->current_cursor == CURSOR_UNSET)
+		return;
+	cursor = tablet->input->display->cursors[tablet->current_cursor];
+	if (!cursor)
+		return;
+
+	/* FIXME We don't have the current time on the first call so we set
+	 * the animation start to the time of the first frame callback. */
+	if (time == 0)
+		tablet->cursor_anim_start = 0;
+	else if (tablet->cursor_anim_start == 0)
+		tablet->cursor_anim_start = time;
+
+	if (time == 0 || tablet->cursor_anim_start == 0)
+		i = 0;
+	else
+		i = wl_cursor_frame(cursor, time - tablet->cursor_anim_start);
+
+	if (cursor->image_count > 1) {
+		tablet->cursor_frame_cb =
+			wl_surface_frame(tablet->cursor_surface);
+		wl_callback_add_listener(tablet->cursor_frame_cb,
+					 &tablet_cursor_surface_listener,
+					 tablet);
+	}
+
+	tablet_set_cursor_image_index(tablet, i);
+}
+
+static const struct wl_callback_listener tablet_cursor_surface_listener = {
+	tablet_surface_frame_callback
+};
+
+static void
+tablet_set_cursor_image(struct tablet *tablet, int cursor)
+{
+	int force = 0;
+
+	if (tablet->enter_serial > tablet->cursor_serial)
+		force = 1;
+
+	if (!force && cursor == tablet->current_cursor)
+		return;
+
+	tablet->current_cursor = cursor;
+	tablet->cursor_serial = tablet->enter_serial;
+
+	if (!tablet->cursor_frame_cb)
+		tablet_surface_frame_callback(tablet, NULL, 0);
+	else if (force) {
+		/* The current frame callback may be stuck if, for instance,
+		 * the set cursor request was processed by the server after
+		 * this client lost the focus. In this case the cursor surface
+		 * might not be mapped and the frame callback wouldn't ever
+		 * complete. Send a set_cursor and attach to try to map the
+		 * cursor surface again so that the callback will finish */
+		tablet_set_cursor_image_index(tablet, 0);
+	}
+}
+
+static void
 tablet_set_focus_widget(struct tablet *tablet, struct window *window,
 			wl_fixed_t sx, wl_fixed_t sy)
 {
@@ -3166,6 +3290,7 @@ tablet_handle_proximity_in(void *data, struct wl_tablet *wl_tablet,
 		return;
 	}
 	tablet->focus = window;
+	tablet->enter_serial = serial;
 }
 
 static void
@@ -3192,6 +3317,7 @@ tablet_handle_motion(void *data, struct wl_tablet *wl_tablet, uint32_t time,
 	double sy = wl_fixed_to_double(w_sy);
 	struct window *window = tablet->focus;
 	struct widget *widget;
+	int cursor;
 
 	DBG("tablet_handle_motion");
 
@@ -3208,10 +3334,13 @@ tablet_handle_motion(void *data, struct wl_tablet *wl_tablet, uint32_t time,
 	tablet_set_focus_widget(tablet, window, sx, sy);
 	widget = tablet->focus_widget;
 
-	if (widget && widget->tablet_motion_handler) {
-		widget->tablet_motion_handler(widget, tablet, sx, sy, time,
-					      widget->user_data);
-	}
+	if (widget && widget->tablet_motion_handler)
+		cursor = widget->tablet_motion_handler(
+		    widget, tablet, sx, sy, time, widget->user_data);
+	else
+		cursor = widget->default_tablet_cursor;
+
+	tablet_set_cursor_image(tablet, cursor);
 }
 
 static void
@@ -3266,6 +3395,9 @@ tablet_manager_handle_device_added(void *data,
 	};
 	wl_list_insert(&input->tablet_list, &tablet->link);
 
+	tablet->cursor_surface =
+		wl_compositor_create_surface(input->display->compositor);
+
 	DBG("tablet_manager_handle_device_added");
 
 	wl_tablet_add_listener(wl_tablet, &tablet_listener, tablet);
diff --git a/clients/window.h b/clients/window.h
index af02f9b..8adf065 100644
--- a/clients/window.h
+++ b/clients/window.h
@@ -271,12 +271,12 @@ typedef void (*widget_axis_handler_t)(struct widget *widget,
 				      uint32_t axis,
 				      wl_fixed_t value,
 				      void *data);
-typedef void (*widget_tablet_motion_handler_t)(struct widget *widget,
-					       struct tablet *tablet,
-					       float x,
-					       float y,
-					       uint32_t time,
-					       void *data);
+typedef int (*widget_tablet_motion_handler_t)(struct widget *widget,
+					      struct tablet *tablet,
+					      float x,
+					      float y,
+					      uint32_t time,
+					      void *data);
 typedef void (*widget_tablet_proximity_in_handler_t)(struct widget *widget,
 						     struct tablet *tablet,
 						     void *data);
@@ -473,6 +473,8 @@ widget_destroy(struct widget *widget);
 void
 widget_set_default_cursor(struct widget *widget, int cursor);
 void
+widget_set_default_tablet_cursor(struct widget *widget, int cursor);
+void
 widget_get_allocation(struct widget *widget, struct rectangle *allocation);
 
 void
-- 
1.8.5.5



More information about the wayland-devel mailing list