// switches parent focus to c. will switch it accordingly // TODO: Everything needs a handle, so we can set front/back position properly static void update_focus(swayc_t *c) { // Handle if focus switches swayc_t *parent = c->parent; if (parent->focused != c) { switch (c->type) { case C_ROOT: return; case C_OUTPUT: wlc_output_focus(c->handle); break; // switching workspaces case C_WORKSPACE: if (parent->focused) { swayc_t *ws = parent->focused; // hide visibility of old workspace uint32_t mask = 1; container_map(ws, set_view_visibility, &mask); // set visibility of new workspace mask = 2; container_map(c, set_view_visibility, &mask); wlc_output_set_mask(parent->handle, 2); destroy_workspace(ws); } active_workspace = c; break; default: case C_VIEW: case C_CONTAINER: // TODO whatever to do when container changes // for example, stacked and tabbing change stuff. break; } } c->parent->focused = c; }
void ipc_client_handle_command(struct ipc_client *client) { if (!sway_assert(client != NULL, "client != NULL")) { return; } char buf[client->payload_length + 1]; if (client->payload_length > 0) { ssize_t received = recv(client->fd, buf, client->payload_length, 0); if (received == -1) { sway_log_errno(L_INFO, "Unable to receive payload from IPC client"); ipc_client_disconnect(client); return; } } switch (client->current_command) { case IPC_COMMAND: { buf[client->payload_length] = '\0'; bool success = handle_command(config, buf); char reply[64]; int length = snprintf(reply, sizeof(reply), "{\"success\":%s}", success ? "true" : "false"); ipc_send_reply(client, reply, (uint32_t) length); break; } case IPC_GET_WORKSPACES: { list_t *workspaces = create_list(); container_map(&root_container, ipc_get_workspaces_callback, workspaces); char *json = json_list(workspaces); free_flat_list(workspaces); ipc_send_reply(client, json, strlen(json)); free(json); break; } case IPC_GET_OUTPUTS: { list_t *outputs = create_list(); container_map(&root_container, ipc_get_outputs_callback, outputs); char *json = json_list(outputs); free_flat_list(outputs); ipc_send_reply(client, json, strlen(json)); free(json); break; } default: sway_log(L_INFO, "Unknown IPC command type %i", client->current_command); ipc_client_disconnect(client); break; } client->payload_length = 0; }
swayc_t *new_output(wlc_handle handle) { const struct wlc_size* size = wlc_output_get_resolution(handle); const char *name = wlc_output_get_name(handle); sway_log(L_DEBUG, "Added output %u %s", (unsigned int)handle, name); swayc_t *output = new_swayc(C_OUTPUT); output->width = size->w; output->height = size->h; output->handle = handle; if (name) { output->name = strdup(name); } add_child(&root_container, output); //TODO something with this int total_width = 0; container_map(&root_container, add_output_widths, &total_width); //Create workspace char *ws_name = workspace_next_name(); new_workspace(output, ws_name); free(ws_name); return output; }
bool read_config(FILE *file, bool is_active) { struct sway_config *temp_config = malloc(sizeof(struct sway_config)); config_defaults(temp_config); if (is_active) { sway_log(L_DEBUG, "Performing configuration file reload"); temp_config->reloading = true; temp_config->active = true; } bool success = true; int temp_depth = 0; // Temporary: skip all config sections with depth while (!feof(file)) { int _; char *line = read_line(file); line = strip_whitespace(line, &_); line = strip_comments(line); if (!line[0]) { goto _continue; } if (temp_depth && line[0] == '}') { temp_depth--; goto _continue; } // Any command which would require wlc to be initialized // should be queued for later execution list_t *args = split_string(line, " "); if (!is_active && ( strcmp("exec", args->items[0]) == 0 || strcmp("exec_always", args->items[0]) == 0 )) { sway_log(L_DEBUG, "Deferring command %s", line); char *cmd = malloc(strlen(line) + 1); strcpy(cmd, line); list_add(temp_config->cmd_queue, cmd); } else if (!temp_depth && !handle_command(temp_config, line)) { sway_log(L_DEBUG, "Config load failed for line %s", line); success = false; temp_config->failed = true; } list_free(args); _continue: if (line && line[strlen(line) - 1] == '{') { temp_depth++; } free(line); } if (is_active) { temp_config->reloading = false; container_map(&root_container, reset_gaps, NULL); arrange_windows(&root_container, -1, -1); } config = temp_config; return success; }
void container_map(swayc_t *container, void (*f)(swayc_t *view, void *data), void *data) { if (container && container->children && container->children->length) { int i; for (i = 0; i < container->children->length; ++i) { swayc_t *child = container->children->items[i]; f(child, data); container_map(child, f, data); } if (container->type == C_WORKSPACE) { for (i = 0; i < container->floating->length; ++i) { swayc_t *child = container->floating->items[i]; f(child, data); container_map(child, f, data); } } } }
void container_map(swayc_t *container, void (*f)(swayc_t *view, void *data), void *data) { if (!container->children || !container->children->length) { return; } int i; for (i = 0; i < container->children->length; ++i) { swayc_t *child = container->children->items[i]; f(child, data); container_map(child, f, data); } }
// switches parent focus to c. will switch it accordingly static void update_focus(swayc_t *c) { // Handle if focus switches swayc_t *parent = c->parent; if (!parent) return; if (parent->focused != c) { // Get previous focus swayc_t *prev = parent->focused; // Set new focus parent->focused = c; switch (c->type) { // Shouldn't happen case C_ROOT: return; // Case where output changes case C_OUTPUT: // update borders for views in prev container_map(prev, map_update_view_border, NULL); wlc_output_focus(c->handle); break; // Case where workspace changes case C_WORKSPACE: if (prev) { ipc_event_workspace(prev, c, "focus"); // if the old workspace has no children, destroy it if(prev->children->length == 0 && prev->floating->length == 0 && !suspend_workspace_cleanup) { destroy_workspace(prev); } else { // update visibility of old workspace update_visibility(prev); } } // Update visibility of newly focused workspace update_visibility(c); break; default: case C_VIEW: case C_CONTAINER: // TODO whatever to do when container changes // for example, stacked and tabbing change stuff. break; } } }
swayc_t *new_output(wlc_handle handle) { const struct wlc_size* size = wlc_output_get_resolution(handle); const char *name = wlc_output_get_name(handle); sway_log(L_DEBUG, "Added output %u %s", (unsigned int)handle, name); swayc_t *output = new_swayc(C_OUTPUT); output->width = size->w; output->height = size->h; output->handle = handle; output->name = name ? strdup(name) : NULL; add_child(&root_container, output); //TODO something with this int total_width = 0; container_map(&root_container, add_output_widths, &total_width); //Create workspace char *ws_name = NULL; if (name) { int i; for (i = 0; i < config->workspace_outputs->length; ++i) { struct workspace_output *wso = config->workspace_outputs->items[i]; if (strcasecmp(wso->output, name) == 0) { sway_log(L_DEBUG, "Matched workspace to output: %s for %s", wso->workspace, wso->output); ws_name = strdup(wso->workspace); break; } } } if (!ws_name) { ws_name = workspace_next_name(); } new_workspace(output, ws_name); free(ws_name); return output; }
struct cmd_results *cmd_gaps(int argc, char **argv) { struct cmd_results *error = NULL; if ((error = checkarg(argc, "gaps", EXPECTED_AT_LEAST, 1))) { return error; } const char *expected_syntax = "Expected 'gaps edge_gaps <on|off|toggle>' or " "'gaps <inner|outer> <current|all|workspace> <set|plus|minus n>'"; const char *amount_str = argv[0]; // gaps amount if (argc >= 1 && isdigit(*amount_str)) { int amount = (int)strtol(amount_str, NULL, 10); if (errno == ERANGE) { errno = 0; return cmd_results_new(CMD_INVALID, "gaps", "Number is out out of range."); } config->gaps_inner = config->gaps_outer = amount; arrange_windows(&root_container, -1, -1); return cmd_results_new(CMD_SUCCESS, NULL, NULL); } // gaps inner|outer n else if (argc >= 2 && isdigit((amount_str = argv[1])[0])) { int amount = (int)strtol(amount_str, NULL, 10); if (errno == ERANGE) { errno = 0; return cmd_results_new(CMD_INVALID, "gaps", "Number is out out of range."); } const char *target_str = argv[0]; if (strcasecmp(target_str, "inner") == 0) { config->gaps_inner = amount; } else if (strcasecmp(target_str, "outer") == 0) { config->gaps_outer = amount; } arrange_windows(&root_container, -1, -1); return cmd_results_new(CMD_SUCCESS, NULL, NULL); } else if (argc == 2 && strcasecmp(argv[0], "edge_gaps") == 0) { // gaps edge_gaps <on|off|toggle> if (strcasecmp(argv[1], "toggle") == 0) { if (config->reading) { return cmd_results_new(CMD_FAILURE, "gaps edge_gaps toggle", "Can't be used in config file."); } config->edge_gaps = !config->edge_gaps; } else { config->edge_gaps = (strcasecmp(argv[1], "yes") == 0 || strcasecmp(argv[1], "on") == 0); } arrange_windows(&root_container, -1, -1); return cmd_results_new(CMD_SUCCESS, NULL, NULL); } // gaps inner|outer current|all set|plus|minus n if (argc < 4 || config->reading) { return cmd_results_new(CMD_INVALID, "gaps", expected_syntax); } // gaps inner|outer ... const char *inout_str = argv[0]; enum {INNER, OUTER} inout; if (strcasecmp(inout_str, "inner") == 0) { inout = INNER; } else if (strcasecmp(inout_str, "outer") == 0) { inout = OUTER; } else { return cmd_results_new(CMD_INVALID, "gaps", expected_syntax); } // gaps ... current|all ... const char *target_str = argv[1]; enum {CURRENT, WORKSPACE, ALL} target; if (strcasecmp(target_str, "current") == 0) { target = CURRENT; } else if (strcasecmp(target_str, "all") == 0) { target = ALL; } else if (strcasecmp(target_str, "workspace") == 0) { if (inout == OUTER) { target = CURRENT; } else { // Set gap for views in workspace target = WORKSPACE; } } else { return cmd_results_new(CMD_INVALID, "gaps", expected_syntax); } // gaps ... n amount_str = argv[3]; int amount = (int)strtol(amount_str, NULL, 10); if (errno == ERANGE) { errno = 0; return cmd_results_new(CMD_INVALID, "gaps", "Number is out out of range."); } // gaps ... set|plus|minus ... const char *method_str = argv[2]; enum {SET, ADD} method; if (strcasecmp(method_str, "set") == 0) { method = SET; } else if (strcasecmp(method_str, "plus") == 0) { method = ADD; } else if (strcasecmp(method_str, "minus") == 0) { method = ADD; amount *= -1; } else { return cmd_results_new(CMD_INVALID, "gaps", expected_syntax); } if (target == CURRENT) { swayc_t *cont; if (inout == OUTER) { if ((cont = swayc_active_workspace()) == NULL) { return cmd_results_new(CMD_FAILURE, "gaps", "There's no active workspace."); } } else { if ((cont = get_focused_view(&root_container))->type != C_VIEW) { return cmd_results_new(CMD_FAILURE, "gaps", "Currently focused item is not a view."); } } cont->gaps = swayc_gap(cont); if (method == SET) { cont->gaps = amount; } else if ((cont->gaps += amount) < 0) { cont->gaps = 0; } arrange_windows(cont->parent, -1, -1); } else if (inout == OUTER) { //resize all workspace. int i,j; for (i = 0; i < root_container.children->length; ++i) { swayc_t *op = root_container.children->items[i]; for (j = 0; j < op->children->length; ++j) { swayc_t *ws = op->children->items[j]; if (method == SET) { ws->gaps = amount; } else if ((ws->gaps += amount) < 0) { ws->gaps = 0; } } } arrange_windows(&root_container, -1, -1); } else { // Resize gaps for all views in workspace swayc_t *top; if (target == WORKSPACE) { if ((top = swayc_active_workspace()) == NULL) { return cmd_results_new(CMD_FAILURE, "gaps", "There's currently no active workspace."); } } else { top = &root_container; } int top_gap = top->gaps; container_map(top, method == SET ? set_gaps : add_gaps, &amount); top->gaps = top_gap; arrange_windows(top, -1, -1); } return cmd_results_new(CMD_SUCCESS, NULL, NULL); }
void ipc_client_handle_command(struct ipc_client *client) { if (!sway_assert(client != NULL, "client != NULL")) { return; } char buf[client->payload_length + 1]; if (client->payload_length > 0) { ssize_t received = recv(client->fd, buf, client->payload_length, 0); if (received == -1) { sway_log_errno(L_INFO, "Unable to receive payload from IPC client"); ipc_client_disconnect(client); return; } } switch (client->current_command) { case IPC_COMMAND: { buf[client->payload_length] = '\0'; struct cmd_results *results = handle_command(buf); const char *json = cmd_results_to_json(results); char reply[256]; int length = snprintf(reply, sizeof(reply), "%s", json); ipc_send_reply(client, reply, (uint32_t) length); free_cmd_results(results); break; } case IPC_SUBSCRIBE: { buf[client->payload_length] = '\0'; struct json_object *request = json_tokener_parse(buf); if (request == NULL) { ipc_send_reply(client, "{\"success\": false}", 18); ipc_client_disconnect(client); return; } // parse requested event types for (int 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 |= IPC_GET_WORKSPACES; } else { ipc_send_reply(client, "{\"success\": false}", 18); ipc_client_disconnect(client); json_object_put(request); return; } } json_object_put(request); ipc_send_reply(client, "{\"success\": true}", 17); break; } case IPC_GET_WORKSPACES: { json_object *workspaces = json_object_new_array(); container_map(&root_container, ipc_get_workspaces_callback, workspaces); const char *json_string = json_object_to_json_string(workspaces); ipc_send_reply(client, json_string, (uint32_t) strlen(json_string)); json_object_put(workspaces); // free break; } case IPC_GET_OUTPUTS: { json_object *outputs = json_object_new_array(); container_map(&root_container, ipc_get_outputs_callback, outputs); const char *json_string = json_object_to_json_string(outputs); ipc_send_reply(client, json_string, (uint32_t) strlen(json_string)); json_object_put(outputs); // free break; } case IPC_GET_VERSION: { #if defined SWAY_GIT_VERSION && defined SWAY_GIT_BRANCH && defined SWAY_VERSION_DATE char *full_version = calloc(strlen(SWAY_GIT_VERSION) + strlen(SWAY_GIT_BRANCH) + strlen(SWAY_VERSION_DATE) + 20, 1); strcat(full_version, SWAY_GIT_VERSION); strcat(full_version, " ("); strcat(full_version, SWAY_VERSION_DATE); strcat(full_version, ", branch \""); strcat(full_version, SWAY_GIT_BRANCH); strcat(full_version, "\")"); json_object *json = json_object_new_object(); json_object_object_add(json, "human_readable", json_object_new_string(full_version)); // Todo once we actually release a version json_object_object_add(json, "major", json_object_new_int(0)); json_object_object_add(json, "minor", json_object_new_int(0)); json_object_object_add(json, "patch", json_object_new_int(1)); #else json_object_object_add(json, "human_readable", json_object_new_string("version not found")); json_object_object_add(json, "major", json_object_new_int(0)); json_object_object_add(json, "minor", json_object_new_int(0)); json_object_object_add(json, "patch", json_object_new_int(0)); #endif const char *json_string = json_object_to_json_string(json); ipc_send_reply(client, json_string, (uint32_t) strlen(json_string)); json_object_put(json); // free free(full_version); break; } default: sway_log(L_INFO, "Unknown IPC command type %i", client->current_command); ipc_client_disconnect(client); return; } client->payload_length = 0; }
void ipc_client_handle_command(struct ipc_client *client) { if (!sway_assert(client != NULL, "client != NULL")) { return; } char *buf = malloc(client->payload_length + 1); if (!buf) { sway_log_errno(L_INFO, "Out of memory"); ipc_client_disconnect(client); return; } if (client->payload_length > 0) { ssize_t received = recv(client->fd, buf, client->payload_length, 0); if (received == -1) { sway_log_errno(L_INFO, "Unable to receive payload from IPC client"); ipc_client_disconnect(client); free(buf); return; } } buf[client->payload_length] = '\0'; const char *error_denied = "{ \"success\": false, \"error\": \"Permission denied\" }"; switch (client->current_command) { case IPC_COMMAND: { if (!(config->ipc_policy & IPC_FEATURE_COMMAND)) { goto exit_denied; } struct cmd_results *results = handle_command(buf, CONTEXT_IPC); const char *json = cmd_results_to_json(results); char reply[256]; int length = snprintf(reply, sizeof(reply), "%s", json); ipc_send_reply(client, reply, (uint32_t) length); free_cmd_results(results); goto exit_cleanup; } case IPC_SUBSCRIBE: { struct json_object *request = json_tokener_parse(buf); if (request == NULL) { ipc_send_reply(client, "{\"success\": false}", 18); sway_log_errno(L_INFO, "Failed to read request"); goto exit_cleanup; } // parse requested event types for (int 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); sway_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_WORKSPACES: { if (!(config->ipc_policy & IPC_FEATURE_GET_WORKSPACES)) { goto exit_denied; } json_object *workspaces = json_object_new_array(); container_map(&root_container, ipc_get_workspaces_callback, workspaces); const char *json_string = json_object_to_json_string(workspaces); ipc_send_reply(client, json_string, (uint32_t) strlen(json_string)); json_object_put(workspaces); // free goto exit_cleanup; } case IPC_GET_INPUTS: { if (!(config->ipc_policy & IPC_FEATURE_GET_INPUTS)) { goto exit_denied; } json_object *inputs = json_object_new_array(); if (input_devices) { for(int i=0; i<input_devices->length; i++) { struct libinput_device *device = input_devices->items[i]; char* identifier = libinput_dev_unique_id(device); json_object *device_object = json_object_new_object(); json_object_object_add(device_object, "identifier", json_object_new_string(identifier)); json_object_array_add(inputs, device_object); free(identifier); } } const char *json_string = json_object_to_json_string(inputs); ipc_send_reply(client, json_string, (uint32_t) strlen(json_string)); json_object_put(inputs); goto exit_cleanup; } case IPC_GET_OUTPUTS: { if (!(config->ipc_policy & IPC_FEATURE_GET_OUTPUTS)) { goto exit_denied; } json_object *outputs = json_object_new_array(); container_map(&root_container, ipc_get_outputs_callback, outputs); const char *json_string = json_object_to_json_string(outputs); ipc_send_reply(client, json_string, (uint32_t) strlen(json_string)); json_object_put(outputs); // free goto exit_cleanup; } case IPC_GET_TREE: { if (!(config->ipc_policy & IPC_FEATURE_GET_TREE)) { goto exit_denied; } json_object *tree = ipc_json_describe_container_recursive(&root_container); const char *json_string = json_object_to_json_string(tree); ipc_send_reply(client, json_string, (uint32_t) strlen(json_string)); json_object_put(tree); goto exit_cleanup; } case IPC_GET_VERSION: { json_object *version = ipc_json_get_version(); const char *json_string = json_object_to_json_string(version); ipc_send_reply(client, json_string, (uint32_t)strlen(json_string)); json_object_put(version); // free goto exit_cleanup; } case IPC_SWAY_GET_PIXELS: { char response_header[9]; memset(response_header, 0, sizeof(response_header)); json_object *obj = json_tokener_parse(buf); json_object *o, *x, *y, *w, *h; json_object_object_get_ex(obj, "output", &o); json_object_object_get_ex(obj, "x", &x); json_object_object_get_ex(obj, "y", &y); json_object_object_get_ex(obj, "w", &w); json_object_object_get_ex(obj, "h", &h); struct wlc_geometry g = { .origin = { .x = json_object_get_int(x), .y = json_object_get_int(y) }, .size = { .w = json_object_get_int(w), .h = json_object_get_int(h) } }; swayc_t *output = swayc_by_test(&root_container, output_by_name_test, (void *)json_object_get_string(o)); json_object_put(obj); if (!output) { sway_log(L_ERROR, "IPC GET_PIXELS request with unknown output name"); ipc_send_reply(client, response_header, sizeof(response_header)); goto exit_cleanup; } struct get_pixels_request *req = malloc(sizeof(struct get_pixels_request)); req->client = client; req->output = output->handle; req->geo = g; list_add(ipc_get_pixel_requests, req); wlc_output_schedule_render(output->handle); goto exit_cleanup; } case IPC_GET_BAR_CONFIG: { if (!(config->ipc_policy & IPC_FEATURE_GET_BAR_CONFIG)) { goto exit_denied; } if (!buf[0]) { // Send list of configured bar IDs json_object *bars = json_object_new_array(); int i; for (i = 0; i < config->bars->length; ++i) { struct bar_config *bar = config->bars->items[i]; json_object_array_add(bars, json_object_new_string(bar->id)); } const char *json_string = json_object_to_json_string(bars); ipc_send_reply(client, json_string, (uint32_t)strlen(json_string)); json_object_put(bars); // free } else { // Send particular bar's details struct bar_config *bar = NULL; int i; for (i = 0; i < config->bars->length; ++i) { bar = config->bars->items[i]; if (strcmp(buf, bar->id) == 0) { break; } bar = NULL; } if (!bar) { const char *error = "{ \"success\": false, \"error\": \"No bar with that ID\" }"; ipc_send_reply(client, error, (uint32_t)strlen(error)); goto exit_cleanup; } json_object *json = ipc_json_describe_bar_config(bar); const char *json_string = json_object_to_json_string(json); ipc_send_reply(client, json_string, (uint32_t)strlen(json_string)); json_object_put(json); // free } goto exit_cleanup; } default: sway_log(L_INFO, "Unknown IPC command type %i", client->current_command); goto exit_cleanup; }