[PATCH weston] window: use wl_cursor_frame_and_duration() for mouse cursor updates

Derek Foreman derekf at osg.samsung.com
Tue Mar 3 13:26:30 PST 2015


Instead of a frame callback we can now use a timerfd and the time
left in the current cursor frame.  This saves us from setting
the cursor at 60hz even when no updates are required.

Signed-off-by: Derek Foreman <derekf at osg.samsung.com>
---
NOTE: this requires a wayland change I just posted...

 clients/window.c | 124 +++++++++++++++++++++++++++++++------------------------
 1 file changed, 71 insertions(+), 53 deletions(-)

diff --git a/clients/window.c b/clients/window.c
index a04cef9..6cef8d4 100644
--- a/clients/window.c
+++ b/clients/window.c
@@ -38,6 +38,7 @@
 #include <sys/mman.h>
 #include <sys/epoll.h>
 #include <sys/timerfd.h>
+#include <stdbool.h>
 
 #ifdef HAVE_CAIRO_EGL
 #include <wayland-egl.h>
@@ -315,7 +316,9 @@ struct input {
 	struct window *touch_focus;
 	int current_cursor;
 	uint32_t cursor_anim_start;
-	struct wl_callback *cursor_frame_cb;
+	int cursor_delay_fd;
+	bool cursor_timer_running;
+	struct task cursor_task;
 	struct wl_surface *pointer_surface;
 	uint32_t modifiers;
 	uint32_t pointer_enter_serial;
@@ -2641,6 +2644,25 @@ input_ungrab(struct input *input)
 }
 
 static void
+cursor_delay_timer_reset(struct input *input, uint32_t duration)
+{
+	struct itimerspec its;
+
+	if (!duration)
+		input->cursor_timer_running = false;
+	else
+		input->cursor_timer_running = true;
+
+	its.it_interval.tv_sec = 0;
+	its.it_interval.tv_nsec = 0;
+	its.it_value.tv_sec = duration / 1000;
+	its.it_value.tv_nsec = (duration % 1000) * 1000 * 1000;
+	if (timerfd_settime(input->cursor_delay_fd, 0, &its, NULL) < 0) {
+		fprintf(stderr, "could not set cursor timerfd\n: %m");
+	}
+}
+
+static void
 input_remove_pointer_focus(struct input *input)
 {
 	struct window *window = input->pointer_focus;
@@ -2652,6 +2674,7 @@ input_remove_pointer_focus(struct input *input)
 
 	input->pointer_focus = NULL;
 	input->current_cursor = CURSOR_UNSET;
+	cursor_delay_timer_reset(input, 0);
 }
 
 static void
@@ -3522,67 +3545,42 @@ input_set_pointer_image_index(struct input *input, int index)
 			      image->hotspot_x, image->hotspot_y);
 }
 
-static const struct wl_callback_listener pointer_surface_listener;
-
 static void
-pointer_surface_frame_callback(void *data, struct wl_callback *callback,
-			       uint32_t time)
+cursor_func(struct task *task, uint32_t events)
 {
-	struct input *input = data;
+	struct input *input = container_of(task, struct input, cursor_task);
+	struct timespec tp;
 	struct wl_cursor *cursor;
+	uint32_t time, duration;
+	uint64_t exp;
 	int i;
 
-	if (callback) {
-		assert(callback == input->cursor_frame_cb);
-		wl_callback_destroy(callback);
-		input->cursor_frame_cb = NULL;
-	}
+	if (read(input->cursor_delay_fd, &exp, sizeof (uint64_t)) != sizeof (uint64_t))
+		abort();
 
-	if (!input->pointer)
+	if (!input->cursor_timer_running)
 		return;
 
-	if (input->current_cursor == CURSOR_BLANK) {
-		wl_pointer_set_cursor(input->pointer,
-				      input->pointer_enter_serial,
-				      NULL, 0, 0);
-		return;
-	}
-
-	if (input->current_cursor == CURSOR_UNSET)
-		return;
 	cursor = input->display->cursors[input->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)
-		input->cursor_anim_start = 0;
-	else if (input->cursor_anim_start == 0)
-		input->cursor_anim_start = time;
-
-	if (time == 0 || input->cursor_anim_start == 0)
-		i = 0;
-	else
-		i = wl_cursor_frame(cursor, time - input->cursor_anim_start);
-
-	if (cursor->image_count > 1) {
-		input->cursor_frame_cb =
-			wl_surface_frame(input->pointer_surface);
-		wl_callback_add_listener(input->cursor_frame_cb,
-					 &pointer_surface_listener, input);
-	}
+	clock_gettime(CLOCK_MONOTONIC, &tp);
+	time = tp.tv_sec * 1000 + tp.tv_nsec / 1000000;
 
+	i = wl_cursor_frame_and_duration(cursor,
+					 time - input->cursor_anim_start,
+					 &duration);
 	input_set_pointer_image_index(input, i);
+	cursor_delay_timer_reset(input, duration);
 }
 
-static const struct wl_callback_listener pointer_surface_listener = {
-	pointer_surface_frame_callback
-};
-
 void
 input_set_pointer_image(struct input *input, int pointer)
 {
+	struct wl_cursor *cursor;
+	struct timespec tp;
+	uint32_t duration;
 	int force = 0;
 
 	if (!input->pointer)
@@ -3596,17 +3594,31 @@ input_set_pointer_image(struct input *input, int pointer)
 
 	input->current_cursor = pointer;
 	input->cursor_serial = input->pointer_enter_serial;
-	if (!input->cursor_frame_cb)
-		pointer_surface_frame_callback(input, 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 */
-		input_set_pointer_image_index(input, 0);
+
+	cursor_delay_timer_reset(input, 0);
+
+	if (input->current_cursor == CURSOR_UNSET)
+		return;
+
+	if (input->current_cursor == CURSOR_BLANK) {
+		wl_pointer_set_cursor(input->pointer,
+				      input->pointer_enter_serial,
+				      NULL, 0, 0);
+		return;
 	}
+
+	cursor = input->display->cursors[input->current_cursor];
+	if (!cursor)
+		return;
+
+	input_set_pointer_image_index(input, 0);
+	if (cursor->image_count == 1)
+		return;
+
+	clock_gettime(CLOCK_MONOTONIC, &tp);
+	input->cursor_anim_start = tp.tv_sec * 1000 + tp.tv_nsec / 1000000;
+	wl_cursor_frame_and_duration(cursor, 0, &duration);
+	cursor_delay_timer_reset(input, duration);
 }
 
 struct wl_data_device *
@@ -5165,6 +5177,11 @@ display_add_input(struct display *d, uint32_t id)
 	}
 
 	input->pointer_surface = wl_compositor_create_surface(d->compositor);
+	input->cursor_task.run = cursor_func;
+
+	input->cursor_delay_fd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC);
+	display_watch_fd(d, input->cursor_delay_fd, EPOLLIN,
+			 &input->cursor_task);
 
 	set_repeat_info(input, 40, 400);
 
@@ -5207,6 +5224,7 @@ input_destroy(struct input *input)
 	wl_list_remove(&input->link);
 	wl_seat_destroy(input->seat);
 	close(input->repeat_timer_fd);
+	close(input->cursor_delay_fd);
 	free(input);
 }
 
-- 
2.1.4



More information about the wayland-devel mailing list