void swap_container(swayc_t *a, swayc_t *b) { if (!sway_assert(a&&b, "parameters must be non null") || !sway_assert(a->parent && b->parent, "containers must have parents")) { return; } size_t a_index = index_child(a); size_t b_index = index_child(b); swayc_t *a_parent = a->parent; swayc_t *b_parent = b->parent; // Swap the pointers if (a->is_floating) { a_parent->floating->items[a_index] = b; } else { a_parent->children->items[a_index] = b; } if (b->is_floating) { b_parent->floating->items[b_index] = a; } else { b_parent->children->items[b_index] = a; } a->parent = b_parent; b->parent = a_parent; if (a_parent->focused == a) { a_parent->focused = b; } // don't want to double switch if (b_parent->focused == b && a_parent != b_parent) { b_parent->focused = a; } }
/* * Join a list of strings, adding separator in between. Separator can be NULL. */ char *join_list(list_t *list, char *separator) { if (!sway_assert(list != NULL, "list != NULL") || list->length == 0) { return NULL; } size_t len = 1; // NULL terminator size_t sep_len = 0; if (separator != NULL) { sep_len = strlen(separator); len += (list->length - 1) * sep_len; } for (int i = 0; i < list->length; i++) { len += strlen(list->items[i]); } char *res = malloc(len); char *p = res + strlen(list->items[0]); strcpy(res, list->items[0]); for (int i = 1; i < list->length; i++) { if (sep_len) { memcpy(p, separator, sep_len); p += sep_len; } strcpy(p, list->items[i]); p += strlen(list->items[i]); } *p = '\0'; return res; }
/** * Get the previous or next workspace. If the first/last workspace on an output is active, * proceed to the previous/next output's previous/next workspace. * If next is false, the previous workspace is returned, otherwise the next one is returned. */ swayc_t *workspace_prev_next_impl(swayc_t *workspace, bool next) { if (!sway_assert(workspace->type == C_WORKSPACE, "Argument must be a workspace, is %d", workspace->type)) { return NULL; } swayc_t *current_output = workspace->parent; int offset = next ? 1 : -1; int start = next ? 0 : 1; int end = next ? (current_output->children->length) - 1 : current_output->children->length; int i; for (i = start; i < end; i++) { if (current_output->children->items[i] == workspace) { return current_output->children->items[i + offset]; } } // Given workspace is the first/last on the output, jump to the previous/next output int num_outputs = root_container.children->length; for (i = 0; i < num_outputs; i++) { if (root_container.children->items[i] == current_output) { swayc_t *next_output = root_container.children->items[wrap(i + offset, num_outputs)]; return workspace_output_prev_next_impl(next_output, next); } } // Doesn't happen, at worst the for loop returns the previously active workspace on the active output return NULL; }
void handle_xdg_shell_v6_surface(struct wl_listener *listener, void *data) { struct wlr_xdg_surface_v6 *xdg_surface = data; if (xdg_surface->role == WLR_XDG_SURFACE_V6_ROLE_POPUP) { sway_log(SWAY_DEBUG, "New xdg_shell_v6 popup"); return; } sway_log(SWAY_DEBUG, "New xdg_shell_v6 toplevel title='%s' app_id='%s'", xdg_surface->toplevel->title, xdg_surface->toplevel->app_id); wlr_xdg_surface_v6_ping(xdg_surface); struct sway_xdg_shell_v6_view *xdg_shell_v6_view = calloc(1, sizeof(struct sway_xdg_shell_v6_view)); if (!sway_assert(xdg_shell_v6_view, "Failed to allocate view")) { return; } view_init(&xdg_shell_v6_view->view, SWAY_VIEW_XDG_SHELL_V6, &view_impl); xdg_shell_v6_view->view.wlr_xdg_surface_v6 = xdg_surface; xdg_shell_v6_view->map.notify = handle_map; wl_signal_add(&xdg_surface->events.map, &xdg_shell_v6_view->map); xdg_shell_v6_view->unmap.notify = handle_unmap; wl_signal_add(&xdg_surface->events.unmap, &xdg_shell_v6_view->unmap); xdg_shell_v6_view->destroy.notify = handle_destroy; wl_signal_add(&xdg_surface->events.destroy, &xdg_shell_v6_view->destroy); xdg_surface->data = xdg_shell_v6_view; }
swayc_t *get_focused_float(swayc_t *ws) { if(!sway_assert(ws->type == C_WORKSPACE, "must be of workspace type")) { ws = swayc_active_workspace(); } if (ws->floating->length) { return ws->floating->items[ws->floating->length - 1]; } return NULL; }
static void popup_destroy(struct sway_view_child *child) { if (!sway_assert(child->impl == &popup_impl, "Expected an xdg_shell_v6 popup")) { return; } struct sway_xdg_popup_v6 *popup = (struct sway_xdg_popup_v6 *)child; wl_list_remove(&popup->new_popup.link); wl_list_remove(&popup->destroy.link); free(popup); }
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; }
void ipc_client_disconnect(struct ipc_client *client) { if (!sway_assert(client != NULL, "client != NULL")) { return; } sway_log(L_INFO, "IPC Client %d disconnected", client->fd); wlc_event_source_remove(client->event_source); close(client->fd); free(client); }
swayc_t *swayc_parent_by_type(swayc_t *container, enum swayc_types type) { if (!ASSERT_NONNULL(container)) { return NULL; } if (!sway_assert(type < C_TYPES && type >= C_ROOT, "%s: invalid type", __func__)) { return NULL; } do { container = container->parent; } while(container && container->type != type); return container; }
swayc_t *swayc_parent_by_layout(swayc_t *container, enum swayc_layouts layout) { if (!ASSERT_NONNULL(container)) { return NULL; } if (!sway_assert(layout < L_LAYOUTS && layout >= L_NONE, "%s: invalid layout", __func__)) { return NULL; } do { container = container->parent; } while (container && container->layout != layout); return container; }
void add_floating(swayc_t *ws, swayc_t *child) { sway_log(L_DEBUG, "Adding %p (%d, %fx%f) to %p (%d, %fx%f)", child, child->type, child->width, child->height, ws, ws->type, ws->width, ws->height); if (!sway_assert(ws->type == C_WORKSPACE, "Must be of workspace type")) { return; } list_add(ws->floating, child); child->parent = ws; child->is_floating = true; if (!ws->focused) { ws->focused = child; } }
/** * Get the previous or next workspace on the specified output. * Wraps around at the end and beginning. * If next is false, the previous workspace is returned, otherwise the next one is returned. */ swayc_t *workspace_output_prev_next_impl(swayc_t *output, bool next) { if (!sway_assert(output->type == C_OUTPUT, "Argument must be an output, is %d", output->type)) { return NULL; } int i; for (i = 0; i < output->children->length; i++) { if (output->children->items[i] == output->focused) { return output->children->items[wrap(i + (next ? 1 : -1), output->children->length)]; } } // Doesn't happen, at worst the for loop returns the previously active workspace return NULL; }
// Like sway_log, but also appends some info about given container to log output. void swayc_log(log_importance_t verbosity, swayc_t *cont, const char* format, ...) { sway_assert(cont, "swayc_log: no container ..."); va_list args; va_start(args, format); char *txt = malloc(128); vsprintf(txt, format, args); va_end(args); char *debug_txt = malloc(32); snprintf(debug_txt, 32, "%s '%s'", swayc_type_string(cont->type), cont->name); sway_log(verbosity, "%s (%s)", txt, debug_txt); free(txt); free(debug_txt); }
void ipc_client_disconnect(struct ipc_client *client) { if (!sway_assert(client != NULL, "client != NULL")) { return; } if (client->fd != -1) { shutdown(client->fd, SHUT_RDWR); } sway_log(L_INFO, "IPC Client %d disconnected", client->fd); wlc_event_source_remove(client->event_source); int i = 0; while (i < ipc_client_list->length && ipc_client_list->items[i] != client) i++; list_del(ipc_client_list, i); close(client->fd); free(client); }
static void handle_unmap(struct wl_listener *listener, void *data) { struct sway_xdg_shell_v6_view *xdg_shell_v6_view = wl_container_of(listener, xdg_shell_v6_view, unmap); struct sway_view *view = &xdg_shell_v6_view->view; if (!sway_assert(view->surface, "Cannot unmap unmapped view")) { return; } view_unmap(view); wl_list_remove(&xdg_shell_v6_view->commit.link); wl_list_remove(&xdg_shell_v6_view->new_popup.link); wl_list_remove(&xdg_shell_v6_view->request_fullscreen.link); wl_list_remove(&xdg_shell_v6_view->request_move.link); wl_list_remove(&xdg_shell_v6_view->request_resize.link); wl_list_remove(&xdg_shell_v6_view->set_title.link); wl_list_remove(&xdg_shell_v6_view->set_app_id.link); }
static void handle_request_fullscreen(struct wl_listener *listener, void *data) { struct sway_xdg_shell_v6_view *xdg_shell_v6_view = wl_container_of(listener, xdg_shell_v6_view, request_fullscreen); struct wlr_xdg_toplevel_v6_set_fullscreen_event *e = data; struct wlr_xdg_surface_v6 *xdg_surface = xdg_shell_v6_view->view.wlr_xdg_surface_v6; struct sway_view *view = &xdg_shell_v6_view->view; if (!sway_assert(xdg_surface->role == WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL, "xdg_shell_v6 requested fullscreen of surface with role %i", xdg_surface->role)) { return; } if (!xdg_surface->mapped) { return; } container_set_fullscreen(view->container, e->fullscreen); arrange_root(); transaction_commit_dirty(); }
json_object *ipc_json_describe_container(swayc_t *c) { if (!(sway_assert(c, "Container must not be null."))) { return NULL; } json_object *object = json_object_new_object(); json_object_object_add(object, "id", json_object_new_int((int)c->id)); json_object_object_add(object, "name", (c->name) ? json_object_new_string(c->name) : NULL); json_object_object_add(object, "rect", ipc_json_create_rect(c)); json_object_object_add(object, "visible", json_object_new_boolean(c->visible)); json_object_object_add(object, "focused", json_object_new_boolean(c == current_focus)); switch (c->type) { case C_ROOT: json_object_object_add(object, "type", json_object_new_string("root")); break; case C_OUTPUT: ipc_json_describe_output(c, object); break; case C_CONTAINER: // fallthrough case C_VIEW: ipc_json_describe_view(c, object); break; case C_WORKSPACE: ipc_json_describe_workspace(c, object); break; case C_TYPES: // fallthrough; this should never happen, I'm just trying to silence compiler warnings default: break; } return object; }
int index_child(const swayc_t *child) { swayc_t *parent = child->parent; int i, len; if (!child->is_floating) { len = parent->children->length; for (i = 0; i < len; ++i) { if (parent->children->items[i] == child) { break; } } } else { len = parent->floating->length; for (i = 0; i < len; ++i) { if (parent->floating->items[i] == child) { break; } } } if (!sway_assert(i < len, "Stray container")) { return -1; } return i; }
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; }
static void popup_unconstrain(struct sway_xdg_popup_v6 *popup) { struct sway_view *view = popup->child.view; struct wlr_xdg_popup_v6 *wlr_popup = popup->wlr_xdg_surface_v6->popup; struct sway_output *output = view->container->workspace->output; // the output box expressed in the coordinate system of the toplevel parent // of the popup struct wlr_box output_toplevel_sx_box = { .x = output->lx - view->container->content_x, .y = output->ly - view->container->content_y, .width = output->width, .height = output->height, }; wlr_xdg_popup_v6_unconstrain_from_box(wlr_popup, &output_toplevel_sx_box); } static struct sway_xdg_popup_v6 *popup_create( struct wlr_xdg_popup_v6 *wlr_popup, struct sway_view *view) { struct wlr_xdg_surface_v6 *xdg_surface = wlr_popup->base; struct sway_xdg_popup_v6 *popup = calloc(1, sizeof(struct sway_xdg_popup_v6)); if (popup == NULL) { return NULL; } view_child_init(&popup->child, &popup_impl, view, xdg_surface->surface); popup->wlr_xdg_surface_v6 = xdg_surface; wl_signal_add(&xdg_surface->events.new_popup, &popup->new_popup); popup->new_popup.notify = popup_handle_new_popup; wl_signal_add(&xdg_surface->events.destroy, &popup->destroy); popup->destroy.notify = popup_handle_destroy; wl_signal_add(&xdg_surface->events.map, &popup->child.surface_map); wl_signal_add(&xdg_surface->events.unmap, &popup->child.surface_unmap); popup_unconstrain(popup); return popup; } static struct sway_xdg_shell_v6_view *xdg_shell_v6_view_from_view( struct sway_view *view) { if (!sway_assert(view->type == SWAY_VIEW_XDG_SHELL_V6, "Expected xdg_shell_v6 view")) { return NULL; } return (struct sway_xdg_shell_v6_view *)view; } static void get_constraints(struct sway_view *view, double *min_width, double *max_width, double *min_height, double *max_height) { struct wlr_xdg_toplevel_v6_state *state = &view->wlr_xdg_surface_v6->toplevel->current; *min_width = state->min_width > 0 ? state->min_width : DBL_MIN; *max_width = state->max_width > 0 ? state->max_width : DBL_MAX; *min_height = state->min_height > 0 ? state->min_height : DBL_MIN; *max_height = state->max_height > 0 ? state->max_height : DBL_MAX; } static const char *get_string_prop(struct sway_view *view, enum sway_view_prop prop) { if (xdg_shell_v6_view_from_view(view) == NULL) { return NULL; } switch (prop) { case VIEW_PROP_TITLE: return view->wlr_xdg_surface_v6->toplevel->title; case VIEW_PROP_APP_ID: return view->wlr_xdg_surface_v6->toplevel->app_id; default: return NULL; } } static uint32_t configure(struct sway_view *view, double lx, double ly, int width, int height) { struct sway_xdg_shell_v6_view *xdg_shell_v6_view = xdg_shell_v6_view_from_view(view); if (xdg_shell_v6_view == NULL) { return 0; } return wlr_xdg_toplevel_v6_set_size( view->wlr_xdg_surface_v6, width, height); } static void set_activated(struct sway_view *view, bool activated) { if (xdg_shell_v6_view_from_view(view) == NULL) { return; } struct wlr_xdg_surface_v6 *surface = view->wlr_xdg_surface_v6; if (surface->role == WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL) { wlr_xdg_toplevel_v6_set_activated(surface, activated); } } static void set_tiled(struct sway_view *view, bool tiled) { if (xdg_shell_v6_view_from_view(view) == NULL) { return; } struct wlr_xdg_surface_v6 *surface = view->wlr_xdg_surface_v6; wlr_xdg_toplevel_v6_set_maximized(surface, tiled); } static void set_fullscreen(struct sway_view *view, bool fullscreen) { if (xdg_shell_v6_view_from_view(view) == NULL) { return; } struct wlr_xdg_surface_v6 *surface = view->wlr_xdg_surface_v6; wlr_xdg_toplevel_v6_set_fullscreen(surface, fullscreen); } static bool wants_floating(struct sway_view *view) { struct wlr_xdg_toplevel_v6 *toplevel = view->wlr_xdg_surface_v6->toplevel; struct wlr_xdg_toplevel_v6_state *state = &toplevel->current; return (state->min_width != 0 && state->min_height != 0 && (state->min_width == state->max_width || state->min_height == state->max_height)) || toplevel->parent; } static void for_each_surface(struct sway_view *view, wlr_surface_iterator_func_t iterator, void *user_data) { if (xdg_shell_v6_view_from_view(view) == NULL) { return; } wlr_xdg_surface_v6_for_each_surface(view->wlr_xdg_surface_v6, iterator, user_data); } static void for_each_popup(struct sway_view *view, wlr_surface_iterator_func_t iterator, void *user_data) { if (xdg_shell_v6_view_from_view(view) == NULL) { return; } wlr_xdg_surface_v6_for_each_popup(view->wlr_xdg_surface_v6, iterator, user_data); } static bool is_transient_for(struct sway_view *child, struct sway_view *ancestor) { if (xdg_shell_v6_view_from_view(child) == NULL) { return false; } struct wlr_xdg_surface_v6 *surface = child->wlr_xdg_surface_v6; while (surface && surface->role == WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL) { if (surface->toplevel->parent == ancestor->wlr_xdg_surface_v6) { return true; } surface = surface->toplevel->parent; } return false; } static void _close(struct sway_view *view) { if (xdg_shell_v6_view_from_view(view) == NULL) { return; } struct wlr_xdg_surface_v6 *surface = view->wlr_xdg_surface_v6; if (surface->role == WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL) { wlr_xdg_surface_v6_send_close(surface); } } static void close_popups_iterator(struct wlr_surface *surface, int sx, int sy, void *data) { struct wlr_xdg_surface_v6 *xdg_surface_v6 = wlr_xdg_surface_v6_from_wlr_surface(surface); wlr_xdg_surface_v6_send_close(xdg_surface_v6); } static void close_popups(struct sway_view *view) { wlr_xdg_surface_v6_for_each_popup(view->wlr_xdg_surface_v6, close_popups_iterator, NULL); }
json_object *ipc_json_describe_bar_config(struct bar_config *bar) { if (!sway_assert(bar, "Bar must not be NULL")) { return NULL; } json_object *json = json_object_new_object(); json_object_object_add(json, "id", json_object_new_string(bar->id)); json_object_object_add(json, "tray_output", NULL); json_object_object_add(json, "mode", json_object_new_string(bar->mode)); json_object_object_add(json, "hidden_state", json_object_new_string(bar->hidden_state)); json_object_object_add(json, "modifier", json_object_new_string(get_modifier_name_by_mask(bar->modifier))); switch (bar->position) { case DESKTOP_SHELL_PANEL_POSITION_TOP: json_object_object_add(json, "position", json_object_new_string("top")); break; case DESKTOP_SHELL_PANEL_POSITION_BOTTOM: json_object_object_add(json, "position", json_object_new_string("bottom")); break; case DESKTOP_SHELL_PANEL_POSITION_LEFT: json_object_object_add(json, "position", json_object_new_string("left")); break; case DESKTOP_SHELL_PANEL_POSITION_RIGHT: json_object_object_add(json, "position", json_object_new_string("right")); break; } json_object_object_add(json, "status_command", json_object_new_string(bar->status_command)); json_object_object_add(json, "font", json_object_new_string((bar->font) ? bar->font : config->font)); if (bar->separator_symbol) { json_object_object_add(json, "separator_symbol", json_object_new_string(bar->separator_symbol)); } json_object_object_add(json, "bar_height", json_object_new_int(bar->height)); json_object_object_add(json, "wrap_scroll", json_object_new_boolean(bar->wrap_scroll)); json_object_object_add(json, "workspace_buttons", json_object_new_boolean(bar->workspace_buttons)); json_object_object_add(json, "strip_workspace_numbers", json_object_new_boolean(bar->strip_workspace_numbers)); json_object_object_add(json, "binding_mode_indicator", json_object_new_boolean(bar->binding_mode_indicator)); json_object_object_add(json, "verbose", json_object_new_boolean(bar->verbose)); json_object_object_add(json, "pango_markup", json_object_new_boolean(bar->pango_markup)); json_object *colors = json_object_new_object(); json_object_object_add(colors, "background", json_object_new_string(bar->colors.background)); json_object_object_add(colors, "statusline", json_object_new_string(bar->colors.statusline)); json_object_object_add(colors, "separator", json_object_new_string(bar->colors.separator)); if (bar->colors.focused_background) { json_object_object_add(colors, "focused_background", json_object_new_string(bar->colors.focused_background)); } else { json_object_object_add(colors, "focused_background", json_object_new_string(bar->colors.background)); } if (bar->colors.focused_statusline) { json_object_object_add(colors, "focused_statusline", json_object_new_string(bar->colors.focused_statusline)); } else { json_object_object_add(colors, "focused_statusline", json_object_new_string(bar->colors.statusline)); } if (bar->colors.focused_separator) { json_object_object_add(colors, "focused_separator", json_object_new_string(bar->colors.focused_separator)); } else { json_object_object_add(colors, "focused_separator", json_object_new_string(bar->colors.separator)); } json_object_object_add(colors, "focused_workspace_border", json_object_new_string(bar->colors.focused_workspace_border)); json_object_object_add(colors, "focused_workspace_bg", json_object_new_string(bar->colors.focused_workspace_bg)); json_object_object_add(colors, "focused_workspace_text", json_object_new_string(bar->colors.focused_workspace_text)); json_object_object_add(colors, "inactive_workspace_border", json_object_new_string(bar->colors.inactive_workspace_border)); json_object_object_add(colors, "inactive_workspace_bg", json_object_new_string(bar->colors.inactive_workspace_bg)); json_object_object_add(colors, "inactive_workspace_text", json_object_new_string(bar->colors.inactive_workspace_text)); json_object_object_add(colors, "active_workspace_border", json_object_new_string(bar->colors.active_workspace_border)); json_object_object_add(colors, "active_workspace_bg", json_object_new_string(bar->colors.active_workspace_bg)); json_object_object_add(colors, "active_workspace_text", json_object_new_string(bar->colors.active_workspace_text)); json_object_object_add(colors, "urgent_workspace_border", json_object_new_string(bar->colors.urgent_workspace_border)); json_object_object_add(colors, "urgent_workspace_bg", json_object_new_string(bar->colors.urgent_workspace_bg)); json_object_object_add(colors, "urgent_workspace_text", json_object_new_string(bar->colors.urgent_workspace_text)); if (bar->colors.binding_mode_border) { json_object_object_add(colors, "binding_mode_border", json_object_new_string(bar->colors.binding_mode_border)); } else { json_object_object_add(colors, "binding_mode_border", json_object_new_string(bar->colors.urgent_workspace_border)); } if (bar->colors.binding_mode_bg) { json_object_object_add(colors, "binding_mode_bg", json_object_new_string(bar->colors.binding_mode_bg)); } else { json_object_object_add(colors, "binding_mode_bg", json_object_new_string(bar->colors.urgent_workspace_bg)); } if (bar->colors.binding_mode_text) { json_object_object_add(colors, "binding_mode_text", json_object_new_string(bar->colors.binding_mode_text)); } else { json_object_object_add(colors, "binding_mode_text", json_object_new_string(bar->colors.urgent_workspace_text)); } json_object_object_add(json, "colors", colors); // Add outputs if defined if (bar->outputs && bar->outputs->length > 0) { json_object *outputs = json_object_new_array(); int i; for (i = 0; i < bar->outputs->length; ++i) { const char *name = bar->outputs->items[i]; json_object_array_add(outputs, json_object_new_string(name)); } json_object_object_add(json, "outputs", outputs); } return json; }
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; }