diff --git a/include/rootston/desktop.h b/include/rootston/desktop.h index 768c3e20c03a521c32471d540cb01c2aeeea48b8..740f2643f5735a73e5d99dd5dbb28331688afb22 100644 --- a/include/rootston/desktop.h +++ b/include/rootston/desktop.h @@ -74,7 +74,7 @@ struct roots_desktop { struct wlr_xwayland *xwayland; struct wl_listener xwayland_surface; #endif - struct phosh *phosh; + struct phosh_private *phosh; }; struct roots_server; diff --git a/include/rootston/phosh.h b/include/rootston/phosh.h index cb0d546b1f944a99807957143febe36033de8608..aa885ad6ae39914c46d90ff1b79e1a367fc8de8a 100644 --- a/include/rootston/phosh.h +++ b/include/rootston/phosh.h @@ -1,19 +1,36 @@ -#ifndef _ROOTSTON_PHOSH_H -#define _ROOTSTON_PHOSH_H - +#pragma once #include -struct phosh { +#define PHOSH_PRIVATE_XDG_SWITCHER_SINCE_VERSION 2 + +struct phosh_private { struct wl_resource* resource; struct wl_global *global; + struct wl_list xdg_switchers; // phosh_private_xdg_switchers::link + struct roots_desktop *desktop; struct { struct wl_listener layer_shell_new_surface; struct wl_listener panel_surface_destroy; } listeners; struct wlr_layer_surface *panel; + struct wl_list apps; }; -struct phosh* phosh_create(struct roots_desktop *desktop, struct wl_display *display); -void phosh_destroy(struct phosh *shell); -#endif + +struct phosh_private_xdg_switcher { + struct wl_list link; + struct wl_resource *resource; + struct phosh_private *phosh; + + struct { + struct wl_signal destroy; + } events; +}; + + +struct phosh_private* phosh_create(struct roots_desktop *desktop, struct wl_display *display); +void phosh_destroy(struct phosh_private *shell); +struct phosh_private *phosh_private_from_resource(struct wl_resource *resource); +struct phosh_private_xdg_switcher *phosh_private_xdg_switcher_from_resource( + struct wl_resource *resource); diff --git a/protocol/phosh-private.xml b/protocol/phosh-private.xml index 79b9b9b0d55952d9fa1b12c349c6b247b5151b0e..4eede1e58be63ed20b7086ba74cf7bfe456713c2 100644 --- a/protocol/phosh-private.xml +++ b/protocol/phosh-private.xml @@ -1,5 +1,5 @@ - + Private protocol between phosh and the compositor. @@ -9,13 +9,61 @@ summary="an invalid argument was provided in a request"/> - - - - Rotate the display clockwise 0, 90, 180 or 270 degree. + + + + Rotate the output clockwise 0, 90, 180 or 270 degree. + + + + + + + + + + The interface is meant to list xdg surfaces (see the xdg-shell + stable wayland protocol) and to raise these surfaces to the top + of the window stack. + + It's up to the compositor if it only lists surfaces of the + xdg-shell stable protocol or also surfaces using unstable versions of + the xdg-shell protocol. + + + + + + + + Request to list xdg shell toplevels. + + + + + + + + + + + + + + + + + + + + + + + diff --git a/rootston/phosh.c b/rootston/phosh.c index f31c7b9a2420008d65735f67f071f593aa339652..16bfcf67ed96c33dc40fe65732596bf6fd2401ee 100644 --- a/rootston/phosh.c +++ b/rootston/phosh.c @@ -12,12 +12,119 @@ #include "rootston/desktop.h" #include "rootston/phosh.h" -static void phosh_rotate_output(struct wl_client *client, +#define PHOSH_PRIVATE_VERSION 2 + +static void xdg_switcher_handle_list_xdg_surfaces(struct wl_client *client, + struct wl_resource *resource) { + struct phosh_private_xdg_switcher *xdg_switcher = + phosh_private_xdg_switcher_from_resource(resource); + struct phosh_private *phosh = xdg_switcher->phosh; + struct roots_desktop *desktop = phosh->desktop; + struct roots_view *view; + + wl_list_for_each(view, &desktop->views, link) { + const char *app_id = NULL; + const char *title = NULL; + + switch (view->type) { + case ROOTS_XDG_SHELL_VIEW: + if (view->xdg_surface->role != WLR_XDG_SURFACE_ROLE_TOPLEVEL) + continue; + app_id = view->xdg_surface->toplevel->app_id; + title = view->xdg_surface->toplevel->title; + break; + case ROOTS_XDG_SHELL_V6_VIEW: + if (view->xdg_surface_v6->role != WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL) + continue; + app_id = view->xdg_surface_v6->toplevel->app_id; + title = view->xdg_surface_v6->toplevel->title; + break; + default: + /* other surface types would go here */ + break; + } + + if (app_id) { + phosh_private_xdg_switcher_send_xdg_surface (resource, app_id, title); + app_id = NULL; + title = NULL; + } + } + phosh_private_xdg_switcher_send_list_xdg_surfaces_done (resource); +} + + +static void xdg_switcher_handle_raise_xdg_surfaces(struct wl_client *client, + struct wl_resource *resource, + const char *app_id, + const char *title) { + struct phosh_private_xdg_switcher *xdg_switcher = + phosh_private_xdg_switcher_from_resource(resource); + struct phosh_private *phosh = xdg_switcher->phosh; + struct roots_desktop *desktop = phosh->desktop; + struct roots_view *view, *found_view; + struct roots_input *input = desktop->server->input; + struct roots_seat *seat = input_last_active_seat(input); + + wlr_log(WLR_DEBUG, "will raise view %s", app_id); + wl_list_for_each(view, &desktop->views, link) { + switch (view->type) { + case ROOTS_XDG_SHELL_VIEW: + if (view->xdg_surface->role != WLR_XDG_SURFACE_ROLE_TOPLEVEL) + continue; + if (!strcmp(app_id, view->xdg_surface->toplevel->app_id) && + !strcmp(title, view->xdg_surface->toplevel->title)) + found_view = view; + break; + case ROOTS_XDG_SHELL_V6_VIEW: + if (view->xdg_surface_v6->role != WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL) + continue; + if (!strcmp(app_id, view->xdg_surface_v6->toplevel->app_id) && + !strcmp(title, view->xdg_surface_v6->toplevel->title)) + found_view = view; + break; + default: + /* other surface types would go here */ + break; + } + } + + /* TODO: check if view belongs to this seat */ + if (found_view) { + roots_seat_set_focus(seat, found_view); + } +} + + +static void xdg_switcher_handle_destroy(struct wl_client *client, + struct wl_resource *resource) { + wl_resource_destroy(resource); +} + + +static void xdg_switcher_handle_resource_destroy(struct wl_resource *resource) { + struct phosh_private_xdg_switcher *xdg_switcher = + phosh_private_xdg_switcher_from_resource(resource); + + wlr_log(WLR_DEBUG, "Destroying xdg_switcher %p (res %p)", xdg_switcher, + xdg_switcher->resource); + wl_list_remove(&xdg_switcher->link); + free(xdg_switcher); +} + + +static const struct phosh_private_xdg_switcher_interface phosh_private_xdg_switcher_impl = { + .destroy = xdg_switcher_handle_destroy, + .list_xdg_surfaces = xdg_switcher_handle_list_xdg_surfaces, + .raise_xdg_surface = xdg_switcher_handle_raise_xdg_surfaces, +}; + + +static void phosh_rotate_display(struct wl_client *client, struct wl_resource *resource, struct wl_resource *surface_resource, - uint32_t degrees) -{ - struct phosh *shell = wl_resource_get_user_data(resource); + uint32_t degrees) { + struct phosh_private *phosh = wl_resource_get_user_data(resource); enum wl_output_transform transform = WL_OUTPUT_TRANSFORM_NORMAL; wlr_log(WLR_DEBUG, "rotation: %d", degrees); @@ -43,17 +150,17 @@ static void phosh_rotate_output(struct wl_client *client, break; } - if (!shell->panel) { + if (!phosh->panel) { wlr_log(WLR_ERROR, "Tried to rotate inexistent panel"); return; } - wlr_output_set_transform(shell->panel->output, transform); + wlr_output_set_transform(phosh->panel->output, transform); } static void handle_phosh_panel_surface_destroy (struct wl_listener *listener, void *data) { - struct phosh *phosh = + struct phosh_private *phosh = wl_container_of(listener, phosh, listeners.panel_surface_destroy); if (phosh->panel) { @@ -65,7 +172,7 @@ static void handle_phosh_panel_surface_destroy (struct wl_listener *listener, vo static void handle_phosh_layer_shell_new_surface(struct wl_listener *listener, void *data) { struct wlr_layer_surface *surface = data; - struct phosh *phosh = + struct phosh_private *phosh = wl_container_of(listener, phosh, listeners.layer_shell_new_surface); /* We're only interested in the panel */ @@ -79,36 +186,72 @@ static void handle_phosh_layer_shell_new_surface(struct wl_listener *listener, v } -static void phosh_resource_destroy(struct wl_resource *resource) { - struct phosh *phosh = wl_resource_get_user_data(resource); +void handle_get_xdg_switcher(struct wl_client *client, + struct wl_resource *phosh_private_resource, + uint32_t id) { + struct phosh_private *phosh = + phosh_private_from_resource(phosh_private_resource); + + struct phosh_private_xdg_switcher *xdg_switcher = + calloc(1, sizeof(struct wlr_gamma_control)); + if (xdg_switcher == NULL) { + wl_client_post_no_memory(client); + return; + } + + int version = wl_resource_get_version(phosh_private_resource); + xdg_switcher->resource = wl_resource_create(client, + &phosh_private_xdg_switcher_interface, version, id); + if (xdg_switcher->resource == NULL) { + free(xdg_switcher); + wl_client_post_no_memory(client); + return; + } + + wlr_log(WLR_DEBUG, "new phosh_private_xdg_switcher %p (res %p)", xdg_switcher, + xdg_switcher->resource); + wl_resource_set_implementation(xdg_switcher->resource, + &phosh_private_xdg_switcher_impl, xdg_switcher, xdg_switcher_handle_resource_destroy); + + xdg_switcher->phosh = phosh; + wl_signal_init(&xdg_switcher->events.destroy); + wl_list_insert(&phosh->xdg_switchers, &xdg_switcher->link); +} + + + +static void phosh_handle_resource_destroy(struct wl_resource *resource) { + struct phosh_private *phosh = wl_resource_get_user_data(resource); phosh->resource = NULL; phosh->panel = NULL; + wlr_log(WLR_DEBUG, "Destroying phosh %p (res %p)", phosh, resource); } -static const struct phosh_private_interface phosh_impl = { - phosh_rotate_output, +static const struct phosh_private_interface phosh_private_impl = { + phosh_rotate_display, + handle_get_xdg_switcher, }; static void -bind_phosh(struct wl_client *client, void *data, uint32_t version, uint32_t id) { - struct phosh *phosh = data; +phosh_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id) { + struct phosh_private *phosh = data; struct wl_resource *resource = wl_resource_create(client, &phosh_private_interface, - 1, id); + version, id); if (phosh->resource) { wl_resource_post_error(resource, WL_DISPLAY_ERROR_INVALID_OBJECT, "Only a single client can bind to phosh's private protocol"); } - /* FIXME: unsafe needs client == shell->child.client */ + /* FIXME: unsafe, needs client == shell->child.client */ if (true) { wlr_log(WLR_ERROR, "FIXME: allowing every client to bind as phosh"); wl_resource_set_implementation(resource, - &phosh_impl, - phosh, phosh_resource_destroy); + &phosh_private_impl, + phosh, phosh_handle_resource_destroy); phosh->resource = resource; return; } @@ -118,29 +261,47 @@ bind_phosh(struct wl_client *client, void *data, uint32_t version, uint32_t id) } -struct phosh* +struct phosh_private* phosh_create(struct roots_desktop *desktop, struct wl_display *display) { - struct phosh *shell = calloc(1, sizeof (struct phosh)); - if (!shell) + struct phosh_private *phosh = calloc(1, sizeof (struct phosh_private)); + if (!phosh) return NULL; - shell->desktop = desktop; + phosh->desktop = desktop; wl_signal_add(&desktop->layer_shell->events.new_surface, - &shell->listeners.layer_shell_new_surface); - shell->listeners.layer_shell_new_surface.notify = handle_phosh_layer_shell_new_surface; + &phosh->listeners.layer_shell_new_surface); + phosh->listeners.layer_shell_new_surface.notify = handle_phosh_layer_shell_new_surface; + wl_list_init(&phosh->xdg_switchers); + + wlr_log(WLR_INFO, "Initializing phosh private interface"); + phosh->global = wl_global_create(display, &phosh_private_interface, PHOSH_PRIVATE_VERSION, phosh, phosh_bind); - wlr_log(WLR_INFO, "Initializing phosh private inrerface"); - shell->global = wl_global_create(display, &phosh_private_interface, 1, shell, bind_phosh); - if (!shell->global) { + if (!phosh->global) { return NULL; } - return shell; + return phosh; +} + + +void phosh_destroy(struct phosh_private *phosh) { + wl_list_remove(&phosh->listeners.layer_shell_new_surface.link); + wl_global_destroy(phosh->global); +} + + +struct phosh_private *phosh_private_from_resource( + struct wl_resource *resource) { + assert(wl_resource_instance_of(resource, &phosh_private_interface, + &phosh_private_impl)); + return wl_resource_get_user_data(resource); } -void phosh_destroy(struct phosh *shell) { - wl_list_remove(&shell->listeners.layer_shell_new_surface.link); - wl_global_destroy(shell->global); +struct phosh_private_xdg_switcher *phosh_private_xdg_switcher_from_resource( + struct wl_resource *resource) { + assert(wl_resource_instance_of(resource, &phosh_private_xdg_switcher_interface, + &phosh_private_xdg_switcher_impl)); + return wl_resource_get_user_data(resource); }