shell: Use a busy cursor animation for unresponsive surfaces
authorKristian Høgsberg <krh@bitplanet.net>
Tue, 5 Jun 2012 13:58:51 +0000 (09:58 -0400)
committerKristian Høgsberg <krh@bitplanet.net>
Tue, 5 Jun 2012 13:58:51 +0000 (09:58 -0400)
clients/desktop-shell.c
clients/window.c
clients/window.h
protocol/desktop-shell.xml
src/shell.c

index cceeadf..b767839 100644 (file)
@@ -46,6 +46,9 @@ struct desktop {
        struct unlock_dialog *unlock_dialog;
        struct task unlock_task;
        struct wl_list outputs;
+
+       struct window *busy_window;
+       struct widget *busy_widget;
 };
 
 struct surface {
@@ -651,6 +654,64 @@ background_create(struct desktop *desktop)
        return background;
 }
 
+static const struct wl_callback_listener busy_cursor_listener;
+
+static void
+busy_cursor_frame_callback(void *data,
+                          struct wl_callback *callback, uint32_t time)
+{
+       struct input *input = data;
+       struct display *display = input_get_display(input);
+       struct desktop *desktop = display_get_user_data(display);
+       struct wl_surface *surface;
+       int index;
+
+       if (callback)
+               wl_callback_destroy(callback);
+       if (input_get_focus_widget(input) != desktop->busy_widget)
+               return;
+
+       /* FIXME: Get frame duration and number of frames from cursor. */
+       index = (time / 100) % 8;
+       input_set_pointer_image_index(input, CURSOR_WATCH, index);
+
+       surface = window_get_wl_surface(desktop->busy_window);
+       callback = wl_surface_frame(surface);
+       wl_callback_add_listener(callback, &busy_cursor_listener, input);
+}
+
+static const struct wl_callback_listener busy_cursor_listener = {
+       busy_cursor_frame_callback
+};
+
+static int
+busy_surface_enter_handler(struct widget *widget, struct input *input,
+                          float x, float y, void *data)
+{
+       busy_cursor_frame_callback(input, NULL, 0);
+
+       return CURSOR_WATCH;
+}
+
+static void
+busy_surface_create(struct desktop *desktop)
+{
+       struct wl_surface *s;
+
+       desktop->busy_window = window_create(desktop->display);
+       s = window_get_wl_surface(desktop->busy_window);
+       desktop_shell_set_busy_surface(desktop->shell, s);
+
+       desktop->busy_widget =
+               window_add_widget(desktop->busy_window, desktop);
+       /* We set the allocation to 1x1 at 0,0 so the fake enter event
+        * at 0,0 will go to this widget. */
+       widget_set_allocation(desktop->busy_widget, 0, 0, 1, 1);
+
+       widget_set_enter_handler(desktop->busy_widget,
+                                busy_surface_enter_handler);
+}
+
 static void
 create_output(struct desktop *desktop, uint32_t id)
 {
@@ -729,6 +790,7 @@ int main(int argc, char *argv[])
                return -1;
        }
 
+       display_set_user_data(desktop.display, &desktop);
        wl_display_add_global_listener(display_get_display(desktop.display),
                                       global_handler, &desktop);
 
@@ -744,6 +806,8 @@ int main(int argc, char *argv[])
                desktop_shell_set_background(desktop.shell, output->output, s);
        }
 
+       busy_surface_create(&desktop);
+
        config_file = config_file_path("weston.ini");
        ret = parse_config_file(config_file,
                                config_sections, ARRAY_LENGTH(config_sections),
index 6b36c84..36d8613 100644 (file)
@@ -2084,6 +2084,12 @@ input_get_position(struct input *input, int32_t *x, int32_t *y)
        *y = input->sy;
 }
 
+struct display *
+input_get_display(struct input *input)
+{
+       return input->display;
+}
+
 struct wl_seat *
 input_get_seat(struct input *input)
 {
index 2521964..8b8e816 100644 (file)
@@ -389,6 +389,9 @@ input_ungrab(struct input *input);
 struct widget *
 input_get_focus_widget(struct input *input);
 
+struct display *
+input_get_display(struct input *input);
+
 struct wl_seat *
 input_get_seat(struct input *input);
 
index d2bb3eb..a9c181c 100644 (file)
 
     <request name="unlock"/>
 
+    <request name="set_busy_surface">
+      <arg name="surface" type="object" interface="wl_surface"/>
+    </request>
+
     <!-- We'll fold most of wl_shell into this interface and then
          they'll share the configure event.  -->
     <event name="configure">
index 40e84b6..ba421b5 100644 (file)
@@ -57,6 +57,9 @@ struct desktop_shell {
        struct weston_layer background_layer;
        struct weston_layer lock_layer;
 
+       struct wl_listener pointer_focus_listener;
+       struct weston_surface *busy_surface;
+
        struct {
                struct weston_process process;
                struct wl_client *client;
@@ -151,13 +154,6 @@ struct shell_surface {
 
        struct ping_timer *ping_timer;
 
-       struct {
-               struct weston_animation current;
-               int exists;
-               int fading_in;
-               uint32_t timestamp;
-       } unresponsive_animation;
-
        struct weston_output *fullscreen_output;
        struct weston_output *output;
        struct wl_list link;
@@ -357,65 +353,68 @@ static const struct wl_pointer_grab_interface move_grab_interface = {
 };
 
 static void
-unresponsive_surface_fade(struct shell_surface *shsurf, bool reverse)
+busy_cursor_grab_focus(struct wl_pointer_grab *base,
+                      struct wl_surface *surface, int32_t x, int32_t y)
 {
-       shsurf->unresponsive_animation.fading_in = reverse ? 0 : 1;
+       struct shell_grab *grab = (struct shell_grab *) base;
+       struct wl_pointer *pointer = base->pointer;
 
-       if(!shsurf->unresponsive_animation.exists) {
-               wl_list_insert(&shsurf->surface->compositor->animation_list,
-                      &shsurf->unresponsive_animation.current.link);
-               shsurf->unresponsive_animation.exists = 1;
-               shsurf->unresponsive_animation.timestamp = weston_compositor_get_time();
-               weston_surface_damage(shsurf->surface);
+       if (grab->grab.focus != surface) {
+               shell_grab_finish(grab);
+               wl_pointer_end_grab(pointer);
+               free(grab);
        }
 }
 
 static void
-unresponsive_fade_frame(struct weston_animation *animation,
-               struct weston_output *output, uint32_t msecs)
+busy_cursor_grab_motion(struct wl_pointer_grab *grab,
+                       uint32_t time, int32_t x, int32_t y)
 {
-       struct shell_surface *shsurf =
-               container_of(animation, struct shell_surface, unresponsive_animation.current);
-       struct weston_surface *surface = shsurf->surface;
-       unsigned int step = 8;
+}
 
-       if (!surface || !shsurf)
+static void
+busy_cursor_grab_button(struct wl_pointer_grab *grab,
+                       uint32_t time, uint32_t button, uint32_t state)
+{
+}
+
+static const struct wl_pointer_grab_interface busy_cursor_grab_interface = {
+       busy_cursor_grab_focus,
+       busy_cursor_grab_motion,
+       busy_cursor_grab_button,
+};
+
+static void
+set_busy_cursor(struct shell_surface *shsurf, struct wl_pointer *pointer)
+{
+       struct shell_grab *grab;
+       struct desktop_shell *shell = shsurf->shell;
+       struct weston_seat *seat = (struct weston_seat *) pointer->seat;
+       struct weston_surface *sprite;
+
+       grab = malloc(sizeof *grab);
+       if (!grab)
                return;
 
-       if (shsurf->unresponsive_animation.fading_in) {
-               while (step < msecs - shsurf->unresponsive_animation.timestamp) {
-                       if (surface->saturation > 1)
-                               surface->saturation -= 5;
-                       if (surface->brightness > 200)
-                               surface->brightness--;
+       shell_grab_init(grab, &busy_cursor_grab_interface, shsurf);
+       grab->grab.focus = &shsurf->surface->surface;
+       wl_pointer_start_grab(pointer, &grab->grab);
+       wl_pointer_set_focus(pointer, &shell->busy_surface->surface, 0, 0);
 
-                       shsurf->unresponsive_animation.timestamp += step;
-               }
+       sprite = (struct weston_surface *) seat->sprite;
+       shell->busy_surface->output = sprite->output;
+}
 
-               if (surface->saturation <= 1 && surface->brightness <= 200) {
-                       wl_list_remove(&shsurf->unresponsive_animation.current.link);
-                       shsurf->unresponsive_animation.exists = 0;
-               }
-       }
-       else {
-               while (step < msecs - shsurf->unresponsive_animation.timestamp) {
-                       if (surface->saturation < 255)
-                               surface->saturation += 5;
-                       if (surface->brightness < 255)
-                               surface->brightness++;
-
-                       shsurf->unresponsive_animation.timestamp += step;
-               }
+static void
+end_busy_cursor(struct shell_surface *shsurf, struct wl_pointer *pointer)
+{
+       struct shell_grab *grab = (struct shell_grab *) pointer->grab;
 
-               if (surface->saturation >= 255 && surface->brightness >= 255) {
-                       surface->saturation = surface->brightness = 255;
-                       wl_list_remove(&shsurf->unresponsive_animation.current.link);
-                       shsurf->unresponsive_animation.exists = 0;
-               }
+       if (grab->grab.interface == &busy_cursor_grab_interface) {
+               shell_grab_finish(grab);
+               wl_pointer_end_grab(pointer);
+               free(grab);
        }
-
-       surface->geometry.dirty = 1;
-       weston_surface_damage(surface);
 }
 
 static void
@@ -435,10 +434,14 @@ static int
 ping_timeout_handler(void *data)
 {
        struct shell_surface *shsurf = data;
+       struct weston_seat *seat;
 
        /* Client is not responding */
        shsurf->unresponsive = 1;
-       unresponsive_surface_fade(shsurf, false);
+
+       wl_list_for_each(seat, &shsurf->surface->compositor->seat_list, link)
+               if (seat->seat.pointer->focus == &shsurf->surface->surface)
+                       set_busy_cursor(shsurf, seat->seat.pointer);
 
        return 1;
 }
@@ -448,7 +451,7 @@ ping_handler(struct weston_surface *surface, uint32_t serial)
 {
        struct shell_surface *shsurf = get_shell_surface(surface);
        struct wl_event_loop *loop;
-       int ping_timeout = 2500;
+       int ping_timeout = 200;
 
        if (!shsurf)
                return;
@@ -471,17 +474,54 @@ ping_handler(struct weston_surface *surface, uint32_t serial)
 }
 
 static void
+handle_pointer_focus(struct wl_listener *listener, void *data)
+{
+       struct wl_pointer *pointer = data;
+       struct weston_surface *surface =
+               (struct weston_surface *) pointer->focus;
+       struct weston_compositor *compositor;
+       struct shell_surface *shsurf;
+       uint32_t serial;
+
+       if (!surface)
+               return;
+
+       compositor = surface->compositor;
+       shsurf = get_shell_surface(surface);
+
+       if (shsurf->unresponsive) {
+               set_busy_cursor(shsurf, pointer);
+       } else {
+               serial = wl_display_next_serial(compositor->wl_display);
+               ping_handler(surface, serial);
+       }
+}
+
+static void
 shell_surface_pong(struct wl_client *client, struct wl_resource *resource,
                                                        uint32_t serial)
 {
        struct shell_surface *shsurf = resource->data;
+       struct desktop_shell *shell = shsurf->shell;
+       struct weston_seat *seat;
+       struct weston_compositor *ec = shsurf->surface->compositor;
+       struct wl_pointer *pointer;
+       int was_unresponsive;
 
        if (shsurf->ping_timer->serial == serial) {
-               if (shsurf->unresponsive) {
+               was_unresponsive = shsurf->unresponsive;
+               shsurf->unresponsive = 0;
+               if (was_unresponsive) {
                        /* Received pong from previously unresponsive client */
-                       unresponsive_surface_fade(shsurf, true);
+                       wl_list_for_each(seat, &ec->seat_list, link) {
+                               pointer = seat->seat.pointer;
+                               if (pointer->focus ==
+                                   &shell->busy_surface->surface &&
+                                   pointer->current ==
+                                   &shsurf->surface->surface)
+                                       end_busy_cursor(shsurf, pointer);
+                       }
                }
-               shsurf->unresponsive = 0;
                ping_timer_destroy(shsurf);
        }
 }
@@ -1223,9 +1263,6 @@ destroy_shell_surface(struct shell_surface *shsurf)
        shsurf->surface->configure = NULL;
        ping_timer_destroy(shsurf);
 
-       if (shsurf->unresponsive_animation.exists)
-               wl_list_remove(&shsurf->unresponsive_animation.current.link);
-
        wl_list_remove(&shsurf->link);
        free(shsurf);
 }
@@ -1291,9 +1328,6 @@ create_shell_surface(void *shell, struct weston_surface *surface,
 
        shsurf->shell = (struct desktop_shell *) shell;
        shsurf->unresponsive = 0;
-       shsurf->unresponsive_animation.exists = 0;
-       shsurf->unresponsive_animation.fading_in = 0;
-       shsurf->unresponsive_animation.current.frame = unresponsive_fade_frame;
        shsurf->saved_position_valid = false;
        shsurf->saved_rotation_valid = false;
        shsurf->surface = surface;
@@ -1522,11 +1556,22 @@ desktop_shell_unlock(struct wl_client *client,
                resume_desktop(shell);
 }
 
+static void
+desktop_shell_set_busy_surface(struct wl_client *client,
+                              struct wl_resource *resource,
+                              struct wl_resource *surface_resource)
+{
+       struct desktop_shell *shell = resource->data;
+
+       shell->busy_surface = surface_resource->data;
+}
+
 static const struct desktop_shell_interface desktop_shell_implementation = {
        desktop_shell_set_background,
        desktop_shell_set_panel,
        desktop_shell_set_lock_surface,
-       desktop_shell_unlock
+       desktop_shell_unlock,
+       desktop_shell_set_busy_surface
 };
 
 static enum shell_surface_type
@@ -2735,6 +2780,10 @@ shell_init(struct weston_compositor *ec)
        if (launch_desktop_shell_process(shell) != 0)
                return -1;
 
+       shell->pointer_focus_listener.notify = handle_pointer_focus;
+       wl_signal_add(&ec->seat->seat.pointer->focus_signal,
+                     &shell->pointer_focus_listener);
+
        shell_add_bindings(ec, shell);
 
        return 0;