summaryrefslogtreecommitdiffstats
path: root/sway/tree/focus.c
diff options
context:
space:
mode:
Diffstat (limited to 'sway/tree/focus.c')
-rw-r--r--sway/tree/focus.c278
1 files changed, 278 insertions, 0 deletions
diff --git a/sway/tree/focus.c b/sway/tree/focus.c
new file mode 100644
index 00000000..66f7ee17
--- /dev/null
+++ b/sway/tree/focus.c
@@ -0,0 +1,278 @@
1#include "stdbool.h"
2#include <wlc/wlc.h>
3#include "sway/focus.h"
4#include "sway/workspace.h"
5#include "sway/layout.h"
6#include "sway/config.h"
7#include "sway/input_state.h"
8#include "sway/ipc-server.h"
9#include "sway/border.h"
10#include "log.h"
11
12bool locked_container_focus = false;
13bool suspend_workspace_cleanup = false;
14
15// switches parent focus to c. will switch it accordingly
16static void update_focus(swayc_t *c) {
17 // Handle if focus switches
18 swayc_t *parent = c->parent;
19 if (!parent) return;
20 if (parent->focused != c) {
21 // Get previous focus
22 swayc_t *prev = parent->focused;
23 // Set new focus
24 parent->focused = c;
25
26 switch (c->type) {
27 // Shouldn't happen
28 case C_ROOT: return;
29
30 // Case where output changes
31 case C_OUTPUT:
32 wlc_output_focus(c->handle);
33 break;
34
35 // Case where workspace changes
36 case C_WORKSPACE:
37 if (prev) {
38 ipc_event_workspace(prev, c, "focus");
39
40 // if the old workspace has no children, destroy it
41 if(prev->children->length == 0 && prev->floating->length == 0 && !suspend_workspace_cleanup) {
42 destroy_workspace(prev);
43 } else {
44 // update visibility of old workspace
45 update_visibility(prev);
46 }
47 }
48 // Update visibility of newly focused workspace
49 update_visibility(c);
50 break;
51
52 default:
53 case C_VIEW:
54 case C_CONTAINER:
55 break;
56 }
57 }
58}
59
60bool move_focus(enum movement_direction direction) {
61 swayc_t *old_view = get_focused_container(&root_container);
62 swayc_t *new_view = get_swayc_in_direction(old_view, direction);
63 if (!new_view) {
64 return false;
65 } else if (new_view->type == C_ROOT) {
66 sway_log(L_DEBUG, "Not setting focus above the workspace level");
67 return false;
68 } else if (new_view->type == C_OUTPUT) {
69 return set_focused_container(swayc_active_workspace_for(new_view));
70 } else if (direction == MOVE_PARENT || direction == MOVE_CHILD) {
71 return set_focused_container(new_view);
72 } else if (config->mouse_warping) {
73 swayc_t *old_op = old_view->type == C_OUTPUT ?
74 old_view : swayc_parent_by_type(old_view, C_OUTPUT);
75 swayc_t *focused = get_focused_view(new_view);
76 if (set_focused_container(focused)) {
77 if (old_op != swayc_active_output() && focused && focused->type == C_VIEW) {
78 center_pointer_on(focused);
79 }
80 return true;
81 }
82 } else {
83 return set_focused_container(get_focused_view(new_view));
84 }
85 return false;
86}
87
88swayc_t *get_focused_container(swayc_t *parent) {
89 if (!parent) {
90 return swayc_active_workspace();
91 }
92 while (!parent->is_focused && parent->focused) {
93 parent = parent->focused;
94 }
95 return parent;
96}
97
98bool set_focused_container(swayc_t *c) {
99 if (locked_container_focus || !c || !c->parent) {
100 return false;
101 }
102
103 // current ("old") workspace for sending workspace change event later
104 swayc_t *old_ws = swayc_active_workspace();
105 // keep track of child count so we can determine if it gets destroyed
106 int old_ws_child_count = 0;
107 if (old_ws) {
108 old_ws_child_count = old_ws->children->length + old_ws->floating->length;
109 }
110
111 // current ("old") focused container
112 swayc_t *old_focus = get_focused_container(&root_container);
113 // if old_focus is a workspace, then it's the same workspace as
114 // old_ws, and we'll need to null its pointer too, since it will
115 // be destroyed in the update_focus() call
116 bool old_focus_was_ws = (old_focus->type == C_WORKSPACE);
117
118 // workspace of new focused container
119 swayc_t *workspace = swayc_active_workspace_for(c);
120
121 if (swayc_is_fullscreen(get_focused_container(workspace))) {
122 // if switching to a workspace with a fullscreen view,
123 // focus on the fullscreen view
124 c = get_focused_container(workspace);
125 }
126
127 swayc_log(L_DEBUG, c, "Setting focus to %p:%" PRIuPTR, c, c->handle);
128
129 if (c->type == C_VIEW) {
130 // dispatch a window event
131 ipc_event_window(c, "focus");
132 }
133
134 // update the global pointer
135 current_focus = c;
136
137 // update container focus from here to root, making necessary changes along
138 // the way
139 swayc_t *p = c;
140 if (p->type != C_OUTPUT && p->type != C_ROOT) {
141 p->is_focused = true;
142 }
143 while (p != &root_container) {
144 update_focus(p);
145 p = p->parent;
146 p->is_focused = false;
147 }
148
149 if (old_focus_was_ws && old_ws_child_count == 0) {
150 // this workspace was destroyed in update_focus(), so null the pointers
151 old_focus = NULL;
152 old_ws = NULL;
153 }
154
155 if (!(wlc_view_get_type(p->handle) & WLC_BIT_POPUP)) {
156 if (old_focus) {
157 if (old_focus->type == C_VIEW) {
158 wlc_view_set_state(old_focus->handle, WLC_BIT_ACTIVATED, false);
159 }
160 update_container_border(old_focus);
161 }
162 if (c->type == C_VIEW) {
163 wlc_view_set_state(c->handle, WLC_BIT_ACTIVATED, true);
164 }
165 /* TODO WLR
166 if (!desktop_shell.is_locked) {
167 // If the system is locked, we do everything _but_ actually setting
168 // focus. This includes making our internals think that this view is
169 // focused.
170 wlc_view_focus(c->handle);
171 }
172 */
173 if (c->parent->layout != L_TABBED && c->parent->layout != L_STACKED) {
174 update_container_border(c);
175 }
176
177 swayc_t *parent = swayc_tabbed_stacked_ancestor(c);
178 if (parent != NULL) {
179 arrange_backgrounds();
180 arrange_windows(parent, -1, -1);
181 }
182 }
183
184 if (old_ws != workspace) {
185 // old_ws might be NULL here but that's ok
186 ipc_event_workspace(old_ws, workspace, "focus");
187 }
188
189 return true;
190}
191
192bool set_focused_container_for(swayc_t *a, swayc_t *c) {
193 if (locked_container_focus || !c) {
194 return false;
195 }
196 swayc_t *find = c;
197 while (find != a && (find = find->parent)) {
198 if (find == &root_container) {
199 return false;
200 }
201 }
202
203 // Get workspace for c, get that workspaces current focused container.
204 swayc_t *workspace = swayc_active_workspace_for(c);
205 swayc_t *focused = get_focused_view(workspace);
206 // if the workspace we are changing focus to has a fullscreen view return
207 if (swayc_is_fullscreen(focused) && c != focused) {
208 return false;
209 }
210
211 // Check if we are changing a parent container that will see change
212 bool effective = true;
213 while (find != &root_container) {
214 if (find->parent->focused != find) {
215 effective = false;
216 }
217 find = find->parent;
218 }
219 if (effective) {
220 // Go to set_focused_container
221 return set_focused_container(c);
222 }
223
224 sway_log(L_DEBUG, "Setting focus for %p:%" PRIuPTR " to %p:%" PRIuPTR,
225 a, a->handle, c, c->handle);
226
227 c->is_focused = true;
228 swayc_t *p = c;
229 while (p != a) {
230 update_focus(p);
231 p = p->parent;
232 p->is_focused = false;
233 }
234 return true;
235}
236
237swayc_t *get_focused_view(swayc_t *parent) {
238 swayc_t *c = parent;
239 while (c && c->type != C_VIEW) {
240 if (c->type == C_WORKSPACE && c->focused == NULL) {
241 return c;
242 }
243 c = c->focused;
244 }
245 if (c == NULL) {
246 c = swayc_active_workspace_for(parent);
247 }
248 return c;
249}
250
251swayc_t *get_focused_float(swayc_t *ws) {
252 if(!sway_assert(ws->type == C_WORKSPACE, "must be of workspace type")) {
253 ws = swayc_active_workspace();
254 }
255 if (ws->floating->length) {
256 return ws->floating->items[ws->floating->length - 1];
257 }
258 return NULL;
259}
260
261swayc_t *get_focused_view_include_floating(swayc_t *parent) {
262 swayc_t *c = parent;
263 swayc_t *f = NULL;
264
265 while (c && c->type != C_VIEW) {
266 if (c->type == C_WORKSPACE && c->focused == NULL) {
267 return ((f = get_focused_float(c))) ? f : c;
268 }
269
270 c = c->focused;
271 }
272
273 if (c == NULL) {
274 c = swayc_active_workspace_for(parent);
275 }
276
277 return c;
278}