diff options
-rw-r--r-- | client/pool-buffer.c | 46 | ||||
-rw-r--r-- | include/pool-buffer.h | 2 | ||||
-rw-r--r-- | include/sway/commands.h | 1 | ||||
-rw-r--r-- | include/sway/config.h | 1 | ||||
-rw-r--r-- | include/sway/criteria.h | 1 | ||||
-rw-r--r-- | include/sway/tree/layout.h | 2 | ||||
-rw-r--r-- | include/sway/tree/view.h | 2 | ||||
-rw-r--r-- | include/swaylock/swaylock.h | 3 | ||||
-rw-r--r-- | sway/commands.c | 1 | ||||
-rw-r--r-- | sway/commands/bind.c | 48 | ||||
-rw-r--r-- | sway/commands/swap.c | 80 | ||||
-rw-r--r-- | sway/criteria.c | 18 | ||||
-rw-r--r-- | sway/desktop/xdg_shell.c | 12 | ||||
-rw-r--r-- | sway/input/keyboard.c | 25 | ||||
-rw-r--r-- | sway/meson.build | 1 | ||||
-rw-r--r-- | sway/sway.5.scd | 23 | ||||
-rw-r--r-- | sway/tree/container.c | 94 | ||||
-rw-r--r-- | sway/tree/layout.c | 124 | ||||
-rw-r--r-- | sway/tree/view.c | 10 | ||||
-rw-r--r-- | swaylock/main.c | 56 | ||||
-rw-r--r-- | swaylock/password.c | 102 | ||||
-rw-r--r-- | swaylock/render.c | 4 |
22 files changed, 508 insertions, 148 deletions
diff --git a/client/pool-buffer.c b/client/pool-buffer.c index 1f54a77c..52438303 100644 --- a/client/pool-buffer.c +++ b/client/pool-buffer.c | |||
@@ -1,37 +1,56 @@ | |||
1 | #define _XOPEN_SOURCE 500 | 1 | #define _XOPEN_SOURCE 500 |
2 | #include <assert.h> | 2 | #include <assert.h> |
3 | #include <cairo/cairo.h> | 3 | #include <cairo/cairo.h> |
4 | #include <fcntl.h> | ||
5 | #include <pango/pangocairo.h> | ||
4 | #include <stdio.h> | 6 | #include <stdio.h> |
5 | #include <stdlib.h> | 7 | #include <stdlib.h> |
6 | #include <string.h> | 8 | #include <string.h> |
7 | #include <sys/mman.h> | 9 | #include <sys/mman.h> |
8 | #include <pango/pangocairo.h> | ||
9 | #include <unistd.h> | 10 | #include <unistd.h> |
10 | #include <wayland-client.h> | 11 | #include <wayland-client.h> |
11 | #include "config.h" | 12 | #include "config.h" |
12 | #include "pool-buffer.h" | 13 | #include "pool-buffer.h" |
13 | 14 | ||
15 | static bool set_cloexec(int fd) { | ||
16 | long flags = fcntl(fd, F_GETFD); | ||
17 | if (flags == -1) { | ||
18 | return false; | ||
19 | } | ||
20 | |||
21 | if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1) { | ||
22 | return false; | ||
23 | } | ||
24 | |||
25 | return true; | ||
26 | } | ||
27 | |||
14 | static int create_pool_file(size_t size, char **name) { | 28 | static int create_pool_file(size_t size, char **name) { |
15 | static const char template[] = "sway-client-XXXXXX"; | 29 | static const char template[] = "sway-client-XXXXXX"; |
16 | const char *path = getenv("XDG_RUNTIME_DIR"); | 30 | const char *path = getenv("XDG_RUNTIME_DIR"); |
17 | if (!path) { | 31 | if (path == NULL) { |
32 | fprintf(stderr, "XDG_RUNTIME_DIR is not set\n"); | ||
18 | return -1; | 33 | return -1; |
19 | } | 34 | } |
20 | 35 | ||
21 | int ts = (path[strlen(path) - 1] == '/'); | 36 | size_t name_size = strlen(template) + 1 + strlen(path) + 1; |
22 | 37 | *name = malloc(name_size); | |
23 | *name = malloc( | 38 | if (*name == NULL) { |
24 | strlen(template) + | 39 | fprintf(stderr, "allocation failed\n"); |
25 | strlen(path) + | 40 | return -1; |
26 | (ts ? 0 : 1) + 1); | 41 | } |
27 | sprintf(*name, "%s%s%s", path, ts ? "" : "/", template); | 42 | snprintf(*name, name_size, "%s/%s", path, template); |
28 | 43 | ||
29 | int fd = mkstemp(*name); | 44 | int fd = mkstemp(*name); |
30 | |||
31 | if (fd < 0) { | 45 | if (fd < 0) { |
32 | return -1; | 46 | return -1; |
33 | } | 47 | } |
34 | 48 | ||
49 | if (!set_cloexec(fd)) { | ||
50 | close(fd); | ||
51 | return -1; | ||
52 | } | ||
53 | |||
35 | if (ftruncate(fd, size) < 0) { | 54 | if (ftruncate(fd, size) < 0) { |
36 | close(fd); | 55 | close(fd); |
37 | return -1; | 56 | return -1; |
@@ -53,7 +72,7 @@ static struct pool_buffer *create_buffer(struct wl_shm *shm, | |||
53 | struct pool_buffer *buf, int32_t width, int32_t height, | 72 | struct pool_buffer *buf, int32_t width, int32_t height, |
54 | uint32_t format) { | 73 | uint32_t format) { |
55 | uint32_t stride = width * 4; | 74 | uint32_t stride = width * 4; |
56 | uint32_t size = stride * height; | 75 | size_t size = stride * height; |
57 | 76 | ||
58 | char *name; | 77 | char *name; |
59 | int fd = create_pool_file(size, &name); | 78 | int fd = create_pool_file(size, &name); |
@@ -68,8 +87,10 @@ static struct pool_buffer *create_buffer(struct wl_shm *shm, | |||
68 | free(name); | 87 | free(name); |
69 | fd = -1; | 88 | fd = -1; |
70 | 89 | ||
90 | buf->size = size; | ||
71 | buf->width = width; | 91 | buf->width = width; |
72 | buf->height = height; | 92 | buf->height = height; |
93 | buf->data = data; | ||
73 | buf->surface = cairo_image_surface_create_for_data(data, | 94 | buf->surface = cairo_image_surface_create_for_data(data, |
74 | CAIRO_FORMAT_ARGB32, width, height, stride); | 95 | CAIRO_FORMAT_ARGB32, width, height, stride); |
75 | buf->cairo = cairo_create(buf->surface); | 96 | buf->cairo = cairo_create(buf->surface); |
@@ -92,6 +113,9 @@ void destroy_buffer(struct pool_buffer *buffer) { | |||
92 | if (buffer->pango) { | 113 | if (buffer->pango) { |
93 | g_object_unref(buffer->pango); | 114 | g_object_unref(buffer->pango); |
94 | } | 115 | } |
116 | if (buffer->data) { | ||
117 | munmap(buffer->data, buffer->size); | ||
118 | } | ||
95 | memset(buffer, 0, sizeof(struct pool_buffer)); | 119 | memset(buffer, 0, sizeof(struct pool_buffer)); |
96 | } | 120 | } |
97 | 121 | ||
diff --git a/include/pool-buffer.h b/include/pool-buffer.h index 856f7c8c..54f5be06 100644 --- a/include/pool-buffer.h +++ b/include/pool-buffer.h | |||
@@ -12,6 +12,8 @@ struct pool_buffer { | |||
12 | cairo_t *cairo; | 12 | cairo_t *cairo; |
13 | PangoContext *pango; | 13 | PangoContext *pango; |
14 | uint32_t width, height; | 14 | uint32_t width, height; |
15 | void *data; | ||
16 | size_t size; | ||
15 | bool busy; | 17 | bool busy; |
16 | }; | 18 | }; |
17 | 19 | ||
diff --git a/include/sway/commands.h b/include/sway/commands.h index d39ac56c..365068ae 100644 --- a/include/sway/commands.h +++ b/include/sway/commands.h | |||
@@ -144,6 +144,7 @@ sway_cmd cmd_splitt; | |||
144 | sway_cmd cmd_splitv; | 144 | sway_cmd cmd_splitv; |
145 | sway_cmd cmd_sticky; | 145 | sway_cmd cmd_sticky; |
146 | sway_cmd cmd_swaybg_command; | 146 | sway_cmd cmd_swaybg_command; |
147 | sway_cmd cmd_swap; | ||
147 | sway_cmd cmd_title_format; | 148 | sway_cmd cmd_title_format; |
148 | sway_cmd cmd_unmark; | 149 | sway_cmd cmd_unmark; |
149 | sway_cmd cmd_workspace; | 150 | sway_cmd cmd_workspace; |
diff --git a/include/sway/config.h b/include/sway/config.h index 33f52156..118981e3 100644 --- a/include/sway/config.h +++ b/include/sway/config.h | |||
@@ -28,6 +28,7 @@ struct sway_variable { | |||
28 | struct sway_binding { | 28 | struct sway_binding { |
29 | int order; | 29 | int order; |
30 | bool release; | 30 | bool release; |
31 | bool locked; | ||
31 | bool bindcode; | 32 | bool bindcode; |
32 | list_t *keys; | 33 | list_t *keys; |
33 | uint32_t modifiers; | 34 | uint32_t modifiers; |
diff --git a/include/sway/criteria.h b/include/sway/criteria.h index 74da132c..bd3ca0ac 100644 --- a/include/sway/criteria.h +++ b/include/sway/criteria.h | |||
@@ -18,6 +18,7 @@ struct criteria { | |||
18 | char *target; // workspace or output name for `assign` criteria | 18 | char *target; // workspace or output name for `assign` criteria |
19 | 19 | ||
20 | pcre *title; | 20 | pcre *title; |
21 | pcre *shell; | ||
21 | pcre *app_id; | 22 | pcre *app_id; |
22 | pcre *class; | 23 | pcre *class; |
23 | pcre *instance; | 24 | pcre *instance; |
diff --git a/include/sway/tree/layout.h b/include/sway/tree/layout.h index cc999871..2e0f2abf 100644 --- a/include/sway/tree/layout.h +++ b/include/sway/tree/layout.h | |||
@@ -69,4 +69,6 @@ struct sway_container *container_split(struct sway_container *child, | |||
69 | void container_recursive_resize(struct sway_container *container, | 69 | void container_recursive_resize(struct sway_container *container, |
70 | double amount, enum resize_edge edge); | 70 | double amount, enum resize_edge edge); |
71 | 71 | ||
72 | void container_swap(struct sway_container *con1, struct sway_container *con2); | ||
73 | |||
72 | #endif | 74 | #endif |
diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index 0fb8f1b3..a8bf4955 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h | |||
@@ -201,7 +201,7 @@ const char *view_get_window_role(struct sway_view *view); | |||
201 | 201 | ||
202 | uint32_t view_get_window_type(struct sway_view *view); | 202 | uint32_t view_get_window_type(struct sway_view *view); |
203 | 203 | ||
204 | const char *view_get_type(struct sway_view *view); | 204 | const char *view_get_shell(struct sway_view *view); |
205 | 205 | ||
206 | void view_configure(struct sway_view *view, double ox, double oy, int width, | 206 | void view_configure(struct sway_view *view, double ox, double oy, int width, |
207 | int height); | 207 | int height); |
diff --git a/include/swaylock/swaylock.h b/include/swaylock/swaylock.h index dae823b8..2931fd61 100644 --- a/include/swaylock/swaylock.h +++ b/include/swaylock/swaylock.h | |||
@@ -56,6 +56,7 @@ struct swaylock_surface { | |||
56 | struct zwlr_layer_surface_v1 *layer_surface; | 56 | struct zwlr_layer_surface_v1 *layer_surface; |
57 | struct pool_buffer buffers[2]; | 57 | struct pool_buffer buffers[2]; |
58 | struct pool_buffer *current_buffer; | 58 | struct pool_buffer *current_buffer; |
59 | bool frame_pending, dirty; | ||
59 | uint32_t width, height; | 60 | uint32_t width, height; |
60 | int32_t scale; | 61 | int32_t scale; |
61 | char *output_name; | 62 | char *output_name; |
@@ -74,5 +75,7 @@ void swaylock_handle_key(struct swaylock_state *state, | |||
74 | xkb_keysym_t keysym, uint32_t codepoint); | 75 | xkb_keysym_t keysym, uint32_t codepoint); |
75 | void render_frame(struct swaylock_surface *surface); | 76 | void render_frame(struct swaylock_surface *surface); |
76 | void render_frames(struct swaylock_state *state); | 77 | void render_frames(struct swaylock_state *state); |
78 | void damage_surface(struct swaylock_surface *surface); | ||
79 | void damage_state(struct swaylock_state *state); | ||
77 | 80 | ||
78 | #endif | 81 | #endif |
diff --git a/sway/commands.c b/sway/commands.c index 6cba0a1c..c3728afd 100644 --- a/sway/commands.c +++ b/sway/commands.c | |||
@@ -186,6 +186,7 @@ static struct cmd_handler command_handlers[] = { | |||
186 | { "splith", cmd_splith }, | 186 | { "splith", cmd_splith }, |
187 | { "splitt", cmd_splitt }, | 187 | { "splitt", cmd_splitt }, |
188 | { "splitv", cmd_splitv }, | 188 | { "splitv", cmd_splitv }, |
189 | { "swap", cmd_swap }, | ||
189 | { "title_format", cmd_title_format }, | 190 | { "title_format", cmd_title_format }, |
190 | { "unmark", cmd_unmark }, | 191 | { "unmark", cmd_unmark }, |
191 | }; | 192 | }; |
diff --git a/sway/commands/bind.c b/sway/commands/bind.c index cbabb07b..c6b3368a 100644 --- a/sway/commands/bind.c +++ b/sway/commands/bind.c | |||
@@ -83,20 +83,26 @@ struct cmd_results *cmd_bindsym(int argc, char **argv) { | |||
83 | binding->keys = create_list(); | 83 | binding->keys = create_list(); |
84 | binding->modifiers = 0; | 84 | binding->modifiers = 0; |
85 | binding->release = false; | 85 | binding->release = false; |
86 | binding->locked = false; | ||
86 | binding->bindcode = false; | 87 | binding->bindcode = false; |
87 | 88 | ||
88 | // Handle --release | 89 | // Handle --release and --locked |
89 | if (strcmp("--release", argv[0]) == 0) { | 90 | while (argc > 0) { |
90 | if (argc >= 3) { | 91 | if (strcmp("--release", argv[0]) == 0) { |
91 | binding->release = true; | 92 | binding->release = true; |
92 | argv++; | 93 | } else if (strcmp("--locked", argv[0]) == 0) { |
93 | argc--; | 94 | binding->locked = true; |
94 | } else { | 95 | } else { |
95 | free_sway_binding(binding); | 96 | break; |
96 | return cmd_results_new(CMD_FAILURE, "bindsym", | ||
97 | "Invalid bindsym command " | ||
98 | "(expected more than 2 arguments, got %d)", argc); | ||
99 | } | 97 | } |
98 | argv++; | ||
99 | argc--; | ||
100 | } | ||
101 | if (argc < 2) { | ||
102 | free_sway_binding(binding); | ||
103 | return cmd_results_new(CMD_FAILURE, "bindsym", | ||
104 | "Invalid bindsym command " | ||
105 | "(expected at least 2 non-option arguments, got %d)", argc); | ||
100 | } | 106 | } |
101 | 107 | ||
102 | binding->command = join_args(argv + 1, argc - 1); | 108 | binding->command = join_args(argv + 1, argc - 1); |
@@ -176,20 +182,26 @@ struct cmd_results *cmd_bindcode(int argc, char **argv) { | |||
176 | binding->keys = create_list(); | 182 | binding->keys = create_list(); |
177 | binding->modifiers = 0; | 183 | binding->modifiers = 0; |
178 | binding->release = false; | 184 | binding->release = false; |
185 | binding->locked = false; | ||
179 | binding->bindcode = true; | 186 | binding->bindcode = true; |
180 | 187 | ||
181 | // Handle --release | 188 | // Handle --release and --locked |
182 | if (strcmp("--release", argv[0]) == 0) { | 189 | while (argc > 0) { |
183 | if (argc >= 3) { | 190 | if (strcmp("--release", argv[0]) == 0) { |
184 | binding->release = true; | 191 | binding->release = true; |
185 | argv++; | 192 | } else if (strcmp("--locked", argv[0]) == 0) { |
186 | argc--; | 193 | binding->locked = true; |
187 | } else { | 194 | } else { |
188 | free_sway_binding(binding); | 195 | break; |
189 | return cmd_results_new(CMD_FAILURE, "bindcode", | ||
190 | "Invalid bindcode command " | ||
191 | "(expected more than 2 arguments, got %d)", argc); | ||
192 | } | 196 | } |
197 | argv++; | ||
198 | argc--; | ||
199 | } | ||
200 | if (argc < 2) { | ||
201 | free_sway_binding(binding); | ||
202 | return cmd_results_new(CMD_FAILURE, "bindcode", | ||
203 | "Invalid bindcode command " | ||
204 | "(expected at least 2 non-option arguments, got %d)", argc); | ||
193 | } | 205 | } |
194 | 206 | ||
195 | binding->command = join_args(argv + 1, argc - 1); | 207 | binding->command = join_args(argv + 1, argc - 1); |
diff --git a/sway/commands/swap.c b/sway/commands/swap.c new file mode 100644 index 00000000..e925ad33 --- /dev/null +++ b/sway/commands/swap.c | |||
@@ -0,0 +1,80 @@ | |||
1 | #include <strings.h> | ||
2 | #include <wlr/util/log.h> | ||
3 | #include "sway/commands.h" | ||
4 | #include "sway/tree/layout.h" | ||
5 | #include "sway/tree/view.h" | ||
6 | #include "stringop.h" | ||
7 | |||
8 | static const char* EXPECTED_SYNTAX = | ||
9 | "Expected 'swap container with id|con_id|mark <arg>'"; | ||
10 | |||
11 | static bool test_con_id(struct sway_container *container, void *con_id) { | ||
12 | return container->id == (size_t)con_id; | ||
13 | } | ||
14 | |||
15 | static bool test_id(struct sway_container *container, void *id) { | ||
16 | xcb_window_t *wid = id; | ||
17 | return (container->type == C_VIEW | ||
18 | && container->sway_view->type == SWAY_VIEW_XWAYLAND | ||
19 | && container->sway_view->wlr_xwayland_surface->window_id == *wid); | ||
20 | } | ||
21 | |||
22 | static bool test_mark(struct sway_container *container, void *mark) { | ||
23 | if (container->type == C_VIEW && container->sway_view->marks->length) { | ||
24 | return !list_seq_find(container->sway_view->marks, | ||
25 | (int (*)(const void *, const void *))strcmp, mark); | ||
26 | } | ||
27 | return false; | ||
28 | } | ||
29 | |||
30 | struct cmd_results *cmd_swap(int argc, char **argv) { | ||
31 | struct cmd_results *error = NULL; | ||
32 | if ((error = checkarg(argc, "swap", EXPECTED_AT_LEAST, 4))) { | ||
33 | return error; | ||
34 | } | ||
35 | |||
36 | if (strcasecmp(argv[0], "container") || strcasecmp(argv[1], "with")) { | ||
37 | return cmd_results_new(CMD_INVALID, "swap", EXPECTED_SYNTAX); | ||
38 | } | ||
39 | |||
40 | struct sway_container *current = config->handler_context.current_container; | ||
41 | struct sway_container *other; | ||
42 | |||
43 | char *value = join_args(argv + 3, argc - 3); | ||
44 | if (strcasecmp(argv[2], "id") == 0) { | ||
45 | xcb_window_t id = strtol(value, NULL, 0); | ||
46 | other = container_find(&root_container, test_id, (void *)&id); | ||
47 | } else if (strcasecmp(argv[2], "con_id") == 0) { | ||
48 | size_t con_id = atoi(value); | ||
49 | other = container_find(&root_container, test_con_id, (void *)con_id); | ||
50 | } else if (strcasecmp(argv[2], "mark") == 0) { | ||
51 | other = container_find(&root_container, test_mark, (void *)value); | ||
52 | } else { | ||
53 | free(value); | ||
54 | return cmd_results_new(CMD_INVALID, "swap", EXPECTED_SYNTAX); | ||
55 | } | ||
56 | |||
57 | if (!other) { | ||
58 | error = cmd_results_new(CMD_FAILURE, "swap", | ||
59 | "Failed to find %s '%s'", argv[2], value); | ||
60 | } else if (current->type < C_CONTAINER || other->type < C_CONTAINER) { | ||
61 | error = cmd_results_new(CMD_FAILURE, "swap", | ||
62 | "Can only swap with containers and views"); | ||
63 | } else if (container_has_anscestor(current, other) | ||
64 | || container_has_anscestor(other, current)) { | ||
65 | error = cmd_results_new(CMD_FAILURE, "swap", | ||
66 | "Cannot swap ancestor and descendant"); | ||
67 | } else if (current->layout == L_FLOATING || other->layout == L_FLOATING) { | ||
68 | error = cmd_results_new(CMD_FAILURE, "swap", | ||
69 | "Swapping with floating containers is not supported"); | ||
70 | } | ||
71 | |||
72 | free(value); | ||
73 | |||
74 | if (error) { | ||
75 | return error; | ||
76 | } | ||
77 | |||
78 | container_swap(current, other); | ||
79 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | ||
80 | } | ||
diff --git a/sway/criteria.c b/sway/criteria.c index 4295cacc..dec5fed7 100644 --- a/sway/criteria.c +++ b/sway/criteria.c | |||
@@ -13,6 +13,7 @@ | |||
13 | 13 | ||
14 | bool criteria_is_empty(struct criteria *criteria) { | 14 | bool criteria_is_empty(struct criteria *criteria) { |
15 | return !criteria->title | 15 | return !criteria->title |
16 | && !criteria->shell | ||
16 | && !criteria->app_id | 17 | && !criteria->app_id |
17 | && !criteria->class | 18 | && !criteria->class |
18 | && !criteria->instance | 19 | && !criteria->instance |
@@ -29,6 +30,7 @@ bool criteria_is_empty(struct criteria *criteria) { | |||
29 | 30 | ||
30 | void criteria_destroy(struct criteria *criteria) { | 31 | void criteria_destroy(struct criteria *criteria) { |
31 | pcre_free(criteria->title); | 32 | pcre_free(criteria->title); |
33 | pcre_free(criteria->shell); | ||
32 | pcre_free(criteria->app_id); | 34 | pcre_free(criteria->app_id); |
33 | pcre_free(criteria->class); | 35 | pcre_free(criteria->class); |
34 | pcre_free(criteria->instance); | 36 | pcre_free(criteria->instance); |
@@ -53,6 +55,13 @@ static bool criteria_matches_view(struct criteria *criteria, | |||
53 | } | 55 | } |
54 | } | 56 | } |
55 | 57 | ||
58 | if (criteria->shell) { | ||
59 | const char *shell = view_get_shell(view); | ||
60 | if (!shell || regex_cmp(shell, criteria->shell) != 0) { | ||
61 | return false; | ||
62 | } | ||
63 | } | ||
64 | |||
56 | if (criteria->app_id) { | 65 | if (criteria->app_id) { |
57 | const char *app_id = view_get_app_id(view); | 66 | const char *app_id = view_get_app_id(view); |
58 | if (!app_id || regex_cmp(app_id, criteria->app_id) != 0) { | 67 | if (!app_id || regex_cmp(app_id, criteria->app_id) != 0) { |
@@ -206,6 +215,7 @@ enum criteria_token { | |||
206 | T_FLOATING, | 215 | T_FLOATING, |
207 | T_ID, | 216 | T_ID, |
208 | T_INSTANCE, | 217 | T_INSTANCE, |
218 | T_SHELL, | ||
209 | T_TILING, | 219 | T_TILING, |
210 | T_TITLE, | 220 | T_TITLE, |
211 | T_URGENT, | 221 | T_URGENT, |
@@ -229,6 +239,8 @@ static enum criteria_token token_from_name(char *name) { | |||
229 | return T_ID; | 239 | return T_ID; |
230 | } else if (strcmp(name, "instance") == 0) { | 240 | } else if (strcmp(name, "instance") == 0) { |
231 | return T_INSTANCE; | 241 | return T_INSTANCE; |
242 | } else if (strcmp(name, "shell") == 0) { | ||
243 | return T_SHELL; | ||
232 | } else if (strcmp(name, "title") == 0) { | 244 | } else if (strcmp(name, "title") == 0) { |
233 | return T_TITLE; | 245 | return T_TITLE; |
234 | } else if (strcmp(name, "urgent") == 0) { | 246 | } else if (strcmp(name, "urgent") == 0) { |
@@ -271,6 +283,9 @@ static char *get_focused_prop(enum criteria_token token) { | |||
271 | case T_INSTANCE: | 283 | case T_INSTANCE: |
272 | value = view_get_instance(view); | 284 | value = view_get_instance(view); |
273 | break; | 285 | break; |
286 | case T_SHELL: | ||
287 | value = view_get_shell(view); | ||
288 | break; | ||
274 | case T_TITLE: | 289 | case T_TITLE: |
275 | value = view_get_class(view); | 290 | value = view_get_class(view); |
276 | break; | 291 | break; |
@@ -332,6 +347,9 @@ static bool parse_token(struct criteria *criteria, char *name, char *value) { | |||
332 | case T_TITLE: | 347 | case T_TITLE: |
333 | generate_regex(&criteria->title, effective_value); | 348 | generate_regex(&criteria->title, effective_value); |
334 | break; | 349 | break; |
350 | case T_SHELL: | ||
351 | generate_regex(&criteria->shell, effective_value); | ||
352 | break; | ||
335 | case T_APP_ID: | 353 | case T_APP_ID: |
336 | generate_regex(&criteria->app_id, effective_value); | 354 | generate_regex(&criteria->app_id, effective_value); |
337 | break; | 355 | break; |
diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index 9a0d282b..b2b95fa0 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c | |||
@@ -3,13 +3,14 @@ | |||
3 | #include <stdlib.h> | 3 | #include <stdlib.h> |
4 | #include <wayland-server.h> | 4 | #include <wayland-server.h> |
5 | #include <wlr/types/wlr_xdg_shell.h> | 5 | #include <wlr/types/wlr_xdg_shell.h> |
6 | #include <wlr/util/edges.h> | ||
7 | #include "log.h" | ||
8 | #include "sway/input/input-manager.h" | ||
9 | #include "sway/input/seat.h" | ||
10 | #include "sway/server.h" | ||
6 | #include "sway/tree/container.h" | 11 | #include "sway/tree/container.h" |
7 | #include "sway/tree/layout.h" | 12 | #include "sway/tree/layout.h" |
8 | #include "sway/server.h" | ||
9 | #include "sway/tree/view.h" | 13 | #include "sway/tree/view.h" |
10 | #include "sway/input/seat.h" | ||
11 | #include "sway/input/input-manager.h" | ||
12 | #include "log.h" | ||
13 | 14 | ||
14 | static const struct sway_view_child_impl popup_impl; | 15 | static const struct sway_view_child_impl popup_impl; |
15 | 16 | ||
@@ -248,7 +249,8 @@ void handle_xdg_shell_surface(struct wl_listener *listener, void *data) { | |||
248 | wlr_log(L_DEBUG, "New xdg_shell toplevel title='%s' app_id='%s'", | 249 | wlr_log(L_DEBUG, "New xdg_shell toplevel title='%s' app_id='%s'", |
249 | xdg_surface->toplevel->title, xdg_surface->toplevel->app_id); | 250 | xdg_surface->toplevel->title, xdg_surface->toplevel->app_id); |
250 | wlr_xdg_surface_ping(xdg_surface); | 251 | wlr_xdg_surface_ping(xdg_surface); |
251 | wlr_xdg_toplevel_set_maximized(xdg_surface, true); | 252 | wlr_xdg_toplevel_set_tiled(xdg_surface, WLR_EDGE_LEFT | WLR_EDGE_RIGHT | |
253 | WLR_EDGE_TOP | WLR_EDGE_BOTTOM); | ||
252 | 254 | ||
253 | struct sway_xdg_shell_view *xdg_shell_view = | 255 | struct sway_xdg_shell_view *xdg_shell_view = |
254 | calloc(1, sizeof(struct sway_xdg_shell_view)); | 256 | calloc(1, sizeof(struct sway_xdg_shell_view)); |
diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c index c07557db..e873eea3 100644 --- a/sway/input/keyboard.c +++ b/sway/input/keyboard.c | |||
@@ -141,7 +141,7 @@ static bool keyboard_execute_compositor_binding(struct sway_keyboard *keyboard, | |||
141 | */ | 141 | */ |
142 | static bool keyboard_execute_bindsym(struct sway_keyboard *keyboard, | 142 | static bool keyboard_execute_bindsym(struct sway_keyboard *keyboard, |
143 | xkb_keysym_t *pressed_keysyms, uint32_t modifiers, | 143 | xkb_keysym_t *pressed_keysyms, uint32_t modifiers, |
144 | enum wlr_key_state key_state) { | 144 | enum wlr_key_state key_state, bool locked) { |
145 | // configured bindings | 145 | // configured bindings |
146 | int n = pressed_keysyms_length(pressed_keysyms); | 146 | int n = pressed_keysyms_length(pressed_keysyms); |
147 | list_t *keysym_bindings = config->current_mode->keysym_bindings; | 147 | list_t *keysym_bindings = config->current_mode->keysym_bindings; |
@@ -149,7 +149,7 @@ static bool keyboard_execute_bindsym(struct sway_keyboard *keyboard, | |||
149 | struct sway_binding *binding = keysym_bindings->items[i]; | 149 | struct sway_binding *binding = keysym_bindings->items[i]; |
150 | if (!binding_matches_key_state(binding, key_state) || | 150 | if (!binding_matches_key_state(binding, key_state) || |
151 | modifiers ^ binding->modifiers || | 151 | modifiers ^ binding->modifiers || |
152 | n != binding->keys->length) { | 152 | n != binding->keys->length || locked > binding->locked) { |
153 | continue; | 153 | continue; |
154 | } | 154 | } |
155 | 155 | ||
@@ -174,7 +174,7 @@ static bool keyboard_execute_bindsym(struct sway_keyboard *keyboard, | |||
174 | } | 174 | } |
175 | 175 | ||
176 | static bool binding_matches_keycodes(struct wlr_keyboard *keyboard, | 176 | static bool binding_matches_keycodes(struct wlr_keyboard *keyboard, |
177 | struct sway_binding *binding, struct wlr_event_keyboard_key *event) { | 177 | struct sway_binding *binding, struct wlr_event_keyboard_key *event, bool locked) { |
178 | assert(binding->bindcode); | 178 | assert(binding->bindcode); |
179 | 179 | ||
180 | uint32_t keycode = event->keycode + 8; | 180 | uint32_t keycode = event->keycode + 8; |
@@ -183,6 +183,10 @@ static bool binding_matches_keycodes(struct wlr_keyboard *keyboard, | |||
183 | return false; | 183 | return false; |
184 | } | 184 | } |
185 | 185 | ||
186 | if (locked > binding->locked) { | ||
187 | return false; | ||
188 | } | ||
189 | |||
186 | uint32_t modifiers = wlr_keyboard_get_modifiers(keyboard); | 190 | uint32_t modifiers = wlr_keyboard_get_modifiers(keyboard); |
187 | if (modifiers ^ binding->modifiers) { | 191 | if (modifiers ^ binding->modifiers) { |
188 | return false; | 192 | return false; |
@@ -265,13 +269,13 @@ static bool binding_matches_keycodes(struct wlr_keyboard *keyboard, | |||
265 | * should be propagated to clients. | 269 | * should be propagated to clients. |
266 | */ | 270 | */ |
267 | static bool keyboard_execute_bindcode(struct sway_keyboard *keyboard, | 271 | static bool keyboard_execute_bindcode(struct sway_keyboard *keyboard, |
268 | struct wlr_event_keyboard_key *event) { | 272 | struct wlr_event_keyboard_key *event, bool locked) { |
269 | struct wlr_keyboard *wlr_keyboard = | 273 | struct wlr_keyboard *wlr_keyboard = |
270 | keyboard->seat_device->input_device->wlr_device->keyboard; | 274 | keyboard->seat_device->input_device->wlr_device->keyboard; |
271 | list_t *keycode_bindings = config->current_mode->keycode_bindings; | 275 | list_t *keycode_bindings = config->current_mode->keycode_bindings; |
272 | for (int i = 0; i < keycode_bindings->length; ++i) { | 276 | for (int i = 0; i < keycode_bindings->length; ++i) { |
273 | struct sway_binding *binding = keycode_bindings->items[i]; | 277 | struct sway_binding *binding = keycode_bindings->items[i]; |
274 | if (binding_matches_keycodes(wlr_keyboard, binding, event)) { | 278 | if (binding_matches_keycodes(wlr_keyboard, binding, event, locked)) { |
275 | keyboard_execute_command(keyboard, binding); | 279 | keyboard_execute_command(keyboard, binding); |
276 | return true; | 280 | return true; |
277 | } | 281 | } |
@@ -333,19 +337,20 @@ static void handle_keyboard_key(struct wl_listener *listener, void *data) { | |||
333 | keyboard->seat_device->input_device->wlr_device; | 337 | keyboard->seat_device->input_device->wlr_device; |
334 | wlr_idle_notify_activity(keyboard->seat_device->sway_seat->input->server->idle, wlr_seat); | 338 | wlr_idle_notify_activity(keyboard->seat_device->sway_seat->input->server->idle, wlr_seat); |
335 | struct wlr_event_keyboard_key *event = data; | 339 | struct wlr_event_keyboard_key *event = data; |
340 | bool input_inhibited = keyboard->seat_device->sway_seat->exclusive_client != NULL; | ||
336 | 341 | ||
337 | xkb_keycode_t keycode = event->keycode + 8; | 342 | xkb_keycode_t keycode = event->keycode + 8; |
338 | bool handled = false; | 343 | bool handled = false; |
339 | 344 | ||
340 | // handle keycodes | 345 | // handle keycodes |
341 | handled = keyboard_execute_bindcode(keyboard, event); | 346 | handled = keyboard_execute_bindcode(keyboard, event, input_inhibited); |
342 | 347 | ||
343 | // handle translated keysyms | 348 | // handle translated keysyms |
344 | if (!handled && event->state == WLR_KEY_RELEASED) { | 349 | if (!handled && event->state == WLR_KEY_RELEASED) { |
345 | handled = keyboard_execute_bindsym(keyboard, | 350 | handled = keyboard_execute_bindsym(keyboard, |
346 | keyboard->pressed_keysyms_translated, | 351 | keyboard->pressed_keysyms_translated, |
347 | keyboard->modifiers_translated, | 352 | keyboard->modifiers_translated, |
348 | event->state); | 353 | event->state, input_inhibited); |
349 | } | 354 | } |
350 | const xkb_keysym_t *translated_keysyms; | 355 | const xkb_keysym_t *translated_keysyms; |
351 | size_t translated_keysyms_len = | 356 | size_t translated_keysyms_len = |
@@ -357,14 +362,14 @@ static void handle_keyboard_key(struct wl_listener *listener, void *data) { | |||
357 | handled = keyboard_execute_bindsym(keyboard, | 362 | handled = keyboard_execute_bindsym(keyboard, |
358 | keyboard->pressed_keysyms_translated, | 363 | keyboard->pressed_keysyms_translated, |
359 | keyboard->modifiers_translated, | 364 | keyboard->modifiers_translated, |
360 | event->state); | 365 | event->state, input_inhibited); |
361 | } | 366 | } |
362 | 367 | ||
363 | // Handle raw keysyms | 368 | // Handle raw keysyms |
364 | if (!handled && event->state == WLR_KEY_RELEASED) { | 369 | if (!handled && event->state == WLR_KEY_RELEASED) { |
365 | handled = keyboard_execute_bindsym(keyboard, | 370 | handled = keyboard_execute_bindsym(keyboard, |
366 | keyboard->pressed_keysyms_raw, keyboard->modifiers_raw, | 371 | keyboard->pressed_keysyms_raw, keyboard->modifiers_raw, |
367 | event->state); | 372 | event->state, input_inhibited); |
368 | } | 373 | } |
369 | const xkb_keysym_t *raw_keysyms; | 374 | const xkb_keysym_t *raw_keysyms; |
370 | size_t raw_keysyms_len = | 375 | size_t raw_keysyms_len = |
@@ -374,7 +379,7 @@ static void handle_keyboard_key(struct wl_listener *listener, void *data) { | |||
374 | if (!handled && event->state == WLR_KEY_PRESSED) { | 379 | if (!handled && event->state == WLR_KEY_PRESSED) { |
375 | handled = keyboard_execute_bindsym(keyboard, | 380 | handled = keyboard_execute_bindsym(keyboard, |
376 | keyboard->pressed_keysyms_raw, keyboard->modifiers_raw, | 381 | keyboard->pressed_keysyms_raw, keyboard->modifiers_raw, |
377 | event->state); | 382 | event->state, input_inhibited); |
378 | } | 383 | } |
379 | 384 | ||
380 | // Compositor bindings | 385 | // Compositor bindings |
diff --git a/sway/meson.build b/sway/meson.build index 72347d51..9c942e8e 100644 --- a/sway/meson.build +++ b/sway/meson.build | |||
@@ -63,6 +63,7 @@ sway_sources = files( | |||
63 | 'commands/show_marks.c', | 63 | 'commands/show_marks.c', |
64 | 'commands/split.c', | 64 | 'commands/split.c', |
65 | 'commands/swaybg_command.c', | 65 | 'commands/swaybg_command.c', |
66 | 'commands/swap.c', | ||
66 | 'commands/title_format.c', | 67 | 'commands/title_format.c', |
67 | 'commands/unmark.c', | 68 | 'commands/unmark.c', |
68 | 'commands/workspace.c', | 69 | 'commands/workspace.c', |
diff --git a/sway/sway.5.scd b/sway/sway.5.scd index ff138562..5d99c9d6 100644 --- a/sway/sway.5.scd +++ b/sway/sway.5.scd | |||
@@ -167,6 +167,15 @@ They are expected to be used with *bindsym* or at runtime through *swaymsg*(1). | |||
167 | "Sticks" a floating window to the current output so that it shows up on all | 167 | "Sticks" a floating window to the current output so that it shows up on all |
168 | workspaces. | 168 | workspaces. |
169 | 169 | ||
170 | *swap* container with id|con\_id|mark <arg> | ||
171 | Swaps the position, geometry, and fullscreen status of two containers. The | ||
172 | first container can be selected either by criteria or focus. The second | ||
173 | container can be selected by _id_, _con\_id_, or _mark_. _id_ can only be | ||
174 | used with xwayland views. If the first container has focus, it will retain | ||
175 | focus unless it is moved to a different workspace or the second container | ||
176 | becomes fullscreen on the same workspace as the first container. In either | ||
177 | of those cases, the second container will gain focus. | ||
178 | |||
170 | The following commands may be used either in the configuration file or at | 179 | The following commands may be used either in the configuration file or at |
171 | runtime. | 180 | runtime. |
172 | 181 | ||
@@ -177,17 +186,20 @@ runtime. | |||
177 | 186 | ||
178 | for\_window <criteria> move container to workspace <workspace> | 187 | for\_window <criteria> move container to workspace <workspace> |
179 | 188 | ||
180 | *bindsym* <key combo> <command> | 189 | *bindsym* [--release|--locked] <key combo> <command> |
181 | Binds _key combo_ to execute the sway command _command_ when pressed. You | 190 | Binds _key combo_ to execute the sway command _command_ when pressed. You |
182 | may use XKB key names here (*xev*(1) is a good tool for discovering these). | 191 | may use XKB key names here (*xev*(1) is a good tool for discovering these). |
192 | With the flag _--release_, the command is executed when the key combo is | ||
193 | released. Unless the flag _--locked_ is set, the command will not be run | ||
194 | when a screen locking program is active. | ||
183 | 195 | ||
184 | Example: | 196 | Example: |
185 | 197 | ||
186 | # Execute firefox when alt, shift, and f are pressed together | 198 | # Execute firefox when alt, shift, and f are pressed together |
187 | bindsym Mod1+Shift+f exec firefox | 199 | bindsym Mod1+Shift+f exec firefox |
188 | 200 | ||
189 | *bindcode* <code> <command> is also available for binding with key codes | 201 | *bindcode* [--release|--locked] <code> <command> is also available for |
190 | instead of key names. | 202 | binding with key codes instead of key names. |
191 | 203 | ||
192 | *client.<class>* <border> <background> <text> <indicator> <child\_border> | 204 | *client.<class>* <border> <background> <text> <indicator> <child\_border> |
193 | Configures the color of window borders and title bars. All 5 colors are | 205 | Configures the color of window borders and title bars. All 5 colors are |
@@ -551,6 +563,11 @@ The following attributes may be matched with: | |||
551 | value is \_\_focused\_\_, then the window instance must be the same as that | 563 | value is \_\_focused\_\_, then the window instance must be the same as that |
552 | of the currently focused window. | 564 | of the currently focused window. |
553 | 565 | ||
566 | *shell* | ||
567 | Compare value against the window shell, such as "xdg\_shell" or "xwayland". | ||
568 | Can be a regular expression. If value is \_\_focused\_\_, then the shell | ||
569 | must be the same as that of the currently focused window. | ||
570 | |||
554 | *tiling* | 571 | *tiling* |
555 | Matches tiling windows. | 572 | Matches tiling windows. |
556 | 573 | ||
diff --git a/sway/tree/container.c b/sway/tree/container.c index f29a9adc..a4798c7e 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c | |||
@@ -176,45 +176,6 @@ static void _container_destroy(struct sway_container *cont) { | |||
176 | free(cont); | 176 | free(cont); |
177 | } | 177 | } |
178 | 178 | ||
179 | static struct sway_container *container_output_destroy( | ||
180 | struct sway_container *output) { | ||
181 | if (!sway_assert(output, "cannot destroy null output")) { | ||
182 | return NULL; | ||
183 | } | ||
184 | |||
185 | if (output->children->length > 0) { | ||
186 | // TODO save workspaces when there are no outputs. | ||
187 | // TODO also check if there will ever be no outputs except for exiting | ||
188 | // program | ||
189 | if (root_container.children->length > 1) { | ||
190 | int p = root_container.children->items[0] == output; | ||
191 | // Move workspace from this output to another output | ||
192 | while (output->children->length) { | ||
193 | struct sway_container *child = output->children->items[0]; | ||
194 | container_remove_child(child); | ||
195 | container_add_child(root_container.children->items[p], child); | ||
196 | } | ||
197 | container_sort_workspaces(root_container.children->items[p]); | ||
198 | arrange_output(root_container.children->items[p]); | ||
199 | } | ||
200 | } | ||
201 | |||
202 | wl_list_remove(&output->sway_output->destroy.link); | ||
203 | wl_list_remove(&output->sway_output->mode.link); | ||
204 | wl_list_remove(&output->sway_output->transform.link); | ||
205 | wl_list_remove(&output->sway_output->scale.link); | ||
206 | |||
207 | wl_list_remove(&output->sway_output->damage_destroy.link); | ||
208 | wl_list_remove(&output->sway_output->damage_frame.link); | ||
209 | |||
210 | // clear the wlr_output reference to this container | ||
211 | output->sway_output->wlr_output->data = NULL; | ||
212 | |||
213 | wlr_log(L_DEBUG, "OUTPUT: Destroying output '%s'", output->name); | ||
214 | _container_destroy(output); | ||
215 | return &root_container; | ||
216 | } | ||
217 | |||
218 | static struct sway_container *container_workspace_destroy( | 179 | static struct sway_container *container_workspace_destroy( |
219 | struct sway_container *workspace) { | 180 | struct sway_container *workspace) { |
220 | if (!sway_assert(workspace, "cannot destroy null workspace")) { | 181 | if (!sway_assert(workspace, "cannot destroy null workspace")) { |
@@ -232,7 +193,7 @@ static struct sway_container *container_workspace_destroy( | |||
232 | // destroy the WS if there are no children (TODO check for floating) | 193 | // destroy the WS if there are no children (TODO check for floating) |
233 | wlr_log(L_DEBUG, "destroying workspace '%s'", workspace->name); | 194 | wlr_log(L_DEBUG, "destroying workspace '%s'", workspace->name); |
234 | ipc_event_workspace(workspace, NULL, "empty"); | 195 | ipc_event_workspace(workspace, NULL, "empty"); |
235 | } else { | 196 | } else if (output) { |
236 | // Move children to a different workspace on this output | 197 | // Move children to a different workspace on this output |
237 | struct sway_container *new_workspace = NULL; | 198 | struct sway_container *new_workspace = NULL; |
238 | // TODO move floating | 199 | // TODO move floating |
@@ -253,11 +214,62 @@ static struct sway_container *container_workspace_destroy( | |||
253 | free(workspace->sway_workspace); | 214 | free(workspace->sway_workspace); |
254 | _container_destroy(workspace); | 215 | _container_destroy(workspace); |
255 | 216 | ||
256 | output_damage_whole(output->sway_output); | 217 | if (output) { |
218 | output_damage_whole(output->sway_output); | ||
219 | } | ||
257 | 220 | ||
258 | return parent; | 221 | return parent; |
259 | } | 222 | } |
260 | 223 | ||
224 | static struct sway_container *container_output_destroy( | ||
225 | struct sway_container *output) { | ||
226 | if (!sway_assert(output, "cannot destroy null output")) { | ||
227 | return NULL; | ||
228 | } | ||
229 | |||
230 | if (output->children->length > 0) { | ||
231 | // TODO save workspaces when there are no outputs. | ||
232 | // TODO also check if there will ever be no outputs except for exiting | ||
233 | // program | ||
234 | if (root_container.children->length > 1) { | ||
235 | // Move workspace from this output to another output | ||
236 | struct sway_container *other_output = | ||
237 | root_container.children->items[0]; | ||
238 | if (other_output == output) { | ||
239 | other_output = root_container.children->items[1]; | ||
240 | } | ||
241 | |||
242 | while (output->children->length) { | ||
243 | struct sway_container *workspace = output->children->items[0]; | ||
244 | container_remove_child(workspace); | ||
245 | if (workspace->children->length > 0) { | ||
246 | container_add_child(other_output, workspace); | ||
247 | ipc_event_workspace(workspace, NULL, "move"); | ||
248 | } else { | ||
249 | container_workspace_destroy(workspace); | ||
250 | } | ||
251 | } | ||
252 | container_sort_workspaces(other_output); | ||
253 | arrange_output(other_output); | ||
254 | } | ||
255 | } | ||
256 | |||
257 | wl_list_remove(&output->sway_output->destroy.link); | ||
258 | wl_list_remove(&output->sway_output->mode.link); | ||
259 | wl_list_remove(&output->sway_output->transform.link); | ||
260 | wl_list_remove(&output->sway_output->scale.link); | ||
261 | |||
262 | wl_list_remove(&output->sway_output->damage_destroy.link); | ||
263 | wl_list_remove(&output->sway_output->damage_frame.link); | ||
264 | |||
265 | // clear the wlr_output reference to this container | ||
266 | output->sway_output->wlr_output->data = NULL; | ||
267 | |||
268 | wlr_log(L_DEBUG, "OUTPUT: Destroying output '%s'", output->name); | ||
269 | _container_destroy(output); | ||
270 | return &root_container; | ||
271 | } | ||
272 | |||
261 | static void container_root_finish(struct sway_container *con) { | 273 | static void container_root_finish(struct sway_container *con) { |
262 | wlr_log(L_ERROR, "TODO: destroy the root container"); | 274 | wlr_log(L_ERROR, "TODO: destroy the root container"); |
263 | } | 275 | } |
diff --git a/sway/tree/layout.c b/sway/tree/layout.c index 21cec529..624d5516 100644 --- a/sway/tree/layout.c +++ b/sway/tree/layout.c | |||
@@ -882,3 +882,127 @@ void container_recursive_resize(struct sway_container *container, | |||
882 | } | 882 | } |
883 | } | 883 | } |
884 | } | 884 | } |
885 | |||
886 | static void swap_places(struct sway_container *con1, | ||
887 | struct sway_container *con2) { | ||
888 | struct sway_container *temp = malloc(sizeof(struct sway_container)); | ||
889 | temp->x = con1->x; | ||
890 | temp->y = con1->y; | ||
891 | temp->width = con1->width; | ||
892 | temp->height = con1->height; | ||
893 | temp->parent = con1->parent; | ||
894 | |||
895 | con1->x = con2->x; | ||
896 | con1->y = con2->y; | ||
897 | con1->width = con2->width; | ||
898 | con1->height = con2->height; | ||
899 | |||
900 | con2->x = temp->x; | ||
901 | con2->y = temp->y; | ||
902 | con2->width = temp->width; | ||
903 | con2->height = temp->height; | ||
904 | |||
905 | int temp_index = index_child(con1); | ||
906 | container_insert_child(con2->parent, con1, index_child(con2)); | ||
907 | container_insert_child(temp->parent, con2, temp_index); | ||
908 | |||
909 | free(temp); | ||
910 | } | ||
911 | |||
912 | static void swap_focus(struct sway_container *con1, | ||
913 | struct sway_container *con2, struct sway_seat *seat, | ||
914 | struct sway_container *focus) { | ||
915 | if (focus == con1 || focus == con2) { | ||
916 | struct sway_container *ws1 = container_parent(con1, C_WORKSPACE); | ||
917 | struct sway_container *ws2 = container_parent(con2, C_WORKSPACE); | ||
918 | if (focus == con1 && (con2->parent->layout == L_TABBED | ||
919 | || con2->parent->layout == L_STACKED)) { | ||
920 | if (workspace_is_visible(ws2)) { | ||
921 | seat_set_focus_warp(seat, con2, false); | ||
922 | } | ||
923 | seat_set_focus(seat, ws1 != ws2 ? con2 : con1); | ||
924 | } else if (focus == con2 && (con1->parent->layout == L_TABBED | ||
925 | || con1->parent->layout == L_STACKED)) { | ||
926 | if (workspace_is_visible(ws1)) { | ||
927 | seat_set_focus_warp(seat, con1, false); | ||
928 | } | ||
929 | seat_set_focus(seat, ws1 != ws2 ? con1 : con2); | ||
930 | } else if (ws1 != ws2) { | ||
931 | seat_set_focus(seat, focus == con1 ? con2 : con1); | ||
932 | } else { | ||
933 | seat_set_focus(seat, focus); | ||
934 | } | ||
935 | } else { | ||
936 | seat_set_focus(seat, focus); | ||
937 | } | ||
938 | } | ||
939 | |||
940 | void container_swap(struct sway_container *con1, struct sway_container *con2) { | ||
941 | if (!sway_assert(con1 && con2, "Cannot swap with nothing")) { | ||
942 | return; | ||
943 | } | ||
944 | if (!sway_assert(con1->type >= C_CONTAINER && con2->type >= C_CONTAINER, | ||
945 | "Can only swap containers and views")) { | ||
946 | return; | ||
947 | } | ||
948 | if (!sway_assert(!container_has_anscestor(con1, con2) | ||
949 | && !container_has_anscestor(con2, con1), | ||
950 | "Cannot swap anscestor and descendant")) { | ||
951 | return; | ||
952 | } | ||
953 | if (!sway_assert(con1->layout != L_FLOATING && con2->layout != L_FLOATING, | ||
954 | "Swapping with floating containers is not supported")) { | ||
955 | return; | ||
956 | } | ||
957 | |||
958 | wlr_log(L_DEBUG, "Swapping containers %zu and %zu", con1->id, con2->id); | ||
959 | |||
960 | int fs1 = con1->type == C_VIEW && con1->sway_view->is_fullscreen; | ||
961 | int fs2 = con2->type == C_VIEW && con2->sway_view->is_fullscreen; | ||
962 | if (fs1) { | ||
963 | view_set_fullscreen(con1->sway_view, false); | ||
964 | } | ||
965 | if (fs2) { | ||
966 | view_set_fullscreen(con2->sway_view, false); | ||
967 | } | ||
968 | |||
969 | struct sway_seat *seat = input_manager_get_default_seat(input_manager); | ||
970 | struct sway_container *focus = seat_get_focus(seat); | ||
971 | struct sway_container *vis1 = container_parent( | ||
972 | seat_get_focus_inactive(seat, container_parent(con1, C_OUTPUT)), | ||
973 | C_WORKSPACE); | ||
974 | struct sway_container *vis2 = container_parent( | ||
975 | seat_get_focus_inactive(seat, container_parent(con2, C_OUTPUT)), | ||
976 | C_WORKSPACE); | ||
977 | |||
978 | char *stored_prev_name = NULL; | ||
979 | if (prev_workspace_name) { | ||
980 | stored_prev_name = strdup(prev_workspace_name); | ||
981 | } | ||
982 | |||
983 | swap_places(con1, con2); | ||
984 | |||
985 | if (!workspace_is_visible(vis1)) { | ||
986 | seat_set_focus(seat, seat_get_focus_inactive(seat, vis1)); | ||
987 | } | ||
988 | if (!workspace_is_visible(vis2)) { | ||
989 | seat_set_focus(seat, seat_get_focus_inactive(seat, vis2)); | ||
990 | } | ||
991 | |||
992 | swap_focus(con1, con2, seat, focus); | ||
993 | |||
994 | if (stored_prev_name) { | ||
995 | free(prev_workspace_name); | ||
996 | prev_workspace_name = stored_prev_name; | ||
997 | } | ||
998 | |||
999 | arrange_children_of(con1->parent); | ||
1000 | arrange_children_of(con2->parent); | ||
1001 | |||
1002 | if (fs1 && con2->type == C_VIEW) { | ||
1003 | view_set_fullscreen(con2->sway_view, true); | ||
1004 | } | ||
1005 | if (fs2 && con1->type == C_VIEW) { | ||
1006 | view_set_fullscreen(con1->sway_view, true); | ||
1007 | } | ||
1008 | } | ||
diff --git a/sway/tree/view.c b/sway/tree/view.c index 812d7740..d91182ed 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c | |||
@@ -107,7 +107,7 @@ uint32_t view_get_window_type(struct sway_view *view) { | |||
107 | return 0; | 107 | return 0; |
108 | } | 108 | } |
109 | 109 | ||
110 | const char *view_get_type(struct sway_view *view) { | 110 | const char *view_get_shell(struct sway_view *view) { |
111 | switch(view->type) { | 111 | switch(view->type) { |
112 | case SWAY_VIEW_XDG_SHELL_V6: | 112 | case SWAY_VIEW_XDG_SHELL_V6: |
113 | return "xdg_shell_v6"; | 113 | return "xdg_shell_v6"; |
@@ -654,10 +654,12 @@ static size_t parse_title_format(struct sway_view *view, char *buffer) { | |||
654 | return title ? strlen(title) : 0; | 654 | return title ? strlen(title) : 0; |
655 | } | 655 | } |
656 | const char *title = view_get_title(view); | 656 | const char *title = view_get_title(view); |
657 | const char *app_id = view_get_app_id(view); | ||
657 | const char *class = view_get_class(view); | 658 | const char *class = view_get_class(view); |
658 | const char *instance = view_get_instance(view); | 659 | const char *instance = view_get_instance(view); |
659 | const char *shell = view_get_type(view); | 660 | const char *shell = view_get_shell(view); |
660 | size_t title_len = title ? strlen(title) : 0; | 661 | size_t title_len = title ? strlen(title) : 0; |
662 | size_t app_id_len = app_id ? strlen(app_id) : 0; | ||
661 | size_t class_len = class ? strlen(class) : 0; | 663 | size_t class_len = class ? strlen(class) : 0; |
662 | size_t instance_len = instance ? strlen(instance) : 0; | 664 | size_t instance_len = instance ? strlen(instance) : 0; |
663 | size_t shell_len = shell ? strlen(shell) : 0; | 665 | size_t shell_len = shell ? strlen(shell) : 0; |
@@ -675,6 +677,10 @@ static size_t parse_title_format(struct sway_view *view, char *buffer) { | |||
675 | lenient_strcat(buffer, title); | 677 | lenient_strcat(buffer, title); |
676 | len += title_len; | 678 | len += title_len; |
677 | format += 6; | 679 | format += 6; |
680 | } else if (strncmp(next, "%app_id", 7) == 0) { | ||
681 | lenient_strcat(buffer, app_id); | ||
682 | len += app_id_len; | ||
683 | format += 7; | ||
678 | } else if (strncmp(next, "%class", 6) == 0) { | 684 | } else if (strncmp(next, "%class", 6) == 0) { |
679 | lenient_strcat(buffer, class); | 685 | lenient_strcat(buffer, class); |
680 | len += class_len; | 686 | len += class_len; |
diff --git a/swaylock/main.c b/swaylock/main.c index f89f2849..591df7b4 100644 --- a/swaylock/main.c +++ b/swaylock/main.c | |||
@@ -131,14 +131,58 @@ static const struct zwlr_layer_surface_v1_listener layer_surface_listener = { | |||
131 | .closed = layer_surface_closed, | 131 | .closed = layer_surface_closed, |
132 | }; | 132 | }; |
133 | 133 | ||
134 | static void handle_wl_output_geometry(void *data, struct wl_output *output, int32_t x, | 134 | static const struct wl_callback_listener surface_frame_listener; |
135 | int32_t y, int32_t width_mm, int32_t height_mm, int32_t subpixel, | 135 | |
136 | const char *make, const char *model, int32_t transform) { | 136 | static void surface_frame_handle_done(void *data, struct wl_callback *callback, |
137 | uint32_t time) { | ||
138 | struct swaylock_surface *surface = data; | ||
139 | |||
140 | wl_callback_destroy(callback); | ||
141 | surface->frame_pending = false; | ||
142 | |||
143 | if (surface->dirty) { | ||
144 | // Schedule a frame in case the surface is damaged again | ||
145 | struct wl_callback *callback = wl_surface_frame(surface->surface); | ||
146 | wl_callback_add_listener(callback, &surface_frame_listener, surface); | ||
147 | surface->frame_pending = true; | ||
148 | |||
149 | render_frame(surface); | ||
150 | surface->dirty = false; | ||
151 | } | ||
152 | } | ||
153 | |||
154 | static const struct wl_callback_listener surface_frame_listener = { | ||
155 | .done = surface_frame_handle_done, | ||
156 | }; | ||
157 | |||
158 | void damage_surface(struct swaylock_surface *surface) { | ||
159 | surface->dirty = true; | ||
160 | if (surface->frame_pending) { | ||
161 | return; | ||
162 | } | ||
163 | |||
164 | struct wl_callback *callback = wl_surface_frame(surface->surface); | ||
165 | wl_callback_add_listener(callback, &surface_frame_listener, surface); | ||
166 | surface->frame_pending = true; | ||
167 | wl_surface_commit(surface->surface); | ||
168 | } | ||
169 | |||
170 | void damage_state(struct swaylock_state *state) { | ||
171 | struct swaylock_surface *surface; | ||
172 | wl_list_for_each(surface, &state->surfaces, link) { | ||
173 | damage_surface(surface); | ||
174 | } | ||
175 | } | ||
176 | |||
177 | static void handle_wl_output_geometry(void *data, struct wl_output *output, | ||
178 | int32_t x, int32_t y, int32_t width_mm, int32_t height_mm, | ||
179 | int32_t subpixel, const char *make, const char *model, | ||
180 | int32_t transform) { | ||
137 | // Who cares | 181 | // Who cares |
138 | } | 182 | } |
139 | 183 | ||
140 | static void handle_wl_output_mode(void *data, struct wl_output *output, uint32_t flags, | 184 | static void handle_wl_output_mode(void *data, struct wl_output *output, |
141 | int32_t width, int32_t height, int32_t refresh) { | 185 | uint32_t flags, int32_t width, int32_t height, int32_t refresh) { |
142 | // Who cares | 186 | // Who cares |
143 | } | 187 | } |
144 | 188 | ||
@@ -151,7 +195,7 @@ static void handle_wl_output_scale(void *data, struct wl_output *output, | |||
151 | struct swaylock_surface *surface = data; | 195 | struct swaylock_surface *surface = data; |
152 | surface->scale = factor; | 196 | surface->scale = factor; |
153 | if (surface->state->run_display) { | 197 | if (surface->state->run_display) { |
154 | render_frames(surface->state); | 198 | damage_surface(surface); |
155 | } | 199 | } |
156 | } | 200 | } |
157 | 201 | ||
diff --git a/swaylock/password.c b/swaylock/password.c index 1ad5cd81..6d493309 100644 --- a/swaylock/password.c +++ b/swaylock/password.c | |||
@@ -93,58 +93,58 @@ static void append_ch(struct swaylock_password *pw, uint32_t codepoint) { | |||
93 | void swaylock_handle_key(struct swaylock_state *state, | 93 | void swaylock_handle_key(struct swaylock_state *state, |
94 | xkb_keysym_t keysym, uint32_t codepoint) { | 94 | xkb_keysym_t keysym, uint32_t codepoint) { |
95 | switch (keysym) { | 95 | switch (keysym) { |
96 | case XKB_KEY_KP_Enter: /* fallthrough */ | 96 | case XKB_KEY_KP_Enter: /* fallthrough */ |
97 | case XKB_KEY_Return: | 97 | case XKB_KEY_Return: |
98 | state->auth_state = AUTH_STATE_VALIDATING; | 98 | state->auth_state = AUTH_STATE_VALIDATING; |
99 | render_frames(state); | 99 | damage_state(state); |
100 | wl_display_roundtrip(state->display); | 100 | wl_display_roundtrip(state->display); |
101 | if (attempt_password(&state->password)) { | 101 | if (attempt_password(&state->password)) { |
102 | state->run_display = false; | 102 | state->run_display = false; |
103 | break; | ||
104 | } | ||
105 | state->auth_state = AUTH_STATE_INVALID; | ||
106 | render_frames(state); | ||
107 | break; | 103 | break; |
108 | case XKB_KEY_Delete: | 104 | } |
109 | case XKB_KEY_BackSpace: | 105 | state->auth_state = AUTH_STATE_INVALID; |
110 | if (backspace(&state->password)) { | 106 | damage_state(state); |
111 | state->auth_state = AUTH_STATE_BACKSPACE; | 107 | break; |
112 | } else { | 108 | case XKB_KEY_Delete: |
113 | state->auth_state = AUTH_STATE_CLEAR; | 109 | case XKB_KEY_BackSpace: |
114 | } | 110 | if (backspace(&state->password)) { |
115 | render_frames(state); | 111 | state->auth_state = AUTH_STATE_BACKSPACE; |
116 | break; | 112 | } else { |
117 | case XKB_KEY_Escape: | ||
118 | clear_password_buffer(&state->password); | ||
119 | state->auth_state = AUTH_STATE_CLEAR; | 113 | state->auth_state = AUTH_STATE_CLEAR; |
120 | render_frames(state); | 114 | } |
121 | break; | 115 | damage_state(state); |
122 | case XKB_KEY_Caps_Lock: | 116 | break; |
123 | /* The state is getting active after this | 117 | case XKB_KEY_Escape: |
124 | * so we need to manually toggle it */ | 118 | clear_password_buffer(&state->password); |
125 | state->xkb.caps_lock = !state->xkb.caps_lock; | 119 | state->auth_state = AUTH_STATE_CLEAR; |
126 | state->auth_state = AUTH_STATE_INPUT_NOP; | 120 | damage_state(state); |
127 | render_frames(state); | 121 | break; |
128 | break; | 122 | case XKB_KEY_Caps_Lock: |
129 | case XKB_KEY_Shift_L: | 123 | /* The state is getting active after this |
130 | case XKB_KEY_Shift_R: | 124 | * so we need to manually toggle it */ |
131 | case XKB_KEY_Control_L: | 125 | state->xkb.caps_lock = !state->xkb.caps_lock; |
132 | case XKB_KEY_Control_R: | 126 | state->auth_state = AUTH_STATE_INPUT_NOP; |
133 | case XKB_KEY_Meta_L: | 127 | damage_state(state); |
134 | case XKB_KEY_Meta_R: | 128 | break; |
135 | case XKB_KEY_Alt_L: | 129 | case XKB_KEY_Shift_L: |
136 | case XKB_KEY_Alt_R: | 130 | case XKB_KEY_Shift_R: |
137 | case XKB_KEY_Super_L: | 131 | case XKB_KEY_Control_L: |
138 | case XKB_KEY_Super_R: | 132 | case XKB_KEY_Control_R: |
139 | state->auth_state = AUTH_STATE_INPUT_NOP; | 133 | case XKB_KEY_Meta_L: |
140 | render_frames(state); | 134 | case XKB_KEY_Meta_R: |
141 | break; | 135 | case XKB_KEY_Alt_L: |
142 | default: | 136 | case XKB_KEY_Alt_R: |
143 | if (codepoint) { | 137 | case XKB_KEY_Super_L: |
144 | append_ch(&state->password, codepoint); | 138 | case XKB_KEY_Super_R: |
145 | state->auth_state = AUTH_STATE_INPUT; | 139 | state->auth_state = AUTH_STATE_INPUT_NOP; |
146 | render_frames(state); | 140 | damage_state(state); |
147 | } | 141 | break; |
148 | break; | 142 | default: |
143 | if (codepoint) { | ||
144 | append_ch(&state->password, codepoint); | ||
145 | state->auth_state = AUTH_STATE_INPUT; | ||
146 | damage_state(state); | ||
147 | } | ||
148 | break; | ||
149 | } | 149 | } |
150 | } | 150 | } |
diff --git a/swaylock/render.c b/swaylock/render.c index 05236dea..2032ddcf 100644 --- a/swaylock/render.c +++ b/swaylock/render.c | |||
@@ -23,6 +23,10 @@ void render_frame(struct swaylock_surface *surface) { | |||
23 | 23 | ||
24 | surface->current_buffer = get_next_buffer(state->shm, | 24 | surface->current_buffer = get_next_buffer(state->shm, |
25 | surface->buffers, buffer_width, buffer_height); | 25 | surface->buffers, buffer_width, buffer_height); |
26 | if (surface->current_buffer == NULL) { | ||
27 | return; | ||
28 | } | ||
29 | |||
26 | cairo_t *cairo = surface->current_buffer->cairo; | 30 | cairo_t *cairo = surface->current_buffer->cairo; |
27 | cairo_identity_matrix(cairo); | 31 | cairo_identity_matrix(cairo); |
28 | 32 | ||