shell: Start implementing the popup surface type

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.
This commit is contained in:
Kristian Høgsberg 2012-01-04 22:19:14 -05:00
parent dade64968c
commit b3cca0a411
5 changed files with 176 additions and 26 deletions

View file

@ -128,19 +128,19 @@ sigchild_handler(int s)
} }
static void 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; int32_t x, y;
struct display *display; static const char *entries[] = {
"Roy", "Pris", "Leon", "Zhora"
};
input_get_position(input, &x, &y); input_get_position(input, &x, &y);
display = window_get_display(panel->window); panel->menu = window_create_menu(window_get_display(panel->window),
panel->menu = window_create_transient(display, panel->window, input, time, panel->window,
x - 10, y - 10, width, height); x - 10, y - 10, entries, 4);
window_set_user_data(panel->menu, panel);
window_draw(panel->menu); window_schedule_redraw(panel->menu);
window_flush(panel->menu);
} }
static void static void
@ -253,9 +253,7 @@ panel_button_handler(struct window *window,
panel_activate_item(panel, pi); panel_activate_item(panel, pi);
} else if (button == BTN_RIGHT) { } else if (button == BTN_RIGHT) {
if (state) if (state)
show_menu(panel, input); show_menu(panel, input, time);
else
window_destroy(panel->menu);
} }
} }

View file

@ -167,7 +167,7 @@ key_handler(struct window *window, struct input *input, uint32_t time,
} }
static void 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; int32_t x, y;
static const char *entries[] = { static const char *entries[] = {
@ -176,7 +176,7 @@ show_menu(struct resizor *resizor, struct input *input)
input_get_position(input, &x, &y); input_get_position(input, &x, &y);
resizor->menu = window_create_menu(resizor->display, resizor->menu = window_create_menu(resizor->display,
resizor->window, input, time, resizor->window,
x - 10, y - 10, entries, 4); x - 10, y - 10, entries, 4);
window_schedule_redraw(resizor->menu); window_schedule_redraw(resizor->menu);
@ -192,7 +192,7 @@ button_handler(struct window *window,
switch (button) { switch (button) {
case BTN_RIGHT: case BTN_RIGHT:
if (state) if (state)
show_menu(resizor, input); show_menu(resizor, input, time);
break; break;
} }
} }

View 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 = { static const struct wl_shell_surface_listener shell_surface_listener = {
handle_configure, handle_configure,
handle_popup_done
}; };
void void
@ -2108,6 +2120,7 @@ window_create_transient(struct display *display, struct window *parent,
struct menu { struct menu {
struct window *window; struct window *window;
const char **entries; const char **entries;
uint32_t time;
int current; int current;
int count; int count;
}; };
@ -2159,7 +2172,7 @@ menu_button_handler(struct window *window,
struct menu *menu = data; struct menu *menu = data;
/* Either relase after press-drag-release or click-motion-click. */ /* 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); window_destroy(window);
} }
@ -2209,7 +2222,8 @@ menu_redraw_handler(struct window *window, void *data)
} }
struct window * 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) int32_t x, int32_t y, const char **entries, int count)
{ {
struct window *window; struct window *window;
@ -2226,14 +2240,16 @@ window_create_menu(struct display *display, struct window *parent,
menu->window = window; menu->window = window;
menu->entries = entries; menu->entries = entries;
menu->count = count; menu->count = count;
menu->time = time;
window->decoration = 0; window->decoration = 0;
window->type = TYPE_MENU; window->type = TYPE_MENU;
window->x = x; window->x = x;
window->y = y; window->y = y;
wl_shell_surface_set_transient(window->shell_surface, wl_shell_surface_set_popup(window->shell_surface,
window->parent->shell_surface, input->input_device, time,
window->x, window->y, 0); window->parent->shell_surface,
window->x, window->y, 0);
window_set_motion_handler(window, menu_motion_handler); window_set_motion_handler(window, menu_motion_handler);
window_set_enter_handler(window, menu_enter_handler); window_set_enter_handler(window, menu_enter_handler);

View file

@ -203,7 +203,8 @@ struct window *
window_create_transient(struct display *display, struct window *parent, window_create_transient(struct display *display, struct window *parent,
int32_t x, int32_t y, int32_t width, int32_t height); int32_t x, int32_t y, int32_t width, int32_t height);
struct window * 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); int32_t x, int32_t y, const char **entries, int count);
void void

View file

@ -76,7 +76,8 @@ enum shell_surface_type {
SHELL_SURFACE_TOPLEVEL, SHELL_SURFACE_TOPLEVEL,
SHELL_SURFACE_TRANSIENT, SHELL_SURFACE_TRANSIENT,
SHELL_SURFACE_FULLSCREEN SHELL_SURFACE_FULLSCREEN,
SHELL_SURFACE_POPUP
}; };
struct shell_surface { struct shell_surface {
@ -84,10 +85,18 @@ struct shell_surface {
struct weston_surface *surface; struct weston_surface *surface;
struct wl_listener surface_destroy_listener; struct wl_listener surface_destroy_listener;
struct shell_surface *parent;
enum shell_surface_type type; enum shell_surface_type type;
int32_t saved_x, saved_y; 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 weston_output *output;
struct wl_list link; struct wl_list link;
}; };
@ -337,6 +346,7 @@ reset_shell_surface_type(struct shell_surface *surface)
case SHELL_SURFACE_NONE: case SHELL_SURFACE_NONE:
case SHELL_SURFACE_TOPLEVEL: case SHELL_SURFACE_TOPLEVEL:
case SHELL_SURFACE_TRANSIENT: case SHELL_SURFACE_TRANSIENT:
case SHELL_SURFACE_POPUP:
break; break;
} }
@ -415,12 +425,118 @@ shell_surface_set_fullscreen(struct wl_client *client,
shsurf->type = SHELL_SURFACE_FULLSCREEN; 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 = { static const struct wl_shell_surface_interface shell_surface_implementation = {
shell_surface_move, shell_surface_move,
shell_surface_resize, shell_surface_resize,
shell_surface_set_toplevel, shell_surface_set_toplevel,
shell_surface_set_transient, shell_surface_set_transient,
shell_surface_set_fullscreen shell_surface_set_fullscreen,
shell_surface_set_popup
}; };
static void static void
@ -428,6 +544,9 @@ destroy_shell_surface(struct wl_resource *resource)
{ {
struct shell_surface *shsurf = resource->data; 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 */ /* in case cleaning up a dead client destroys shell_surface first */
if (shsurf->surface) if (shsurf->surface)
wl_list_remove(&shsurf->surface_destroy_listener.link); wl_list_remove(&shsurf->surface_destroy_listener.link);
@ -1066,9 +1185,25 @@ map(struct weston_shell *base,
} }
} }
if (do_configure) switch (surface_type) {
weston_surface_configure(surface, case SHELL_SURFACE_TOPLEVEL:
surface->x, surface->y, width, height); 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) { switch (surface_type) {
case SHELL_SURFACE_TOPLEVEL: case SHELL_SURFACE_TOPLEVEL: