diff options
-rw-r--r-- | examples/compositor.c | 127 | ||||
-rw-r--r-- | include/wlr/types/wlr_surface.h | 9 | ||||
-rw-r--r-- | include/wlr/types/wlr_xdg_shell_v6.h | 158 | ||||
-rw-r--r-- | types/wlr_surface.c | 23 | ||||
-rw-r--r-- | types/wlr_xdg_shell_v6.c | 724 |
5 files changed, 955 insertions, 86 deletions
diff --git a/examples/compositor.c b/examples/compositor.c index 995c1b7d..fb5d8dfd 100644 --- a/examples/compositor.c +++ b/examples/compositor.c @@ -56,6 +56,19 @@ struct sample_state { struct wl_listener cursor_motion_absolute; struct wl_listener cursor_button; struct wl_listener cursor_axis; + + struct wl_listener new_xdg_surface_v6; +}; + +struct example_xdg_surface_v6 { + struct wlr_xdg_surface_v6 *surface; + + struct wl_listener destroy_listener; + struct wl_listener ping_timeout_listener; + struct wl_listener request_minimize_listener; + struct wl_listener request_move_listener; + struct wl_listener request_resize_listener; + struct wl_listener request_show_window_menu_listener; }; /* @@ -85,6 +98,103 @@ static void output_frame_handle_surface(struct sample_state *sample, } } } + +static void handle_xdg_surface_v6_ping_timeout(struct wl_listener *listener, + void *data) { + struct wlr_xdg_surface_v6 *surface = data; + wlr_log(L_DEBUG, "got ping timeout for surface: %s", surface->title); +} + +static void handle_xdg_surface_v6_destroy(struct wl_listener *listener, + void *data) { + struct example_xdg_surface_v6 *example_surface = + wl_container_of(listener, example_surface, destroy_listener); + wl_list_remove(&example_surface->destroy_listener.link); + wl_list_remove(&example_surface->ping_timeout_listener.link); + wl_list_remove(&example_surface->request_move_listener.link); + wl_list_remove(&example_surface->request_resize_listener.link); + wl_list_remove(&example_surface->request_show_window_menu_listener.link); + wl_list_remove(&example_surface->request_minimize_listener.link); + free(example_surface); +} + +static void handle_xdg_surface_v6_request_move(struct wl_listener *listener, + void *data) { + struct example_xdg_surface_v6 *example_surface = + wl_container_of(listener, example_surface, request_move_listener); + struct wlr_xdg_toplevel_v6_move_event *e = data; + wlr_log(L_DEBUG, "TODO: surface requested move: %s", e->surface->title); +} + +static void handle_xdg_surface_v6_request_resize(struct wl_listener *listener, + void *data) { + struct example_xdg_surface_v6 *example_surface = + wl_container_of(listener, example_surface, request_resize_listener); + struct wlr_xdg_toplevel_v6_resize_event *e = data; + wlr_log(L_DEBUG, "TODO: surface requested resize: %s", e->surface->title); +} + +static void handle_xdg_surface_v6_request_show_window_menu( + struct wl_listener *listener, void *data) { + struct example_xdg_surface_v6 *example_surface = + wl_container_of(listener, example_surface, + request_show_window_menu_listener); + struct wlr_xdg_toplevel_v6_show_window_menu_event *e = data; + wlr_log(L_DEBUG, "TODO: surface requested to show window menu: %s", + e->surface->title); +} + +static void handle_xdg_surface_v6_request_minimize( + struct wl_listener *listener, void *data) { + struct example_xdg_surface_v6 *example_surface = + wl_container_of(listener, example_surface, request_minimize_listener); + wlr_log(L_DEBUG, "TODO: surface requested to be minimized: %s", + example_surface->surface->title); +} + +static void handle_new_xdg_surface_v6(struct wl_listener *listener, + void *data) { + struct wlr_xdg_surface_v6 *surface = data; + wlr_log(L_DEBUG, "new xdg surface: title=%s, app_id=%s", + surface->title, surface->app_id); + + wlr_xdg_surface_v6_ping(surface); + + struct example_xdg_surface_v6 *esurface = + calloc(1, sizeof(struct example_xdg_surface_v6)); + if (esurface == NULL) { + return; + } + + esurface->surface = surface; + + wl_signal_add(&surface->events.destroy, &esurface->destroy_listener); + esurface->destroy_listener.notify = handle_xdg_surface_v6_destroy; + + wl_signal_add(&surface->events.ping_timeout, + &esurface->ping_timeout_listener); + esurface->ping_timeout_listener.notify = handle_xdg_surface_v6_ping_timeout; + + wl_signal_add(&surface->events.request_move, + &esurface->request_move_listener); + esurface->request_move_listener.notify = handle_xdg_surface_v6_request_move; + + wl_signal_add(&surface->events.request_resize, + &esurface->request_resize_listener); + esurface->request_resize_listener.notify = + handle_xdg_surface_v6_request_resize; + + wl_signal_add(&surface->events.request_show_window_menu, + &esurface->request_show_window_menu_listener); + esurface->request_show_window_menu_listener.notify = + handle_xdg_surface_v6_request_show_window_menu; + + wl_signal_add(&surface->events.request_minimize, + &esurface->request_minimize_listener); + esurface->request_minimize_listener.notify = + handle_xdg_surface_v6_request_minimize; +} + static void handle_output_frame(struct output_state *output, struct timespec *ts) { struct compositor_state *state = output->compositor; @@ -100,9 +210,12 @@ static void handle_output_frame(struct output_state *output, wl_shell_surface->surface); } struct wlr_xdg_surface_v6 *xdg_surface; - wl_list_for_each(xdg_surface, &sample->xdg_shell->surfaces, link) { - output_frame_handle_surface(sample, wlr_output, ts, - xdg_surface->surface); + struct wlr_xdg_client_v6 *xdg_client; + wl_list_for_each(xdg_client, &sample->xdg_shell->clients, link) { + wl_list_for_each(xdg_surface, &xdg_client->surfaces, link) { + output_frame_handle_surface(sample, wlr_output, ts, + xdg_surface->surface->resource); + } } struct wlr_x11_window *x11_window; wl_list_for_each(x11_window, &sample->xwayland->displayable_windows, link) { @@ -309,6 +422,12 @@ int main(int argc, char *argv[]) { state.wl_shell = wlr_wl_shell_create(compositor.display); state.xdg_shell = wlr_xdg_shell_v6_create(compositor.display); + // shell events + wl_signal_add(&state.xdg_shell->events.new_surface, + &state.new_xdg_surface_v6); + state.new_xdg_surface_v6.notify = handle_new_xdg_surface_v6; + + state.data_device_manager = wlr_data_device_manager_create(compositor.display); @@ -341,6 +460,8 @@ int main(int argc, char *argv[]) { wl_display_run(compositor.display); + wl_list_remove(&state.new_xdg_surface_v6.link); + wlr_xwayland_destroy(state.xwayland); close(state.keymap_fd); wlr_seat_destroy(state.wl_seat); diff --git a/include/wlr/types/wlr_surface.h b/include/wlr/types/wlr_surface.h index f0765160..d76fff16 100644 --- a/include/wlr/types/wlr_surface.h +++ b/include/wlr/types/wlr_surface.h @@ -42,6 +42,7 @@ struct wlr_surface { struct { struct wl_signal commit; + struct wl_signal destroy; } signals; struct wl_list frame_callback_list; // wl_surface.frame @@ -69,4 +70,12 @@ void wlr_surface_get_matrix(struct wlr_surface *surface, const float (*projection)[16], const float (*transform)[16]); + +/** + * Set the lifetime role for this surface. Returns 0 on success or -1 if the + * role cannot be set. + */ +int wlr_surface_set_role(struct wlr_surface *surface, const char *role, + struct wl_resource *error_resource, uint32_t error_code); + #endif diff --git a/include/wlr/types/wlr_xdg_shell_v6.h b/include/wlr/types/wlr_xdg_shell_v6.h index 41cf483a..786bf4e6 100644 --- a/include/wlr/types/wlr_xdg_shell_v6.h +++ b/include/wlr/types/wlr_xdg_shell_v6.h @@ -1,24 +1,174 @@ #ifndef _WLR_XDG_SHELL_V6_H #define _WLR_XDG_SHELL_V6_H +#include <wlr/types/wlr_box.h> #include <wayland-server.h> struct wlr_xdg_shell_v6 { struct wl_global *wl_global; - struct wl_list wl_resources; - struct wl_list surfaces; + struct wl_list clients; + uint32_t ping_timeout; + + struct { + struct wl_signal new_surface; + } events; void *data; }; +struct wlr_xdg_client_v6 { + struct wlr_xdg_shell_v6 *shell; + struct wl_resource *resource; + struct wl_client *client; + struct wl_list surfaces; + + struct wl_list link; // wlr_xdg_shell_v6::clients + + uint32_t ping_serial; + struct wl_event_source *ping_timer; +}; + + +enum wlr_xdg_surface_v6_role { + WLR_XDG_SURFACE_V6_ROLE_NONE, + WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL, + WLR_XDG_SURFACE_V6_ROLE_POPUP, +}; + +struct wlr_xdg_toplevel_v6_state { + bool maximized; + bool fullscreen; + bool resizing; + bool activated; + + uint32_t width; + uint32_t height; + + uint32_t max_width; + uint32_t max_height; + + uint32_t min_width; + uint32_t min_height; +}; + +struct wlr_xdg_toplevel_v6 { + struct wl_resource *resource; + struct wlr_xdg_surface_v6 *base; + struct wlr_xdg_surface_v6 *parent; + bool added; + struct wlr_xdg_toplevel_v6_state next; // client protocol requests + struct wlr_xdg_toplevel_v6_state pending; // user configure requests + 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; + struct wlr_xdg_toplevel_v6_state state; +}; + struct wlr_xdg_surface_v6 { + struct wlr_xdg_client_v6 *client; struct wl_resource *resource; - struct wl_resource *surface; - struct wl_list link; + 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; + + bool configured; + struct wl_event_source *configure_idle; + struct wl_list configure_list; + + char *title; + char *app_id; + + bool has_next_geometry; + struct wlr_box *next_geometry; + struct wlr_box *geometry; + + struct wl_listener surface_destroy_listener; + struct wl_listener surface_commit_listener; + + struct { + struct wl_signal commit; + struct wl_signal destroy; + struct wl_signal ack_configure; + struct wl_signal ping_timeout; + + struct wl_signal request_minimize; + struct wl_signal request_move; + struct wl_signal request_resize; + struct wl_signal request_show_window_menu; + } events; void *data; }; +struct wlr_xdg_toplevel_v6_move_event { + struct wl_client *client; + struct wlr_xdg_surface_v6 *surface; + struct wlr_seat_handle *seat_handle; + uint32_t serial; +}; + +struct wlr_xdg_toplevel_v6_resize_event { + struct wl_client *client; + struct wlr_xdg_surface_v6 *surface; + struct wlr_seat_handle *seat_handle; + uint32_t serial; + uint32_t edges; +}; + +struct wlr_xdg_toplevel_v6_show_window_menu_event { + struct wl_client *client; + struct wlr_xdg_surface_v6 *surface; + struct wlr_seat_handle *seat_handle; + uint32_t serial; + uint32_t x; + uint32_t y; +}; + struct wlr_xdg_shell_v6 *wlr_xdg_shell_v6_create(struct wl_display *display); void wlr_xdg_shell_v6_destroy(struct wlr_xdg_shell_v6 *xdg_shell); +/** + * Send a ping to the surface. If the surface does not respond in a reasonable + * amount of time, the ping_timeout event will be emitted. + */ +void wlr_xdg_surface_v6_ping(struct wlr_xdg_surface_v6 *surface); + +/** + * Request that this toplevel surface be the given size. + */ +void wlr_xdg_toplevel_v6_set_size(struct wlr_xdg_surface_v6 *surface, + uint32_t width, uint32_t height); + +/** + * Request that this toplevel surface show itself in an activated or deactivated + * state. + */ +void wlr_xdg_toplevel_v6_set_activated(struct wlr_xdg_surface_v6 *surface, + bool activated); + +/** + * Request that this toplevel surface consider itself maximized or not + * maximized. + */ +void wlr_xdg_toplevel_v6_set_maximized(struct wlr_xdg_surface_v6 *surface, + bool maximized); + +/** + * Request that this toplevel surface consider itself fullscreen or not + * fullscreen. + */ +void wlr_xdg_toplevel_v6_set_fullscreen(struct wlr_xdg_surface_v6 *surface, + bool fullscreen); + +/** + * Request that this toplevel surface consider itself to be resizing or not + * resizing. + */ +void wlr_xdg_toplevel_v6_set_resizing(struct wlr_xdg_surface_v6 *surface, + bool resizing); + #endif diff --git a/types/wlr_surface.c b/types/wlr_surface.c index e733c544..a9a54abe 100644 --- a/types/wlr_surface.c +++ b/types/wlr_surface.c @@ -365,6 +365,7 @@ const struct wl_surface_interface surface_interface = { static void destroy_surface(struct wl_resource *resource) { struct wlr_surface *surface = wl_resource_get_user_data(resource); + wl_signal_emit(&surface->signals.destroy, surface); wlr_texture_destroy(surface->texture); struct wlr_frame_callback *cb, *next; @@ -399,6 +400,7 @@ struct wlr_surface *wlr_surface_create(struct wl_resource *res, pixman_region32_init(&surface->pending.opaque); pixman_region32_init(&surface->pending.input); wl_signal_init(&surface->signals.commit); + wl_signal_init(&surface->signals.destroy); wl_list_init(&surface->frame_callback_list); wl_resource_set_implementation(res, &surface_interface, surface, destroy_surface); @@ -420,3 +422,24 @@ void wlr_surface_get_matrix(struct wlr_surface *surface, wlr_matrix_mul(matrix, &scale, matrix); wlr_matrix_mul(projection, matrix, matrix); } + +int wlr_surface_set_role(struct wlr_surface *surface, const char *role, + struct wl_resource *error_resource, uint32_t error_code) { + assert(role); + + if (surface->role == NULL || + surface->role == role || + strcmp(surface->role, role) == 0) { + surface->role = role; + + return 0; + } + + wl_resource_post_error(error_resource, error_code, + "Cannot assign role %s to wl_surface@%d, already has role %s\n", + role, + wl_resource_get_id(surface->resource), + surface->role); + + return -1; +} diff --git a/types/wlr_xdg_shell_v6.c b/types/wlr_xdg_shell_v6.c index b5df9bd8..a7450add 100644 --- a/types/wlr_xdg_shell_v6.c +++ b/types/wlr_xdg_shell_v6.c @@ -1,111 +1,266 @@ +#ifndef _POSIX_C_SOURCE +#define _POSIX_C_SOURCE 200809L +#endif #include <assert.h> #include <stdlib.h> +#include <string.h> #include <wayland-server.h> #include <wlr/types/wlr_xdg_shell_v6.h> +#include <wlr/types/wlr_surface.h> +#include <wlr/types/wlr_seat.h> #include <wlr/util/log.h> #include "xdg-shell-unstable-v6-protocol.h" -static void resource_destroy(struct wl_client *client, struct wl_resource *resource) { - // TODO: we probably need to do more than this +static const char *wlr_desktop_xdg_toplevel_role = "xdg_toplevel"; + +static void resource_destroy(struct wl_client *client, + struct wl_resource *resource) { wl_resource_destroy(resource); } -static void xdg_toplevel_set_parent(struct wl_client *client, +static void xdg_toplevel_protocol_set_parent(struct wl_client *client, struct wl_resource *resource, struct wl_resource *parent_resource) { - wlr_log(L_DEBUG, "TODO: toplevel set parent"); + struct wlr_xdg_surface_v6 *surface = wl_resource_get_user_data(resource); + struct wlr_xdg_surface_v6 *parent = NULL; + + if (parent_resource != NULL) { + parent = wl_resource_get_user_data(parent_resource); + } + + surface->toplevel_state->parent = parent; } -static void xdg_toplevel_set_title(struct wl_client *client, +static void xdg_toplevel_protocol_set_title(struct wl_client *client, struct wl_resource *resource, const char *title) { - wlr_log(L_DEBUG, "TODO: toplevel set title"); + struct wlr_xdg_surface_v6 *surface = wl_resource_get_user_data(resource); + char *tmp; + + tmp = strdup(title); + if (tmp == NULL) { + return; + } + + free(surface->title); + surface->title = tmp; } -static void xdg_toplevel_set_app_id(struct wl_client *client, +static void xdg_toplevel_protocol_set_app_id(struct wl_client *client, struct wl_resource *resource, const char *app_id) { - wlr_log(L_DEBUG, "TODO: toplevel set app id"); + struct wlr_xdg_surface_v6 *surface = wl_resource_get_user_data(resource); + char *tmp; + + tmp = strdup(app_id); + if (tmp == NULL) { + return; + } + + free(surface->app_id); + surface->app_id = tmp; } -static void xdg_toplevel_show_window_menu(struct wl_client *client, - struct wl_resource *resource, struct wl_resource *seat, uint32_t serial, - int32_t x, int32_t y) { - wlr_log(L_DEBUG, "TODO: toplevel show window menu"); +static void xdg_toplevel_protocol_show_window_menu(struct wl_client *client, + struct wl_resource *resource, struct wl_resource *seat_resource, + uint32_t serial, int32_t x, int32_t y) { + struct wlr_xdg_surface_v6 *surface = wl_resource_get_user_data(resource); + struct wlr_seat_handle *seat_handle = + wl_resource_get_user_data(seat_resource); + + if (!surface->configured) { + wl_resource_post_error(surface->toplevel_state->resource, + ZXDG_SURFACE_V6_ERROR_NOT_CONSTRUCTED, + "surface has not been configured yet"); + return; + } + + struct wlr_xdg_toplevel_v6_show_window_menu_event *event = + calloc(1, sizeof(struct wlr_xdg_toplevel_v6_show_window_menu_event)); + if (event == NULL) { + wl_client_post_no_memory(client); + return; + } + + event->client = client; + event->surface = surface; + event->seat_handle = seat_handle; + event->serial = serial; + event->x = x; + event->y = y; + + wl_signal_emit(&surface->events.request_show_window_menu, event); + + free(event); } -static void xdg_toplevel_move(struct wl_client *client, +static void xdg_toplevel_protocol_move(struct wl_client *client, struct wl_resource *resource, struct wl_resource *seat_resource, uint32_t serial) { - wlr_log(L_DEBUG, "TODO: toplevel move"); + struct wlr_xdg_surface_v6 *surface = wl_resource_get_user_data(resource); + struct wlr_seat_handle *seat_handle = + wl_resource_get_user_data(seat_resource); + + if (!surface->configured) { + wl_resource_post_error(surface->toplevel_state->resource, + ZXDG_SURFACE_V6_ERROR_NOT_CONSTRUCTED, + "surface has not been configured yet"); + return; + } + + struct wlr_xdg_toplevel_v6_move_event *event = + calloc(1, sizeof(struct wlr_xdg_toplevel_v6_move_event)); + if (event == NULL) { + wl_client_post_no_memory(client); + return; + } + + event->client = client; + event->surface = surface; + event->seat_handle = seat_handle; + event->serial = serial; + + wl_signal_emit(&surface->events.request_move, event); + + free(event); } -static void xdg_toplevel_resize(struct wl_client *client, +static void xdg_toplevel_protocol_resize(struct wl_client *client, struct wl_resource *resource, struct wl_resource *seat_resource, uint32_t serial, uint32_t edges) { - wlr_log(L_DEBUG, "TODO: toplevel resize"); + struct wlr_xdg_surface_v6 *surface = wl_resource_get_user_data(resource); + struct wlr_seat_handle *seat_handle = + wl_resource_get_user_data(seat_resource); + + if (!surface->configured) { + wl_resource_post_error(surface->toplevel_state->resource, + ZXDG_SURFACE_V6_ERROR_NOT_CONSTRUCTED, + "surface has not been configured yet"); + return; + } + + struct wlr_xdg_toplevel_v6_resize_event *event = + calloc(1, sizeof(struct wlr_xdg_toplevel_v6_resize_event)); + if (event == NULL) { + wl_client_post_no_memory(client); + return; + } + + event->client = client; + event->surface = surface; + event->seat_handle = seat_handle; + event->serial = serial; + event->edges = edges; + + wl_signal_emit(&surface->events.request_resize, event); + + free(event); } -static void xdg_toplevel_set_max_size(struct wl_client *client, +static void xdg_toplevel_protocol_set_max_size(struct wl_client *client, struct wl_resource *resource, int32_t width, int32_t height) { - wlr_log(L_DEBUG, "TODO: toplevel set max size"); + struct wlr_xdg_surface_v6 *surface = wl_resource_get_user_data(resource); + surface->toplevel_state->next.max_width = width; + surface->toplevel_state->next.max_height = height; } -static void xdg_toplevel_set_min_size(struct wl_client *client, +static void xdg_toplevel_protocol_set_min_size(struct wl_client *client, struct wl_resource *resource, int32_t width, int32_t height) { - wlr_log(L_DEBUG, "TODO: toplevel set min size"); + struct wlr_xdg_surface_v6 *surface = wl_resource_get_user_data(resource); + surface->toplevel_state->next.min_width = width; + surface->toplevel_state->next.min_height = height; } -static void xdg_toplevel_set_maximized(struct wl_client *client, +static void xdg_toplevel_protocol_set_maximized(struct wl_client *client, struct wl_resource *resource) { - wlr_log(L_DEBUG, "TODO: toplevel set maximized"); + struct wlr_xdg_surface_v6 *surface = wl_resource_get_user_data(resource); + surface->toplevel_state->next.maximized = true; } -static void xdg_toplevel_unset_maximized(struct wl_client *client, struct wl_resource *resource) { - wlr_log(L_DEBUG, "TODO: toplevel unset maximized"); +static void xdg_toplevel_protocol_unset_maximized(struct wl_client *client, + struct wl_resource *resource) { + struct wlr_xdg_surface_v6 *surface = wl_resource_get_user_data(resource); + surface->toplevel_state->next.maximized = false; } -static void xdg_toplevel_set_fullscreen(struct wl_client *client, struct wl_resource *resource, struct wl_resource *output_resource) { - wlr_log(L_DEBUG, "TODO: toplevel set fullscreen"); +static void xdg_toplevel_protocol_set_fullscreen(struct wl_client *client, + struct wl_resource *resource, struct wl_resource *output_resource) { + struct wlr_xdg_surface_v6 *surface = wl_resource_get_user_data(resource); + surface->toplevel_state->next.fullscreen = true; } -static void xdg_toplevel_unset_fullscreen(struct wl_client *client, struct wl_resource *resource) { - wlr_log(L_DEBUG, "TODO: toplevel unset fullscreen"); +static void xdg_toplevel_protocol_unset_fullscreen(struct wl_client *client, + struct wl_resource *resource) { + struct wlr_xdg_surface_v6 *surface = wl_resource_get_user_data(resource); + surface->toplevel_state->next.fullscreen = false; } -static void xdg_toplevel_set_minimized(struct wl_client *client, struct wl_resource *resource) { - wlr_log(L_DEBUG, "TODO: toplevel set minimized"); +static void xdg_toplevel_protocol_set_minimized(struct wl_client *client, + struct wl_resource *resource) { + struct wlr_xdg_surface_v6 *surface = wl_resource_get_user_data(resource); + wl_signal_emit(&surface->events.request_minimize, surface); } -static const struct zxdg_toplevel_v6_interface zxdg_toplevel_v6_implementation = { +static const struct zxdg_toplevel_v6_interface zxdg_toplevel_v6_implementation = +{ .destroy = resource_destroy, - .set_parent = xdg_toplevel_set_parent, - .set_title = xdg_toplevel_set_title, - .set_app_id = xdg_toplevel_set_app_id, - .show_window_menu = xdg_toplevel_show_window_menu, - .move = xdg_toplevel_move, - .resize = xdg_toplevel_resize, - .set_max_size = xdg_toplevel_set_max_size, - .set_min_size = xdg_toplevel_set_min_size, - .set_maximized = xdg_toplevel_set_maximized, - .unset_maximized = xdg_toplevel_unset_maximized, - .set_fullscreen = xdg_toplevel_set_fullscreen, - .unset_fullscreen = xdg_toplevel_unset_fullscreen, - .set_minimized = xdg_toplevel_set_minimized + .set_parent = xdg_toplevel_protocol_set_parent, + .set_title = xdg_toplevel_protocol_set_title, + .set_app_id = xdg_toplevel_protocol_set_app_id, + .show_window_menu = xdg_toplevel_protocol_show_window_menu, + .move = xdg_toplevel_protocol_move, + .resize = xdg_toplevel_protocol_resize, + .set_max_size = xdg_toplevel_protocol_set_max_size, + .set_min_size = xdg_toplevel_protocol_set_min_size, + .set_maximized = xdg_toplevel_protocol_set_maximized, + .unset_maximized = xdg_toplevel_protocol_unset_maximized, + .set_fullscreen = xdg_toplevel_protocol_set_fullscreen, + .unset_fullscreen = xdg_toplevel_protocol_unset_fullscreen, + .set_minimized = xdg_toplevel_protocol_set_minimized }; -static void xdg_surface_destroy(struct wl_resource *resource) { - struct wlr_xdg_surface_v6 *surface = wl_resource_get_user_data(resource); +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); 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) { + xdg_surface_destroy(surface); + } +} + static void xdg_surface_get_toplevel(struct wl_client *client, struct wl_resource *resource, uint32_t id) { - // TODO: Flesh out + struct wlr_xdg_surface_v6 *surface = wl_resource_get_user_data(resource); + + if (wlr_surface_set_role(surface->surface, wlr_desktop_xdg_toplevel_role, + resource, ZXDG_SHELL_V6_ERROR_ROLE)) { + return; + } + + surface->toplevel_state = calloc(1, sizeof(struct wlr_xdg_toplevel_v6)); + if (surface->toplevel_state == NULL) { + wl_client_post_no_memory(client); + return; + } + + surface->role = WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL; + surface->toplevel_state->base = surface; + struct wl_resource *toplevel_resource = wl_resource_create(client, &zxdg_toplevel_v6_interface, wl_resource_get_version(resource), id); + + surface->toplevel_state->resource = toplevel_resource; + wl_resource_set_implementation(toplevel_resource, - &zxdg_toplevel_v6_implementation, NULL, NULL); - struct wl_display *display = wl_client_get_display(client); - zxdg_surface_v6_send_configure(resource, wl_display_next_serial(display)); + &zxdg_toplevel_v6_implementation, surface, NULL); } static void xdg_surface_get_popup(struct wl_client *client, @@ -114,15 +269,80 @@ static void xdg_surface_get_popup(struct wl_client *client, 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) { + assert(surface->role == WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL); + surface->toplevel_state->next = configure->state; + surface->toplevel_state->pending.width = 0; + surface->toplevel_state->pending.height = 0; +} + static void xdg_surface_ack_configure(struct wl_client *client, struct wl_resource *resource, uint32_t serial) { - wlr_log(L_DEBUG, "TODO xdg surface ack configure"); + struct wlr_xdg_surface_v6 *surface = wl_resource_get_user_data(resource); + + if (surface->role == WLR_XDG_SURFACE_V6_ROLE_NONE) { + wl_resource_post_error(surface->resource, + ZXDG_SURFACE_V6_ERROR_NOT_CONSTRUCTED, + "xdg_surface must have a role"); + return; + } + + bool found = false; + struct wlr_xdg_surface_v6_configure *configure, *tmp; + wl_list_for_each_safe(configure, tmp, &surface->configure_list, link) { + if (configure->serial < serial) { + wl_list_remove(&configure->link); + free(configure); + } else if (configure->serial == serial) { + wl_list_remove(&configure->link); + found = true; + break; + } else { + break; + } + } + if (!found) { + wl_resource_post_error(surface->client->resource, + ZXDG_SHELL_V6_ERROR_INVALID_SURFACE_STATE, + "wrong configure serial: %u", serial); + return; + } + + // TODO handle popups + if (surface->role == WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL) { + wlr_xdg_toplevel_v6_ack_configure(surface, configure); + } + + if (!surface->configured) { + surface->configured = true; + wl_signal_emit(&surface->client->shell->events.new_surface, surface); + } + + wl_signal_emit(&surface->events.ack_configure, surface); + + free(configure); } - + static void xdg_surface_set_window_geometry(struct wl_client *client, struct wl_resource *resource, int32_t x, int32_t y, int32_t width, int32_t height) { - wlr_log(L_DEBUG, "TODO xdg surface set window geometry"); + struct wlr_xdg_surface_v6 *surface = wl_resource_get_user_data(resource); + + if (surface->role == WLR_XDG_SURFACE_V6_ROLE_NONE) { + wl_resource_post_error(surface->resource, + ZXDG_SURFACE_V6_ERROR_NOT_CONSTRUCTED, + "xdg_surface must have a role"); + return; + } + + surface->has_next_geometry = true; + surface->next_geometry->height = height; + surface->next_geometry->width = width; + surface->next_geometry->x = x; + surface->next_geometry->y = y; + } static const struct zxdg_surface_v6_interface zxdg_surface_v6_implementation = { @@ -138,26 +358,270 @@ static void xdg_shell_create_positioner(struct wl_client *client, wlr_log(L_DEBUG, "TODO: xdg shell create positioner"); } -static void xdg_shell_get_xdg_surface(struct wl_client *client, - struct wl_resource *_xdg_shell, uint32_t id, - struct wl_resource *_surface) { - struct wlr_xdg_shell_v6 *xdg_shell = wl_resource_get_user_data(_xdg_shell); +static bool wlr_xdg_surface_v6_toplevel_state_compare( + struct wlr_xdg_toplevel_v6 *state) { + // is pending state different from current state? + if (state->pending.activated != state->current.activated) { + return false; + } + if (state->pending.fullscreen != state->current.fullscreen) { + return false; + } + if (state->pending.maximized != state->current.maximized) { + return false; + } + if (state->pending.resizing != state->current.resizing) { + return false; + } + + if ((uint32_t)state->base->geometry->width == state->pending.width && + (uint32_t)state->base->geometry->height == state->pending.height) { + return true; + } + + if (state->pending.width == 0 && state->pending.height == 0) { + return true; + } + + return false; +} + +static void wlr_xdg_toplevel_v6_send_configure( + struct wlr_xdg_surface_v6 *surface, + struct wlr_xdg_surface_v6_configure *configure) { + assert(surface->role == WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL); + uint32_t *s; + struct wl_array states; + + configure->state = surface->toplevel_state->pending; + + wl_array_init(&states); + if (surface->toplevel_state->pending.maximized) { + s = wl_array_add(&states, sizeof(uint32_t)); + *s = ZXDG_TOPLEVEL_V6_STATE_MAXIMIZED; + } + if (surface->toplevel_state->pending.fullscreen) { + s = wl_array_add(&states, sizeof(uint32_t)); + *s = ZXDG_TOPLEVEL_V6_STATE_FULLSCREEN; + } + if (surface->toplevel_state->pending.resizing) { + s = wl_array_add(&states, sizeof(uint32_t)); + *s = ZXDG_TOPLEVEL_V6_STATE_RESIZING; + } + if (surface->toplevel_state->pending.activated) { + s = wl_array_add(&states, sizeof(uint32_t)); + *s = ZXDG_TOPLEVEL_V6_STATE_ACTIVATED; + } + + uint32_t width = surface->toplevel_state->pending.width; + uint32_t height = surface->toplevel_state->pending.height; + + if (width == 0 || height == 0) { + width = surface->geometry->width; + height = surface->geometry->height; + } + + zxdg_toplevel_v6_send_configure(surface->toplevel_state->resource, width, + height, &states); + + wl_array_release(&states); +} + +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 = + calloc(1, sizeof(struct wlr_xdg_surface_v6_configure)); + if (configure == NULL) { + wl_client_post_no_memory(surface->client->client); + return; + } + + wl_list_insert(surface->configure_list.prev, &configure->link); + configure->serial = wl_display_next_serial(display); + + wlr_xdg_toplevel_v6_send_configure(surface, configure); + + 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 = !force && + wlr_xdg_surface_v6_toplevel_state_compare(surface->toplevel_state); + + if (surface->configure_idle != NULL) { + if (!pending_same) { + // configure request already scheduled + return; + } + + // configure request not necessary anymore + wl_event_source_remove(surface->configure_idle); + surface->configure_idle = NULL; + } else { + if (pending_same) { + // configure request not necessary + return; + } + + surface->configure_idle = + wl_event_loop_add_idle( + loop, + wlr_xdg_surface_send_configure, + surface); + } +} + +static void handle_wlr_surface_destroyed(struct wl_listener *listener, + void *data) { + struct wlr_xdg_surface_v6 *xdg_surface = + wl_container_of(listener, xdg_surface, surface_destroy_listener); + xdg_surface_destroy(xdg_surface); +} + +static void wlr_xdg_surface_v6_toplevel_committed( + struct wlr_xdg_surface_v6 *surface) { + assert(surface->role == WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL); + + if (!surface->surface->current.buffer && !surface->toplevel_state->added) { + // on the first commit, send a configure request to tell the client it + // is added + wlr_xdg_surface_v6_schedule_configure(surface, true); + surface->toplevel_state->added = true; + return; + } + + if (!surface->surface->current.buffer) { + return; + } + + surface->toplevel_state->current = surface->toplevel_state->next; +} + +static void handle_wlr_surface_committed(struct wl_listener *listener, + void *data) { + struct wlr_xdg_surface_v6 *surface = + wl_container_of(listener, surface, surface_commit_listener); + + if (surface->surface->current.buffer && !surface->configured) { + wl_resource_post_error(surface->resource, + ZXDG_SURFACE_V6_ERROR_UNCONFIGURED_BUFFER, + "xdg_surface has never been configured"); + return; + } + + if (surface->has_next_geometry) { + surface->has_next_geometry = false; + surface->geometry->x = surface->next_geometry->x; + surface->geometry->y = surface->next_geometry->y; + surface->geometry->width = surface->next_geometry->width; + surface->geometry->height = surface->next_geometry->height; + } + + switch (surface->role) { + case WLR_XDG_SURFACE_V6_ROLE_NONE: + wl_resource_post_error(surface->resource, + ZXDG_SURFACE_V6_ERROR_NOT_CONSTRUCTED, + "xdg_surface must have a role"); + break; + case WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL: + wlr_xdg_surface_v6_toplevel_committed(surface); + break; + case WLR_XDG_SURFACE_V6_ROLE_POPUP: + wlr_log(L_DEBUG, "TODO: popup surface committed"); + break; + } + + wl_signal_emit(&surface->events.commit, surface); +} + +static void xdg_shell_get_xdg_surface(struct wl_client *wl_client, + struct wl_resource *client_resource, uint32_t id, + struct wl_resource *surface_resource) { + struct wlr_xdg_client_v6 *client = + wl_resource_get_user_data(client_resource); + struct wlr_xdg_surface_v6 *surface; if (!(surface = calloc(1, sizeof(struct wlr_xdg_surface_v6)))) { + wl_client_post_no_memory(wl_client); + return; + } + + if (!(surface->geometry = calloc(1, sizeof(struct wlr_box)))) { + free(surface); + wl_client_post_no_memory(wl_client); return; } - surface->surface = _surface; - surface->resource = wl_resource_create(client, - &zxdg_surface_v6_interface, wl_resource_get_version(_xdg_shell), id); + + if (!(surface->next_geometry = calloc(1, sizeof(struct wlr_box)))) { + free(surface->geometry); + free(surface); + wl_client_post_no_memory(wl_client); + return; + } + + surface->client = client; + surface->role = WLR_XDG_SURFACE_V6_ROLE_NONE; + surface->surface = wl_resource_get_user_data(surface_resource); + surface->resource = wl_resource_create(wl_client, + &zxdg_surface_v6_interface, wl_resource_get_version(client_resource), + id); + + if (surface->surface->current.buffer != NULL) { + wl_resource_post_error(surface->resource, + ZXDG_SURFACE_V6_ERROR_UNCONFIGURED_BUFFER, + "xdg_surface must not have a buffer at creation"); + return; + } + + wl_list_init(&surface->configure_list); + + wl_signal_init(&surface->events.request_minimize); + wl_signal_init(&surface->events.request_move); + wl_signal_init(&surface->events.request_resize); + wl_signal_init(&surface->events.request_show_window_menu); + wl_signal_init(&surface->events.commit); + wl_signal_init(&surface->events.destroy); + wl_signal_init(&surface->events.ack_configure); + wl_signal_init(&surface->events.ping_timeout); + + wl_signal_add(&surface->surface->signals.destroy, + &surface->surface_destroy_listener); + surface->surface_destroy_listener.notify = handle_wlr_surface_destroyed; + + wl_signal_add(&surface->surface->signals.commit, + &surface->surface_commit_listener); + surface->surface_commit_listener.notify = handle_wlr_surface_committed; + wlr_log(L_DEBUG, "new xdg_surface %p (res %p)", surface, surface->resource); wl_resource_set_implementation(surface->resource, - &zxdg_surface_v6_implementation, surface, xdg_surface_destroy); - wl_list_insert(&xdg_shell->surfaces, &surface->link); + &zxdg_surface_v6_implementation, surface, xdg_surface_resource_destroy); + wl_list_insert(&client->surfaces, &surface->link); } -static void xdg_shell_pong(struct wl_client *client, +static void xdg_shell_pong(struct wl_client *wl_client, struct wl_resource *resource, uint32_t serial) { - wlr_log(L_DEBUG, "TODO xdg shell pong"); + struct wlr_xdg_client_v6 *client = wl_resource_get_user_data(resource); + + if (client->ping_serial != serial) { + return; + } + + wl_event_source_timer_update(client->ping_timer, 0); + client->ping_serial = 0; } static struct zxdg_shell_v6_interface xdg_shell_impl = { @@ -166,8 +630,33 @@ static struct zxdg_shell_v6_interface xdg_shell_impl = { .pong = xdg_shell_pong, }; -static void xdg_shell_destroy(struct wl_resource *resource) { - wl_list_remove(wl_resource_get_link(resource)); +static void wlr_xdg_client_v6_destroy(struct wl_resource *resource) { + struct wlr_xdg_client_v6 *client = wl_resource_get_user_data(resource); + + struct wlr_xdg_surface_v6 *surface, *tmp = NULL; + wl_list_for_each_safe(surface, tmp, &client->surfaces, link) { + wl_list_remove(&surface->link); + wl_list_init(&surface->link); + } + + if (client->ping_timer != NULL) { + wl_event_source_remove(client->ping_timer); + } + + wl_list_remove(&client->link); + free(client); +} + +static int wlr_xdg_client_v6_ping_timeout(void *user_data) { + struct wlr_xdg_client_v6 *client = user_data; + + struct wlr_xdg_surface_v6 *surface; + wl_list_for_each(surface, &client->surfaces, link) { + wl_signal_emit(&surface->events.ping_timeout, surface); + } + + client->ping_serial = 0; + return 1; } static void xdg_shell_bind(struct wl_client *wl_client, void *_xdg_shell, @@ -175,14 +664,36 @@ static void xdg_shell_bind(struct wl_client *wl_client, void *_xdg_shell, struct wlr_xdg_shell_v6 *xdg_shell = _xdg_shell; assert(wl_client && xdg_shell); if (version > 1) { - wlr_log(L_ERROR, "Client requested unsupported xdg_shell_v6 version, disconnecting"); + wlr_log(L_ERROR, + "Client requested unsupported xdg_shell_v6 version, disconnecting"); wl_client_destroy(wl_client); return; } - struct wl_resource *wl_resource = wl_resource_create( - wl_client, &zxdg_shell_v6_interface, version, id); - wl_resource_set_implementation(wl_resource, &xdg_shell_impl, xdg_shell, xdg_shell_destroy); - wl_list_insert(&xdg_shell->wl_resources, wl_resource_get_link(wl_resource)); + struct wlr_xdg_client_v6 *client = + calloc(1, sizeof(struct wlr_xdg_client_v6)); + if (client == NULL) { + wl_client_post_no_memory(wl_client); + return; + } + + wl_list_init(&client->surfaces); + + client->resource = + wl_resource_create(wl_client, &zxdg_shell_v6_interface, version, id); + client->client = wl_client; + client->shell = xdg_shell; + + wl_resource_set_implementation(client->resource, &xdg_shell_impl, client, + wlr_xdg_client_v6_destroy); + wl_list_insert(&xdg_shell->clients, &client->link); + + struct wl_display *display = wl_client_get_display(client->client); + struct wl_event_loop *loop = wl_display_get_event_loop(display); + client->ping_timer = wl_event_loop_add_timer(loop, + wlr_xdg_client_v6_ping_timeout, client); + if (client->ping_timer == NULL) { + wl_client_post_no_memory(client->client); + } } struct wlr_xdg_shell_v6 *wlr_xdg_shell_v6_create(struct wl_display *display) { @@ -191,6 +702,11 @@ struct wlr_xdg_shell_v6 *wlr_xdg_shell_v6_create(struct wl_display *display) { if (!xdg_shell) { return NULL; } + + xdg_shell->ping_timeout = 10000; + + wl_list_init(&xdg_shell->clients); + struct wl_global *wl_global = wl_global_create(display, &zxdg_shell_v6_interface, 1, xdg_shell, xdg_shell_bind); if (!wl_global) { @@ -198,8 +714,9 @@ struct wlr_xdg_shell_v6 *wlr_xdg_shell_v6_create(struct wl_display *display) { return NULL; } xdg_shell->wl_global = wl_global; - wl_list_init(&xdg_shell->wl_resources); - wl_list_init(&xdg_shell->surfaces); + + wl_signal_init(&xdg_shell->events.new_surface); + return xdg_shell; } @@ -207,13 +724,62 @@ void wlr_xdg_shell_v6_destroy(struct wlr_xdg_shell_v6 *xdg_shell) { if (!xdg_shell) { return; } - struct wl_resource *resource = NULL, *temp = NULL; - wl_resource_for_each_safe(resource, temp, &xdg_shell->wl_resources) { - struct wl_list *link = wl_resource_get_link(resource); - wl_list_remove(link); - } - // TODO: destroy surfaces // TODO: this segfault (wl_display->registry_resource_list is not init) // wl_global_destroy(xdg_shell->wl_global); free(xdg_shell); } + +void wlr_xdg_surface_v6_ping(struct wlr_xdg_surface_v6 *surface) { + if (surface->client->ping_serial != 0) { + // already pinged + return; + } + + surface->client->ping_serial = + wl_display_next_serial(wl_client_get_display(surface->client->client)); + wl_event_source_timer_update(surface->client->ping_timer, + surface->client->shell->ping_timeout); + zxdg_shell_v6_send_ping(surface->client->resource, + surface->client->ping_serial); +} + +void wlr_xdg_toplevel_v6_set_size(struct wlr_xdg_surface_v6 *surface, + uint32_t width, uint32_t height) { + assert(surface->role == WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL); + surface->toplevel_state->pending.width = width; + surface->toplevel_state->pending.height = height; + + wlr_xdg_surface_v6_schedule_configure(surface, false); +} + +void wlr_xdg_toplevel_v6_set_activated(struct wlr_xdg_surface_v6 *surface, + bool activated) { + assert(surface->role == WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL); + surface->toplevel_state->pending.activated = activated; + + wlr_xdg_surface_v6_schedule_configure(surface, false); +} + +void wlr_xdg_toplevel_v6_set_maximized(struct wlr_xdg_surface_v6 *surface, + bool maximized) { + assert(surface->role == WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL); + surface->toplevel_state->pending.maximized = maximized; + + wlr_xdg_surface_v6_schedule_configure(surface, false); +} + +void wlr_xdg_toplevel_v6_set_fullscreen(struct wlr_xdg_surface_v6 *surface, + bool fullscreen) { + assert(surface->role == WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL); + surface->toplevel_state->pending.fullscreen = fullscreen; + + wlr_xdg_surface_v6_schedule_configure(surface, false); +} + +void wlr_xdg_toplevel_v6_set_resizing(struct wlr_xdg_surface_v6 *surface, + bool resizing) { + assert(surface->role == WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL); + surface->toplevel_state->pending.fullscreen = resizing; + + wlr_xdg_surface_v6_schedule_configure(surface, false); +} |