diff --git a/clients/desktop-shell.c b/clients/desktop-shell.c index f20e19a7..25fcee60 100644 --- a/clients/desktop-shell.c +++ b/clients/desktop-shell.c @@ -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); } } diff --git a/clients/resizor.c b/clients/resizor.c index 2f32ad5b..867ceb90 100644 --- a/clients/resizor.c +++ b/clients/resizor.c @@ -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; } } diff --git a/clients/window.c b/clients/window.c index aa5f48b2..3be93b9c 100644 --- a/clients/window.c +++ b/clients/window.c @@ -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); diff --git a/clients/window.h b/clients/window.h index 75bafc13..f1bad3fd 100644 --- a/clients/window.h +++ b/clients/window.h @@ -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 diff --git a/src/shell.c b/src/shell.c index b27067dc..9386d1ef 100644 --- a/src/shell.c +++ b/src/shell.c @@ -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: