diff options
Diffstat (limited to 'src/shell.c')
-rw-r--r-- | src/shell.c | 145 |
1 files changed, 140 insertions, 5 deletions
diff --git a/src/shell.c b/src/shell.c index b27067d..9386d1e 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: |