shell: Start implementing the popup surface type
authorKristian Høgsberg <krh@bitplanet.net>
Thu, 5 Jan 2012 03:19:14 +0000 (22:19 -0500)
committerKristian Høgsberg <krh@bitplanet.net>
Fri, 6 Jan 2012 16:41:55 +0000 (11:41 -0500)
This lands the basic behavior of the popup surface type, but there are still
a number of details to be worked out.  Mainly there's a hardcoded timeout
to handle the case of releasing the popup button outside any of the
client windows, which triggers popup_end if it happens after the timeout.
Maybe we just need to add that as an argument, or we could add a new event
that fires in this case to let the client decide whether it ends the popup
or not.

clients/desktop-shell.c
clients/resizor.c
clients/window.c
clients/window.h
src/shell.c

index f20e19a..25fcee6 100644 (file)
@@ -128,19 +128,19 @@ sigchild_handler(int s)
 }
 
 static void
-show_menu(struct panel *panel, struct input *input)
+show_menu(struct panel *panel, struct input *input, uint32_t time)
 {
-       int32_t x, y, width = 200, height = 200;
-       struct display *display;
+       int32_t x, y;
+       static const char *entries[] = {
+               "Roy", "Pris", "Leon", "Zhora"
+       };
 
        input_get_position(input, &x, &y);
-       display = window_get_display(panel->window);
-       panel->menu = window_create_transient(display, panel->window,
-                                             x - 10, y - 10, width, height);
-       window_set_user_data(panel->menu, panel);
+       panel->menu = window_create_menu(window_get_display(panel->window),
+                                        input, time, panel->window,
+                                        x - 10, y - 10, entries, 4);
 
-       window_draw(panel->menu);
-       window_flush(panel->menu);
+       window_schedule_redraw(panel->menu);
 }
 
 static void
@@ -253,9 +253,7 @@ panel_button_handler(struct window *window,
                        panel_activate_item(panel, pi);
        } else if (button == BTN_RIGHT) {
                if (state)
-                       show_menu(panel, input);
-               else
-                       window_destroy(panel->menu);
+                       show_menu(panel, input, time);
        }
 }
 
index 2f32ad5..867ceb9 100644 (file)
@@ -167,7 +167,7 @@ key_handler(struct window *window, struct input *input, uint32_t time,
 }
 
 static void
-show_menu(struct resizor *resizor, struct input *input)
+show_menu(struct resizor *resizor, struct input *input, uint32_t time)
 {
        int32_t x, y;
        static const char *entries[] = {
@@ -176,7 +176,7 @@ show_menu(struct resizor *resizor, struct input *input)
 
        input_get_position(input, &x, &y);
        resizor->menu = window_create_menu(resizor->display,
-                                          resizor->window,
+                                          input, time, resizor->window,
                                           x - 10, y - 10, entries, 4);
 
        window_schedule_redraw(resizor->menu);
@@ -192,7 +192,7 @@ button_handler(struct window *window,
        switch (button) {
        case BTN_RIGHT:
                if (state)
-                       show_menu(resizor, input);
+                       show_menu(resizor, input, time);
                break;
        }
 }
index aa5f48b..3be93b9 100644 (file)
@@ -1799,8 +1799,20 @@ handle_configure(void *data, struct wl_shell_surface *shell_surface,
        }
 }
 
+static void
+handle_popup_done(void *data, struct wl_shell_surface *shell_surface)
+{
+       struct window *window = data;
+
+       /* FIXME: Need more context in this event, at least the input
+        * device.  Or just use wl_callback. */
+
+       window_destroy(window);
+}
+
 static const struct wl_shell_surface_listener shell_surface_listener = {
        handle_configure,
+       handle_popup_done
 };
 
 void
@@ -2108,6 +2120,7 @@ window_create_transient(struct display *display, struct window *parent,
 struct menu {
        struct window *window;
        const char **entries;
+       uint32_t time;
        int current;
        int count;
 };
@@ -2159,7 +2172,7 @@ menu_button_handler(struct window *window,
        struct menu *menu = data;
 
        /* Either relase after press-drag-release or click-motion-click. */
-       if (state == 0 && 0 <= menu->current && menu->current < menu->count)
+       if (state == 0 && time - menu->time > 500)
                window_destroy(window);
 }
 
@@ -2209,7 +2222,8 @@ menu_redraw_handler(struct window *window, void *data)
 }
 
 struct window *
-window_create_menu(struct display *display, struct window *parent,
+window_create_menu(struct display *display,
+                  struct input *input, uint32_t time, struct window *parent,
                   int32_t x, int32_t y, const char **entries, int count)
 {
        struct window *window;
@@ -2226,14 +2240,16 @@ window_create_menu(struct display *display, struct window *parent,
        menu->window = window;
        menu->entries = entries;
        menu->count = count;
+       menu->time = time;
        window->decoration = 0;
        window->type = TYPE_MENU;
        window->x = x;
        window->y = y;
 
-       wl_shell_surface_set_transient(window->shell_surface,
-                                 window->parent->shell_surface,
-                                 window->x, window->y, 0);
+       wl_shell_surface_set_popup(window->shell_surface,
+                                  input->input_device, time,
+                                  window->parent->shell_surface,
+                                  window->x, window->y, 0);
 
        window_set_motion_handler(window, menu_motion_handler);
        window_set_enter_handler(window, menu_enter_handler);
index 75bafc1..f1bad3f 100644 (file)
@@ -203,7 +203,8 @@ struct window *
 window_create_transient(struct display *display, struct window *parent,
                        int32_t x, int32_t y, int32_t width, int32_t height);
 struct window *
-window_create_menu(struct display *display, struct window *parent,
+window_create_menu(struct display *display,
+                  struct input *input, uint32_t time, struct window *parent,
                   int32_t x, int32_t y, const char **entries, int count);
 
 void
index b27067d..9386d1e 100644 (file)
@@ -76,7 +76,8 @@ enum shell_surface_type {
 
        SHELL_SURFACE_TOPLEVEL,
        SHELL_SURFACE_TRANSIENT,
-       SHELL_SURFACE_FULLSCREEN
+       SHELL_SURFACE_FULLSCREEN,
+       SHELL_SURFACE_POPUP
 };
 
 struct shell_surface {
@@ -84,10 +85,18 @@ struct shell_surface {
 
        struct weston_surface *surface;
        struct wl_listener surface_destroy_listener;
+       struct shell_surface *parent;
 
        enum shell_surface_type type;
        int32_t saved_x, saved_y;
 
+       struct {
+               struct wl_grab grab;
+               uint32_t time;
+               int32_t x, y;
+               int32_t initial_up;
+       } popup;
+
        struct weston_output *output;
        struct wl_list link;
 };
@@ -337,6 +346,7 @@ reset_shell_surface_type(struct shell_surface *surface)
        case SHELL_SURFACE_NONE:
        case SHELL_SURFACE_TOPLEVEL:
        case SHELL_SURFACE_TRANSIENT:
+       case SHELL_SURFACE_POPUP:
                break;
        }
 
@@ -415,12 +425,118 @@ shell_surface_set_fullscreen(struct wl_client *client,
        shsurf->type = SHELL_SURFACE_FULLSCREEN;
 }
 
+static void
+popup_grab_focus(struct wl_grab *grab, uint32_t time,
+                struct wl_surface *surface, int32_t x, int32_t y)
+{
+       struct wl_input_device *device = grab->input_device;
+       struct shell_surface *priv =
+               container_of(grab, struct shell_surface, popup.grab);
+       struct wl_client *client = priv->surface->surface.resource.client;
+
+       if (surface->resource.client == client) {
+               wl_input_device_set_pointer_focus(device, surface, time,
+                                                 device->x, device->y, x, y);
+               grab->focus = surface;
+       } else {
+               wl_input_device_set_pointer_focus(device, NULL,
+                                                 time, 0, 0, 0, 0);
+               grab->focus = NULL;
+       }
+}
+
+static void
+popup_grab_motion(struct wl_grab *grab,
+                 uint32_t time, int32_t x, int32_t y)
+{
+       struct wl_input_device *device = grab->input_device;
+       struct wl_resource *resource;
+
+       resource = grab->input_device->pointer_focus_resource;
+       if (resource)
+               wl_resource_post_event(resource, WL_INPUT_DEVICE_MOTION,
+                                      time, device->x, device->y, x, y);
+}
+
+static void
+popup_grab_button(struct wl_grab *grab,
+                 uint32_t time, int32_t button, int32_t state)
+{
+       struct wl_resource *resource;
+       struct shell_surface *shsurf =
+               container_of(grab, struct shell_surface, popup.grab);
+
+       resource = grab->input_device->pointer_focus_resource;
+       if (resource) {
+               wl_resource_post_event(resource, WL_INPUT_DEVICE_BUTTON,
+                                      time, button, state);
+       } else if (state == 0 &&
+                  (shsurf->popup.initial_up ||
+                   time - shsurf->popup.time > 500)) {
+               wl_resource_post_event(&shsurf->resource,
+                                      WL_SHELL_SURFACE_POPUP_DONE);
+               wl_input_device_end_grab(grab->input_device, time);
+               shsurf->popup.grab.input_device = NULL;
+       }
+
+       if (state == 0)
+               shsurf->popup.initial_up = 1;
+}
+
+static const struct wl_grab_interface popup_grab_interface = {
+       popup_grab_focus,
+       popup_grab_motion,
+       popup_grab_button,
+};
+
+static void
+shell_map_popup(struct shell_surface *shsurf, uint32_t time)
+{
+       struct wl_input_device *device;
+       struct weston_surface *es = shsurf->surface;
+       struct weston_surface *parent = shsurf->parent->surface;
+
+       es->output = parent->output;
+
+       shsurf->popup.grab.interface = &popup_grab_interface;
+       device = es->compositor->input_device;
+
+       es->x = shsurf->parent->surface->x + shsurf->popup.x;
+       es->y = shsurf->parent->surface->y + shsurf->popup.y;
+
+       shsurf->popup.grab.input_device = device;
+       shsurf->popup.time = device->grab_time;
+       shsurf->popup.initial_up = 0;
+
+       wl_input_device_start_grab(shsurf->popup.grab.input_device,
+                                  &shsurf->popup.grab, shsurf->popup.time);
+}
+
+static void
+shell_surface_set_popup(struct wl_client *client,
+                       struct wl_resource *resource,
+                       struct wl_resource *input_device_resource,
+                       uint32_t time,
+                       struct wl_resource *parent_resource,
+                       int32_t x, int32_t y, uint32_t flags)
+{
+       struct shell_surface *shsurf = resource->data;
+       struct weston_surface *es = shsurf->surface;
+
+       weston_surface_damage(es);
+       shsurf->type = SHELL_SURFACE_POPUP;
+       shsurf->parent = parent_resource->data;
+       shsurf->popup.x = x;
+       shsurf->popup.y = y;
+}
+
 static const struct wl_shell_surface_interface shell_surface_implementation = {
        shell_surface_move,
        shell_surface_resize,
        shell_surface_set_toplevel,
        shell_surface_set_transient,
-       shell_surface_set_fullscreen
+       shell_surface_set_fullscreen,
+       shell_surface_set_popup
 };
 
 static void
@@ -428,6 +544,9 @@ destroy_shell_surface(struct wl_resource *resource)
 {
        struct shell_surface *shsurf = resource->data;
 
+       if (shsurf->popup.grab.input_device)
+               wl_input_device_end_grab(shsurf->popup.grab.input_device, 0);
+
        /* in case cleaning up a dead client destroys shell_surface first */
        if (shsurf->surface)
                wl_list_remove(&shsurf->surface_destroy_listener.link);
@@ -1066,9 +1185,25 @@ map(struct weston_shell *base,
                }
        }
 
-       if (do_configure)
-               weston_surface_configure(surface,
-                                      surface->x, surface->y, width, height);
+       switch (surface_type) {
+       case SHELL_SURFACE_TOPLEVEL:
+               surface->x = 10 + random() % 400;
+               surface->y = 10 + random() % 400;
+               break;
+       case SHELL_SURFACE_POPUP:
+               shell_map_popup(shsurf, shsurf->popup.time);
+               break;
+       default:
+               break;
+       }
+
+       surface->width = width;
+       surface->height = height;
+       if (do_configure) {
+               weston_surface_configure(surface, surface->x, surface->y,
+                                        width, height);
+               weston_compositor_repick(compositor);
+       }
 
        switch (surface_type) {
        case SHELL_SURFACE_TOPLEVEL: