diff options
Diffstat (limited to 'sway')
-rw-r--r-- | sway/CMakeLists.txt | 4 | ||||
-rw-r--r-- | sway/commands.c | 6 | ||||
-rw-r--r-- | sway/commands/bind.c | 5 | ||||
-rw-r--r-- | sway/commands/move.c | 24 | ||||
-rw-r--r-- | sway/commands/output.c | 74 | ||||
-rw-r--r-- | sway/commands/set.c | 2 | ||||
-rw-r--r-- | sway/config.c | 7 | ||||
-rw-r--r-- | sway/container.c | 12 | ||||
-rw-r--r-- | sway/criteria.c | 58 | ||||
-rw-r--r-- | sway/handlers.c | 104 | ||||
-rw-r--r-- | sway/input_state.c | 33 | ||||
-rw-r--r-- | sway/ipc-json.c | 94 | ||||
-rw-r--r-- | sway/ipc-server.c | 108 | ||||
-rw-r--r-- | sway/main.c | 41 | ||||
-rw-r--r-- | sway/sway-security.7.txt | 2 | ||||
-rw-r--r-- | sway/sway.1.txt | 2 | ||||
-rw-r--r-- | sway/workspace.c | 2 |
17 files changed, 416 insertions, 162 deletions
diff --git a/sway/CMakeLists.txt b/sway/CMakeLists.txt index 11bec4df..48f7a7a8 100644 --- a/sway/CMakeLists.txt +++ b/sway/CMakeLists.txt @@ -94,6 +94,10 @@ endfunction() add_config(config config sway) add_config(00-defaults security.d/00-defaults sway/security.d) +if (CMAKE_SYSTEM_NAME STREQUAL FreeBSD) + add_config(10-freebsd security.d/10-freebsd sway/security.d) +endif (CMAKE_SYSTEM_NAME STREQUAL FreeBSD) + if (A2X_FOUND) add_manpage(sway 1) add_manpage(sway 5) diff --git a/sway/commands.c b/sway/commands.c index d55d9a96..c7dbf731 100644 --- a/sway/commands.c +++ b/sway/commands.c @@ -458,7 +458,11 @@ struct cmd_results *handle_command(char *_exec, enum command_context context) { if (!containers) { current_container = get_focused_container(&root_container); } else if (containers->length == 0) { - break; + if (results) { + free_cmd_results(results); + } + results = cmd_results_new(CMD_FAILURE, argv[0], "No matching container"); + goto cleanup; } else { current_container = (swayc_t *)containers->items[i]; } diff --git a/sway/commands/bind.c b/sway/commands/bind.c index af5a01e5..d9ea37b7 100644 --- a/sway/commands/bind.c +++ b/sway/commands/bind.c @@ -61,10 +61,11 @@ struct cmd_results *cmd_bindsym(int argc, char **argv) { sym = ((char *)split->items[i])[strlen("button")] - '1' + M_LEFT_CLICK; } if (!sym) { + struct cmd_results *ret = cmd_results_new(CMD_INVALID, "bindsym", + "Unknown key '%s'", (char *)split->items[i]); free_sway_binding(binding); free_flat_list(split); - return cmd_results_new(CMD_INVALID, "bindsym", "Unknown key '%s'", - (char *)split->items[i]); + return ret; } xkb_keysym_t *key = malloc(sizeof(xkb_keysym_t)); if (!key) { diff --git a/sway/commands/move.c b/sway/commands/move.c index a38687c1..8d89f2ef 100644 --- a/sway/commands/move.c +++ b/sway/commands/move.c @@ -7,6 +7,7 @@ #include "sway/output.h" #include "sway/workspace.h" #include "list.h" +#include "stringop.h" struct cmd_results *cmd_move(int argc, char **argv) { struct cmd_results *error = NULL; @@ -59,18 +60,23 @@ struct cmd_results *cmd_move(int argc, char **argv) { return cmd_results_new(CMD_FAILURE, "move", "Can only move containers and views."); } - const char *ws_name = argv[3]; swayc_t *ws; - if (argc == 5 && strcasecmp(ws_name, "number") == 0) { + const char *num_name = NULL; + char *ws_name = NULL; + if (argc == 5 && strcasecmp(argv[3], "number") == 0) { // move "container to workspace number x" - ws_name = argv[4]; - ws = workspace_by_number(ws_name); + num_name = argv[4]; + ws = workspace_by_number(num_name); } else { + ws_name = join_args(argv + 3, argc - 3); ws = workspace_by_name(ws_name); } if (ws == NULL) { - ws = workspace_create(ws_name); + ws = workspace_create(ws_name ? ws_name : num_name); + } + if (ws_name) { + free(ws_name); } move_container_to(view, get_focused_container(ws)); } else if (strcasecmp(argv[1], "to") == 0 && strcasecmp(argv[2], "output") == 0) { @@ -161,11 +167,11 @@ struct cmd_results *cmd_move(int argc, char **argv) { wlc_view_get_visible_geometry(view->handle, &g); const struct wlc_size *size = wlc_output_get_resolution(output->handle); - struct wlc_point origin; - wlc_pointer_get_position(&origin); + double x_pos, y_pos; + wlc_pointer_get_position_v2(&x_pos, &y_pos); - int32_t x = origin.x - g.size.w / 2; - int32_t y = origin.y - g.size.h / 2; + int32_t x = x_pos - g.size.w / 2; + int32_t y = y_pos - g.size.h / 2; uint32_t w = size->w - g.size.w; uint32_t h = size->h - g.size.h; diff --git a/sway/commands/output.c b/sway/commands/output.c index e5d4b317..911391d2 100644 --- a/sway/commands/output.c +++ b/sway/commands/output.c @@ -46,7 +46,8 @@ struct cmd_results *cmd_output(int argc, char **argv) { output->enabled = 0; } else if (strcasecmp(command, "resolution") == 0 || strcasecmp(command, "res") == 0) { if (++i >= argc) { - return cmd_results_new(CMD_INVALID, "output", "Missing resolution argument."); + error = cmd_results_new(CMD_INVALID, "output", "Missing resolution argument."); + goto fail; } char *res = argv[i]; char *x = strchr(res, 'x'); @@ -61,7 +62,8 @@ struct cmd_results *cmd_output(int argc, char **argv) { // Format is 1234 4321 width = atoi(res); if (++i >= argc) { - return cmd_results_new(CMD_INVALID, "output", "Missing resolution argument (height)."); + error = cmd_results_new(CMD_INVALID, "output", "Missing resolution argument (height)."); + goto fail; } res = argv[i]; height = atoi(res); @@ -70,7 +72,8 @@ struct cmd_results *cmd_output(int argc, char **argv) { output->height = height; } else if (strcasecmp(command, "position") == 0 || strcasecmp(command, "pos") == 0) { if (++i >= argc) { - return cmd_results_new(CMD_INVALID, "output", "Missing position argument."); + error = cmd_results_new(CMD_INVALID, "output", "Missing position argument."); + goto fail; } char *res = argv[i]; char *c = strchr(res, ','); @@ -85,7 +88,8 @@ struct cmd_results *cmd_output(int argc, char **argv) { // Format is 1234 4321 x = atoi(res); if (++i >= argc) { - return cmd_results_new(CMD_INVALID, "output", "Missing position argument (y)."); + error = cmd_results_new(CMD_INVALID, "output", "Missing position argument (y)."); + goto fail; } res = argv[i]; y = atoi(res); @@ -94,25 +98,49 @@ struct cmd_results *cmd_output(int argc, char **argv) { output->y = y; } else if (strcasecmp(command, "scale") == 0) { if (++i >= argc) { - return cmd_results_new(CMD_INVALID, "output", "Missing scale parameter."); + error = cmd_results_new(CMD_INVALID, "output", "Missing scale parameter."); + goto fail; } output->scale = atoi(argv[i]); } else if (strcasecmp(command, "background") == 0 || strcasecmp(command, "bg") == 0) { wordexp_t p; if (++i >= argc) { - return cmd_results_new(CMD_INVALID, "output", "Missing background file or color specification."); + error = cmd_results_new(CMD_INVALID, "output", "Missing background file or color specification."); + goto fail; } if (i + 1 >= argc) { - return cmd_results_new(CMD_INVALID, "output", "Missing background scaling mode or `solid_color`."); + error = cmd_results_new(CMD_INVALID, "output", "Missing background scaling mode or `solid_color`."); + goto fail; } - if (strcasecmp(argv[argc - 1], "solid_color") == 0) { + if (strcasecmp(argv[i + 1], "solid_color") == 0) { output->background = strdup(argv[argc - 2]); output->background_option = strdup("solid_color"); } else { - char *src = join_args(argv + i, argc - i - 1); - char *mode = argv[argc - 1]; + // argv[i+j]=bg_option + bool valid = false; + char *mode; + size_t j; + for (j = 0; j < (size_t) (argc - i); ++j) { + mode = argv[i + j]; + for (size_t k = 0; k < sizeof(bg_options) / sizeof(char *); ++k) { + if (strcasecmp(mode, bg_options[k]) == 0) { + valid = true; + break; + } + } + if (valid) { + break; + } + } + if (!valid) { + error = cmd_results_new(CMD_INVALID, "output", "Missing background scaling mode."); + goto fail; + } + + char *src = join_args(argv + i, j); if (wordexp(src, &p, 0) != 0 || p.we_wordv[0] == NULL) { - return cmd_results_new(CMD_INVALID, "output", "Invalid syntax (%s)", src); + error = cmd_results_new(CMD_INVALID, "output", "Invalid syntax (%s)", src); + goto fail; } free(src); src = p.we_wordv[0]; @@ -132,27 +160,19 @@ struct cmd_results *cmd_output(int argc, char **argv) { } } if (!src || access(src, F_OK) == -1) { - return cmd_results_new(CMD_INVALID, "output", "Background file unreadable (%s)", src); - } - for (char *m = mode; *m; ++m) *m = tolower(*m); - // Check mode - bool valid = false; - size_t j; - for (j = 0; j < sizeof(bg_options) / sizeof(char *); ++j) { - if (strcasecmp(mode, bg_options[j]) == 0) { - valid = true; - break; - } - } - if (!valid) { - return cmd_results_new(CMD_INVALID, "output", "Invalid background scaling mode."); + error = cmd_results_new(CMD_INVALID, "output", "Background file unreadable (%s)", src); + wordfree(&p); + goto fail; } + output->background = strdup(src); output->background_option = strdup(mode); if (src != p.we_wordv[0]) { free(src); } wordfree(&p); + + i += j; } } } @@ -192,4 +212,8 @@ struct cmd_results *cmd_output(int argc, char **argv) { } return cmd_results_new(CMD_SUCCESS, NULL, NULL); + +fail: + free_output_config(output); + return error; } diff --git a/sway/commands/set.c b/sway/commands/set.c index e3d08dd3..46fc6d38 100644 --- a/sway/commands/set.c +++ b/sway/commands/set.c @@ -30,7 +30,7 @@ struct cmd_results *cmd_set(int argc, char **argv) { if (!tmp) { return cmd_results_new(CMD_FAILURE, "set", "Not possible to create variable $'%s'", argv[0]); } - snprintf(tmp, size, "$%s", argv[0]); + snprintf(tmp, size+1, "$%s", argv[0]); argv[0] = tmp; } diff --git a/sway/config.c b/sway/config.c index e0b65615..5b2b6569 100644 --- a/sway/config.c +++ b/sway/config.c @@ -128,6 +128,8 @@ void free_output_config(struct output_config *oc) { return; } free(oc->name); + free(oc->background); + free(oc->background_option); free(oc); } @@ -548,9 +550,12 @@ bool load_main_config(const char *file, bool is_active) { strcpy(_path, base); strcat(_path, ent->d_name); lstat(_path, &s); - if (S_ISREG(s.st_mode)) { + if (S_ISREG(s.st_mode) && ent->d_name[0] != '.') { list_add(secconfigs, _path); } + else { + free(_path); + } ent = readdir(dir); } closedir(dir); diff --git a/sway/container.c b/sway/container.c index 358ba767..718608ff 100644 --- a/sway/container.c +++ b/sway/container.c @@ -516,11 +516,11 @@ swayc_t *destroy_view(swayc_t *view) { return NULL; } sway_log(L_DEBUG, "Destroying view '%p'", view); - swayc_t *parent = view->parent; free_swayc(view); // Destroy empty containers - if (parent->type == C_CONTAINER) { + swayc_t *parent = view->parent; + if (parent && parent->type == C_CONTAINER) { return destroy_container(parent); } return parent; @@ -707,8 +707,10 @@ swayc_t *container_under_pointer(void) { if (lookup->children && !lookup->unmanaged) { return NULL; } - struct wlc_point origin; - wlc_pointer_get_position(&origin); + double x, y; + wlc_pointer_get_position_v2(&x, &y); + struct wlc_point origin = { .x = x, .y = y }; + while (lookup && lookup->type != C_VIEW) { int i; int len; @@ -847,7 +849,6 @@ int swayc_gap(swayc_t *container) { void container_map(swayc_t *container, void (*f)(swayc_t *view, void *data), void *data) { if (container) { - f(container, data); int i; if (container->children) { for (i = 0; i < container->children->length; ++i) { @@ -861,6 +862,7 @@ void container_map(swayc_t *container, void (*f)(swayc_t *view, void *data), voi container_map(child, f, data); } } + f(container, data); } } diff --git a/sway/criteria.c b/sway/criteria.c index 04683f66..f5fe40cb 100644 --- a/sway/criteria.c +++ b/sway/criteria.c @@ -12,9 +12,12 @@ enum criteria_type { // *must* keep in sync with criteria_strings[] CRIT_CLASS, + CRIT_CON_ID, CRIT_CON_MARK, + CRIT_FLOATING, CRIT_ID, CRIT_INSTANCE, + CRIT_TILING, CRIT_TITLE, CRIT_URGENT, CRIT_WINDOW_ROLE, @@ -25,9 +28,12 @@ enum criteria_type { // *must* keep in sync with criteria_strings[] static const char * const criteria_strings[CRIT_LAST] = { [CRIT_CLASS] = "class", + [CRIT_CON_ID] = "con_id", [CRIT_CON_MARK] = "con_mark", + [CRIT_FLOATING] = "floating", [CRIT_ID] = "id", [CRIT_INSTANCE] = "instance", + [CRIT_TILING] = "tiling", [CRIT_TITLE] = "title", [CRIT_URGENT] = "urgent", // either "latest" or "oldest" ... [CRIT_WINDOW_ROLE] = "window_role", @@ -108,6 +114,7 @@ static char *crit_tokens(int *argc, char ***buf, const char * const criteria_str char **argv = *buf = calloc(max_tokens, sizeof(char*)); argv[0] = base; // this needs to be freed by caller + bool quoted = true; *argc = 1; // uneven = name, even = value while (*head && *argc < max_tokens) { @@ -128,7 +135,8 @@ static char *crit_tokens(int *argc, char ***buf, const char * const criteria_str if (*(namep) == ' ') { namep = strrchr(namep, ' ') + 1; } - argv[(*argc)++] = namep; + argv[*argc] = namep; + *argc += 1; } } else if (*head == '"') { if (*argc % 2 != 0) { @@ -137,21 +145,38 @@ static char *crit_tokens(int *argc, char ***buf, const char * const criteria_str "Found quoted value where it was not expected"); } else if (!valp) { // value starts here valp = head + 1; + quoted = true; } else { // value ends here - argv[(*argc)++] = valp; + argv[*argc] = valp; + *argc += 1; *head = '\0'; valp = NULL; namep = head + 1; } - } else if (*argc % 2 == 0 && !valp && *head != ' ') { - // We're expecting a quoted value, haven't found one yet, and this - // is not an empty space. - return strdup("Unable to parse criteria: " - "Names must be unquoted, values must be quoted"); + } else if (*argc % 2 == 0 && *head != ' ') { + // parse unquoted values + if (!valp) { + quoted = false; + valp = head; // value starts here + } + } else if (valp && !quoted && *head == ' ') { + // value ends here + argv[*argc] = valp; + *argc += 1; + *head = '\0'; + valp = NULL; + namep = head + 1; } head++; } + + // catch last unquoted value if needed + if (valp && !quoted && !*head) { + argv[*argc] = valp; + *argc += 1; + } + return NULL; } @@ -263,6 +288,15 @@ static bool criteria_test(swayc_t *cont, list_t *tokens) { matches++; } break; + case CRIT_CON_ID: { + char *endptr; + size_t crit_id = strtoul(crit->raw, &endptr, 10); + + if (*endptr == 0 && cont->id == crit_id) { + ++matches; + } + break; + } case CRIT_CON_MARK: if (crit->regex && cont->marks && (list_seq_find(cont->marks, (int (*)(const void *, const void *))regex_cmp, crit->regex) != -1)) { // Make sure it isn't matching the NUL string @@ -271,6 +305,11 @@ static bool criteria_test(swayc_t *cont, list_t *tokens) { } } break; + case CRIT_FLOATING: + if (cont->is_floating) { + matches++; + } + break; case CRIT_ID: if (!cont->app_id) { // ignore @@ -290,6 +329,11 @@ static bool criteria_test(swayc_t *cont, list_t *tokens) { matches++; } break; + case CRIT_TILING: + if (!cont->is_floating) { + matches++; + } + break; case CRIT_TITLE: if (!cont->name) { // ignore diff --git a/sway/handlers.c b/sway/handlers.c index 052789ca..d37142a9 100644 --- a/sway/handlers.c +++ b/sway/handlers.c @@ -452,6 +452,7 @@ static bool handle_view_created(wlc_handle handle) { wlc_view_focus(handle); wlc_view_bring_to_front(handle); newview = new_floating_view(handle); + /* fallthrough */ case WLC_BIT_POPUP: wlc_view_bring_to_front(handle); break; @@ -552,21 +553,24 @@ static void handle_view_destroyed(wlc_handle handle) { bool fullscreen = swayc_is_fullscreen(view); remove_view_from_scratchpad(view); swayc_t *parent = destroy_view(view); - if (fullscreen) { - parent->fullscreen = NULL; - } + if (parent) { + if (fullscreen) { + parent->fullscreen = NULL; + } - // Destroy empty workspaces - if (parent->type == C_WORKSPACE && - parent->children->length == 0 && - parent->floating->length == 0 && - swayc_active_workspace() != parent && - !parent->visible) { - parent = destroy_workspace(parent); - } + ipc_event_window(parent, "close"); - arrange_windows(parent, -1, -1); - ipc_event_window(parent, "close"); + // Destroy empty workspaces + if (parent->type == C_WORKSPACE && + parent->children->length == 0 && + parent->floating->length == 0 && + swayc_active_workspace() != parent && + !parent->visible) { + parent = destroy_workspace(parent); + } + + arrange_windows(parent, -1, -1); + } } else { // Is it unmanaged? int i; @@ -582,6 +586,15 @@ static void handle_view_destroyed(wlc_handle handle) { } } } + // Is it in the scratchpad? + for (i = 0; i < scratchpad->length; ++i) { + swayc_t *item = scratchpad->items[i]; + if (item->handle == handle) { + list_del(scratchpad, i); + destroy_view(item); + break; + } + } } set_focused_container(get_focused_view(&root_container)); } @@ -805,11 +818,11 @@ static bool handle_key(wlc_handle view, uint32_t time, const struct wlc_modifier struct sway_binding *binding = mode->bindings->items[i]; if ((modifiers->mods ^ binding->modifiers) == 0) { switch (state) { - case WLC_KEY_STATE_PRESSED: { + case WLC_KEY_STATE_PRESSED: if (!binding->release && valid_bindsym(binding)) { list_add(candidates, binding); } - } + break; case WLC_KEY_STATE_RELEASED: if (binding->release && handle_bindsym_release(binding)) { list_free(candidates); @@ -842,12 +855,13 @@ static bool handle_key(wlc_handle view, uint32_t time, const struct wlc_modifier return EVENT_PASSTHROUGH; } -static bool handle_pointer_motion(wlc_handle handle, uint32_t time, const struct wlc_point *origin) { +static bool handle_pointer_motion(wlc_handle handle, uint32_t time, double x, double y) { if (desktop_shell.is_locked) { return EVENT_PASSTHROUGH; } - struct wlc_point new_origin = *origin; + double new_x = x; + double new_y = y; // Switch to adjacent output if touching output edge. // // Since this doesn't currently support moving windows between outputs we @@ -856,45 +870,43 @@ static bool handle_pointer_motion(wlc_handle handle, uint32_t time, const struct !pointer_state.left.held && !pointer_state.right.held && !pointer_state.scroll.held) { swayc_t *output = swayc_active_output(), *adjacent = NULL; - struct wlc_point abs_pos = *origin; - abs_pos.x += output->x; - abs_pos.y += output->y; - if (origin->x == 0) { // Left edge + struct wlc_point abs_pos = { .x = x + output->x, .y = y + output->y }; + if (x <= 0) { // Left edge if ((adjacent = swayc_adjacent_output(output, MOVE_LEFT, &abs_pos, false))) { if (workspace_switch(swayc_active_workspace_for(adjacent))) { - new_origin.x = adjacent->width; + new_x = adjacent->width; // adjust for differently aligned outputs (well, this is // only correct when the two outputs have the same // resolution or the same dpi I guess, it should take // physical attributes into account) - new_origin.y += (output->y - adjacent->y); + new_y += (output->y - adjacent->y); } } - } else if ((double)origin->x == output->width) { // Right edge + } else if (x >= output->width) { // Right edge if ((adjacent = swayc_adjacent_output(output, MOVE_RIGHT, &abs_pos, false))) { if (workspace_switch(swayc_active_workspace_for(adjacent))) { - new_origin.x = 0; - new_origin.y += (output->y - adjacent->y); + new_x = 0; + new_y += (output->y - adjacent->y); } } - } else if (origin->y == 0) { // Top edge + } else if (y <= 0) { // Top edge if ((adjacent = swayc_adjacent_output(output, MOVE_UP, &abs_pos, false))) { if (workspace_switch(swayc_active_workspace_for(adjacent))) { - new_origin.y = adjacent->height; - new_origin.x += (output->x - adjacent->x); + new_y = adjacent->height; + new_x += (output->x - adjacent->x); } } - } else if ((double)origin->y == output->height) { // Bottom edge + } else if (y >= output->height) { // Bottom edge if ((adjacent = swayc_adjacent_output(output, MOVE_DOWN, &abs_pos, false))) { if (workspace_switch(swayc_active_workspace_for(adjacent))) { - new_origin.y = 0; - new_origin.x += (output->x - adjacent->x); + new_y = 0; + new_x += (output->x - adjacent->x); } } } } - pointer_position_set(&new_origin, false); + pointer_position_set(new_x, new_y, false); swayc_t *focused = get_focused_container(&root_container); if (focused->type == C_VIEW) { @@ -935,15 +947,15 @@ static bool handle_pointer_button(wlc_handle view, uint32_t time, const struct w struct sway_binding *binding = mode->bindings->items[i]; if ((modifiers->mods ^ binding->modifiers) == 0) { switch (state) { - case WLC_BUTTON_STATE_PRESSED: { - if (!binding->release && handle_bindsym(binding, button, 0)) { - return EVENT_HANDLED; - } + case WLC_BUTTON_STATE_PRESSED: + if (!binding->release && handle_bindsym(binding, button, 0)) { + return EVENT_HANDLED; + } + break; + case WLC_BUTTON_STATE_RELEASED: + if (binding->release && handle_bindsym(binding, button, 0)) { + return EVENT_HANDLED; } - case WLC_BUTTON_STATE_RELEASED: - if (binding->release && handle_bindsym(binding, button, 0)) { - return EVENT_HANDLED; - } break; } } @@ -1084,16 +1096,8 @@ bool handle_pointer_scroll(wlc_handle view, uint32_t time, const struct wlc_modi return EVENT_PASSTHROUGH; } -static void clip_test_cb(void *data, const char *type, int fd) { - const char *str = data; - write(fd, str, strlen(str)); - close(fd); -} - static void handle_wlc_ready(void) { sway_log(L_DEBUG, "Compositor is ready, executing cmds in queue"); - const char *type = "text/plain;charset=utf-8"; - wlc_set_selection("test", &type, 1, &clip_test_cb); // Execute commands until there are none left config->active = true; while (config->cmd_queue->length) { @@ -1122,7 +1126,7 @@ void register_wlc_handlers() { wlc_set_view_request_state_cb(handle_view_state_request); wlc_set_view_properties_updated_cb(handle_view_properties_updated); wlc_set_keyboard_key_cb(handle_key); - wlc_set_pointer_motion_cb(handle_pointer_motion); + wlc_set_pointer_motion_cb_v2(handle_pointer_motion); wlc_set_pointer_button_cb(handle_pointer_button); wlc_set_pointer_scroll_cb(handle_pointer_scroll); wlc_set_compositor_ready_cb(handle_wlc_ready); diff --git a/sway/input_state.c b/sway/input_state.c index 68df17de..04aafd37 100644 --- a/sway/input_state.c +++ b/sway/input_state.c @@ -202,13 +202,13 @@ static void reset_initial_sibling(void) { pointer_state.mode = 0; } -void pointer_position_set(struct wlc_point *new_origin, bool force_focus) { - struct wlc_point origin; - wlc_pointer_get_position(&origin); - pointer_state.delta.x = new_origin->x - origin.x; - pointer_state.delta.y = new_origin->y - origin.y; +void pointer_position_set(double new_x, double new_y, bool force_focus) { + double x, y; + wlc_pointer_get_position_v2(&x, &y); + pointer_state.delta.x = new_x - x; + pointer_state.delta.y = new_y - y; - wlc_pointer_set_position(new_origin); + wlc_pointer_set_position_v2(new_x, new_y); // Update view under pointer swayc_t *prev_view = pointer_state.view; @@ -226,10 +226,7 @@ void pointer_position_set(struct wlc_point *new_origin, bool force_focus) { } void center_pointer_on(swayc_t *view) { - struct wlc_point new_origin; - new_origin.x = view->x + view->width/2; - new_origin.y = view->y + view->height/2; - pointer_position_set(&new_origin, true); + pointer_position_set(view->x + view->width/2, view->y + view->height/2, true); } // Mode set left/right click @@ -269,10 +266,10 @@ static void pointer_mode_set_resizing(void) { int midway_x = initial.ptr->x + initial.ptr->width/2; int midway_y = initial.ptr->y + initial.ptr->height/2; - struct wlc_point origin; - wlc_pointer_get_position(&origin); - lock.left = origin.x > midway_x; - lock.top = origin.y > midway_y; + double x, y; + wlc_pointer_get_position_v2(&x, &y); + lock.left = x > midway_x; + lock.top = y > midway_y; if (initial.ptr->is_floating) { pointer_state.mode = M_RESIZING | M_FLOATING; @@ -346,10 +343,10 @@ void pointer_mode_update(void) { pointer_state.mode = 0; return; } - struct wlc_point origin; - wlc_pointer_get_position(&origin); - int dx = origin.x; - int dy = origin.y; + double x, y; + wlc_pointer_get_position_v2(&x, &y); + int dx = x; + int dy = y; switch (pointer_state.mode) { case M_FLOATING | M_DRAGGING: diff --git a/sway/ipc-json.c b/sway/ipc-json.c index 31de53f0..6ab63c75 100644 --- a/sway/ipc-json.c +++ b/sway/ipc-json.c @@ -138,7 +138,6 @@ static void ipc_json_describe_workspace(swayc_t *workspace, json_object *object) json_object_object_add(object, "num", json_object_new_int(num)); json_object_object_add(object, "output", (workspace->parent) ? json_object_new_string(workspace->parent->name) : NULL); - json_object_object_add(object, "urgent", json_object_new_boolean(false)); json_object_object_add(object, "type", json_object_new_string("workspace")); json_object_object_add(object, "layout", (strcmp(layout, "null") == 0) ? NULL : json_object_new_string(layout)); } @@ -156,38 +155,14 @@ static const char *ipc_json_get_scratchpad_state(swayc_t *c) { 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")); + wlc_handle parent = wlc_view_get_parent(c->handle); 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) : @@ -203,9 +178,29 @@ static void ipc_json_describe_view(swayc_t *c, json_object *object) { 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); + + if (c->parent) { + 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"; + 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))); + } +} + +static void ipc_json_describe_root(swayc_t *c, json_object *object) { + json_object_object_add(object, "type", json_object_new_string("root")); + json_object_object_add(object, "layout", json_object_new_string("splith")); } json_object *ipc_json_describe_container(swayc_t *c) { + float percent = ipc_json_child_percentage(c); + if (!(sway_assert(c, "Container must not be null."))) { return NULL; } @@ -218,9 +213,19 @@ json_object *ipc_json_describe_container(swayc_t *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)); + json_object_object_add(object, "border", json_object_new_string(ipc_json_border_description(c))); + json_object_object_add(object, "window_rect", ipc_json_create_rect_from_geometry(c->actual_geometry)); + 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, "percent", (percent > 0) ? json_object_new_double(percent) : NULL); + json_object_object_add(object, "window", json_object_new_int(c->handle)); // for the sake of i3 compat + // 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, "current_border_width", json_object_new_int(c->border_thickness)); + switch (c->type) { case C_ROOT: - json_object_object_add(object, "type", json_object_new_string("root")); + ipc_json_describe_root(c, object); break; case C_OUTPUT: @@ -451,21 +456,50 @@ json_object *ipc_json_describe_container_recursive(swayc_t *c) { int i; json_object *floating = json_object_new_array(); - if (c->type != C_VIEW && c->floating && c->floating->length > 0) { + if (c->type != C_VIEW && c->floating) { for (i = 0; i < c->floating->length; ++i) { - json_object_array_add(floating, ipc_json_describe_container_recursive(c->floating->items[i])); + swayc_t *item = c->floating->items[i]; + json_object_array_add(floating, ipc_json_describe_container_recursive(item)); } } json_object_object_add(object, "floating_nodes", floating); json_object *children = json_object_new_array(); - if (c->type != C_VIEW && c->children && c->children->length > 0) { + if (c->type != C_VIEW && c->children) { for (i = 0; i < c->children->length; ++i) { json_object_array_add(children, ipc_json_describe_container_recursive(c->children->items[i])); } } json_object_object_add(object, "nodes", children); + json_object *focus = json_object_new_array(); + if (c->type != C_VIEW) { + if (c->focused) { + json_object_array_add(focus, json_object_new_double(c->focused->id)); + } + if (c->floating) { + for (i = 0; i < c->floating->length; ++i) { + swayc_t *item = c->floating->items[i]; + if (item == c->focused) { + continue; + } + + json_object_array_add(focus, json_object_new_double(item->id)); + } + } + if (c->children) { + for (i = 0; i < c->children->length; ++i) { + swayc_t *item = c->children->items[i]; + if (item == c->focused) { + continue; + } + + json_object_array_add(focus, json_object_new_double(item->id)); + } + } + } + json_object_object_add(object, "focus", focus); + if (c->type == C_ROOT) { json_object *scratchpad_json = json_object_new_array(); if (scratchpad->length > 0) { diff --git a/sway/ipc-server.c b/sway/ipc-server.c index 5e1e93ce..4ce2b7eb 100644 --- a/sway/ipc-server.c +++ b/sway/ipc-server.c @@ -41,11 +41,15 @@ static const char ipc_magic[] = {'i', '3', '-', 'i', 'p', 'c'}; struct ipc_client { struct wlc_event_source *event_source; + struct wlc_event_source *writable_event_source; int fd; uint32_t payload_length; uint32_t security_policy; enum ipc_command_type current_command; enum ipc_command_type subscribed_events; + size_t write_buffer_len; + size_t write_buffer_size; + char *write_buffer; }; static list_t *ipc_get_pixel_requests = NULL; @@ -72,6 +76,7 @@ struct get_clipboard_request { struct sockaddr_un *ipc_user_sockaddr(void); int ipc_handle_connection(int fd, uint32_t mask, void *data); int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data); +int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data); void ipc_client_disconnect(struct ipc_client *client); void ipc_client_handle_command(struct ipc_client *client); bool ipc_send_reply(struct ipc_client *client, const char *payload, uint32_t payload_length); @@ -182,6 +187,12 @@ int ipc_handle_connection(int fd, uint32_t mask, void *data) { close(client_fd); return 0; } + if ((flags = fcntl(client_fd, F_GETFL)) == -1 + || fcntl(client_fd, F_SETFL, flags|O_NONBLOCK) == -1) { + sway_log_errno(L_ERROR, "Unable to set NONBLOCK on IPC client socket"); + close(client_fd); + return 0; + } struct ipc_client* client = malloc(sizeof(struct ipc_client)); if (!client) { @@ -193,10 +204,22 @@ int ipc_handle_connection(int fd, uint32_t mask, void *data) { client->fd = client_fd; client->subscribed_events = 0; client->event_source = wlc_event_loop_add_fd(client_fd, WLC_EVENT_READABLE, ipc_client_handle_readable, client); + client->writable_event_source = NULL; + + client->write_buffer_size = 128; + client->write_buffer_len = 0; + client->write_buffer = malloc(client->write_buffer_size); + if (!client->write_buffer) { + sway_log(L_ERROR, "Unable to allocate ipc client write buffer"); + close(client_fd); + return 0; + } pid_t pid = get_client_pid(client->fd); client->security_policy = get_ipc_policy_mask(pid); + sway_log(L_DEBUG, "New client: fd %d, pid %d", client_fd, pid); + list_add(ipc_client_list, client); return 0; @@ -219,6 +242,8 @@ int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data) { return 0; } + sway_log(L_DEBUG, "Client %d readable", client->fd); + int read_available; if (ioctl(client_fd, FIONREAD, &read_available) == -1) { sway_log_errno(L_INFO, "Unable to read IPC socket buffer size"); @@ -240,6 +265,7 @@ int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data) { uint8_t buf[ipc_header_size]; uint32_t *buf32 = (uint32_t*)(buf + sizeof(ipc_magic)); + // Should be fully available, because read_available >= ipc_header_size ssize_t received = recv(client_fd, buf, ipc_header_size, 0); if (received == -1) { sway_log_errno(L_INFO, "Unable to receive header from IPC client"); @@ -263,6 +289,48 @@ int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data) { return 0; } +int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data) { + struct ipc_client *client = data; + + if (mask & WLC_EVENT_ERROR) { + sway_log(L_ERROR, "IPC Client socket error, removing client"); + ipc_client_disconnect(client); + return 0; + } + + if (mask & WLC_EVENT_HANGUP) { + sway_log(L_DEBUG, "Client %d hung up", client->fd); + ipc_client_disconnect(client); + return 0; + } + + if (client->write_buffer_len <= 0) { + return 0; + } + + sway_log(L_DEBUG, "Client %d writable", client->fd); + + ssize_t written = write(client->fd, client->write_buffer, client->write_buffer_len); + + if (written == -1 && errno == EAGAIN) { + return 0; + } else if (written == -1) { + sway_log_errno(L_INFO, "Unable to send data from queue to IPC client"); + ipc_client_disconnect(client); + return 0; + } + + memmove(client->write_buffer, client->write_buffer + written, client->write_buffer_len - written); + client->write_buffer_len -= written; + + if (client->write_buffer_len == 0 && client->writable_event_source) { + wlc_event_source_remove(client->writable_event_source); + client->writable_event_source = NULL; + } + + return 0; +} + void ipc_client_disconnect(struct ipc_client *client) { if (!sway_assert(client != NULL, "client != NULL")) { return; @@ -274,9 +342,13 @@ void ipc_client_disconnect(struct ipc_client *client) { sway_log(L_INFO, "IPC Client %d disconnected", client->fd); wlc_event_source_remove(client->event_source); + if (client->writable_event_source) { + wlc_event_source_remove(client->writable_event_source); + } int i = 0; while (i < ipc_client_list->length && ipc_client_list->items[i] != client) i++; list_del(ipc_client_list, i); + free(client->write_buffer); close(client->fd); free(client); } @@ -608,6 +680,7 @@ void ipc_client_handle_command(struct ipc_client *client) { return; } if (client->payload_length > 0) { + // Payload should be fully available ssize_t received = recv(client->fd, buf, client->payload_length, 0); if (received == -1) { @@ -874,17 +947,36 @@ bool ipc_send_reply(struct ipc_client *client, const char *payload, uint32_t pay data32[0] = payload_length; data32[1] = client->current_command; - if (write(client->fd, data, ipc_header_size) == -1) { - sway_log_errno(L_INFO, "Unable to send header to IPC client"); + while (client->write_buffer_len + ipc_header_size + payload_length >= + client->write_buffer_size) { + client->write_buffer_size *= 2; + } + + // TODO: reduce the limit back to 4 MB when screenshooter is implemented + if (client->write_buffer_size > (1 << 28)) { // 256 MB + sway_log(L_ERROR, "Client write buffer too big, disconnecting client"); + ipc_client_disconnect(client); return false; } - if (write(client->fd, payload, payload_length) == -1) { - sway_log_errno(L_INFO, "Unable to send payload to IPC client"); + char *new_buffer = realloc(client->write_buffer, client->write_buffer_size); + if (!new_buffer) { + sway_log(L_ERROR, "Unable to reallocate ipc client write buffer"); + ipc_client_disconnect(client); return false; } + client->write_buffer = new_buffer; + + memcpy(client->write_buffer + client->write_buffer_len, data, ipc_header_size); + client->write_buffer_len += ipc_header_size; + memcpy(client->write_buffer + client->write_buffer_len, payload, payload_length); + client->write_buffer_len += payload_length; + + if (!client->writable_event_source) { + client->writable_event_source = wlc_event_loop_add_fd(client->fd, WLC_EVENT_WRITABLE, ipc_client_handle_writable, client); + } - sway_log(L_DEBUG, "Send IPC reply: %s", payload); + sway_log(L_DEBUG, "Added IPC reply to client %d queue: %s", client->fd, payload); return true; } @@ -984,11 +1076,7 @@ void ipc_event_window(swayc_t *window, const char *change) { sway_log(L_DEBUG, "Sending window::%s event", change); json_object *obj = json_object_new_object(); json_object_object_add(obj, "change", json_object_new_string(change)); - if (strcmp(change, "close") == 0 || !window) { - json_object_object_add(obj, "container", NULL); - } else { - json_object_object_add(obj, "container", ipc_json_describe_container(window)); - } + json_object_object_add(obj, "container", ipc_json_describe_container_recursive(window)); const char *json_string = json_object_to_json_string(obj); ipc_send_event(json_string, IPC_EVENT_WINDOW); diff --git a/sway/main.c b/sway/main.c index 82375e0b..6d13955c 100644 --- a/sway/main.c +++ b/sway/main.c @@ -53,6 +53,46 @@ static void wlc_log_handler(enum wlc_log_type type, const char *str) { } } +void detect_raspi() { + bool raspi = false; + FILE *f = fopen("/sys/firmware/devicetree/base/model", "r"); + if (!f) { + return; + } + char *line; + while(!feof(f)) { + if (!(line = read_line(f))) { + break; + } + if (strstr(line, "Raspberry Pi")) { + raspi = true; + } + free(line); + } + fclose(f); + FILE *g = fopen("/proc/modules", "r"); + if (!g) { + return; + } + bool vc4 = false; + while (!feof(g)) { + if (!(line = read_line(g))) { + break; + } + if (strstr(line, "vc4")) { + vc4 = true; + } + free(line); + } + fclose(g); + if (!vc4 && raspi) { + fprintf(stderr, "\x1B[1;31mWarning: You have a " + "Raspberry Pi, but the vc4 Module is " + "not loaded! Set 'dtoverlay=vc4-kms-v3d'" + "in /boot/config.txt and reboot.\x1B[0m\n"); + } +} + void detect_proprietary() { FILE *f = fopen("/proc/modules", "r"); if (!f) { @@ -366,6 +406,7 @@ int main(int argc, char **argv) { log_distro(); log_env(); detect_proprietary(); + detect_raspi(); input_devices = create_list(); diff --git a/sway/sway-security.7.txt b/sway/sway-security.7.txt index ec6df1f3..aee3793c 100644 --- a/sway/sway-security.7.txt +++ b/sway/sway-security.7.txt @@ -237,4 +237,4 @@ Authors ------- Maintained by Drew DeVault <sir@cmpwn.com>, who is assisted by other open source contributors. For more information about sway development, see -<https://github.com/SirCmpwn/sway>. +<https://github.com/swaywm/sway>. diff --git a/sway/sway.1.txt b/sway/sway.1.txt index bc827bd5..4a1aef99 100644 --- a/sway/sway.1.txt +++ b/sway/sway.1.txt @@ -119,7 +119,7 @@ Authors Maintained by Drew DeVault <sir@cmpwn.com>, who is assisted by other open source contributors. For more information about sway development, see -<https://github.com/SirCmpwn/sway>. +<https://github.com/swaywm/sway>. See Also -------- diff --git a/sway/workspace.c b/sway/workspace.c index 29cacce9..e0367190 100644 --- a/sway/workspace.c +++ b/sway/workspace.c @@ -61,7 +61,7 @@ char *workspace_next_name(const char *output_name) { // workspace n char *cmd = argsep(&cmdlist, " "); if (cmdlist) { - name = argsep(&cmdlist, " ,;"); + name = argsep(&cmdlist, ",;"); } if (strcmp("workspace", cmd) == 0 && name) { |