diff --git a/include/sway/ipc-server.h b/include/sway/ipc-server.h index b85ff535d..bcf1c4338 100644 --- a/include/sway/ipc-server.h +++ b/include/sway/ipc-server.h @@ -1,6 +1,7 @@ #ifndef _SWAY_IPC_SERVER_H #define _SWAY_IPC_SERVER_H #include +#include "sway/container.h" #include "ipc.h" struct sway_server; @@ -9,4 +10,6 @@ void ipc_init(struct sway_server *server); void ipc_terminate(void); struct sockaddr_un *ipc_user_sockaddr(void); +void ipc_event_window(swayc_t *window, const char *change); + #endif diff --git a/include/sway/view.h b/include/sway/view.h index ac33e11a5..b28862113 100644 --- a/include/sway/view.h +++ b/include/sway/view.h @@ -115,4 +115,6 @@ void view_set_activated(struct sway_view *view, bool activated); void view_close(struct sway_view *view); +void view_update_outputs(struct sway_view *view, const struct wlr_box *before); + #endif diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 63420d0cc..08fe58778 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -63,7 +63,7 @@ static void render_surface(struct wlr_surface *surface, wlr_matrix_project_box(&matrix, &render_box, surface->current->transform, 0, &wlr_output->transform_matrix); wlr_render_with_matrix(server.renderer, surface->texture, - &matrix); + &matrix, 1.0f); // TODO: configurable alpha wlr_surface_send_frame_done(surface, when); } diff --git a/sway/desktop/wl_shell.c b/sway/desktop/wl_shell.c index 0cde6583d..0356aa811 100644 --- a/sway/desktop/wl_shell.c +++ b/sway/desktop/wl_shell.c @@ -131,14 +131,11 @@ void handle_wl_shell_surface(struct wl_listener *listener, void *data) { sway_surface->destroy.notify = handle_destroy; wl_signal_add(&shell_surface->events.destroy, &sway_surface->destroy); - // TODO: actual focus semantics - swayc_t *parent = root_container.children->items[0]; - parent = parent->children->items[0]; // workspace - - swayc_t *cont = new_view(parent, sway_view); + struct sway_seat *seat = input_manager_current_seat(input_manager); + swayc_t *focus = sway_seat_get_focus_inactive(seat, &root_container); + swayc_t *cont = new_view(focus, sway_view); sway_view->swayc = cont; arrange_windows(cont->parent, -1, -1); - sway_input_manager_set_focus(input_manager, cont); } diff --git a/sway/desktop/xdg_shell_v6.c b/sway/desktop/xdg_shell_v6.c index b44d9e54f..7371b829a 100644 --- a/sway/desktop/xdg_shell_v6.c +++ b/sway/desktop/xdg_shell_v6.c @@ -72,6 +72,7 @@ static void handle_commit(struct wl_listener *listener, void *data) { wl_container_of(listener, sway_surface, commit); struct sway_view *view = sway_surface->view; // NOTE: We intentionally discard the view's desired width here + // TODO: Store this for restoration when moving to floating plane // TODO: Let floating views do whatever view->width = sway_surface->pending_width; view->height = sway_surface->pending_height; diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index 7603d3ca9..519c050ec 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c @@ -226,17 +226,16 @@ void handle_xwayland_surface(struct wl_listener *listener, void *data) { wl_signal_add(&xsurface->events.map_notify, &sway_surface->map_notify); sway_surface->map_notify.notify = handle_map_notify; - if (xsurface->override_redirect) { + if (wlr_xwayland_surface_is_unmanaged(xsurface)) { // these don't get a container in the tree wl_list_insert(&root_container.sway_root->unmanaged_views, &sway_view->unmanaged_view_link); return; } - swayc_t *parent = root_container.children->items[0]; - parent = parent->children->items[0]; // workspace - - swayc_t *cont = new_view(parent, sway_view); + struct sway_seat *seat = input_manager_current_seat(input_manager); + swayc_t *focus = sway_seat_get_focus_inactive(seat, &root_container); + swayc_t *cont = new_view(focus, sway_view); sway_view->swayc = cont; arrange_windows(cont->parent, -1, -1); diff --git a/sway/ipc-server.c b/sway/ipc-server.c index 4c0953e8a..156de3807 100644 --- a/sway/ipc-server.c +++ b/sway/ipc-server.c @@ -240,6 +240,57 @@ int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data) { return 0; } +static void ipc_send_event(const char *json_string, enum ipc_command_type event) { + static struct { + enum ipc_command_type event; + enum ipc_feature feature; + } security_mappings[] = { + { IPC_EVENT_WORKSPACE, IPC_FEATURE_EVENT_WORKSPACE }, + { IPC_EVENT_OUTPUT, IPC_FEATURE_EVENT_OUTPUT }, + { IPC_EVENT_MODE, IPC_FEATURE_EVENT_MODE }, + { IPC_EVENT_WINDOW, IPC_FEATURE_EVENT_WINDOW }, + { IPC_EVENT_BINDING, IPC_FEATURE_EVENT_BINDING }, + { IPC_EVENT_INPUT, IPC_FEATURE_EVENT_INPUT } + }; + + uint32_t security_mask = 0; + for (size_t i = 0; i < sizeof(security_mappings) / sizeof(security_mappings[0]); ++i) { + if (security_mappings[i].event == event) { + security_mask = security_mappings[i].feature; + break; + } + } + + int i; + struct ipc_client *client; + for (i = 0; i < ipc_client_list->length; i++) { + client = ipc_client_list->items[i]; + if (!(client->security_policy & security_mask)) { + continue; + } + if ((client->subscribed_events & event_mask(event)) == 0) { + continue; + } + client->current_command = event; + if (!ipc_send_reply(client, json_string, (uint32_t) strlen(json_string))) { + wlr_log_errno(L_INFO, "Unable to send reply to IPC client"); + ipc_client_disconnect(client); + } + } +} + +void ipc_event_window(swayc_t *window, const char *change) { + wlr_log(L_DEBUG, "Sending window::%s event", change); + json_object *obj = json_object_new_object(); + json_object_object_add(obj, "change", json_object_new_string(change)); + json_object_object_add(obj, "container", ipc_json_describe_container_recursive(window)); + + const char *json_string = json_object_to_json_string(obj); + ipc_send_event(json_string, IPC_EVENT_WINDOW); + + json_object_put(obj); // free +} + int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data) { struct ipc_client *client = data; @@ -361,6 +412,45 @@ void ipc_client_handle_command(struct ipc_client *client) { goto exit_cleanup; } + case IPC_SUBSCRIBE: + { + // TODO: Check if they're permitted to use these events + struct json_object *request = json_tokener_parse(buf); + if (request == NULL) { + ipc_send_reply(client, "{\"success\": false}", 18); + wlr_log_errno(L_INFO, "Failed to read request"); + goto exit_cleanup; + } + + // parse requested event types + for (size_t i = 0; i < json_object_array_length(request); i++) { + const char *event_type = json_object_get_string(json_object_array_get_idx(request, i)); + if (strcmp(event_type, "workspace") == 0) { + client->subscribed_events |= event_mask(IPC_EVENT_WORKSPACE); + } else if (strcmp(event_type, "barconfig_update") == 0) { + client->subscribed_events |= event_mask(IPC_EVENT_BARCONFIG_UPDATE); + } else if (strcmp(event_type, "mode") == 0) { + client->subscribed_events |= event_mask(IPC_EVENT_MODE); + } else if (strcmp(event_type, "window") == 0) { + client->subscribed_events |= event_mask(IPC_EVENT_WINDOW); + } else if (strcmp(event_type, "modifier") == 0) { + client->subscribed_events |= event_mask(IPC_EVENT_MODIFIER); + } else if (strcmp(event_type, "binding") == 0) { + client->subscribed_events |= event_mask(IPC_EVENT_BINDING); + } else { + ipc_send_reply(client, "{\"success\": false}", 18); + json_object_put(request); + wlr_log_errno(L_INFO, "Failed to parse request"); + goto exit_cleanup; + } + } + + json_object_put(request); + + ipc_send_reply(client, "{\"success\": true}", 17); + goto exit_cleanup; + } + case IPC_GET_INPUTS: { json_object *inputs = json_object_new_array(); diff --git a/sway/tree/container.c b/sway/tree/container.c index 6abf57e31..ac88ffb71 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -15,6 +15,7 @@ #include "sway/server.h" #include "sway/view.h" #include "sway/workspace.h" +#include "sway/ipc-server.h" #include "log.h" static list_t *bfs_queue; @@ -32,6 +33,11 @@ static list_t *get_bfs_queue() { return bfs_queue; } +static void notify_new_container(swayc_t *container) { + wl_signal_emit(&root_container.sway_root->events.new_container, container); + ipc_event_window(container, "new"); +} + swayc_t *swayc_by_test(swayc_t *container, bool (*test)(swayc_t *view, void *data), void *data) { if (!container->children) { @@ -175,7 +181,7 @@ swayc_t *new_output(struct sway_output *sway_output) { } free(ws_name); - wl_signal_emit(&root_container.sway_root->events.new_container, output); + notify_new_container(output); return output; } @@ -197,7 +203,7 @@ swayc_t *new_workspace(swayc_t *output, const char *name) { add_child(output, workspace); sort_workspaces(output); - wl_signal_emit(&root_container.sway_root->events.new_container, workspace); + notify_new_container(workspace); return workspace; } @@ -222,7 +228,7 @@ swayc_t *new_view(swayc_t *sibling, struct sway_view *sway_view) { // Regular case, create as sibling of current container add_sibling(sibling, swayc); } - wl_signal_emit(&root_container.sway_root->events.new_container, swayc); + notify_new_container(swayc); return swayc; } diff --git a/sway/tree/view.c b/sway/tree/view.c index b46c3b172..9499adca7 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -1,3 +1,7 @@ +#include +#include +#include "sway/container.h" +#include "sway/layout.h" #include "sway/view.h" const char *view_get_title(struct sway_view *view) { @@ -30,13 +34,27 @@ const char *view_get_instance(struct sway_view *view) { void view_set_size(struct sway_view *view, int width, int height) { if (view->iface.set_size) { + struct wlr_box box = { + .x = view->swayc->x, + .y = view->swayc->y, + .width = view->width, + .height = view->height, + }; view->iface.set_size(view, width, height); + view_update_outputs(view, &box); } } void view_set_position(struct sway_view *view, double ox, double oy) { if (view->iface.set_position) { + struct wlr_box box = { + .x = view->swayc->x, + .y = view->swayc->y, + .width = view->width, + .height = view->height, + }; view->iface.set_position(view, ox, oy); + view_update_outputs(view, &box); } } @@ -51,3 +69,27 @@ void view_close(struct sway_view *view) { view->iface.close(view); } } + +void view_update_outputs(struct sway_view *view, const struct wlr_box *before) { + struct wlr_output_layout *output_layout = + root_container.sway_root->output_layout; + struct wlr_box box = { + .x = view->swayc->x, + .y = view->swayc->y, + .width = view->width, + .height = view->height, + }; + struct wlr_output_layout_output *layout_output; + wl_list_for_each(layout_output, &output_layout->outputs, link) { + bool intersected = before != NULL && wlr_output_layout_intersects( + output_layout, layout_output->output, before); + bool intersects = wlr_output_layout_intersects(output_layout, + layout_output->output, &box); + if (intersected && !intersects) { + wlr_surface_send_leave(view->surface, layout_output->output); + } + if (!intersected && intersects) { + wlr_surface_send_enter(view->surface, layout_output->output); + } + } +}