[PATCH weston] input: Schedule pointer sprite repaint when cursor is set

Pekka Paalanen ppaalanen at gmail.com
Wed Sep 10 04:21:50 PDT 2014


On Mon,  8 Sep 2014 19:33:41 +0200
Jonas Ådahl <jadahl at gmail.com> wrote:

> If a cursor was set with wl_pointer.set_cursor but not in combination
> with an action that has the side effect of damaging the region where
> the cursor is positioned, it would not be drawn. This patch explicitly
> schedules a repaint of the pointer sprite when it is set.
> 
> clickdot is updated to illustrate the bug; when moving the pointer
> over clickdot, the pointer is hidden. When not having moved the
> pointer for 500 ms it is made visible using wl_pointer.set_pointer.
> 
> Signed-off-by: Jonas Ådahl <jadahl at gmail.com>
> ---
>  clients/clickdot.c | 50
> +++++++++++++++++++++++++++++++++++++++++++++++++- src/input.c
> |  4 +++- 2 files changed, 52 insertions(+), 2 deletions(-)
> 
> diff --git a/clients/clickdot.c b/clients/clickdot.c
> index e09cb14..5137ba6 100644
> --- a/clients/clickdot.c
> +++ b/clients/clickdot.c
> @@ -31,6 +31,9 @@
>  #include <cairo.h>
>  #include <math.h>
>  #include <assert.h>
> +#include <sys/timerfd.h>
> +#include <sys/epoll.h>
> +#include <unistd.h>
>  
>  #include <linux/input.h>
>  #include <wayland-client.h>
> @@ -54,6 +57,10 @@ struct clickdot {
>  	} line;
>  
>  	int reset;
> +
> +	struct input *cursor_timeout_input;
> +	int cursor_timeout_fd;
> +	struct task cursor_timeout_task;
>  };
>  
>  static void
> @@ -211,6 +218,19 @@ button_handler(struct widget *widget,
>  	widget_schedule_redraw(widget);
>  }
>  
> +static void
> +cursor_timeout_reset(struct clickdot *clickdot)
> +{
> +	const long cursor_timeout = 500;
> +	struct itimerspec its;
> +
> +	its.it_interval.tv_sec = 0;
> +	its.it_interval.tv_nsec = 0;
> +	its.it_value.tv_sec = cursor_timeout / 1000;
> +	its.it_value.tv_nsec = (cursor_timeout % 1000) * 1000 * 1000;
> +	timerfd_settime(clickdot->cursor_timeout_fd, 0, &its, NULL);
> +}
> +
>  static int
>  motion_handler(struct widget *widget,
>  	       struct input *input, uint32_t time,
> @@ -222,7 +242,10 @@ motion_handler(struct widget *widget,
>  
>  	window_schedule_redraw(clickdot->window);
>  
> -	return CURSOR_LEFT_PTR;
> +	cursor_timeout_reset(clickdot);
> +	clickdot->cursor_timeout_input = input;
> +
> +	return CURSOR_BLANK;
>  }
>  
>  static void
> @@ -244,6 +267,21 @@ leave_handler(struct widget *widget,
>  	clickdot->reset = 1;
>  }
>  
> +static void
> +cursor_timeout_func(struct task *task, uint32_t events)
> +{
> +	struct clickdot *clickdot =
> +		container_of(task, struct clickdot,
> cursor_timeout_task);
> +	uint64_t exp;
> +
> +	if (read(clickdot->cursor_timeout_fd, &exp, sizeof
> (uint64_t)) !=
> +	    sizeof(uint64_t))
> +		abort();
> +
> +	input_set_pointer_image(clickdot->cursor_timeout_input,
> +				CURSOR_LEFT_PTR);
> +}
> +
>  static struct clickdot *
>  clickdot_create(struct display *display)
>  {
> @@ -276,12 +314,22 @@ clickdot_create(struct display *display)
>  	clickdot->line.old_y = -1;
>  	clickdot->reset = 0;
>  
> +	clickdot->cursor_timeout_fd =
> +		timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC);
> +	clickdot->cursor_timeout_task.run = cursor_timeout_func;
> +	display_watch_fd(window_get_display(clickdot->window),
> +			 clickdot->cursor_timeout_fd,
> +			 EPOLLIN, &clickdot->cursor_timeout_task);
> +
>  	return clickdot;
>  }
>  
>  static void
>  clickdot_destroy(struct clickdot *clickdot)
>  {
> +	display_unwatch_fd(window_get_display(clickdot->window),
> +			   clickdot->cursor_timeout_fd);
> +	close(clickdot->cursor_timeout_fd);
>  	if (clickdot->buffer)
>  		cairo_surface_destroy(clickdot->buffer);
>  	widget_destroy(clickdot->widget);
> diff --git a/src/input.c b/src/input.c
> index 5e82f5d..530904d 100644
> --- a/src/input.c
> +++ b/src/input.c
> @@ -1665,8 +1665,10 @@ pointer_set_cursor(struct wl_client *client,
> struct wl_resource *resource, pointer->hotspot_x = x;
>  	pointer->hotspot_y = y;
>  
> -	if (surface->buffer_ref.buffer)
> +	if (surface->buffer_ref.buffer) {
>  		pointer_cursor_surface_configure(surface, 0, 0);
> +		weston_view_schedule_repaint(pointer->sprite);
> +	}
>  }
>  
>  static void

This patch does exactly as it says on the label, except scheduling a
repaint does not imply damage.

The have the following call chain:
pointer_cursor_surface_configure(surface, 0, 0);
  weston_view_set_position(pointer->sprite, x, y);
    weston_view_geometry_dirty(view);
-->output repaint
  ... weston_view_update_transform()
        weston_view_damage_below(view)
          ->damage to the plane to force repaint

Except, weston_view_geometry_dirty() does not happen, if the x,y happen
to be the old values, i.e. no move, which I think is exactly what
happens here. That explains why we need something to force repaint, but
I'm still not sure where the damage comes from... it's probably because
clickdot explicitly damages the cursor surface before set_cursor.

I think weston_view_damage_below() would be more appropriate here,
because despite the same, it does what we want: ensure the next repaint
will paint the cursor area, and schedule a repaint.

Oh well, I also think there may be something not quite right in
pointer_cursor_surface_configure() and is_mapped(), but I don't want to
dig that rabbit hole right now.

Your patch works, so I pushed it. If you agree that
weston_view_damage_below() is more correct, I can do a follow-up.


Thanks,
pq


More information about the wayland-devel mailing list