void move_container_to(swayc_t* container, swayc_t* destination) { if (container == destination || swayc_is_parent_of(container, destination)) { return; } swayc_t *parent = remove_child(container); // reset container geometry container->width = container->height = 0; // Send to new destination if (container->is_floating) { add_floating(swayc_active_workspace_for(destination), container); } else if (destination->type == C_WORKSPACE) { add_child(destination, container); } else { add_sibling(destination, container); } // Destroy old container if we need to parent = destroy_container(parent); // Refocus swayc_t *op1 = swayc_parent_by_type(destination, C_OUTPUT); swayc_t *op2 = swayc_parent_by_type(parent, C_OUTPUT); set_focused_container(get_focused_view(op1)); arrange_windows(op1, -1, -1); update_visibility(op1); if (op1 != op2) { set_focused_container(get_focused_view(op2)); arrange_windows(op2, -1, -1); update_visibility(op2); } }
bool workspace_switch(swayc_t *workspace) { if (!workspace) { return false; } swayc_t *active_ws = swayc_active_workspace(); if (config->auto_back_and_forth && active_ws == workspace && prev_workspace_name) { swayc_t *new_ws = workspace_by_name(prev_workspace_name); workspace = new_ws ? new_ws : workspace_create(prev_workspace_name); } if (!prev_workspace_name || (strcmp(prev_workspace_name, active_ws->name) && active_ws != workspace)) { free(prev_workspace_name); prev_workspace_name = malloc(strlen(active_ws->name)+1); strcpy(prev_workspace_name, active_ws->name); } // move sticky containers if (swayc_parent_by_type(active_ws, C_OUTPUT) == swayc_parent_by_type(workspace, C_OUTPUT)) { // don't change list while traversing it, use intermediate list instead list_t *stickies = create_list(); for (int i = 0; i < active_ws->floating->length; i++) { swayc_t *cont = active_ws->floating->items[i]; if (cont->sticky) { list_add(stickies, cont); } } for (int i = 0; i < stickies->length; i++) { swayc_t *cont = stickies->items[i]; sway_log(L_DEBUG, "Moving sticky container %p to %p:%s", cont, workspace, workspace->name); swayc_t *parent = remove_child(cont); add_floating(workspace, cont); // Destroy old container if we need to destroy_container(parent); } list_free(stickies); } sway_log(L_DEBUG, "Switching to workspace %p:%s", workspace, workspace->name); if (!set_focused_container(get_focused_view(workspace))) { return false; } swayc_t *output = swayc_parent_by_type(workspace, C_OUTPUT); arrange_backgrounds(); arrange_windows(output, -1, -1); return true; }
static void update_border_geometry_floating(swayc_t *c, struct wlc_geometry *geometry) { struct wlc_geometry g = *geometry; c->actual_geometry = g; swayc_t *output = swayc_parent_by_type(c, C_OUTPUT); const struct wlc_size *res = wlc_output_get_resolution(output->handle); switch (c->border_type) { case B_NONE: break; case B_PIXEL: adjust_border_geometry(c, &g, res, c->border_thickness, c->border_thickness, c->border_thickness, c->border_thickness); break; case B_NORMAL: { int title_bar_height = config->font_height + 4; // borders + padding adjust_border_geometry(c, &g, res, c->border_thickness, c->border_thickness, title_bar_height, c->border_thickness); struct wlc_geometry title_bar = { .origin = { .x = c->actual_geometry.origin.x - c->border_thickness, .y = c->actual_geometry.origin.y - title_bar_height }, .size = { .w = c->actual_geometry.size.w + (2 * c->border_thickness), .h = title_bar_height } }; c->title_bar_geometry = title_bar; break; } }
bool move_focus(enum movement_direction direction) { swayc_t *old_view = get_focused_container(&root_container); swayc_t *new_view = get_swayc_in_direction(old_view, direction); if (!new_view) { return false; } else if (new_view->type == C_ROOT) { sway_log(L_DEBUG, "Not setting focus above the workspace level"); return false; } else if (new_view->type == C_OUTPUT) { return set_focused_container(swayc_active_workspace_for(new_view)); } else if (direction == MOVE_PARENT || direction == MOVE_CHILD) { return set_focused_container(new_view); } else if (config->mouse_warping) { swayc_t *old_op = old_view->type == C_OUTPUT ? old_view : swayc_parent_by_type(old_view, C_OUTPUT); swayc_t *focused = get_focused_view(new_view); if (set_focused_container(focused)) { if (old_op != swayc_active_output() && focused && focused->type == C_VIEW) { center_pointer_on(focused); } return true; } } else { return set_focused_container(get_focused_view(new_view)); } return false; }
static void handle_view_state_request(wlc_handle view, enum wlc_view_state_bit state, bool toggle) { swayc_t *c = get_swayc_for_handle(view, &root_container); switch (state) { case WLC_BIT_FULLSCREEN: // i3 just lets it become fullscreen wlc_view_set_state(view, state, toggle); if (c) { sway_log(L_DEBUG, "setting view %ld %s, fullscreen %d", view, c->name, toggle); arrange_windows(c->parent, -1, -1); // Set it as focused window for that workspace if its going fullscreen if (toggle) { swayc_t *ws = swayc_parent_by_type(c, C_WORKSPACE); // Set ws focus to c set_focused_container_for(ws, c); } } break; case WLC_BIT_MAXIMIZED: case WLC_BIT_RESIZING: case WLC_BIT_MOVING: break; case WLC_BIT_ACTIVATED: sway_log(L_DEBUG, "View %p requested to be activated", c); break; } return; }
void move_container_to(swayc_t* container, swayc_t* destination) { if (container == destination || swayc_is_parent_of(container, destination)) { return; } swayc_t *parent = remove_child(container); // Send to new destination if (container->is_floating) { swayc_t *ws = swayc_active_workspace_for(destination); add_floating(ws, container); // If the workspace only has one child after adding one, it // means that the workspace was just initialized. if (ws->children->length + ws->floating->length == 1) { ipc_event_workspace(NULL, ws, "init"); } } else if (destination->type == C_WORKSPACE) { // reset container geometry container->width = container->height = 0; add_child(destination, container); // If the workspace only has one child after adding one, it // means that the workspace was just initialized. if (destination->children->length + destination->floating->length == 1) { ipc_event_workspace(NULL, destination, "init"); } } else { // reset container geometry container->width = container->height = 0; add_sibling(destination, container); } // Destroy old container if we need to parent = destroy_container(parent); // Refocus swayc_t *op1 = swayc_parent_by_type(destination, C_OUTPUT); swayc_t *op2 = swayc_parent_by_type(parent, C_OUTPUT); set_focused_container(get_focused_view(op1)); arrange_windows(op1, -1, -1); update_visibility(op1); if (op1 != op2) { set_focused_container(get_focused_view(op2)); arrange_windows(op2, -1, -1); update_visibility(op2); } }
static bool pointer_test(swayc_t *view, void *_origin) { const struct wlc_origin *origin = _origin; // Determine the output that the view is under swayc_t *parent = swayc_parent_by_type(view, C_OUTPUT); if (origin->x >= view->x && origin->y >= view->y && origin->x < view->x + view->width && origin->y < view->y + view->height && view->visible && parent == root_container.focused) { return true; } return false; }
static void ipc_json_describe_view(swayc_t *c, json_object *object) { json_object *props = json_object_new_object(); float percent = ipc_json_child_percentage(c); const char *layout = (c->parent->type == C_CONTAINER) ? ipc_json_layout_description(c->parent->layout) : "none"; const char *last_layout = (c->parent->type == C_CONTAINER) ? ipc_json_layout_description(c->parent->prev_layout) : "none"; wlc_handle parent = wlc_view_get_parent(c->handle); json_object_object_add(object, "type", json_object_new_string((c->is_floating) ? "floating_con" : "con")); json_object_object_add(object, "scratchpad_state", json_object_new_string(ipc_json_get_scratchpad_state(c))); json_object_object_add(object, "percent", (percent > 0) ? json_object_new_double(percent) : NULL); // TODO: make urgency actually work once Sway supports it json_object_object_add(object, "urgent", json_object_new_boolean(false)); json_object_object_add(object, "layout", (strcmp(layout, "null") == 0) ? NULL : json_object_new_string(layout)); json_object_object_add(object, "last_split_layout", (strcmp(last_layout, "null") == 0) ? NULL : json_object_new_string(last_layout)); json_object_object_add(object, "workspace_layout", json_object_new_string(ipc_json_layout_description(swayc_parent_by_type(c, C_WORKSPACE)->workspace_layout))); json_object_object_add(object, "border", json_object_new_string(ipc_json_border_description(c))); json_object_object_add(object, "current_border_width", json_object_new_int(c->border_thickness)); json_object_object_add(object, "rect", ipc_json_create_rect(c)); json_object_object_add(object, "deco_rect", ipc_json_create_rect_from_geometry(c->title_bar_geometry)); json_object_object_add(object, "geometry", ipc_json_create_rect_from_geometry(c->cached_geometry)); json_object_object_add(object, "window_rect", ipc_json_create_rect_from_geometry(c->actual_geometry)); json_object_object_add(object, "name", (c->name) ? json_object_new_string(c->name) : NULL); json_object_object_add(object, "window", json_object_new_int(c->handle)); // for the sake of i3 compat json_object_object_add(props, "class", c->class ? json_object_new_string(c->class) : c->app_id ? json_object_new_string(c->app_id) : NULL); json_object_object_add(props, "instance", c->instance ? json_object_new_string(c->instance) : c->app_id ? json_object_new_string(c->app_id) : NULL); json_object_object_add(props, "title", (c->name) ? json_object_new_string(c->name) : NULL); json_object_object_add(props, "transient_for", parent ? json_object_new_int(parent) : NULL); json_object_object_add(object, "window_properties", props); json_object_object_add(object, "fullscreen_mode", json_object_new_int(swayc_is_fullscreen(c) ? 1 : 0)); json_object_object_add(object, "sticky", json_object_new_boolean(c->sticky)); json_object_object_add(object, "floating", json_object_new_string( c->is_floating ? "auto_on" : "auto_off")); // we can't state the cause json_object_object_add(object, "app_id", c->app_id ? json_object_new_string(c->app_id) : NULL); }
swayc_t *destroy_workspace(swayc_t *workspace) { if (!ASSERT_NONNULL(workspace)) { return NULL; } // NOTE: This is called from elsewhere without checking children length // TODO move containers to other workspaces? // for now just dont delete // Do not destroy this if it's the last workspace on this output swayc_t *output = swayc_parent_by_type(workspace, C_OUTPUT); if (output && output->children->length == 1) { return NULL; } if (workspace->children->length == 0) { sway_log(L_DEBUG, "%s: '%s'", __func__, workspace->name); swayc_t *parent = workspace->parent; free_swayc(workspace); return parent; } return NULL; }
bool move_focus(enum movement_direction direction) { swayc_t *old_view = get_focused_container(&root_container); swayc_t *new_view = get_swayc_in_direction(old_view, direction); if (!new_view) { return false; } else if (direction == MOVE_PARENT) { return set_focused_container(new_view); } else if (config->mouse_warping) { swayc_t *old_op = old_view->type == C_OUTPUT ? old_view : swayc_parent_by_type(old_view, C_OUTPUT); swayc_t *focused = get_focused_view(new_view); if (set_focused_container(focused)) { if (old_op != swayc_active_output() && focused && focused->type == C_VIEW) { center_pointer_on(focused); } return true; } } else { return set_focused_container(get_focused_view(new_view)); } return false; }
void update_geometry(swayc_t *container) { if (container->type != C_VIEW) { return; } swayc_t *ws = swayc_parent_by_type(container, C_WORKSPACE); swayc_t *op = ws->parent; int gap = container->is_floating ? 0 : swayc_gap(container); if (gap % 2 != 0) { // because gaps are implemented as "half sized margins" it's currently // not possible to align views properly with odd sized gaps. gap -= 1; } struct wlc_geometry geometry = { .origin = { .x = container->x + gap/2 < op->width ? container->x + gap/2 : op->width-1, .y = container->y + gap/2 < op->height ? container->y + gap/2 : op->height-1 }, .size = { .w = container->width > gap ? container->width - gap : 1, .h = container->height > gap ? container->height - gap : 1, } };
struct cmd_results *cmd_fullscreen(int argc, char **argv) { struct cmd_results *error = NULL; if (config->reading) return cmd_results_new(CMD_FAILURE, "fullscreen", "Can't be used in config file."); if (!config->active) return cmd_results_new(CMD_FAILURE, "fullscreen", "Can only be used when sway is running."); if ((error = checkarg(argc, "fullscreen", EXPECTED_AT_LEAST, 0))) { return error; } swayc_t *container = get_focused_view(&root_container); if(container->type != C_VIEW){ return cmd_results_new(CMD_INVALID, "fullscreen", "Only views can fullscreen"); } swayc_t *workspace = swayc_parent_by_type(container, C_WORKSPACE); bool current = swayc_is_fullscreen(container); wlc_view_set_state(container->handle, WLC_BIT_FULLSCREEN, !current); if (container->is_floating) { if (current) { // set dimensions back to what they were before we fullscreened this container->x = container->cached_geometry.origin.x; container->y = container->cached_geometry.origin.y; container->width = container->cached_geometry.size.w; container->height = container->cached_geometry.size.h; } else { // cache dimensions so we can reset them after we "unfullscreen" this struct wlc_geometry geo = { .origin = { .x = container->x, .y = container->y }, .size = { .w = container->width, .h = container->height } }; container->cached_geometry = geo; } }
swayc_t *workspace_create(const char* name) { swayc_t *parent; // Search for workspace<->output pair int i, e = config->workspace_outputs->length; for (i = 0; i < e; ++i) { struct workspace_output *wso = config->workspace_outputs->items[i]; if (strcasecmp(wso->workspace, name) == 0) { // Find output to use if it exists e = root_container.children->length; for (i = 0; i < e; ++i) { parent = root_container.children->items[i]; if (strcmp(parent->name, wso->output) == 0) { return new_workspace(parent, name); } } break; } } // Otherwise create a new one parent = get_focused_container(&root_container); parent = swayc_parent_by_type(parent, C_OUTPUT); return new_workspace(parent, name); }
void arrange_windows(swayc_t *container, int width, int height) { int i; if (width == -1 || height == -1) { sway_log(L_DEBUG, "Arranging layout for %p", container); width = container->width; height = container->height; } int x = 0, y = 0; switch (container->type) { case C_ROOT: for (i = 0; i < container->children->length; ++i) { swayc_t *child = container->children->items[i]; sway_log(L_DEBUG, "Arranging output at %d", x); child->x = x; child->y = y; arrange_windows(child, -1, -1); // Removed for now because wlc works with relative positions // Addition can be reconsidered once wlc positions are changed // x += child->width; } return; case C_OUTPUT: container->width = width; container->height = height; // These lines make x/y negative and result in stuff glitching out // Their addition can be reconsidered once wlc positions are changed // x -= container->x; // y -= container->y; for (i = 0; i < container->children->length; ++i) { swayc_t *child = container->children->items[i]; child->x = x + container->gaps; child->y = y + container->gaps; child->width = width - container->gaps * 2; child->height = height - container->gaps * 2; sway_log(L_DEBUG, "Arranging workspace #%d at %d, %d", i, child->x, child->y); arrange_windows(child, -1, -1); } return; case C_VIEW: { struct wlc_geometry geometry = { .origin = { .x = container->x + container->gaps / 2, .y = container->y + container->gaps / 2 }, .size = { .w = width - container->gaps, .h = height - container->gaps } }; if (wlc_view_get_state(container->handle) & WLC_BIT_FULLSCREEN) { swayc_t *parent = swayc_parent_by_type(container, C_OUTPUT); geometry.origin.x = 0; geometry.origin.y = 0; geometry.size.w = parent->width; geometry.size.h = parent->height; wlc_view_set_geometry(container->handle, 0, &geometry); wlc_view_bring_to_front(container->handle); } else { wlc_view_set_geometry(container->handle, 0, &geometry); container->width = width; container->height = height; } sway_log(L_DEBUG, "Set view to %d x %d @ %d, %d", geometry.size.w, geometry.size.h, geometry.origin.x, geometry.origin.y); } return; default: container->width = width; container->height = height; break; }
static bool handle_view_created(wlc_handle handle) { // if view is child of another view, the use that as focused container wlc_handle parent = wlc_view_get_parent(handle); swayc_t *focused = NULL; swayc_t *newview = NULL; // Get parent container, to add view in if (parent) { focused = get_swayc_for_handle(parent, &root_container); } if (!focused || focused->type == C_OUTPUT) { focused = get_focused_container(&root_container); } sway_log(L_DEBUG, "handle:%ld type:%x state:%x parent:%ld " "mask:%d (x:%d y:%d w:%d h:%d) title:%s " "class:%s appid:%s", handle, wlc_view_get_type(handle), wlc_view_get_state(handle), parent, wlc_view_get_mask(handle), wlc_view_get_geometry(handle)->origin.x, wlc_view_get_geometry(handle)->origin.y,wlc_view_get_geometry(handle)->size.w, wlc_view_get_geometry(handle)->size.h, wlc_view_get_title(handle), wlc_view_get_class(handle), wlc_view_get_app_id(handle)); // TODO properly figure out how each window should be handled. switch (wlc_view_get_type(handle)) { // regular view created regularly case 0: newview = new_view(focused, handle); wlc_view_set_state(handle, WLC_BIT_MAXIMIZED, true); break; // Dmenu keeps viewfocus, but others with this flag dont, for now simulate // dmenu case WLC_BIT_OVERRIDE_REDIRECT: // locked_view_focus = true; wlc_view_focus(handle); wlc_view_set_state(handle, WLC_BIT_ACTIVATED, true); wlc_view_bring_to_front(handle); break; // Firefox popups have this flag set. case WLC_BIT_OVERRIDE_REDIRECT|WLC_BIT_UNMANAGED: wlc_view_bring_to_front(handle); locked_container_focus = true; break; // Modals, get focus, popups do not case WLC_BIT_MODAL: wlc_view_focus(handle); wlc_view_bring_to_front(handle); newview = new_floating_view(handle); case WLC_BIT_POPUP: wlc_view_bring_to_front(handle); break; } if (newview) { set_focused_container(newview); swayc_t *output = swayc_parent_by_type(newview, C_OUTPUT); arrange_windows(output, -1, -1); } return true; }
struct cmd_results *cmd_layout(int argc, char **argv) { struct cmd_results *error = NULL; if (config->reading) return cmd_results_new(CMD_FAILURE, "layout", "Can't be used in config file."); if (!config->active) return cmd_results_new(CMD_FAILURE, "layout", "Can only be used when sway is running."); if ((error = checkarg(argc, "layout", EXPECTED_MORE_THAN, 0))) { return error; } swayc_t *parent = get_focused_container(&root_container); if (parent->is_floating) { return cmd_results_new(CMD_FAILURE, "layout", "Unable to change layout of floating windows"); } while (parent->type == C_VIEW) { parent = parent->parent; } enum swayc_layouts old_layout = parent->layout; if (strcasecmp(argv[0], "default") == 0) { swayc_change_layout(parent, parent->prev_layout); if (parent->layout == L_NONE) { swayc_t *output = swayc_parent_by_type(parent, C_OUTPUT); swayc_change_layout(parent, default_layout(output)); } } else { if (parent->layout != L_TABBED && parent->layout != L_STACKED) { parent->prev_layout = parent->layout; } if (strcasecmp(argv[0], "tabbed") == 0) { if (parent->type != C_CONTAINER && !swayc_is_empty_workspace(parent)){ parent = new_container(parent, L_TABBED); } swayc_change_layout(parent, L_TABBED); } else if (strcasecmp(argv[0], "stacking") == 0) { if (parent->type != C_CONTAINER && !swayc_is_empty_workspace(parent)) { parent = new_container(parent, L_STACKED); } swayc_change_layout(parent, L_STACKED); } else if (strcasecmp(argv[0], "splith") == 0) { swayc_change_layout(parent, L_HORIZ); } else if (strcasecmp(argv[0], "splitv") == 0) { swayc_change_layout(parent, L_VERT); } else if (strcasecmp(argv[0], "toggle") == 0 && argc == 2 && strcasecmp(argv[1], "split") == 0) { if (parent->layout == L_HORIZ && (parent->workspace_layout == L_NONE || parent->workspace_layout == L_HORIZ)) { swayc_change_layout(parent, L_VERT); } else { swayc_change_layout(parent, L_HORIZ); } } } update_layout_geometry(parent, old_layout); update_geometry(parent); arrange_windows(parent, parent->width, parent->height); return cmd_results_new(CMD_SUCCESS, NULL, NULL); }
swayc_t *workspace_create(const char* name) { swayc_t *parent = get_focused_container(&root_container); parent = swayc_parent_by_type(parent, C_OUTPUT); return new_workspace(parent, name); }