diff --git a/sway/ipc-json.c b/sway/ipc-json.c index 05e453ec0..40fbd3e76 100644 --- a/sway/ipc-json.c +++ b/sway/ipc-json.c @@ -15,6 +15,9 @@ #include #include "wlr-layer-shell-unstable-v1-protocol.h" +static const int i3_output_id = INT32_MAX; +static const int i3_scratch_id = INT32_MAX - 1; + static const char *ipc_json_layout_description(enum sway_container_layout l) { switch (l) { case L_VERT: @@ -32,15 +35,66 @@ static const char *ipc_json_layout_description(enum sway_container_layout l) { } static const char *ipc_json_orientation_description(enum sway_container_layout l) { - if (l == L_VERT) { + switch (l) { + case L_VERT: return "vertical"; - } - - if (l == L_HORIZ) { + case L_HORIZ: return "horizontal"; + default: + return "none"; } +} - return "none"; +static const char *ipc_json_border_description(enum sway_container_border border) { + switch (border) { + case B_NONE: + return "none"; + case B_PIXEL: + return "pixel"; + case B_NORMAL: + return "normal"; + case B_CSD: + return "csd"; + } + return "unknown"; +} + +static const char *ipc_json_output_transform_description(enum wl_output_transform transform) { + switch (transform) { + case WL_OUTPUT_TRANSFORM_NORMAL: + return "normal"; + case WL_OUTPUT_TRANSFORM_90: + return "90"; + case WL_OUTPUT_TRANSFORM_180: + return "180"; + case WL_OUTPUT_TRANSFORM_270: + return "270"; + case WL_OUTPUT_TRANSFORM_FLIPPED: + return "flipped"; + case WL_OUTPUT_TRANSFORM_FLIPPED_90: + return "flipped-90"; + case WL_OUTPUT_TRANSFORM_FLIPPED_180: + return "flipped-180"; + case WL_OUTPUT_TRANSFORM_FLIPPED_270: + return "flipped-270"; + } + return NULL; +} + +static const char *ipc_json_device_type_description(struct sway_input_device *device) { + switch (device->wlr_device->type) { + case WLR_INPUT_DEVICE_POINTER: + return "pointer"; + case WLR_INPUT_DEVICE_KEYBOARD: + return "keyboard"; + case WLR_INPUT_DEVICE_TOUCH: + return "touch"; + case WLR_INPUT_DEVICE_TABLET_TOOL: + return "tablet_tool"; + case WLR_INPUT_DEVICE_TABLET_PAD: + return "tablet_pad"; + } + return "unknown"; } json_object *ipc_json_get_version(void) { @@ -76,30 +130,43 @@ static json_object *ipc_json_create_empty_rect(void) { return ipc_json_create_rect(&empty); } -static void ipc_json_describe_root(struct sway_root *root, json_object *object) { - json_object_object_add(object, "type", json_object_new_string("root")); +static json_object *ipc_json_create_node(int id, char *name, + bool focused, json_object *focus, struct wlr_box *box) { + json_object *object = json_object_new_object(); + + json_object_object_add(object, "id", json_object_new_int(id)); + json_object_object_add(object, "name", + name ? json_object_new_string(name) : NULL); + json_object_object_add(object, "rect", ipc_json_create_rect(box)); + json_object_object_add(object, "focused", json_object_new_boolean(focused)); + json_object_object_add(object, "focus", focus); + + // set default values to be compatible with i3 + json_object_object_add(object, "border", + json_object_new_string( + ipc_json_border_description(B_NONE))); + json_object_object_add(object, "current_border_width", + json_object_new_int(0)); + json_object_object_add(object, "layout", + json_object_new_string( + ipc_json_layout_description(L_HORIZ))); + json_object_object_add(object, "orientation", + json_object_new_string( + ipc_json_orientation_description(L_HORIZ))); + json_object_object_add(object, "percent", NULL); + json_object_object_add(object, "window_rect", ipc_json_create_empty_rect()); + json_object_object_add(object, "deco_rect", ipc_json_create_empty_rect()); + json_object_object_add(object, "geometry", ipc_json_create_empty_rect()); + json_object_object_add(object, "window", NULL); + json_object_object_add(object, "urgent", json_object_new_boolean(false)); + json_object_object_add(object, "floating_nodes", json_object_new_array()); + json_object_object_add(object, "sticky", json_object_new_boolean(false)); + + return object; } -static const char *ipc_json_get_output_transform(enum wl_output_transform transform) { - switch (transform) { - case WL_OUTPUT_TRANSFORM_NORMAL: - return "normal"; - case WL_OUTPUT_TRANSFORM_90: - return "90"; - case WL_OUTPUT_TRANSFORM_180: - return "180"; - case WL_OUTPUT_TRANSFORM_270: - return "270"; - case WL_OUTPUT_TRANSFORM_FLIPPED: - return "flipped"; - case WL_OUTPUT_TRANSFORM_FLIPPED_90: - return "flipped-90"; - case WL_OUTPUT_TRANSFORM_FLIPPED_180: - return "flipped-180"; - case WL_OUTPUT_TRANSFORM_FLIPPED_270: - return "flipped-270"; - } - return NULL; +static void ipc_json_describe_root(struct sway_root *root, json_object *object) { + json_object_object_add(object, "type", json_object_new_string("root")); } static void ipc_json_describe_output(struct sway_output *output, @@ -110,7 +177,8 @@ static void ipc_json_describe_output(struct sway_output *output, json_object_object_add(object, "primary", json_object_new_boolean(false)); json_object_object_add(object, "layout", json_object_new_string("output")); json_object_object_add(object, "orientation", - json_object_new_string(ipc_json_orientation_description(L_NONE))); + json_object_new_string( + ipc_json_orientation_description(L_NONE))); json_object_object_add(object, "make", json_object_new_string(wlr_output->make)); json_object_object_add(object, "model", @@ -121,7 +189,7 @@ static void ipc_json_describe_output(struct sway_output *output, json_object_new_double(wlr_output->scale)); json_object_object_add(object, "transform", json_object_new_string( - ipc_json_get_output_transform(wlr_output->transform))); + ipc_json_output_transform_description(wlr_output->transform))); struct sway_workspace *ws = output_get_active_workspace(output); json_object_object_add(object, "current_workspace", @@ -187,6 +255,52 @@ json_object *ipc_json_describe_disabled_output(struct sway_output *output) { return object; } +static json_object *ipc_json_describe_scratchpad_output(void) { + struct wlr_box box; + root_get_box(root, &box); + + // Create focus stack for __i3_scratch workspace + json_object *workspace_focus = json_object_new_array(); + for (int i = root->scratchpad->length - 1; i >= 0; --i) { + struct sway_container *container = root->scratchpad->items[i]; + json_object_array_add(workspace_focus, + json_object_new_int(container->node.id)); + } + + json_object *workspace = ipc_json_create_node(i3_scratch_id, + "__i3_scratch", false, workspace_focus, &box); + json_object_object_add(workspace, "type", + json_object_new_string("workspace")); + + // List all hidden scratchpad containers as floating nodes + json_object *floating_array = json_object_new_array(); + for (int i = 0; i < root->scratchpad->length; ++i) { + struct sway_container *container = root->scratchpad->items[i]; + if (!container->workspace) { + json_object_array_add(floating_array, + ipc_json_describe_node_recursive(&container->node)); + } + } + json_object_object_add(workspace, "floating_nodes", floating_array); + + // Create focus stack for __i3 output + json_object *output_focus = json_object_new_array(); + json_object_array_add(output_focus, json_object_new_int(i3_scratch_id)); + + json_object *output = ipc_json_create_node(i3_output_id, + "__i3", false, output_focus, &box); + json_object_object_add(output, "type", + json_object_new_string("output")); + json_object_object_add(output, "layout", + json_object_new_string("output")); + + json_object *nodes = json_object_new_array(); + json_object_array_add(nodes, workspace); + json_object_object_add(output, "nodes", nodes); + + return output; +} + static void ipc_json_describe_workspace(struct sway_workspace *workspace, json_object *object) { int num = isdigit(workspace->name[0]) ? atoi(workspace->name) : -1; @@ -200,11 +314,12 @@ static void ipc_json_describe_workspace(struct sway_workspace *workspace, json_object_object_add(object, "representation", workspace->representation ? json_object_new_string(workspace->representation) : NULL); - const char *layout = ipc_json_layout_description(workspace->layout); - json_object_object_add(object, "layout", json_object_new_string(layout)); - - const char *orientation = ipc_json_orientation_description(workspace->layout); - json_object_object_add(object, "orientation", json_object_new_string(orientation)); + json_object_object_add(object, "layout", + json_object_new_string( + ipc_json_layout_description(workspace->layout))); + json_object_object_add(object, "orientation", + json_object_new_string( + ipc_json_orientation_description(workspace->layout))); // Floating json_object *floating_array = json_object_new_array(); @@ -216,20 +331,6 @@ static void ipc_json_describe_workspace(struct sway_workspace *workspace, json_object_object_add(object, "floating_nodes", floating_array); } -static const char *describe_container_border(enum sway_container_border border) { - switch (border) { - case B_NONE: - return "none"; - case B_PIXEL: - return "pixel"; - case B_NORMAL: - return "normal"; - case B_CSD: - return "csd"; - } - return "unknown"; -} - static void ipc_json_describe_view(struct sway_container *c, json_object *object) { json_object_object_add(object, "pid", json_object_new_int(c->view->pid)); @@ -307,10 +408,12 @@ static void ipc_json_describe_container(struct sway_container *c, json_object *o json_object_new_string(container_is_floating(c) ? "floating_con" : "con")); json_object_object_add(object, "layout", - json_object_new_string(ipc_json_layout_description(c->layout))); + json_object_new_string( + ipc_json_layout_description(c->layout))); json_object_object_add(object, "orientation", - json_object_new_string(ipc_json_orientation_description(c->layout))); + json_object_new_string( + ipc_json_orientation_description(c->layout))); bool urgent = c->view ? view_is_urgent(c->view) : container_has_urgent_child(c); @@ -331,7 +434,8 @@ static void ipc_json_describe_container(struct sway_container *c, json_object *o } json_object_object_add(object, "border", - json_object_new_string(describe_container_border(c->current.border))); + json_object_new_string( + ipc_json_border_description(c->current.border))); json_object_object_add(object, "current_border_width", json_object_new_int(c->current.border_thickness)); json_object_object_add(object, "floating_nodes", json_object_new_array()); @@ -372,17 +476,10 @@ static void focus_inactive_children_iterator(struct sway_node *node, json_object *ipc_json_describe_node(struct sway_node *node) { struct sway_seat *seat = input_manager_get_default_seat(); bool focused = seat_get_focus(seat) == node; - - json_object *object = json_object_new_object(); char *name = node_get_name(node); struct wlr_box box; node_get_box(node, &box); - json_object_object_add(object, "id", json_object_new_int((int)node->id)); - json_object_object_add(object, "name", - name ? json_object_new_string(name) : NULL); - json_object_object_add(object, "rect", ipc_json_create_rect(&box)); - json_object_object_add(object, "focused", json_object_new_boolean(focused)); json_object *focus = json_object_new_array(); struct focus_inactive_data data = { @@ -390,24 +487,9 @@ json_object *ipc_json_describe_node(struct sway_node *node) { .object = focus, }; seat_for_each_node(seat, focus_inactive_children_iterator, &data); - json_object_object_add(object, "focus", focus); - // set default values to be compatible with i3 - json_object_object_add(object, "border", - json_object_new_string(describe_container_border(B_NONE))); - json_object_object_add(object, "current_border_width", json_object_new_int(0)); - json_object_object_add(object, "layout", - json_object_new_string(ipc_json_layout_description(L_HORIZ))); - json_object_object_add(object, "orientation", - json_object_new_string(ipc_json_orientation_description(L_HORIZ))); - json_object_object_add(object, "percent", NULL); - json_object_object_add(object, "window_rect", ipc_json_create_empty_rect()); - json_object_object_add(object, "deco_rect", ipc_json_create_empty_rect()); - json_object_object_add(object, "geometry", ipc_json_create_empty_rect()); - json_object_object_add(object, "window", NULL); - json_object_object_add(object, "urgent", json_object_new_boolean(false)); - json_object_object_add(object, "floating_nodes", json_object_new_array()); - json_object_object_add(object, "sticky", json_object_new_boolean(false)); + json_object *object = ipc_json_create_node( + (int)node->id, name, focused, focus, &box); switch (node->type) { case N_ROOT: @@ -434,6 +516,8 @@ json_object *ipc_json_describe_node_recursive(struct sway_node *node) { json_object *children = json_object_new_array(); switch (node->type) { case N_ROOT: + json_object_array_add(children, + ipc_json_describe_scratchpad_output()); for (i = 0; i < root->outputs->length; ++i) { struct sway_output *output = root->outputs->items[i]; json_object_array_add(children, @@ -470,22 +554,6 @@ json_object *ipc_json_describe_node_recursive(struct sway_node *node) { return object; } -static const char *describe_device_type(struct sway_input_device *device) { - switch (device->wlr_device->type) { - case WLR_INPUT_DEVICE_POINTER: - return "pointer"; - case WLR_INPUT_DEVICE_KEYBOARD: - return "keyboard"; - case WLR_INPUT_DEVICE_TOUCH: - return "touch"; - case WLR_INPUT_DEVICE_TABLET_TOOL: - return "tablet_tool"; - case WLR_INPUT_DEVICE_TABLET_PAD: - return "tablet_pad"; - } - return "unknown"; -} - json_object *ipc_json_describe_input(struct sway_input_device *device) { if (!(sway_assert(device, "Device must not be null"))) { return NULL; @@ -502,7 +570,8 @@ json_object *ipc_json_describe_input(struct sway_input_device *device) { json_object_object_add(object, "product", json_object_new_int(device->wlr_device->product)); json_object_object_add(object, "type", - json_object_new_string(describe_device_type(device))); + json_object_new_string( + ipc_json_device_type_description(device))); if (device->wlr_device->type == WLR_INPUT_DEVICE_KEYBOARD) { struct wlr_keyboard *keyboard = device->wlr_device->keyboard; diff --git a/sway/tree/root.c b/sway/tree/root.c index 544d666a7..9f6bf6073 100644 --- a/sway/tree/root.c +++ b/sway/tree/root.c @@ -5,6 +5,7 @@ #include #include "sway/desktop/transaction.h" #include "sway/input/seat.h" +#include "sway/ipc-server.h" #include "sway/output.h" #include "sway/tree/arrange.h" #include "sway/tree/container.h" @@ -75,6 +76,8 @@ void root_scratchpad_add_container(struct sway_container *con) { arrange_workspace(workspace); seat_set_focus(seat, seat_get_focus_inactive(seat, &workspace->node)); } + + ipc_event_window(con, "move"); } void root_scratchpad_remove_container(struct sway_container *con) { @@ -85,45 +88,51 @@ void root_scratchpad_remove_container(struct sway_container *con) { int index = list_find(root->scratchpad, con); if (index != -1) { list_del(root->scratchpad, index); + ipc_event_window(con, "move"); } } void root_scratchpad_show(struct sway_container *con) { struct sway_seat *seat = input_manager_current_seat(); - struct sway_workspace *ws = seat_get_focused_workspace(seat); + struct sway_workspace *new_ws = seat_get_focused_workspace(seat); + struct sway_workspace *old_ws = con->workspace; - // If the current con or any of its parents are in fullscreen mode, we - // first need to disable it before showing the scratchpad con. - if (ws->fullscreen) { - container_set_fullscreen(ws->fullscreen, false); + // If the current con or any of its parents are in fullscreen mode, we + // first need to disable it before showing the scratchpad con. + if (new_ws->fullscreen) { + container_set_fullscreen(new_ws->fullscreen, false); } // Show the container - if (con->workspace) { + if (old_ws) { container_detach(con); } - workspace_add_floating(ws, con); + workspace_add_floating(new_ws, con); // Make sure the container's center point overlaps this workspace double center_lx = con->x + con->width / 2; double center_ly = con->y + con->height / 2; struct wlr_box workspace_box; - workspace_get_box(ws, &workspace_box); + workspace_get_box(new_ws, &workspace_box); if (!wlr_box_contains_point(&workspace_box, center_lx, center_ly)) { // Maybe resize it - if (con->width > ws->width || con->height > ws->height) { + if (con->width > new_ws->width || con->height > new_ws->height) { container_init_floating(con); } // Center it - double new_lx = ws->x + (ws->width - con->width) / 2; - double new_ly = ws->y + (ws->height - con->height) / 2; + double new_lx = new_ws->x + (new_ws->width - con->width) / 2; + double new_ly = new_ws->y + (new_ws->height - con->height) / 2; container_floating_move_to(con, new_lx, new_ly); } - arrange_workspace(ws); + arrange_workspace(new_ws); seat_set_focus(seat, seat_get_focus_inactive(seat, &con->node)); + + if (new_ws != old_ws) { + ipc_event_window(con, "move"); + } } void root_scratchpad_hide(struct sway_container *con) { @@ -137,6 +146,8 @@ void root_scratchpad_hide(struct sway_container *con) { seat_set_focus(seat, seat_get_focus_inactive(seat, &ws->node)); } list_move_to_end(root->scratchpad, con); + + ipc_event_window(con, "move"); } struct pid_workspace {