diff --git a/backend/x11/backend.c b/backend/x11/backend.c index ed88064e..2134536c 100644 --- a/backend/x11/backend.c +++ b/backend/x11/backend.c @@ -47,7 +47,7 @@ static bool handle_x11_event(struct wlr_x11_backend *x11, xcb_generic_event_t *e xcb_key_press_event_t *ev = (xcb_key_press_event_t *)event; struct wlr_event_keyboard_key key = { .time_sec = ev->time / 1000, - .time_usec = (ev->time % 1000) * 1000, + .time_usec = ev->time * 1000, .keycode = ev->detail - 8, .state = event->response_type == XCB_KEY_PRESS ? WLR_KEY_PRESSED : WLR_KEY_RELEASED, @@ -65,7 +65,7 @@ static bool handle_x11_event(struct wlr_x11_backend *x11, xcb_generic_event_t *e struct wlr_event_pointer_button button = { .device = &x11->pointer_dev, .time_sec = ev->time / 1000, - .time_usec = (ev->time % 1000) * 1000, + .time_usec = ev->time * 1000, .button = xcb_button_to_wl(ev->detail), .state = event->response_type == XCB_BUTTON_PRESS ? WLR_BUTTON_PRESSED : WLR_BUTTON_RELEASED, @@ -80,7 +80,7 @@ static bool handle_x11_event(struct wlr_x11_backend *x11, xcb_generic_event_t *e struct wlr_event_pointer_motion_absolute abs = { .device = &x11->pointer_dev, .time_sec = ev->time / 1000, - .time_usec = (ev->time % 1000) * 1000, + .time_usec = ev->time * 1000, .x_mm = ev->event_x, .y_mm = ev->event_y, .width_mm = output->wlr_output.width, @@ -111,7 +111,7 @@ static bool handle_x11_event(struct wlr_x11_backend *x11, xcb_generic_event_t *e struct wlr_event_pointer_motion_absolute abs = { .device = &x11->pointer_dev, .time_sec = x11->time / 1000, - .time_usec = (x11->time % 1000) * 1000, + .time_usec = x11->time * 1000, .x_mm = pointer->root_x, .y_mm = pointer->root_y, .width_mm = output->wlr_output.width, diff --git a/include/rootston/input.h b/include/rootston/input.h index 09509e85..e20446ea 100644 --- a/include/rootston/input.h +++ b/include/rootston/input.h @@ -105,6 +105,8 @@ struct roots_input { struct wl_listener cursor_axis; struct wl_listener cursor_tool_axis; struct wl_listener cursor_tool_tip; + + struct wl_listener pointer_grab_end; }; struct roots_input *input_create(struct roots_server *server, diff --git a/include/wlr/types/wlr_seat.h b/include/wlr/types/wlr_seat.h index 489bd529..d3d3e00d 100644 --- a/include/wlr/types/wlr_seat.h +++ b/include/wlr/types/wlr_seat.h @@ -24,6 +24,52 @@ struct wlr_seat_handle { struct wl_list link; }; +struct wlr_seat_pointer_grab; + +struct wlr_pointer_grab_interface { + void (*enter)(struct wlr_seat_pointer_grab *grab, + struct wlr_surface *surface, double sx, double sy); + void (*motion)(struct wlr_seat_pointer_grab *grab, uint32_t time, + double sx, double sy); + uint32_t (*button)(struct wlr_seat_pointer_grab *grab, uint32_t time, + uint32_t button, uint32_t state); + void (*axis)(struct wlr_seat_pointer_grab *grab, uint32_t time, + enum wlr_axis_orientation orientation, double value); + void (*cancel)(struct wlr_seat_pointer_grab *grab); +}; + +struct wlr_seat_keyboard_grab; + +struct wlr_keyboard_grab_interface { + void (*enter)(struct wlr_seat_keyboard_grab *grab, struct wlr_surface *surface); + void (*key)(struct wlr_seat_keyboard_grab *grab, uint32_t time, + uint32_t key, uint32_t state); + void (*modifiers)(struct wlr_seat_keyboard_grab *grab, + uint32_t mods_depressed, uint32_t mods_latched, + uint32_t mods_locked, uint32_t group); + void (*cancel)(struct wlr_seat_keyboard_grab *grab); +}; + +/** + * Passed to `wlr_seat_keyboard_start_grab()` to start a grab of the keyboard. + * The grabber is responsible for handling keyboard events for the seat. + */ +struct wlr_seat_keyboard_grab { + const struct wlr_keyboard_grab_interface *interface; + struct wlr_seat *seat; + void *data; +}; + +/** + * Passed to `wlr_seat_pointer_start_grab()` to start a grab of the pointer. The + * grabber is responsible for handling pointer events for the seat. + */ +struct wlr_seat_pointer_grab { + const struct wlr_pointer_grab_interface *interface; + struct wlr_seat *seat; + void *data; +}; + struct wlr_seat_pointer_state { struct wlr_seat *wlr_seat; struct wlr_seat_handle *focused_handle; @@ -31,6 +77,9 @@ struct wlr_seat_pointer_state { struct wl_listener surface_destroy; struct wl_listener resource_destroy; + + struct wlr_seat_pointer_grab *grab; + struct wlr_seat_pointer_grab *default_grab; }; struct wlr_seat_keyboard { @@ -50,6 +99,9 @@ struct wlr_seat_keyboard_state { struct wl_listener surface_destroy; struct wl_listener resource_destroy; + + struct wlr_seat_keyboard_grab *grab; + struct wlr_seat_keyboard_grab *default_grab; }; struct wlr_seat { @@ -67,12 +119,17 @@ struct wlr_seat { struct { struct wl_signal client_bound; struct wl_signal client_unbound; + + struct wl_signal pointer_grab_begin; + struct wl_signal pointer_grab_end; + + struct wl_signal keyboard_grab_begin; + struct wl_signal keyboard_grab_end; } events; void *data; }; - /** * Allocates a new wlr_seat and adds a wl_seat global to the display. */ @@ -109,6 +166,8 @@ bool wlr_seat_pointer_surface_has_focus(struct wlr_seat *wlr_seat, * Send a pointer enter event to the given surface and consider it to be the * focused surface for the pointer. This will send a leave event to the last * surface that was entered. Coordinates for the enter event are surface-local. + * Compositor should use `wlr_seat_pointer_notify_enter()` to change pointer + * focus to respect pointer grabs. */ void wlr_seat_pointer_enter(struct wlr_seat *wlr_seat, struct wlr_surface *surface, double sx, double sy); @@ -120,21 +179,71 @@ void wlr_seat_pointer_clear_focus(struct wlr_seat *wlr_seat); /** * Send a motion event to the surface with pointer focus. Coordinates for the - * motion event are surface-local. + * motion event are surface-local. Compositors should use + * `wlr_seat_pointer_notify_motion()` to send motion events to respect pointer + * grabs. */ void wlr_seat_pointer_send_motion(struct wlr_seat *wlr_seat, uint32_t time, double sx, double sy); /** * Send a button event to the surface with pointer focus. Coordinates for the - * button event are surface-local. Returns the serial. + * button event are surface-local. Returns the serial. Compositors should use + * `wlr_seat_pointer_notify_button()` to send button events to respect pointer + * grabs. */ uint32_t wlr_seat_pointer_send_button(struct wlr_seat *wlr_seat, uint32_t time, uint32_t button, uint32_t state); +/** + * Send an axis event to the surface with pointer focus. Compositors should use + * `wlr_seat_pointer_notify_axis()` to send axis events to respect pointer + * grabs. + **/ void wlr_seat_pointer_send_axis(struct wlr_seat *wlr_seat, uint32_t time, enum wlr_axis_orientation orientation, double value); +/** + * Start a grab of the pointer of this seat. The grabber is responsible for + * handling all pointer events until the grab ends. + */ +void wlr_seat_pointer_start_grab(struct wlr_seat *wlr_seat, + struct wlr_seat_pointer_grab *grab); + +/** + * End the grab of the pointer of this seat. This reverts the grab back to the + * default grab for the pointer. + */ +void wlr_seat_pointer_end_grab(struct wlr_seat *wlr_seat); + +/** + * Notify the seat of a pointer enter event to the given surface and request it to be the + * focused surface for the pointer. Pass surface-local coordinates where the + * enter occurred. + */ +void wlr_seat_pointer_notify_enter(struct wlr_seat *wlr_seat, + struct wlr_surface *surface, double sx, double sy); + +/** + * Notify the seat of motion over the given surface. Pass surface-local + * coordinates where the pointer motion occurred. + */ +void wlr_seat_pointer_notify_motion(struct wlr_seat *wlr_seat, uint32_t time, + double sx, double sy); + +/** + * Notify the seat that a button has been pressed. Returns the serial of the + * button press or zero if no button press was sent. + */ +uint32_t wlr_seat_pointer_notify_button(struct wlr_seat *wlr_seat, + uint32_t time, uint32_t button, uint32_t state); + +/** + * Notify the seat of an axis event. + */ +void wlr_seat_pointer_notify_axis(struct wlr_seat *wlr_seat, uint32_t time, + enum wlr_axis_orientation orientation, double value); + /** * Attaches this keyboard to the seat. Key events from this keyboard will be * propegated to the focused client. @@ -149,10 +258,48 @@ void wlr_seat_attach_keyboard(struct wlr_seat *seat, */ void wlr_seat_detach_keyboard(struct wlr_seat *seat, struct wlr_keyboard *kb); +/** + * Start a grab of the keyboard of this seat. The grabber is responsible for + * handling all keyboard events until the grab ends. + */ +void wlr_seat_keyboard_start_grab(struct wlr_seat *wlr_seat, + struct wlr_seat_keyboard_grab *grab); + +/** + * End the grab of the keyboard of this seat. This reverts the grab back to the + * default grab for the keyboard. + */ +void wlr_seat_keyboard_end_grab(struct wlr_seat *wlr_seat); + +/** + * Send the keyboard key to focused keyboard resources. Compositors should use + * `wlr_seat_attach_keyboard()` to automatically handle keyboard events. + */ +void wlr_seat_keyboard_send_key(struct wlr_seat *seat, uint32_t time, + uint32_t key, uint32_t state); + +/** + * Send the modifier state to focused keyboard resources. Compositors should use + * `wlr_seat_attach_keyboard()` to automatically handle keyboard events. + */ +void wlr_seat_keyboard_send_modifiers(struct wlr_seat *seat, + uint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_locked, + uint32_t group); + +/** + * Notify the seat that the keyboard focus has changed and request it to be the + * focused surface for this keyboard. Defers to any current grab of the seat's + * keyboard. + */ +void wlr_seat_keyboard_notify_enter(struct wlr_seat *wlr_seat, + struct wlr_surface *surface); + /** * Send a keyboard enter event to the given surface and consider it to be the * focused surface for the keyboard. This will send a leave event to the last - * surface that was entered. Pass an array of currently pressed keys. + * surface that was entered. Compositors should use + * `wlr_seat_keyboard_notify_enter()` to change keyboard focus to respect + * keyboard grabs. */ void wlr_seat_keyboard_enter(struct wlr_seat *wlr_seat, struct wlr_surface *surface); diff --git a/include/wlr/types/wlr_xdg_shell_v6.h b/include/wlr/types/wlr_xdg_shell_v6.h index de8dfca7..00295ec1 100644 --- a/include/wlr/types/wlr_xdg_shell_v6.h +++ b/include/wlr/types/wlr_xdg_shell_v6.h @@ -2,11 +2,13 @@ #define WLR_TYPES_WLR_XDG_SHELL_V6_H #include +#include #include struct wlr_xdg_shell_v6 { struct wl_global *wl_global; struct wl_list clients; + struct wl_list popup_grabs; uint32_t ping_timeout; struct { @@ -28,6 +30,27 @@ struct wlr_xdg_client_v6 { struct wl_event_source *ping_timer; }; +struct wlr_xdg_popup_v6 { + struct wlr_xdg_surface_v6 *base; + + struct wl_resource *resource; + bool committed; + struct wlr_xdg_surface_v6 *parent; + struct wlr_seat *seat; + struct wlr_box geometry; + + struct wl_list grab_link; // wlr_xdg_popup_grab_v6::popups +}; + +// each seat gets a popup grab +struct wlr_xdg_popup_grab_v6 { + struct wl_client *client; + struct wlr_seat_pointer_grab pointer_grab; + struct wlr_seat_keyboard_grab keyboard_grab; + struct wlr_seat *seat; + struct wl_list popups; + struct wl_list link; // wlr_xdg_shell_v6::popup_grabs +}; enum wlr_xdg_surface_v6_role { WLR_XDG_SURFACE_V6_ROLE_NONE, @@ -61,7 +84,6 @@ struct wlr_xdg_toplevel_v6 { struct wlr_xdg_toplevel_v6_state current; }; -// TODO split up into toplevel and popup configure struct wlr_xdg_surface_v6_configure { struct wl_list link; // wlr_xdg_surface_v6::configure_list uint32_t serial; @@ -74,7 +96,14 @@ struct wlr_xdg_surface_v6 { struct wlr_surface *surface; struct wl_list link; // wlr_xdg_client_v6::surfaces enum wlr_xdg_surface_v6_role role; - struct wlr_xdg_toplevel_v6 *toplevel_state; + + union { + struct wlr_xdg_toplevel_v6 *toplevel_state; + struct wlr_xdg_popup_v6 *popup_state; + }; + + struct wl_list popups; + struct wl_list popup_link; bool configured; struct wl_event_source *configure_idle; diff --git a/rootston/cursor.c b/rootston/cursor.c index 9c4b0dd8..8eb22459 100644 --- a/rootston/cursor.c +++ b/rootston/cursor.c @@ -68,8 +68,8 @@ void cursor_update_position(struct roots_input *input, uint32_t time) { view = view_at(desktop, input->cursor->x, input->cursor->y, &surface, &sx, &sy); if (view) { - wlr_seat_pointer_enter(input->wl_seat, surface, sx, sy); - wlr_seat_pointer_send_motion(input->wl_seat, time, sx, sy); + wlr_seat_pointer_notify_enter(input->wl_seat, surface, sx, sy); + wlr_seat_pointer_notify_motion(input->wl_seat, time, sx, sy); } else { wlr_seat_pointer_clear_focus(input->wl_seat); } @@ -156,7 +156,7 @@ static void handle_cursor_motion(struct wl_listener *listener, void *data) { struct wlr_event_pointer_motion *event = data; wlr_cursor_move(input->cursor, event->device, event->delta_x, event->delta_y); - cursor_update_position(input, (uint32_t)event->time_usec); + cursor_update_position(input, (uint32_t)(event->time_usec / 1000)); } static void handle_cursor_motion_absolute(struct wl_listener *listener, @@ -166,14 +166,14 @@ static void handle_cursor_motion_absolute(struct wl_listener *listener, struct wlr_event_pointer_motion_absolute *event = data; wlr_cursor_warp_absolute(input->cursor, event->device, event->x_mm / event->width_mm, event->y_mm / event->height_mm); - cursor_update_position(input, (uint32_t)event->time_usec); + cursor_update_position(input, (uint32_t)(event->time_usec / 1000)); } static void handle_cursor_axis(struct wl_listener *listener, void *data) { struct roots_input *input = wl_container_of(listener, input, cursor_axis); struct wlr_event_pointer_axis *event = data; - wlr_seat_pointer_send_axis(input->wl_seat, event->time_sec, + wlr_seat_pointer_notify_axis(input->wl_seat, event->time_sec, event->orientation, event->delta); } @@ -221,7 +221,7 @@ static void do_cursor_button_press(struct roots_input *input, return; } - uint32_t serial = wlr_seat_pointer_send_button(input->wl_seat, time, button, + uint32_t serial = wlr_seat_pointer_notify_button(input->wl_seat, time, button, state); int i; @@ -238,7 +238,7 @@ static void do_cursor_button_press(struct roots_input *input, % (sizeof(input->input_events) / sizeof(input->input_events[0])); set_view_focus(input, desktop, view); if (view) { - wlr_seat_keyboard_enter(input->wl_seat, surface); + wlr_seat_keyboard_notify_enter(input->wl_seat, surface); } break; } @@ -248,7 +248,7 @@ static void handle_cursor_button(struct wl_listener *listener, void *data) { struct roots_input *input = wl_container_of(listener, input, cursor_button); struct wlr_event_pointer_button *event = data; do_cursor_button_press(input, input->cursor, event->device, - (uint32_t)event->time_usec, event->button, event->state); + (uint32_t)(event->time_usec / 1000), event->button, event->state); } static void handle_tool_axis(struct wl_listener *listener, void *data) { @@ -258,7 +258,7 @@ static void handle_tool_axis(struct wl_listener *listener, void *data) { (event->updated_axes & WLR_TABLET_TOOL_AXIS_Y)) { wlr_cursor_warp_absolute(input->cursor, event->device, event->x_mm / event->width_mm, event->y_mm / event->height_mm); - cursor_update_position(input, (uint32_t)event->time_usec); + cursor_update_position(input, (uint32_t)(event->time_usec / 1000)); } } @@ -266,7 +266,13 @@ static void handle_tool_tip(struct wl_listener *listener, void *data) { struct roots_input *input = wl_container_of(listener, input, cursor_tool_tip); struct wlr_event_tablet_tool_tip *event = data; do_cursor_button_press(input, input->cursor, event->device, - (uint32_t)event->time_usec, BTN_LEFT, event->state); + (uint32_t)(event->time_usec / 1000), BTN_LEFT, event->state); +} + +static void handle_pointer_grab_end(struct wl_listener *listener, void *data) { + struct roots_input *input = + wl_container_of(listener, input, pointer_grab_end); + cursor_update_position(input, 0); } void cursor_initialize(struct roots_input *input) { @@ -296,6 +302,9 @@ void cursor_initialize(struct roots_input *input) { wl_list_init(&input->cursor_tool_tip.link); wl_signal_add(&cursor->events.tablet_tool_tip, &input->cursor_tool_tip); input->cursor_tool_tip.notify = handle_tool_tip; + + wl_signal_add(&input->wl_seat->events.pointer_grab_end, &input->pointer_grab_end); + input->pointer_grab_end.notify = handle_pointer_grab_end; } static void reset_device_mappings(struct roots_config *config, diff --git a/rootston/desktop.c b/rootston/desktop.c index e8b6b0e9..555d592f 100644 --- a/rootston/desktop.c +++ b/rootston/desktop.c @@ -51,6 +51,15 @@ void view_get_input_bounds(struct roots_view *view, struct wlr_box *box) { view->get_input_bounds(view, box); return; } + + if (view->type == ROOTS_XDG_SHELL_V6_VIEW) { + box->x = view->xdg_surface_v6->geometry->x; + box->y = view->xdg_surface_v6->geometry->y; + box->width = view->xdg_surface_v6->geometry->width; + box->height = view->xdg_surface_v6->geometry->height; + return; + } + box->x = box->y = 0; box->width = view->wlr_surface->current->width; box->height = view->wlr_surface->current->height; @@ -101,6 +110,36 @@ static struct wlr_subsurface *subsurface_at(struct wlr_surface *surface, return NULL; } +static struct wlr_xdg_surface_v6 *xdg_v6_popup_at( + struct wlr_xdg_surface_v6 *surface, double sx, double sy, + double *popup_sx, double *popup_sy) { + struct wlr_xdg_surface_v6 *popup; + wl_list_for_each(popup, &surface->popups, popup_link) { + double _popup_sx = surface->geometry->x + popup->popup_state->geometry.x; + double _popup_sy = surface->geometry->y + popup->popup_state->geometry.y; + int popup_width = popup->popup_state->geometry.width; + int popup_height = popup->popup_state->geometry.height; + + struct wlr_xdg_surface_v6 *_popup = + xdg_v6_popup_at(popup, sx - _popup_sx + popup->geometry->x, + sy - _popup_sy + popup->geometry->y, popup_sx, popup_sy); + if (_popup) { + *popup_sx = *popup_sx + _popup_sx - popup->geometry->x; + *popup_sy = *popup_sy + _popup_sy - popup->geometry->y; + return _popup; + } + + if ((sx > _popup_sx && sx < _popup_sx + popup_width) && + (sy > _popup_sy && sy < _popup_sy + popup_height)) { + *popup_sx = _popup_sx - popup->geometry->x; + *popup_sy = _popup_sy - popup->geometry->y; + return popup; + } + } + + return NULL; +} + struct roots_view *view_at(struct roots_desktop *desktop, double lx, double ly, struct wlr_surface **surface, double *sx, double *sy) { for (int i = desktop->views->length - 1; i >= 0; --i) { @@ -122,6 +161,21 @@ struct roots_view *view_at(struct roots_desktop *desktop, double lx, double ly, view_sy = ry + (double)box.height/2; } + if (view->type == ROOTS_XDG_SHELL_V6_VIEW) { + // TODO: test if this works with rotated views + double popup_sx, popup_sy; + struct wlr_xdg_surface_v6 *popup = + xdg_v6_popup_at(view->xdg_surface_v6, view_sx, view_sy, + &popup_sx, &popup_sy); + + if (popup) { + *sx = view_sx - popup_sx; + *sy = view_sy - popup_sy; + *surface = popup->surface; + return view; + } + } + double sub_x, sub_y; struct wlr_subsurface *subsurface = subsurface_at(view->wlr_surface, view_sx, view_sy, &sub_x, &sub_y); diff --git a/rootston/output.c b/rootston/output.c index c6627182..6c7fbf51 100644 --- a/rootston/output.c +++ b/rootston/output.c @@ -78,10 +78,34 @@ static void render_surface(struct wlr_surface *surface, } } +static void render_xdg_v6_popups(struct wlr_xdg_surface_v6 *surface, + struct roots_desktop *desktop, struct wlr_output *wlr_output, + struct timespec *when, double base_x, double base_y, float rotation) { + // TODO: make sure this works with view rotation + struct wlr_xdg_surface_v6 *popup; + wl_list_for_each(popup, &surface->popups, popup_link) { + if (!popup->configured) { + continue; + } + + double popup_x = base_x + surface->geometry->x + + popup->popup_state->geometry.x - popup->geometry->x; + double popup_y = base_y + surface->geometry->y + + popup->popup_state->geometry.y - popup->geometry->y; + render_surface(popup->surface, desktop, wlr_output, when, popup_x, + popup_y, rotation); + render_xdg_v6_popups(popup, desktop, wlr_output, when, popup_x, popup_y, rotation); + } +} + static void render_view(struct roots_view *view, struct roots_desktop *desktop, struct wlr_output *wlr_output, struct timespec *when) { render_surface(view->wlr_surface, desktop, wlr_output, when, view->x, view->y, view->rotation); + if (view->type == ROOTS_XDG_SHELL_V6_VIEW) { + render_xdg_v6_popups(view->xdg_surface_v6, desktop, wlr_output, + when, view->x, view->y, view->rotation); + } } static void output_frame_notify(struct wl_listener *listener, void *data) { diff --git a/rootston/xdg_shell_v6.c b/rootston/xdg_shell_v6.c index c7072354..c0124d60 100644 --- a/rootston/xdg_shell_v6.c +++ b/rootston/xdg_shell_v6.c @@ -81,11 +81,18 @@ static void handle_destroy(struct wl_listener *listener, void *data) { } void handle_xdg_shell_v6_surface(struct wl_listener *listener, void *data) { + struct wlr_xdg_surface_v6 *surface = data; + assert(surface->role != WLR_XDG_SURFACE_V6_ROLE_NONE); + + if (surface->role == WLR_XDG_SURFACE_V6_ROLE_POPUP) { + wlr_log(L_DEBUG, "new xdg popup"); + return; + } + struct roots_desktop *desktop = wl_container_of(listener, desktop, xdg_shell_v6_surface); - struct wlr_xdg_surface_v6 *surface = data; - wlr_log(L_DEBUG, "new xdg surface: title=%s, app_id=%s", + wlr_log(L_DEBUG, "new xdg toplevel: title=%s, app_id=%s", surface->title, surface->app_id); wlr_xdg_surface_v6_ping(surface); diff --git a/types/wlr_seat.c b/types/wlr_seat.c index bb24fa15..4f8d3744 100644 --- a/types/wlr_seat.c +++ b/types/wlr_seat.c @@ -173,6 +173,66 @@ static void wl_seat_bind(struct wl_client *wl_client, void *_wlr_seat, wl_signal_emit(&wlr_seat->events.client_bound, handle); } +static void default_pointer_enter(struct wlr_seat_pointer_grab *grab, + struct wlr_surface *surface, double sx, double sy) { + wlr_seat_pointer_enter(grab->seat, surface, sx, sy); +} + +static void default_pointer_motion(struct wlr_seat_pointer_grab *grab, + uint32_t time, double sx, double sy) { + wlr_seat_pointer_send_motion(grab->seat, time, sx, sy); +} + +static uint32_t default_pointer_button(struct wlr_seat_pointer_grab *grab, + uint32_t time, uint32_t button, uint32_t state) { + return wlr_seat_pointer_send_button(grab->seat, time, button, state); +} + +static void default_pointer_axis(struct wlr_seat_pointer_grab *grab, + uint32_t time, enum wlr_axis_orientation orientation, double value) { + wlr_seat_pointer_send_axis(grab->seat, time, orientation, value); +} + +static void default_pointer_cancel(struct wlr_seat_pointer_grab *grab) { + // cannot be cancelled +} + +static const struct wlr_pointer_grab_interface default_pointer_grab_impl = { + .enter = default_pointer_enter, + .motion = default_pointer_motion, + .button = default_pointer_button, + .axis = default_pointer_axis, + .cancel = default_pointer_cancel, +}; + +static void default_keyboard_enter(struct wlr_seat_keyboard_grab *grab, + struct wlr_surface *surface) { + wlr_seat_keyboard_enter(grab->seat, surface); +} + +static void default_keyboard_key(struct wlr_seat_keyboard_grab *grab, + uint32_t time, uint32_t key, uint32_t state) { + wlr_seat_keyboard_send_key(grab->seat, time, key, state); +} + +static void default_keyboard_modifiers(struct wlr_seat_keyboard_grab *grab, + uint32_t mods_depressed, uint32_t mods_latched, + uint32_t mods_locked, uint32_t group) { + wlr_seat_keyboard_send_modifiers(grab->seat, mods_depressed, + mods_latched, mods_locked, group); +} + +static void default_keyboard_cancel(struct wlr_seat_keyboard_grab *grab) { + // cannot be cancelled +} + +static const struct wlr_keyboard_grab_interface default_keyboard_grab_impl = { + .enter = default_keyboard_enter, + .key = default_keyboard_key, + .modifiers = default_keyboard_modifiers, + .cancel = default_keyboard_cancel, +}; + struct wlr_seat *wlr_seat_create(struct wl_display *display, const char *name) { struct wlr_seat *wlr_seat = calloc(1, sizeof(struct wlr_seat)); if (!wlr_seat) { @@ -183,6 +243,29 @@ struct wlr_seat *wlr_seat_create(struct wl_display *display, const char *name) { wl_list_init(&wlr_seat->pointer_state.surface_destroy.link); wl_list_init(&wlr_seat->pointer_state.resource_destroy.link); + struct wlr_seat_pointer_grab *pointer_grab = + calloc(1, sizeof(struct wlr_seat_pointer_grab)); + if (!pointer_grab) { + free(wlr_seat); + return NULL; + } + pointer_grab->interface = &default_pointer_grab_impl; + pointer_grab->seat = wlr_seat; + wlr_seat->pointer_state.default_grab = pointer_grab; + wlr_seat->pointer_state.grab = pointer_grab; + + struct wlr_seat_keyboard_grab *keyboard_grab = + calloc(1, sizeof(struct wlr_seat_keyboard_grab)); + if (!keyboard_grab) { + free(pointer_grab); + free(wlr_seat); + return NULL; + } + keyboard_grab->interface = &default_keyboard_grab_impl; + keyboard_grab->seat = wlr_seat; + wlr_seat->keyboard_state.default_grab = keyboard_grab; + wlr_seat->keyboard_state.grab = keyboard_grab; + wlr_seat->keyboard_state.wlr_seat = wlr_seat; wl_list_init(&wlr_seat->keyboard_state.resource_destroy.link); wl_list_init( @@ -203,6 +286,12 @@ struct wlr_seat *wlr_seat_create(struct wl_display *display, const char *name) { wl_signal_init(&wlr_seat->events.client_bound); wl_signal_init(&wlr_seat->events.client_unbound); + wl_signal_init(&wlr_seat->events.pointer_grab_begin); + wl_signal_init(&wlr_seat->events.pointer_grab_end); + + wl_signal_init(&wlr_seat->events.keyboard_grab_begin); + wl_signal_init(&wlr_seat->events.keyboard_grab_end); + return wlr_seat; } @@ -218,6 +307,8 @@ void wlr_seat_destroy(struct wlr_seat *wlr_seat) { } wl_global_destroy(wlr_seat->wl_global); + free(wlr_seat->pointer_state.default_grab); + free(wlr_seat->keyboard_state.default_grab); free(wlr_seat->data_device); free(wlr_seat->name); free(wlr_seat); @@ -390,6 +481,44 @@ void wlr_seat_pointer_send_axis(struct wlr_seat *wlr_seat, uint32_t time, pointer_send_frame(pointer); } +void wlr_seat_pointer_start_grab(struct wlr_seat *wlr_seat, + struct wlr_seat_pointer_grab *grab) { + grab->seat = wlr_seat; + wlr_seat->pointer_state.grab = grab; + + wl_signal_emit(&wlr_seat->events.pointer_grab_begin, grab); +} + +void wlr_seat_pointer_end_grab(struct wlr_seat *wlr_seat) { + struct wlr_seat_pointer_grab *grab = wlr_seat->pointer_state.grab; + wlr_seat->pointer_state.grab = wlr_seat->pointer_state.default_grab; + wl_signal_emit(&wlr_seat->events.pointer_grab_end, grab); +} + +void wlr_seat_pointer_notify_enter(struct wlr_seat *wlr_seat, + struct wlr_surface *surface, double sx, double sy) { + struct wlr_seat_pointer_grab *grab = wlr_seat->pointer_state.grab; + grab->interface->enter(grab, surface, sx, sy); +} + +void wlr_seat_pointer_notify_motion(struct wlr_seat *wlr_seat, uint32_t time, + double sx, double sy) { + struct wlr_seat_pointer_grab *grab = wlr_seat->pointer_state.grab; + grab->interface->motion(grab, time, sx, sy); +} + +uint32_t wlr_seat_pointer_notify_button(struct wlr_seat *wlr_seat, + uint32_t time, uint32_t button, uint32_t state) { + struct wlr_seat_pointer_grab *grab = wlr_seat->pointer_state.grab; + return grab->interface->button(grab, time, button, state); +} + +void wlr_seat_pointer_notify_axis(struct wlr_seat *wlr_seat, uint32_t time, + enum wlr_axis_orientation orientation, double value) { + struct wlr_seat_pointer_grab *grab = wlr_seat->pointer_state.grab; + grab->interface->axis(grab, time, orientation, value); +} + static void keyboard_switch_seat_keyboard(struct wlr_seat_handle *handle, struct wlr_seat_keyboard *seat_kb) { if (handle->seat_keyboard == seat_kb) { @@ -410,6 +539,18 @@ static void keyboard_switch_seat_keyboard(struct wlr_seat_handle *handle, handle->seat_keyboard = seat_kb; } +void wlr_seat_keyboard_send_key(struct wlr_seat *wlr_seat, uint32_t time, + uint32_t key, uint32_t state) { + struct wlr_seat_handle *handle = wlr_seat->keyboard_state.focused_handle; + if (!handle || !handle->keyboard) { + return; + } + + uint32_t serial = wl_display_next_serial(wlr_seat->display); + wl_keyboard_send_key(handle->keyboard, serial, + time, key, state); +} + static void keyboard_key_notify(struct wl_listener *listener, void *data) { struct wlr_seat_keyboard *seat_kb = wl_container_of(listener, seat_kb, key); struct wlr_seat *seat = seat_kb->seat; @@ -423,9 +564,9 @@ static void keyboard_key_notify(struct wl_listener *listener, void *data) { struct wlr_event_keyboard_key *event = data; enum wlr_key_state key_state = event->state; - uint32_t key_serial = wl_display_next_serial(seat->display); - wl_keyboard_send_key(handle->keyboard, key_serial, - (uint32_t)event->time_usec, event->keycode, key_state); + struct wlr_seat_keyboard_grab *grab = seat->keyboard_state.grab; + grab->interface->key(grab, (uint32_t)(event->time_usec / 1000), + event->keycode, key_state); } static void keyboard_modifiers_notify(struct wl_listener *listener, @@ -442,8 +583,9 @@ static void keyboard_modifiers_notify(struct wl_listener *listener, struct wlr_keyboard *keyboard = seat_kb->keyboard; - uint32_t modifiers_serial = wl_display_next_serial(seat->display); - wl_keyboard_send_modifiers(handle->keyboard, modifiers_serial, + struct wlr_seat_keyboard_grab *grab = seat->keyboard_state.grab; + + grab->interface->modifiers(grab, keyboard->modifiers.depressed, keyboard->modifiers.latched, keyboard->modifiers.locked, keyboard->modifiers.group); } @@ -499,6 +641,21 @@ void wlr_seat_detach_keyboard(struct wlr_seat *seat, struct wlr_keyboard *kb) { } } +void wlr_seat_keyboard_start_grab(struct wlr_seat *wlr_seat, + struct wlr_seat_keyboard_grab *grab) { + grab->seat = wlr_seat; + wlr_seat->keyboard_state.grab = grab; + + wl_signal_emit(&wlr_seat->events.keyboard_grab_begin, grab); +} + +void wlr_seat_keyboard_end_grab(struct wlr_seat *wlr_seat) { + struct wlr_seat_keyboard_grab *grab = wlr_seat->keyboard_state.grab; + wlr_seat->keyboard_state.grab = wlr_seat->keyboard_state.default_grab; + + wl_signal_emit(&wlr_seat->events.keyboard_grab_end, grab); +} + static void keyboard_surface_destroy_notify(struct wl_listener *listener, void *data) { struct wlr_seat_keyboard_state *state = wl_container_of( @@ -519,6 +676,21 @@ static void keyboard_resource_destroy_notify(struct wl_listener *listener, wlr_seat_keyboard_clear_focus(state->wlr_seat); } +void wlr_seat_keyboard_send_modifiers(struct wlr_seat *seat, + uint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_locked, + uint32_t group) { + struct wlr_seat_handle *handle = seat->keyboard_state.focused_handle; + if (!handle || !handle->keyboard) { + return; + } + + uint32_t serial = wl_display_next_serial(seat->display); + + wl_keyboard_send_modifiers(handle->keyboard, serial, + mods_depressed, mods_latched, + mods_locked, group); +} + void wlr_seat_keyboard_enter(struct wlr_seat *wlr_seat, struct wlr_surface *surface) { if (wlr_seat->keyboard_state.focused_surface == surface) { @@ -575,6 +747,12 @@ void wlr_seat_keyboard_enter(struct wlr_seat *wlr_seat, wlr_seat->keyboard_state.focused_surface = surface; } +void wlr_seat_keyboard_notify_enter(struct wlr_seat *wlr_seat, struct + wlr_surface *surface) { + struct wlr_seat_keyboard_grab *grab = wlr_seat->keyboard_state.grab; + grab->interface->enter(grab, surface); +} + void wlr_seat_keyboard_clear_focus(struct wlr_seat *wlr_seat) { struct wl_array keys; wl_array_init(&keys); diff --git a/types/wlr_xdg_shell_v6.c b/types/wlr_xdg_shell_v6.c index fc029f2d..fbe01528 100644 --- a/types/wlr_xdg_shell_v6.c +++ b/types/wlr_xdg_shell_v6.c @@ -12,12 +12,504 @@ #include "xdg-shell-unstable-v6-protocol.h" static const char *wlr_desktop_xdg_toplevel_role = "xdg_toplevel"; +static const char *wlr_desktop_xdg_popup_role = "xdg_popup"; + +struct wlr_xdg_positioner_v6 { + struct wl_resource *resource; + + struct wlr_box anchor_rect; + enum zxdg_positioner_v6_anchor anchor; + enum zxdg_positioner_v6_gravity gravity; + enum zxdg_positioner_v6_constraint_adjustment constraint_adjustment; + + struct { + int32_t width, height; + } size; + + struct { + int32_t x, y; + } offset; +}; + static void resource_destroy(struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy(resource); } +static struct wlr_xdg_surface_v6 *xdg_popup_grab_get_topmost( + struct wlr_xdg_popup_grab_v6 *grab) { + struct wlr_xdg_popup_v6 *popup; + wl_list_for_each(popup, &grab->popups, grab_link) { + return popup->base; + } + + return NULL; +} + +static void xdg_pointer_grab_end(struct wlr_seat_pointer_grab *grab) { + struct wlr_xdg_popup_grab_v6 *popup_grab = grab->data; + + struct wlr_xdg_popup_v6 *popup, *tmp; + wl_list_for_each_safe(popup, tmp, &popup_grab->popups, grab_link) { + zxdg_popup_v6_send_popup_done(popup->resource); + } + + wlr_seat_pointer_end_grab(grab->seat); +} + +static void xdg_pointer_grab_enter(struct wlr_seat_pointer_grab *grab, + struct wlr_surface *surface, double sx, double sy) { + struct wlr_xdg_popup_grab_v6 *popup_grab = grab->data; + if (wl_resource_get_client(surface->resource) == popup_grab->client) { + wlr_seat_pointer_enter(grab->seat, surface, sx, sy); + } else { + wlr_seat_pointer_clear_focus(grab->seat); + } +} + +static void xdg_pointer_grab_motion(struct wlr_seat_pointer_grab *grab, + uint32_t time, double sx, double sy) { + wlr_seat_pointer_send_motion(grab->seat, time, sx, sy); +} + +static uint32_t xdg_pointer_grab_button(struct wlr_seat_pointer_grab *grab, + uint32_t time, uint32_t button, uint32_t state) { + uint32_t serial = + wlr_seat_pointer_send_button(grab->seat, time, button, state); + if (serial) { + return serial; + } else { + xdg_pointer_grab_end(grab); + return 0; + } +} + +static void xdg_pointer_grab_axis(struct wlr_seat_pointer_grab *grab, + uint32_t time, enum wlr_axis_orientation orientation, double value) { + wlr_seat_pointer_send_axis(grab->seat, time, orientation, value); +} + +static void xdg_pointer_grab_cancel(struct wlr_seat_pointer_grab *grab) { + xdg_pointer_grab_end(grab); +} + +static const struct wlr_pointer_grab_interface xdg_pointer_grab_impl = { + .enter = xdg_pointer_grab_enter, + .motion = xdg_pointer_grab_motion, + .button = xdg_pointer_grab_button, + .cancel = xdg_pointer_grab_cancel, + .axis = xdg_pointer_grab_axis, +}; + +static void xdg_keyboard_grab_enter(struct wlr_seat_keyboard_grab *grab, struct wlr_surface *surface) { + // keyboard focus should remain on the popup +} + +static void xdg_keyboard_grab_key(struct wlr_seat_keyboard_grab *grab, uint32_t time, + uint32_t key, uint32_t state) { + wlr_seat_keyboard_send_key(grab->seat, time, key, state); +} + +static void xdg_keyboard_grab_modifiers(struct wlr_seat_keyboard_grab *grab, + uint32_t mods_depressed, uint32_t mods_latched, + uint32_t mods_locked, uint32_t group) { + wlr_seat_keyboard_send_modifiers(grab->seat, mods_depressed, mods_latched, + mods_locked, group); +} + +static void xdg_keyboard_grab_cancel(struct wlr_seat_keyboard_grab *grab) { + wlr_seat_keyboard_end_grab(grab->seat); +} + +static const struct wlr_keyboard_grab_interface xdg_keyboard_grab_impl = { + .enter = xdg_keyboard_grab_enter, + .key = xdg_keyboard_grab_key, + .modifiers = xdg_keyboard_grab_modifiers, + .cancel = xdg_keyboard_grab_cancel, +}; + +static struct wlr_xdg_popup_grab_v6 *xdg_shell_popup_grab_from_seat( + struct wlr_xdg_shell_v6 *shell, struct wlr_seat *seat) { + struct wlr_xdg_popup_grab_v6 *xdg_grab; + wl_list_for_each(xdg_grab, &shell->popup_grabs, link) { + if (xdg_grab->seat == seat) { + return xdg_grab; + } + } + + xdg_grab = calloc(1, sizeof(struct wlr_xdg_popup_grab_v6)); + if (!xdg_grab) { + return NULL; + } + + xdg_grab->pointer_grab.data = xdg_grab; + xdg_grab->pointer_grab.interface = &xdg_pointer_grab_impl; + xdg_grab->keyboard_grab.data = xdg_grab; + xdg_grab->keyboard_grab.interface = &xdg_keyboard_grab_impl; + + wl_list_init(&xdg_grab->popups); + + wl_list_insert(&shell->popup_grabs, &xdg_grab->link); + xdg_grab->seat = seat; + + return xdg_grab; +} + + +static void xdg_surface_destroy(struct wlr_xdg_surface_v6 *surface) { + // TODO: probably need to ungrab before this event + wl_signal_emit(&surface->events.destroy, surface); + + if (surface->configure_idle) { + wl_event_source_remove(surface->configure_idle); + } + + struct wlr_xdg_surface_v6_configure *configure, *tmp; + wl_list_for_each_safe(configure, tmp, &surface->configure_list, link) { + free(configure); + } + + if (surface->role == WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL) { + wl_resource_set_user_data(surface->toplevel_state->resource, NULL); + free(surface->toplevel_state); + } + + if (surface->role == WLR_XDG_SURFACE_V6_ROLE_POPUP) { + wl_resource_set_user_data(surface->popup_state->resource, NULL); + + if (surface->popup_state->seat) { + struct wlr_xdg_popup_grab_v6 *grab = + xdg_shell_popup_grab_from_seat(surface->client->shell, + surface->popup_state->seat); + + struct wlr_xdg_surface_v6 *topmost = + xdg_popup_grab_get_topmost(grab); + + if (topmost != surface) { + wl_resource_post_error(surface->client->resource, + ZXDG_SHELL_V6_ERROR_NOT_THE_TOPMOST_POPUP, + "xdg_popup was destroyed while it was not the topmost " + "popup."); + } + + wl_list_remove(&surface->popup_state->grab_link); + + if (wl_list_empty(&grab->popups)) { + if (grab->seat->pointer_state.grab == &grab->pointer_grab) { + wlr_seat_pointer_end_grab(grab->seat); + } + if (grab->seat->keyboard_state.grab == &grab->keyboard_grab) { + wlr_seat_keyboard_end_grab(grab->seat); + } + } + } + + wl_list_remove(&surface->popup_link); + free(surface->popup_state); + } + + wl_resource_set_user_data(surface->resource, NULL); + wl_list_remove(&surface->link); + wl_list_remove(&surface->surface_destroy_listener.link); + wl_list_remove(&surface->surface_commit_listener.link); + free(surface->geometry); + free(surface->next_geometry); + free(surface->title); + free(surface->app_id); + free(surface); +} + + +static void xdg_positioner_destroy(struct wl_resource *resource) { + struct wlr_xdg_positioner_v6 *positioner = + wl_resource_get_user_data(resource); + free(positioner); + +} + +static void xdg_positioner_protocol_set_size(struct wl_client *client, + struct wl_resource *resource, int32_t width, int32_t height) { + struct wlr_xdg_positioner_v6 *positioner = + wl_resource_get_user_data(resource); + + if (width < 1 || height < 1) { + wl_resource_post_error(resource, + ZXDG_POSITIONER_V6_ERROR_INVALID_INPUT, + "width and height must be positives and non-zero"); + return; + } + + positioner->size.width = width; + positioner->size.height = height; +} + +static void xdg_positioner_protocol_set_anchor_rect(struct wl_client *client, + struct wl_resource *resource, int32_t x, int32_t y, int32_t width, + int32_t height) { + struct wlr_xdg_positioner_v6 *positioner = + wl_resource_get_user_data(resource); + + if (width < 1 || height < 1) { + wl_resource_post_error(resource, + ZXDG_POSITIONER_V6_ERROR_INVALID_INPUT, + "width and height must be positives and non-zero"); + return; + } + + positioner->anchor_rect.x = x; + positioner->anchor_rect.y = y; + positioner->anchor_rect.width = width; + positioner->anchor_rect.height = height; +} + +static void xdg_positioner_protocol_set_anchor(struct wl_client *client, + struct wl_resource *resource, uint32_t anchor) { + struct wlr_xdg_positioner_v6 *positioner = + wl_resource_get_user_data(resource); + + if (((anchor & ZXDG_POSITIONER_V6_ANCHOR_TOP ) && + (anchor & ZXDG_POSITIONER_V6_ANCHOR_BOTTOM)) || + ((anchor & ZXDG_POSITIONER_V6_ANCHOR_LEFT) && + (anchor & ZXDG_POSITIONER_V6_ANCHOR_RIGHT))) { + wl_resource_post_error(resource, + ZXDG_POSITIONER_V6_ERROR_INVALID_INPUT, + "same-axis values are not allowed"); + return; + } + + positioner->anchor = anchor; +} + +static void xdg_positioner_protocol_set_gravity(struct wl_client *client, + struct wl_resource *resource, uint32_t gravity) { + struct wlr_xdg_positioner_v6 *positioner = + wl_resource_get_user_data(resource); + + if (((gravity & ZXDG_POSITIONER_V6_GRAVITY_TOP) && + (gravity & ZXDG_POSITIONER_V6_GRAVITY_BOTTOM)) || + ((gravity & ZXDG_POSITIONER_V6_GRAVITY_LEFT) && + (gravity & ZXDG_POSITIONER_V6_GRAVITY_RIGHT))) { + wl_resource_post_error(resource, + ZXDG_POSITIONER_V6_ERROR_INVALID_INPUT, + "same-axis values are not allowed"); + return; + } + + positioner->gravity = gravity; +} + +static void xdg_positioner_protocol_set_constraint_adjustment( + struct wl_client *client, struct wl_resource *resource, + uint32_t constraint_adjustment) { + struct wlr_xdg_positioner_v6 *positioner = + wl_resource_get_user_data(resource); + + positioner->constraint_adjustment = constraint_adjustment; +} + +static void xdg_positioner_protocol_set_offset(struct wl_client *client, + struct wl_resource *resource, int32_t x, int32_t y) { + struct wlr_xdg_positioner_v6 *positioner = + wl_resource_get_user_data(resource); + + positioner->offset.x = x; + positioner->offset.y = y; +} + +static const struct zxdg_positioner_v6_interface + zxdg_positioner_v6_implementation = { + .destroy = resource_destroy, + .set_size = xdg_positioner_protocol_set_size, + .set_anchor_rect = xdg_positioner_protocol_set_anchor_rect, + .set_anchor = xdg_positioner_protocol_set_anchor, + .set_gravity = xdg_positioner_protocol_set_gravity, + .set_constraint_adjustment = + xdg_positioner_protocol_set_constraint_adjustment, + .set_offset = xdg_positioner_protocol_set_offset, +}; + +static void xdg_shell_create_positioner(struct wl_client *wl_client, + struct wl_resource *resource, uint32_t id) { + struct wlr_xdg_positioner_v6 *positioner = + calloc(1, sizeof(struct wlr_xdg_positioner_v6)); + if (positioner == NULL) { + wl_client_post_no_memory(wl_client); + return; + } + + positioner->resource = wl_resource_create(wl_client, + &zxdg_positioner_v6_interface, + wl_resource_get_version(resource), + id); + if (positioner->resource == NULL) { + wl_client_post_no_memory(wl_client); + free(positioner); + return; + } + + wl_resource_set_implementation(positioner->resource, + &zxdg_positioner_v6_implementation, + positioner, xdg_positioner_destroy); +} + +static void xdg_popup_protocol_grab(struct wl_client *client, + struct wl_resource *resource, struct wl_resource *seat_resource, + uint32_t serial) { + struct wlr_xdg_surface_v6 *surface = wl_resource_get_user_data(resource); + struct wlr_seat_handle *handle = wl_resource_get_user_data(seat_resource); + + if (surface->popup_state->committed) { + wl_resource_post_error(surface->popup_state->resource, + ZXDG_POPUP_V6_ERROR_INVALID_GRAB, + "xdg_popup is already mapped"); + return; + } + + struct wlr_xdg_popup_grab_v6 *popup_grab = + xdg_shell_popup_grab_from_seat(surface->client->shell, + handle->wlr_seat); + + struct wlr_xdg_surface_v6 *topmost = xdg_popup_grab_get_topmost(popup_grab); + bool parent_is_toplevel = + surface->popup_state->parent->role == WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL; + + if ((topmost == NULL && !parent_is_toplevel) || + (topmost != NULL && topmost != surface->popup_state->parent)) { + wl_resource_post_error(surface->client->resource, + ZXDG_SHELL_V6_ERROR_NOT_THE_TOPMOST_POPUP, + "xdg_popup was not created on the topmost popup"); + return; + } + + popup_grab->client = surface->client->client; + surface->popup_state->seat = handle->wlr_seat; + + wl_list_insert(&popup_grab->popups, &surface->popup_state->grab_link); + + wlr_seat_pointer_start_grab(handle->wlr_seat, &popup_grab->pointer_grab); + wlr_seat_keyboard_start_grab(handle->wlr_seat, &popup_grab->keyboard_grab); +} + +static const struct zxdg_popup_v6_interface zxdg_popup_v6_implementation = { + .destroy = resource_destroy, + .grab = xdg_popup_protocol_grab, +}; + + +static struct wlr_box xdg_positioner_get_geometry( + struct wlr_xdg_positioner_v6 *positioner, + struct wlr_xdg_surface_v6 *surface, struct wlr_xdg_surface_v6 *parent) { + struct wlr_box geometry = { + .x = positioner->offset.x, + .y = positioner->offset.y, + .width = positioner->size.width, + .height = positioner->size.height, + }; + + if (positioner->anchor & ZXDG_POSITIONER_V6_ANCHOR_TOP) { + geometry.y += positioner->anchor_rect.y; + } else if (positioner->anchor & ZXDG_POSITIONER_V6_ANCHOR_BOTTOM) { + geometry.y += + positioner->anchor_rect.y + positioner->anchor_rect.height; + } else { + geometry.y += + positioner->anchor_rect.y + positioner->anchor_rect.height / 2; + } + + if (positioner->anchor & ZXDG_POSITIONER_V6_ANCHOR_LEFT) { + geometry.x += positioner->anchor_rect.x; + } else if (positioner->anchor & ZXDG_POSITIONER_V6_ANCHOR_RIGHT) { + geometry.x += positioner->anchor_rect.x + positioner->anchor_rect.width; + } else { + geometry.x += + positioner->anchor_rect.x + positioner->anchor_rect.width / 2; + } + + if (positioner->gravity & ZXDG_POSITIONER_V6_GRAVITY_TOP) { + geometry.y -= geometry.height; + } else if (positioner->gravity & ZXDG_POSITIONER_V6_GRAVITY_BOTTOM) { + geometry.y = geometry.y; + } else { + geometry.y -= geometry.height / 2; + } + + if (positioner->gravity & ZXDG_POSITIONER_V6_GRAVITY_LEFT) { + geometry.x -= geometry.width; + } else if (positioner->gravity & ZXDG_POSITIONER_V6_GRAVITY_RIGHT) { + geometry.x = geometry.x; + } else { + geometry.x -= geometry.width / 2; + } + + if (positioner->constraint_adjustment == + ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_NONE) { + return geometry; + } + + // TODO: add compositor policy configuration and the code here + + return geometry; +} + +static void xdg_popup_resource_destroy(struct wl_resource *resource) { + struct wlr_xdg_surface_v6 *surface = wl_resource_get_user_data(resource); + if (surface != NULL) { + xdg_surface_destroy(surface); + } +} + +static void xdg_surface_get_popup(struct wl_client *client, + struct wl_resource *resource, uint32_t id, + struct wl_resource *parent_resource, + struct wl_resource *positioner_resource) { + struct wlr_xdg_surface_v6 *surface = + wl_resource_get_user_data(resource); + struct wlr_xdg_surface_v6 *parent = + wl_resource_get_user_data(parent_resource); + struct wlr_xdg_positioner_v6 *positioner = + wl_resource_get_user_data(positioner_resource); + + if (positioner->size.width == 0 || positioner->anchor_rect.width == 0) { + wl_resource_post_error(resource, + ZXDG_SHELL_V6_ERROR_INVALID_POSITIONER, + "positioner object is not complete"); + return; + } + + if (wlr_surface_set_role(surface->surface, wlr_desktop_xdg_popup_role, + resource, ZXDG_SHELL_V6_ERROR_ROLE)) { + return; + } + + surface->popup_state = calloc(1, sizeof(struct wlr_xdg_popup_v6)); + if (!surface->popup_state) { + wl_client_post_no_memory(client); + return; + } + + surface->popup_state->resource = + wl_resource_create(client, &zxdg_popup_v6_interface, + wl_resource_get_version(resource), id); + if (surface->popup_state->resource == NULL) { + free(surface->popup_state); + return; + } + + surface->role = WLR_XDG_SURFACE_V6_ROLE_POPUP; + surface->popup_state->base = surface; + surface->popup_state->parent = parent; + surface->popup_state->geometry = + xdg_positioner_get_geometry(positioner, surface, parent); + wl_list_insert(&surface->popup_state->parent->popups, + &surface->popup_link); + + wl_resource_set_implementation(surface->popup_state->resource, + &zxdg_popup_v6_implementation, surface, + xdg_popup_resource_destroy); +} + + static void xdg_toplevel_protocol_set_parent(struct wl_client *client, struct wl_resource *resource, struct wl_resource *parent_resource) { struct wlr_xdg_surface_v6 *surface = wl_resource_get_user_data(resource); @@ -216,34 +708,6 @@ static const struct zxdg_toplevel_v6_interface zxdg_toplevel_v6_implementation = .set_minimized = xdg_toplevel_protocol_set_minimized }; -static void xdg_surface_destroy(struct wlr_xdg_surface_v6 *surface) { - wl_signal_emit(&surface->events.destroy, surface); - wl_resource_set_user_data(surface->resource, NULL); - - if (surface->configure_idle) { - wl_event_source_remove(surface->configure_idle); - } - - struct wlr_xdg_surface_v6_configure *configure, *tmp; - wl_list_for_each_safe(configure, tmp, &surface->configure_list, link) { - free(configure); - } - - if (surface->role == WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL) { - wl_resource_set_user_data(surface->toplevel_state->resource, NULL); - free(surface->toplevel_state); - } - - wl_list_remove(&surface->link); - wl_list_remove(&surface->surface_destroy_listener.link); - wl_list_remove(&surface->surface_commit_listener.link); - free(surface->geometry); - free(surface->next_geometry); - free(surface->title); - free(surface->app_id); - free(surface); -} - static void xdg_surface_resource_destroy(struct wl_resource *resource) { struct wlr_xdg_surface_v6 *surface = wl_resource_get_user_data(resource); if (surface != NULL) { @@ -286,12 +750,6 @@ static void xdg_surface_get_toplevel(struct wl_client *client, xdg_toplevel_resource_destroy); } -static void xdg_surface_get_popup(struct wl_client *client, - struct wl_resource *resource, uint32_t id, struct wl_resource *parent, - struct wl_resource *wl_positioner) { - wlr_log(L_DEBUG, "TODO xdg surface get popup"); -} - static void wlr_xdg_toplevel_v6_ack_configure( struct wlr_xdg_surface_v6 *surface, struct wlr_xdg_surface_v6_configure *configure) { @@ -333,9 +791,15 @@ static void xdg_surface_ack_configure(struct wl_client *client, return; } - // TODO handle popups - if (surface->role == WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL) { + switch (surface->role) { + case WLR_XDG_SURFACE_V6_ROLE_NONE: + assert(0 && "not reached"); + break; + case WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL: wlr_xdg_toplevel_v6_ack_configure(surface, configure); + break; + case WLR_XDG_SURFACE_V6_ROLE_POPUP: + break; } if (!surface->configured) { @@ -376,11 +840,6 @@ static const struct zxdg_surface_v6_interface zxdg_surface_v6_implementation = { .set_window_geometry = xdg_surface_set_window_geometry, }; -static void xdg_shell_create_positioner(struct wl_client *client, - struct wl_resource *resource, uint32_t id) { - wlr_log(L_DEBUG, "TODO: xdg shell create positioner"); -} - static bool wlr_xdg_surface_v6_toplevel_state_compare( struct wlr_xdg_toplevel_v6 *state) { // is pending state different from current state? @@ -454,9 +913,6 @@ static void wlr_xdg_surface_send_configure(void *user_data) { struct wlr_xdg_surface_v6 *surface = user_data; struct wl_display *display = wl_client_get_display(surface->client->client); - // TODO handle popups - assert(surface->role == WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL); - surface->configure_idle = NULL; struct wlr_xdg_surface_v6_configure *configure = @@ -469,21 +925,42 @@ static void wlr_xdg_surface_send_configure(void *user_data) { wl_list_insert(surface->configure_list.prev, &configure->link); configure->serial = wl_display_next_serial(display); - wlr_xdg_toplevel_v6_send_configure(surface, configure); + switch (surface->role) { + case WLR_XDG_SURFACE_V6_ROLE_NONE: + assert(0 && "not reached"); + break; + case WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL: + wlr_xdg_toplevel_v6_send_configure(surface, configure); + break; + case WLR_XDG_SURFACE_V6_ROLE_POPUP: + zxdg_popup_v6_send_configure(surface->popup_state->resource, + surface->popup_state->geometry.x, + surface->popup_state->geometry.y, + surface->popup_state->geometry.width, + surface->popup_state->geometry.height); + break; + } zxdg_surface_v6_send_configure(surface->resource, configure->serial); } static void wlr_xdg_surface_v6_schedule_configure( struct wlr_xdg_surface_v6 *surface, bool force) { - // TODO handle popups - assert(surface->role == WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL); - struct wl_display *display = wl_client_get_display(surface->client->client); struct wl_event_loop *loop = wl_display_get_event_loop(display); + bool pending_same = false; - bool pending_same = !force && - wlr_xdg_surface_v6_toplevel_state_compare(surface->toplevel_state); + switch (surface->role) { + case WLR_XDG_SURFACE_V6_ROLE_NONE: + assert(0 && "not reached"); + break; + case WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL: + pending_same = !force && + wlr_xdg_surface_v6_toplevel_state_compare(surface->toplevel_state); + break; + case WLR_XDG_SURFACE_V6_ROLE_POPUP: + break; + } if (surface->configure_idle != NULL) { if (!pending_same) { @@ -534,6 +1011,16 @@ static void wlr_xdg_surface_v6_toplevel_committed( surface->toplevel_state->current = surface->toplevel_state->next; } +static void wlr_xdg_surface_v6_popup_committed( + struct wlr_xdg_surface_v6 *surface) { + assert(surface->role == WLR_XDG_SURFACE_V6_ROLE_POPUP); + + if (!surface->popup_state->committed) { + wlr_xdg_surface_v6_schedule_configure(surface, true); + surface->popup_state->committed = true; + } +} + static void handle_wlr_surface_committed(struct wl_listener *listener, void *data) { struct wlr_xdg_surface_v6 *surface = @@ -564,7 +1051,7 @@ static void handle_wlr_surface_committed(struct wl_listener *listener, wlr_xdg_surface_v6_toplevel_committed(surface); break; case WLR_XDG_SURFACE_V6_ROLE_POPUP: - wlr_log(L_DEBUG, "TODO: popup surface committed"); + wlr_xdg_surface_v6_popup_committed(surface); break; } @@ -611,6 +1098,7 @@ static void xdg_shell_get_xdg_surface(struct wl_client *wl_client, } wl_list_init(&surface->configure_list); + wl_list_init(&surface->popups); wl_signal_init(&surface->events.request_minimize); wl_signal_init(&surface->events.request_move); @@ -730,6 +1218,7 @@ struct wlr_xdg_shell_v6 *wlr_xdg_shell_v6_create(struct wl_display *display) { xdg_shell->ping_timeout = 10000; wl_list_init(&xdg_shell->clients); + wl_list_init(&xdg_shell->popup_grabs); struct wl_global *wl_global = wl_global_create(display, &zxdg_shell_v6_interface, 1, xdg_shell, xdg_shell_bind);