diff options
author | Drew DeVault <sir@cmpwn.com> | 2018-01-30 23:09:21 -0500 |
---|---|---|
committer | Drew DeVault <sir@cmpwn.com> | 2018-01-30 23:09:21 -0500 |
commit | b28602aa7425cf435150e6008624429737e037d3 (patch) | |
tree | dafe7d23c48457299f33803832f6b89e09a915ce | |
parent | Remove include/sway/old/ (diff) | |
download | sway-b28602aa7425cf435150e6008624429737e037d3.tar.gz sway-b28602aa7425cf435150e6008624429737e037d3.tar.zst sway-b28602aa7425cf435150e6008624429737e037d3.zip |
Implement workspaces
-rw-r--r-- | include/sway/container.h | 19 | ||||
-rw-r--r-- | include/sway/input/input-manager.h | 4 | ||||
-rw-r--r-- | include/sway/workspace.h | 14 | ||||
-rw-r--r-- | sway/commands.c | 1 | ||||
-rw-r--r-- | sway/commands/kill.c | 7 | ||||
-rw-r--r-- | sway/commands/workspace.c | 101 | ||||
-rw-r--r-- | sway/config.c | 15 | ||||
-rw-r--r-- | sway/desktop/output.c | 4 | ||||
-rw-r--r-- | sway/desktop/xdg_shell_v6.c | 9 | ||||
-rw-r--r-- | sway/input/input-manager.c | 8 | ||||
-rw-r--r-- | sway/input/seat.c | 15 | ||||
-rw-r--r-- | sway/meson.build | 1 | ||||
-rw-r--r-- | sway/tree/container.c | 41 | ||||
-rw-r--r-- | sway/tree/workspace.c | 211 |
14 files changed, 420 insertions, 30 deletions
diff --git a/include/sway/container.h b/include/sway/container.h index a99e2694..0c66932d 100644 --- a/include/sway/container.h +++ b/include/sway/container.h | |||
@@ -11,6 +11,7 @@ typedef struct sway_container swayc_t; | |||
11 | extern swayc_t root_container; | 11 | extern swayc_t root_container; |
12 | 12 | ||
13 | struct sway_view; | 13 | struct sway_view; |
14 | struct sway_seat; | ||
14 | 15 | ||
15 | /** | 16 | /** |
16 | * Different kinds of containers. | 17 | * Different kinds of containers. |
@@ -140,11 +141,25 @@ swayc_t *new_view(swayc_t *sibling, struct sway_view *sway_view); | |||
140 | swayc_t *destroy_output(swayc_t *output); | 141 | swayc_t *destroy_output(swayc_t *output); |
141 | swayc_t *destroy_view(swayc_t *view); | 142 | swayc_t *destroy_view(swayc_t *view); |
142 | 143 | ||
144 | swayc_t *next_view_sibling(struct sway_seat *seat); | ||
145 | |||
146 | /** | ||
147 | * Finds a container based on test criteria. Returns the first container that | ||
148 | * passes the test. | ||
149 | */ | ||
150 | swayc_t *swayc_by_test(swayc_t *container, | ||
151 | bool (*test)(swayc_t *view, void *data), void *data); | ||
152 | /** | ||
153 | * Finds a parent container with the given swayc_type. | ||
154 | */ | ||
143 | swayc_t *swayc_parent_by_type(swayc_t *container, enum swayc_types type); | 155 | swayc_t *swayc_parent_by_type(swayc_t *container, enum swayc_types type); |
156 | /** | ||
157 | * Maps a container's children over a function. | ||
158 | */ | ||
159 | void container_map(swayc_t *container, | ||
160 | void (*f)(swayc_t *view, void *data), void *data); | ||
144 | 161 | ||
145 | swayc_t *swayc_at(swayc_t *parent, double lx, double ly, | 162 | swayc_t *swayc_at(swayc_t *parent, double lx, double ly, |
146 | struct wlr_surface **surface, double *sx, double *sy); | 163 | struct wlr_surface **surface, double *sx, double *sy); |
147 | 164 | ||
148 | void container_map(swayc_t *container, void (*f)(swayc_t *view, void *data), void *data); | ||
149 | |||
150 | #endif | 165 | #endif |
diff --git a/include/sway/input/input-manager.h b/include/sway/input/input-manager.h index 63806b8e..66ace262 100644 --- a/include/sway/input/input-manager.h +++ b/include/sway/input/input-manager.h | |||
@@ -48,4 +48,8 @@ struct sway_seat *sway_input_manager_get_default_seat( | |||
48 | 48 | ||
49 | struct sway_seat *input_manager_get_seat(struct sway_input_manager *input, | 49 | struct sway_seat *input_manager_get_seat(struct sway_input_manager *input, |
50 | const char *seat_name); | 50 | const char *seat_name); |
51 | |||
52 | /** Gets the last seat the user interacted with */ | ||
53 | struct sway_seat *input_manager_current_seat(struct sway_input_manager *input); | ||
54 | |||
51 | #endif | 55 | #endif |
diff --git a/include/sway/workspace.h b/include/sway/workspace.h index 04b2ea4e..30bbdaa8 100644 --- a/include/sway/workspace.h +++ b/include/sway/workspace.h | |||
@@ -1,6 +1,20 @@ | |||
1 | #ifndef _SWAY_WORKSPACE_H | 1 | #ifndef _SWAY_WORKSPACE_H |
2 | #define _SWAY_WORKSPACE_H | 2 | #define _SWAY_WORKSPACE_H |
3 | 3 | ||
4 | struct sway_container; | ||
5 | |||
6 | extern char *prev_workspace_name; | ||
7 | |||
4 | char *workspace_next_name(const char *output_name); | 8 | char *workspace_next_name(const char *output_name); |
9 | swayc_t *workspace_create(const char *name); | ||
10 | bool workspace_switch(swayc_t *workspace); | ||
11 | |||
12 | struct sway_container *workspace_by_number(const char* name); | ||
13 | swayc_t *workspace_by_name(const char*); | ||
14 | |||
15 | struct sway_container *workspace_output_next(struct sway_container *current); | ||
16 | struct sway_container *workspace_next(struct sway_container *current); | ||
17 | struct sway_container *workspace_output_prev(struct sway_container *current); | ||
18 | struct sway_container *workspace_prev(struct sway_container *current); | ||
5 | 19 | ||
6 | #endif | 20 | #endif |
diff --git a/sway/commands.c b/sway/commands.c index d4262c08..0d4aa104 100644 --- a/sway/commands.c +++ b/sway/commands.c | |||
@@ -139,6 +139,7 @@ static struct cmd_handler handlers[] = { | |||
139 | { "reload", cmd_reload }, | 139 | { "reload", cmd_reload }, |
140 | { "seat", cmd_seat }, | 140 | { "seat", cmd_seat }, |
141 | { "set", cmd_set }, | 141 | { "set", cmd_set }, |
142 | { "workspace", cmd_workspace }, | ||
142 | }; | 143 | }; |
143 | 144 | ||
144 | static int handler_compare(const void *_a, const void *_b) { | 145 | static int handler_compare(const void *_a, const void *_b) { |
diff --git a/sway/commands/kill.c b/sway/commands/kill.c index 3804f0b0..cebf7f3c 100644 --- a/sway/commands/kill.c +++ b/sway/commands/kill.c | |||
@@ -10,11 +10,10 @@ struct cmd_results *cmd_kill(int argc, char **argv) { | |||
10 | return cmd_results_new(CMD_FAILURE, "kill", | 10 | return cmd_results_new(CMD_FAILURE, "kill", |
11 | "Command 'kill' cannot be used in the config file"); | 11 | "Command 'kill' cannot be used in the config file"); |
12 | } | 12 | } |
13 | if (!sway_assert(config->handler_context.current_container, | 13 | enum swayc_types type = config->handler_context.current_container->type; |
14 | "cmd_kill called without container context")) { | 14 | if (type != C_VIEW || type != C_CONTAINER) { |
15 | return cmd_results_new(CMD_INVALID, NULL, | 15 | return cmd_results_new(CMD_INVALID, NULL, |
16 | "cmd_kill called without container context " | 16 | "Can only kill views and containers with this command"); |
17 | "(this is a bug in sway)"); | ||
18 | } | 17 | } |
19 | // TODO close arbitrary containers without a view | 18 | // TODO close arbitrary containers without a view |
20 | struct sway_view *view = | 19 | struct sway_view *view = |
diff --git a/sway/commands/workspace.c b/sway/commands/workspace.c new file mode 100644 index 00000000..12984ed4 --- /dev/null +++ b/sway/commands/workspace.c | |||
@@ -0,0 +1,101 @@ | |||
1 | #define _XOPEN_SOURCE 500 | ||
2 | #include <string.h> | ||
3 | #include <strings.h> | ||
4 | #include "sway/commands.h" | ||
5 | #include "sway/config.h" | ||
6 | #include "sway/input/seat.h" | ||
7 | #include "sway/workspace.h" | ||
8 | #include "list.h" | ||
9 | #include "log.h" | ||
10 | #include "stringop.h" | ||
11 | |||
12 | struct cmd_results *cmd_workspace(int argc, char **argv) { | ||
13 | struct cmd_results *error = NULL; | ||
14 | if ((error = checkarg(argc, "workspace", EXPECTED_AT_LEAST, 1))) { | ||
15 | return error; | ||
16 | } | ||
17 | |||
18 | int output_location = -1; | ||
19 | |||
20 | swayc_t *current_container = config->handler_context.current_container; | ||
21 | swayc_t *old_workspace = NULL, *old_output = NULL; | ||
22 | if (current_container) { | ||
23 | if (current_container->type == C_WORKSPACE) { | ||
24 | old_workspace = current_container; | ||
25 | } else { | ||
26 | old_workspace = swayc_parent_by_type(current_container, C_WORKSPACE); | ||
27 | } | ||
28 | old_output = swayc_parent_by_type(current_container, C_OUTPUT); | ||
29 | } | ||
30 | |||
31 | for (int i = 0; i < argc; ++i) { | ||
32 | if (strcasecmp(argv[i], "output") == 0) { | ||
33 | output_location = i; | ||
34 | break; | ||
35 | } | ||
36 | } | ||
37 | if (output_location >= 0) { | ||
38 | if ((error = checkarg(argc, "workspace", EXPECTED_EQUAL_TO, output_location + 2))) { | ||
39 | return error; | ||
40 | } | ||
41 | struct workspace_output *wso = calloc(1, sizeof(struct workspace_output)); | ||
42 | if (!wso) { | ||
43 | return cmd_results_new(CMD_FAILURE, "workspace output", | ||
44 | "Unable to allocate workspace output"); | ||
45 | } | ||
46 | wso->workspace = join_args(argv, argc - 2); | ||
47 | wso->output = strdup(argv[output_location + 1]); | ||
48 | int i = -1; | ||
49 | if ((i = list_seq_find(config->workspace_outputs, workspace_output_cmp_workspace, wso)) != -1) { | ||
50 | struct workspace_output *old = config->workspace_outputs->items[i]; | ||
51 | free(old); // workspaces can only be assigned to a single output | ||
52 | list_del(config->workspace_outputs, i); | ||
53 | } | ||
54 | wlr_log(L_DEBUG, "Assigning workspace %s to output %s", wso->workspace, wso->output); | ||
55 | list_add(config->workspace_outputs, wso); | ||
56 | } else { | ||
57 | if (config->reading || !config->active) { | ||
58 | return cmd_results_new(CMD_DEFER, "workspace", NULL); | ||
59 | } | ||
60 | swayc_t *ws = NULL; | ||
61 | if (strcasecmp(argv[0], "number") == 0) { | ||
62 | if (!(ws = workspace_by_number(argv[1]))) { | ||
63 | char *name = join_args(argv + 1, argc - 1); | ||
64 | ws = workspace_create(name); | ||
65 | free(name); | ||
66 | } | ||
67 | } else if (strcasecmp(argv[0], "next") == 0) { | ||
68 | ws = workspace_next(old_workspace); | ||
69 | } else if (strcasecmp(argv[0], "prev") == 0) { | ||
70 | ws = workspace_prev(old_workspace); | ||
71 | } else if (strcasecmp(argv[0], "next_on_output") == 0) { | ||
72 | ws = workspace_output_next(old_output); | ||
73 | } else if (strcasecmp(argv[0], "prev_on_output") == 0) { | ||
74 | ws = workspace_output_prev(old_output); | ||
75 | } else if (strcasecmp(argv[0], "back_and_forth") == 0) { | ||
76 | // if auto_back_and_forth is enabled, workspace_switch will swap | ||
77 | // the workspaces. If we created prev_workspace here, workspace_switch | ||
78 | // would put us back on original workspace. | ||
79 | if (config->auto_back_and_forth) { | ||
80 | ws = old_workspace; | ||
81 | } else if (prev_workspace_name | ||
82 | && !(ws = workspace_by_name(prev_workspace_name))) { | ||
83 | ws = workspace_create(prev_workspace_name); | ||
84 | } | ||
85 | } else { | ||
86 | char *name = join_args(argv, argc); | ||
87 | if (!(ws = workspace_by_name(name))) { | ||
88 | ws = workspace_create(name); | ||
89 | } | ||
90 | free(name); | ||
91 | } | ||
92 | workspace_switch(ws); | ||
93 | current_container = config->handler_context.seat->focus; | ||
94 | swayc_t *new_output = swayc_parent_by_type(current_container, C_OUTPUT); | ||
95 | |||
96 | if (config->mouse_warping && old_output != new_output) { | ||
97 | // TODO: Warp mouse | ||
98 | } | ||
99 | } | ||
100 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | ||
101 | } | ||
diff --git a/sway/config.c b/sway/config.c index a67322d1..213e7680 100644 --- a/sway/config.c +++ b/sway/config.c | |||
@@ -318,10 +318,6 @@ static bool load_config(const char *path, struct sway_config *config) { | |||
318 | return true; | 318 | return true; |
319 | } | 319 | } |
320 | 320 | ||
321 | static int qstrcmp(const void* a, const void* b) { | ||
322 | return strcmp(*((char**) a), *((char**) b)); | ||
323 | } | ||
324 | |||
325 | bool load_main_config(const char *file, bool is_active) { | 321 | bool load_main_config(const char *file, bool is_active) { |
326 | char *path; | 322 | char *path; |
327 | if (file != NULL) { | 323 | if (file != NULL) { |
@@ -349,7 +345,9 @@ bool load_main_config(const char *file, bool is_active) { | |||
349 | config->reading = true; | 345 | config->reading = true; |
350 | 346 | ||
351 | // Read security configs | 347 | // Read security configs |
348 | // TODO: Security | ||
352 | bool success = true; | 349 | bool success = true; |
350 | /* | ||
353 | DIR *dir = opendir(SYSCONFDIR "/sway/security.d"); | 351 | DIR *dir = opendir(SYSCONFDIR "/sway/security.d"); |
354 | if (!dir) { | 352 | if (!dir) { |
355 | wlr_log(L_ERROR, | 353 | wlr_log(L_ERROR, |
@@ -392,6 +390,7 @@ bool load_main_config(const char *file, bool is_active) { | |||
392 | 390 | ||
393 | free_flat_list(secconfigs); | 391 | free_flat_list(secconfigs); |
394 | } | 392 | } |
393 | */ | ||
395 | 394 | ||
396 | success = success && load_config(path, config); | 395 | success = success && load_config(path, config); |
397 | 396 | ||
@@ -717,3 +716,11 @@ char *do_var_replacement(char *str) { | |||
717 | } | 716 | } |
718 | return str; | 717 | return str; |
719 | } | 718 | } |
719 | |||
720 | // the naming is intentional (albeit long): a workspace_output_cmp function | ||
721 | // would compare two structs in full, while this method only compares the | ||
722 | // workspace. | ||
723 | int workspace_output_cmp_workspace(const void *a, const void *b) { | ||
724 | const struct workspace_output *wsa = a, *wsb = b; | ||
725 | return lenient_strcmp(wsa->workspace, wsb->workspace); | ||
726 | } | ||
diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 0f00222b..a650665f 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c | |||
@@ -219,8 +219,8 @@ static void output_frame_notify(struct wl_listener *listener, void *data) { | |||
219 | wlr_output_make_current(wlr_output); | 219 | wlr_output_make_current(wlr_output); |
220 | wlr_renderer_begin(server->renderer, wlr_output); | 220 | wlr_renderer_begin(server->renderer, wlr_output); |
221 | 221 | ||
222 | swayc_descendants_of_type( | 222 | swayc_t *workspace = soutput->swayc->focused; |
223 | &root_container, C_VIEW, output_frame_view, soutput); | 223 | swayc_descendants_of_type(workspace, C_VIEW, output_frame_view, soutput); |
224 | 224 | ||
225 | // render unmanaged views on top | 225 | // render unmanaged views on top |
226 | struct sway_view *view; | 226 | struct sway_view *view; |
diff --git a/sway/desktop/xdg_shell_v6.c b/sway/desktop/xdg_shell_v6.c index 4b50093f..ca56a9c0 100644 --- a/sway/desktop/xdg_shell_v6.c +++ b/sway/desktop/xdg_shell_v6.c | |||
@@ -124,8 +124,6 @@ void handle_xdg_shell_v6_surface(struct wl_listener *listener, void *data) { | |||
124 | sway_surface->view = sway_view; | 124 | sway_surface->view = sway_view; |
125 | 125 | ||
126 | // TODO: | 126 | // TODO: |
127 | // - Wire up listeners | ||
128 | // - Handle popups | ||
129 | // - Look up pid and open on appropriate workspace | 127 | // - Look up pid and open on appropriate workspace |
130 | // - Set new view to maximized so it behaves nicely | 128 | // - Set new view to maximized so it behaves nicely |
131 | // - Criteria | 129 | // - Criteria |
@@ -136,11 +134,8 @@ void handle_xdg_shell_v6_surface(struct wl_listener *listener, void *data) { | |||
136 | sway_surface->destroy.notify = handle_destroy; | 134 | sway_surface->destroy.notify = handle_destroy; |
137 | wl_signal_add(&xdg_surface->events.destroy, &sway_surface->destroy); | 135 | wl_signal_add(&xdg_surface->events.destroy, &sway_surface->destroy); |
138 | 136 | ||
139 | // TODO: actual focus semantics | 137 | struct sway_seat *seat = input_manager_current_seat(input_manager); |
140 | swayc_t *parent = root_container.children->items[0]; | 138 | swayc_t *cont = new_view(seat->focus, sway_view); |
141 | parent = parent->children->items[0]; // workspace | ||
142 | |||
143 | swayc_t *cont = new_view(parent, sway_view); | ||
144 | sway_view->swayc = cont; | 139 | sway_view->swayc = cont; |
145 | 140 | ||
146 | arrange_windows(cont->parent, -1, -1); | 141 | arrange_windows(cont->parent, -1, -1); |
diff --git a/sway/input/input-manager.c b/sway/input/input-manager.c index 12b3a430..d789c7eb 100644 --- a/sway/input/input-manager.c +++ b/sway/input/input-manager.c | |||
@@ -23,6 +23,14 @@ struct sway_input_manager *input_manager; | |||
23 | struct input_config *current_input_config = NULL; | 23 | struct input_config *current_input_config = NULL; |
24 | struct seat_config *current_seat_config = NULL; | 24 | struct seat_config *current_seat_config = NULL; |
25 | 25 | ||
26 | struct sway_seat *input_manager_current_seat(struct sway_input_manager *input) { | ||
27 | struct sway_seat *seat = config->handler_context.seat; | ||
28 | if (!seat) { | ||
29 | seat = sway_input_manager_get_default_seat(input_manager); | ||
30 | } | ||
31 | return seat; | ||
32 | } | ||
33 | |||
26 | struct sway_seat *input_manager_get_seat( | 34 | struct sway_seat *input_manager_get_seat( |
27 | struct sway_input_manager *input, const char *seat_name) { | 35 | struct sway_input_manager *input, const char *seat_name) { |
28 | struct sway_seat *seat = NULL; | 36 | struct sway_seat *seat = NULL; |
diff --git a/sway/input/seat.c b/sway/input/seat.c index 9ea08eec..5e87986d 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c | |||
@@ -1,6 +1,7 @@ | |||
1 | #define _XOPEN_SOURCE 700 | 1 | #define _XOPEN_SOURCE 700 |
2 | #include <wlr/types/wlr_cursor.h> | 2 | #include <wlr/types/wlr_cursor.h> |
3 | #include <wlr/types/wlr_xcursor_manager.h> | 3 | #include <wlr/types/wlr_xcursor_manager.h> |
4 | #include "sway/container.h" | ||
4 | #include "sway/input/seat.h" | 5 | #include "sway/input/seat.h" |
5 | #include "sway/input/cursor.h" | 6 | #include "sway/input/cursor.h" |
6 | #include "sway/input/input-manager.h" | 7 | #include "sway/input/input-manager.h" |
@@ -81,7 +82,7 @@ static void seat_configure_keyboard(struct sway_seat *seat, | |||
81 | sway_keyboard_configure(seat_device->keyboard); | 82 | sway_keyboard_configure(seat_device->keyboard); |
82 | wlr_seat_set_keyboard(seat->wlr_seat, | 83 | wlr_seat_set_keyboard(seat->wlr_seat, |
83 | seat_device->input_device->wlr_device); | 84 | seat_device->input_device->wlr_device); |
84 | if (seat->focus) { | 85 | if (seat->focus && seat->focus->type == C_VIEW) { |
85 | // force notify reenter to pick up the new configuration | 86 | // force notify reenter to pick up the new configuration |
86 | wlr_seat_keyboard_clear_focus(seat->wlr_seat); | 87 | wlr_seat_keyboard_clear_focus(seat->wlr_seat); |
87 | wlr_seat_keyboard_notify_enter(seat->wlr_seat, | 88 | wlr_seat_keyboard_notify_enter(seat->wlr_seat, |
@@ -205,10 +206,8 @@ void sway_seat_configure_xcursor(struct sway_seat *seat) { | |||
205 | 206 | ||
206 | static void handle_focus_destroy(struct wl_listener *listener, void *data) { | 207 | static void handle_focus_destroy(struct wl_listener *listener, void *data) { |
207 | struct sway_seat *seat = wl_container_of(listener, seat, focus_destroy); | 208 | struct sway_seat *seat = wl_container_of(listener, seat, focus_destroy); |
208 | //swayc_t *container = data; | 209 | swayc_t *container = data; |
209 | 210 | sway_seat_set_focus(seat, container->parent); | |
210 | // TODO set new focus based on the state of the tree | ||
211 | sway_seat_set_focus(seat, NULL); | ||
212 | } | 211 | } |
213 | 212 | ||
214 | void sway_seat_set_focus(struct sway_seat *seat, swayc_t *container) { | 213 | void sway_seat_set_focus(struct sway_seat *seat, swayc_t *container) { |
@@ -218,11 +217,11 @@ void sway_seat_set_focus(struct sway_seat *seat, swayc_t *container) { | |||
218 | return; | 217 | return; |
219 | } | 218 | } |
220 | 219 | ||
221 | if (last_focus) { | 220 | if (last_focus && last_focus->type == C_VIEW) { |
222 | wl_list_remove(&seat->focus_destroy.link); | 221 | wl_list_remove(&seat->focus_destroy.link); |
223 | } | 222 | } |
224 | 223 | ||
225 | if (container) { | 224 | if (container && container->type == C_VIEW) { |
226 | struct sway_view *view = container->sway_view; | 225 | struct sway_view *view = container->sway_view; |
227 | view_set_activated(view, true); | 226 | view_set_activated(view, true); |
228 | wl_signal_add(&container->events.destroy, &seat->focus_destroy); | 227 | wl_signal_add(&container->events.destroy, &seat->focus_destroy); |
@@ -241,7 +240,7 @@ void sway_seat_set_focus(struct sway_seat *seat, swayc_t *container) { | |||
241 | 240 | ||
242 | seat->focus = container; | 241 | seat->focus = container; |
243 | 242 | ||
244 | if (last_focus && | 243 | if (last_focus && last_focus->type == C_VIEW && |
245 | !sway_input_manager_has_focus(seat->input, last_focus)) { | 244 | !sway_input_manager_has_focus(seat->input, last_focus)) { |
246 | struct sway_view *view = last_focus->sway_view; | 245 | struct sway_view *view = last_focus->sway_view; |
247 | view_set_activated(view, false); | 246 | view_set_activated(view, false); |
diff --git a/sway/meson.build b/sway/meson.build index 51e9e4db..271d4a99 100644 --- a/sway/meson.build +++ b/sway/meson.build | |||
@@ -35,6 +35,7 @@ sway_sources = files( | |||
35 | 'commands/input/xkb_variant.c', | 35 | 'commands/input/xkb_variant.c', |
36 | 'commands/output.c', | 36 | 'commands/output.c', |
37 | 'commands/reload.c', | 37 | 'commands/reload.c', |
38 | 'commands/workspace.c', | ||
38 | 'config.c', | 39 | 'config.c', |
39 | 'config/output.c', | 40 | 'config/output.c', |
40 | 'config/seat.c', | 41 | 'config/seat.c', |
diff --git a/sway/tree/container.c b/sway/tree/container.c index b7b9bc68..48aabd86 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c | |||
@@ -3,10 +3,13 @@ | |||
3 | #include <stdlib.h> | 3 | #include <stdlib.h> |
4 | #include <string.h> | 4 | #include <string.h> |
5 | #include <strings.h> | 5 | #include <strings.h> |
6 | #include <wayland-server.h> | ||
6 | #include <wlr/types/wlr_output_layout.h> | 7 | #include <wlr/types/wlr_output_layout.h> |
7 | #include <wlr/types/wlr_wl_shell.h> | 8 | #include <wlr/types/wlr_wl_shell.h> |
8 | #include "sway/config.h" | 9 | #include "sway/config.h" |
9 | #include "sway/container.h" | 10 | #include "sway/container.h" |
11 | #include "sway/input/input-manager.h" | ||
12 | #include "sway/input/seat.h" | ||
10 | #include "sway/layout.h" | 13 | #include "sway/layout.h" |
11 | #include "sway/output.h" | 14 | #include "sway/output.h" |
12 | #include "sway/server.h" | 15 | #include "sway/server.h" |
@@ -14,6 +17,26 @@ | |||
14 | #include "sway/workspace.h" | 17 | #include "sway/workspace.h" |
15 | #include "log.h" | 18 | #include "log.h" |
16 | 19 | ||
20 | swayc_t *swayc_by_test(swayc_t *container, | ||
21 | bool (*test)(swayc_t *view, void *data), void *data) { | ||
22 | if (!container->children) { | ||
23 | return NULL; | ||
24 | } | ||
25 | // TODO: floating windows | ||
26 | for (int i = 0; i < container->children->length; ++i) { | ||
27 | swayc_t *child = container->children->items[i]; | ||
28 | if (test(child, data)) { | ||
29 | return child; | ||
30 | } else { | ||
31 | swayc_t *res = swayc_by_test(child, test, data); | ||
32 | if (res) { | ||
33 | return res; | ||
34 | } | ||
35 | } | ||
36 | } | ||
37 | return NULL; | ||
38 | } | ||
39 | |||
17 | void swayc_descendants_of_type(swayc_t *root, enum swayc_types type, | 40 | void swayc_descendants_of_type(swayc_t *root, enum swayc_types type, |
18 | void (*func)(swayc_t *item, void *data), void *data) { | 41 | void (*func)(swayc_t *item, void *data), void *data) { |
19 | for (int i = 0; i < root->children->length; ++i) { | 42 | for (int i = 0; i < root->children->length; ++i) { |
@@ -127,7 +150,19 @@ swayc_t *new_output(struct sway_output *sway_output) { | |||
127 | // Create workspace | 150 | // Create workspace |
128 | char *ws_name = workspace_next_name(output->name); | 151 | char *ws_name = workspace_next_name(output->name); |
129 | wlr_log(L_DEBUG, "Creating default workspace %s", ws_name); | 152 | wlr_log(L_DEBUG, "Creating default workspace %s", ws_name); |
130 | new_workspace(output, ws_name); | 153 | swayc_t *ws = new_workspace(output, ws_name); |
154 | output->focused = ws; | ||
155 | // Set each seat's focus if not already set | ||
156 | // TODO FOCUS: this is probably stupid, we shouldn't define focus in two | ||
157 | // places. We should probably put the active workspace on the sway_output | ||
158 | // struct instead of trying to do focus semantics like this | ||
159 | struct sway_seat *seat = NULL; | ||
160 | wl_list_for_each(seat, &input_manager->seats, link) { | ||
161 | if (!seat->focus) { | ||
162 | seat->focus = ws; | ||
163 | } | ||
164 | } | ||
165 | |||
131 | free(ws_name); | 166 | free(ws_name); |
132 | return output; | 167 | return output; |
133 | } | 168 | } |
@@ -159,8 +194,8 @@ swayc_t *new_view(swayc_t *sibling, struct sway_view *sway_view) { | |||
159 | } | 194 | } |
160 | const char *title = view_get_title(sway_view); | 195 | const char *title = view_get_title(sway_view); |
161 | swayc_t *swayc = new_swayc(C_VIEW); | 196 | swayc_t *swayc = new_swayc(C_VIEW); |
162 | wlr_log(L_DEBUG, "Adding new view %p:%s to container %p %d", | 197 | wlr_log(L_DEBUG, "Adding new view %p:%s to container %p %d %s", |
163 | swayc, title, sibling, sibling ? sibling->type : 0); | 198 | swayc, title, sibling, sibling ? sibling->type : 0, sibling->name); |
164 | // Setup values | 199 | // Setup values |
165 | swayc->sway_view = sway_view; | 200 | swayc->sway_view = sway_view; |
166 | swayc->name = title ? strdup(title) : NULL; | 201 | swayc->name = title ? strdup(title) : NULL; |
diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c index c37a873c..23c630b6 100644 --- a/sway/tree/workspace.c +++ b/sway/tree/workspace.c | |||
@@ -2,8 +2,20 @@ | |||
2 | #include <stdbool.h> | 2 | #include <stdbool.h> |
3 | #include <stdlib.h> | 3 | #include <stdlib.h> |
4 | #include <stdio.h> | 4 | #include <stdio.h> |
5 | #include <strings.h> | ||
5 | #include "sway/container.h" | 6 | #include "sway/container.h" |
7 | #include "sway/input/input-manager.h" | ||
8 | #include "sway/input/seat.h" | ||
9 | #include "sway/workspace.h" | ||
6 | #include "log.h" | 10 | #include "log.h" |
11 | #include "util.h" | ||
12 | |||
13 | char *prev_workspace_name = NULL; | ||
14 | struct workspace_by_number_data { | ||
15 | int len; | ||
16 | const char *cset; | ||
17 | const char *name; | ||
18 | }; | ||
7 | 19 | ||
8 | void next_name_map(swayc_t *ws, void *data) { | 20 | void next_name_map(swayc_t *ws, void *data) { |
9 | int *count = data; | 21 | int *count = data; |
@@ -24,3 +36,202 @@ char *workspace_next_name(const char *output_name) { | |||
24 | snprintf(name, len + 1, "%d", count); | 36 | snprintf(name, len + 1, "%d", count); |
25 | return name; | 37 | return name; |
26 | } | 38 | } |
39 | |||
40 | static bool _workspace_by_number(swayc_t *view, void *data) { | ||
41 | if (view->type != C_WORKSPACE) { | ||
42 | return false; | ||
43 | } | ||
44 | struct workspace_by_number_data *wbnd = data; | ||
45 | int a = strspn(view->name, wbnd->cset); | ||
46 | return a == wbnd->len && strncmp(view->name, wbnd->name, a) == 0; | ||
47 | } | ||
48 | |||
49 | swayc_t *workspace_by_number(const char* name) { | ||
50 | struct workspace_by_number_data wbnd = {0, "1234567890", name}; | ||
51 | wbnd.len = strspn(name, wbnd.cset); | ||
52 | if (wbnd.len <= 0) { | ||
53 | return NULL; | ||
54 | } | ||
55 | return swayc_by_test(&root_container, _workspace_by_number, (void *) &wbnd); | ||
56 | } | ||
57 | |||
58 | static bool _workspace_by_name(swayc_t *view, void *data) { | ||
59 | return (view->type == C_WORKSPACE) && | ||
60 | (strcasecmp(view->name, (char *) data) == 0); | ||
61 | } | ||
62 | |||
63 | swayc_t *workspace_by_name(const char *name) { | ||
64 | struct sway_seat *seat = input_manager_current_seat(input_manager); | ||
65 | swayc_t *current_workspace = NULL, *current_output = NULL; | ||
66 | if (seat->focus) { | ||
67 | current_workspace = swayc_parent_by_type(seat->focus, C_WORKSPACE); | ||
68 | current_output = swayc_parent_by_type(seat->focus, C_OUTPUT); | ||
69 | } | ||
70 | if (strcmp(name, "prev") == 0) { | ||
71 | return workspace_prev(current_workspace); | ||
72 | } else if (strcmp(name, "prev_on_output") == 0) { | ||
73 | return workspace_output_prev(current_output); | ||
74 | } else if (strcmp(name, "next") == 0) { | ||
75 | return workspace_next(current_workspace); | ||
76 | } else if (strcmp(name, "next_on_output") == 0) { | ||
77 | return workspace_output_next(current_output); | ||
78 | } else if (strcmp(name, "current") == 0) { | ||
79 | return current_workspace; | ||
80 | } else { | ||
81 | return swayc_by_test(&root_container, _workspace_by_name, (void *) name); | ||
82 | } | ||
83 | } | ||
84 | |||
85 | swayc_t *workspace_create(const char *name) { | ||
86 | swayc_t *parent; | ||
87 | // Search for workspace<->output pair | ||
88 | int i, e = config->workspace_outputs->length; | ||
89 | for (i = 0; i < e; ++i) { | ||
90 | struct workspace_output *wso = config->workspace_outputs->items[i]; | ||
91 | if (strcasecmp(wso->workspace, name) == 0) { | ||
92 | // Find output to use if it exists | ||
93 | e = root_container.children->length; | ||
94 | for (i = 0; i < e; ++i) { | ||
95 | parent = root_container.children->items[i]; | ||
96 | if (strcmp(parent->name, wso->output) == 0) { | ||
97 | return new_workspace(parent, name); | ||
98 | } | ||
99 | } | ||
100 | break; | ||
101 | } | ||
102 | } | ||
103 | // Otherwise create a new one | ||
104 | struct sway_seat *seat = input_manager_current_seat(input_manager); | ||
105 | parent = seat->focus; | ||
106 | parent = swayc_parent_by_type(parent, C_OUTPUT); | ||
107 | return new_workspace(parent, name); | ||
108 | } | ||
109 | |||
110 | /** | ||
111 | * Get the previous or next workspace on the specified output. Wraps around at | ||
112 | * the end and beginning. If next is false, the previous workspace is returned, | ||
113 | * otherwise the next one is returned. | ||
114 | */ | ||
115 | swayc_t *workspace_output_prev_next_impl(swayc_t *output, bool next) { | ||
116 | if (!sway_assert(output->type == C_OUTPUT, | ||
117 | "Argument must be an output, is %d", output->type)) { | ||
118 | return NULL; | ||
119 | } | ||
120 | |||
121 | int i; | ||
122 | for (i = 0; i < output->children->length; i++) { | ||
123 | if (output->children->items[i] == output->focused) { | ||
124 | return output->children->items[ | ||
125 | wrap(i + (next ? 1 : -1), output->children->length)]; | ||
126 | } | ||
127 | } | ||
128 | |||
129 | // Doesn't happen, at worst the for loop returns the previously active workspace | ||
130 | return NULL; | ||
131 | } | ||
132 | |||
133 | /** | ||
134 | * Get the previous or next workspace. If the first/last workspace on an output | ||
135 | * is active, proceed to the previous/next output's previous/next workspace. If | ||
136 | * next is false, the previous workspace is returned, otherwise the next one is | ||
137 | * returned. | ||
138 | */ | ||
139 | swayc_t *workspace_prev_next_impl(swayc_t *workspace, bool next) { | ||
140 | if (!sway_assert(workspace->type == C_WORKSPACE, | ||
141 | "Argument must be a workspace, is %d", workspace->type)) { | ||
142 | return NULL; | ||
143 | } | ||
144 | |||
145 | swayc_t *current_output = workspace->parent; | ||
146 | int offset = next ? 1 : -1; | ||
147 | int start = next ? 0 : 1; | ||
148 | int end; | ||
149 | if (next) { | ||
150 | end = current_output->children->length - 1; | ||
151 | } else { | ||
152 | end = current_output->children->length; | ||
153 | } | ||
154 | int i; | ||
155 | for (i = start; i < end; i++) { | ||
156 | if (current_output->children->items[i] == workspace) { | ||
157 | return current_output->children->items[i + offset]; | ||
158 | } | ||
159 | } | ||
160 | |||
161 | // Given workspace is the first/last on the output, jump to the previous/next output | ||
162 | int num_outputs = root_container.children->length; | ||
163 | for (i = 0; i < num_outputs; i++) { | ||
164 | if (root_container.children->items[i] == current_output) { | ||
165 | swayc_t *next_output = root_container.children->items[ | ||
166 | wrap(i + offset, num_outputs)]; | ||
167 | return workspace_output_prev_next_impl(next_output, next); | ||
168 | } | ||
169 | } | ||
170 | |||
171 | // Doesn't happen, at worst the for loop returns the previously active workspace on the active output | ||
172 | return NULL; | ||
173 | } | ||
174 | |||
175 | swayc_t *workspace_output_next(swayc_t *current) { | ||
176 | return workspace_output_prev_next_impl(current, true); | ||
177 | } | ||
178 | |||
179 | swayc_t *workspace_next(swayc_t *current) { | ||
180 | return workspace_prev_next_impl(current, true); | ||
181 | } | ||
182 | |||
183 | swayc_t *workspace_output_prev(swayc_t *current) { | ||
184 | return workspace_output_prev_next_impl(current, false); | ||
185 | } | ||
186 | |||
187 | swayc_t *workspace_prev(swayc_t *current) { | ||
188 | return workspace_prev_next_impl(current, false); | ||
189 | } | ||
190 | |||
191 | bool workspace_switch(swayc_t *workspace) { | ||
192 | if (!workspace) { | ||
193 | return false; | ||
194 | } | ||
195 | struct sway_seat *seat = input_manager_current_seat(input_manager); | ||
196 | if (!seat || !seat->focus) { | ||
197 | return false; | ||
198 | } | ||
199 | swayc_t *active_ws = seat->focus; | ||
200 | if (active_ws->type != C_WORKSPACE) { | ||
201 | swayc_parent_by_type(seat->focus, C_WORKSPACE); | ||
202 | } | ||
203 | |||
204 | if (config->auto_back_and_forth | ||
205 | && active_ws == workspace | ||
206 | && prev_workspace_name) { | ||
207 | swayc_t *new_ws = workspace_by_name(prev_workspace_name); | ||
208 | workspace = new_ws ? new_ws : workspace_create(prev_workspace_name); | ||
209 | } | ||
210 | |||
211 | if (!prev_workspace_name || (strcmp(prev_workspace_name, active_ws->name) | ||
212 | && active_ws != workspace)) { | ||
213 | free(prev_workspace_name); | ||
214 | prev_workspace_name = malloc(strlen(active_ws->name) + 1); | ||
215 | if (!prev_workspace_name) { | ||
216 | wlr_log(L_ERROR, "Unable to allocate previous workspace name"); | ||
217 | return false; | ||
218 | } | ||
219 | strcpy(prev_workspace_name, active_ws->name); | ||
220 | } | ||
221 | |||
222 | // TODO: Deal with sticky containers | ||
223 | |||
224 | wlr_log(L_DEBUG, "Switching to workspace %p:%s", workspace, workspace->name); | ||
225 | // TODO FOCUS: Focus the last view this seat had focused on this workspace | ||
226 | if (workspace->children->length) { | ||
227 | // TODO FOCUS: This is really fucking stupid | ||
228 | sway_seat_set_focus(seat, workspace->children->items[0]); | ||
229 | } else { | ||
230 | sway_seat_set_focus(seat, workspace); | ||
231 | } | ||
232 | swayc_t *output = swayc_parent_by_type(workspace, C_OUTPUT); | ||
233 | // TODO FOCUS: take a look at this | ||
234 | output->focused = workspace; | ||
235 | arrange_windows(output, -1, -1); | ||
236 | return true; | ||
237 | } | ||