]> git.lizzy.rs Git - bspwm.git/blob - pointer.c
Generalize window commands to nodes
[bspwm.git] / pointer.c
1 /* Copyright (c) 2012, Bastien Dejean
2  * All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are met:
6  *
7  * 1. Redistributions of source code must retain the above copyright notice, this
8  *    list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright notice,
10  *    this list of conditions and the following disclaimer in the documentation
11  *    and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
14  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
17  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
18  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
20  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
22  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23  */
24
25 #include <stdbool.h>
26 #include "bspwm.h"
27 #include "query.h"
28 #include "settings.h"
29 #include "stack.h"
30 #include "tree.h"
31 #include "monitor.h"
32 #include "subscribe.h"
33 #include "window.h"
34
35 void grab_pointer(pointer_action_t pac)
36 {
37         xcb_window_t win = XCB_NONE;
38         xcb_point_t pos;
39
40         query_pointer(&win, &pos);
41
42         coordinates_t loc;
43         if (locate_window(win, &loc)) {
44                 client_t *c = loc.node->client;
45
46                 frozen_pointer->position = pos;
47                 frozen_pointer->action = pac;
48                 frozen_pointer->monitor = loc.monitor;
49                 frozen_pointer->desktop = loc.desktop;
50                 frozen_pointer->node = loc.node;
51                 frozen_pointer->client = c;
52                 frozen_pointer->window = loc.node->id;
53                 frozen_pointer->horizontal_fence = NULL;
54                 frozen_pointer->vertical_fence = NULL;
55
56                 switch (pac)  {
57                         case ACTION_FOCUS:
58                                 if (loc.node != mon->desk->focus) {
59                                         bool backup = pointer_follows_monitor;
60                                         pointer_follows_monitor = false;
61                                         focus_node(loc.monitor, loc.desktop, loc.node);
62                                         pointer_follows_monitor = backup;
63                                 } else if (focus_follows_pointer) {
64                                         stack(loc.desktop, loc.node, true);
65                                 }
66                                 frozen_pointer->action = ACTION_NONE;
67                                 break;
68                         case ACTION_MOVE:
69                         case ACTION_RESIZE_SIDE:
70                         case ACTION_RESIZE_CORNER:
71                                 if (IS_FLOATING(c)) {
72                                         frozen_pointer->rectangle = c->floating_rectangle;
73                                         frozen_pointer->is_tiled = false;
74                                 } else if (IS_TILED(c)) {
75                                         frozen_pointer->rectangle = c->tiled_rectangle;
76                                         frozen_pointer->is_tiled = (pac == ACTION_MOVE || c->state != STATE_PSEUDO_TILED);
77                                 } else {
78                                         frozen_pointer->action = ACTION_NONE;
79                                         return;
80                                 }
81                                 if (pac == ACTION_RESIZE_SIDE) {
82                                         float W = frozen_pointer->rectangle.width;
83                                         float H = frozen_pointer->rectangle.height;
84                                         float ratio = W / H;
85                                         float x = pos.x - frozen_pointer->rectangle.x;
86                                         float y = pos.y - frozen_pointer->rectangle.y;
87                                         float diag_a = ratio * y;
88                                         float diag_b = W - diag_a;
89                                         if (x < diag_a) {
90                                                 if (x < diag_b) {
91                                                         frozen_pointer->side = SIDE_LEFT;
92                                                 } else {
93                                                         frozen_pointer->side = SIDE_BOTTOM;
94                                                 }
95                                         } else {
96                                                 if (x < diag_b) {
97                                                         frozen_pointer->side = SIDE_TOP;
98                                                 } else {
99                                                         frozen_pointer->side = SIDE_RIGHT;
100                                                 }
101                                         }
102                                 } else if (pac == ACTION_RESIZE_CORNER) {
103                                         int16_t mid_x = frozen_pointer->rectangle.x + (frozen_pointer->rectangle.width / 2);
104                                         int16_t mid_y = frozen_pointer->rectangle.y + (frozen_pointer->rectangle.height / 2);
105                                         if (pos.x > mid_x) {
106                                                 if (pos.y > mid_y) {
107                                                         frozen_pointer->corner = CORNER_BOTTOM_RIGHT;
108                                                 } else {
109                                                         frozen_pointer->corner = CORNER_TOP_RIGHT;
110                                                 }
111                                         } else {
112                                                 if (pos.y > mid_y) {
113                                                         frozen_pointer->corner = CORNER_BOTTOM_LEFT;
114                                                 } else {
115                                                         frozen_pointer->corner = CORNER_TOP_LEFT;
116                                                 }
117                                         }
118                                 }
119                                 if (frozen_pointer->is_tiled) {
120                                         if (pac == ACTION_RESIZE_SIDE) {
121                                                 switch (frozen_pointer->side) {
122                                                         case SIDE_TOP:
123                                                                 frozen_pointer->horizontal_fence = find_fence(loc.node, DIR_NORTH);
124                                                                 break;
125                                                         case SIDE_RIGHT:
126                                                                 frozen_pointer->vertical_fence = find_fence(loc.node, DIR_EAST);
127                                                                 break;
128                                                         case SIDE_BOTTOM:
129                                                                 frozen_pointer->horizontal_fence = find_fence(loc.node, DIR_SOUTH);
130                                                                 break;
131                                                         case SIDE_LEFT:
132                                                                 frozen_pointer->vertical_fence = find_fence(loc.node, DIR_WEST);
133                                                                 break;
134                                                 }
135                                         } else if (pac == ACTION_RESIZE_CORNER) {
136                                                 switch (frozen_pointer->corner) {
137                                                         case CORNER_TOP_LEFT:
138                                                                 frozen_pointer->horizontal_fence = find_fence(loc.node, DIR_NORTH);
139                                                                 frozen_pointer->vertical_fence = find_fence(loc.node, DIR_WEST);
140                                                                 break;
141                                                         case CORNER_TOP_RIGHT:
142                                                                 frozen_pointer->horizontal_fence = find_fence(loc.node, DIR_NORTH);
143                                                                 frozen_pointer->vertical_fence = find_fence(loc.node, DIR_EAST);
144                                                                 break;
145                                                         case CORNER_BOTTOM_RIGHT:
146                                                                 frozen_pointer->horizontal_fence = find_fence(loc.node, DIR_SOUTH);
147                                                                 frozen_pointer->vertical_fence = find_fence(loc.node, DIR_EAST);
148                                                                 break;
149                                                         case CORNER_BOTTOM_LEFT:
150                                                                 frozen_pointer->horizontal_fence = find_fence(loc.node, DIR_SOUTH);
151                                                                 frozen_pointer->vertical_fence = find_fence(loc.node, DIR_WEST);
152                                                                 break;
153                                                 }
154                                         }
155                                         if (frozen_pointer->horizontal_fence != NULL) {
156                                                 frozen_pointer->horizontal_ratio = frozen_pointer->horizontal_fence->split_ratio;
157                                         }
158                                         if (frozen_pointer->vertical_fence != NULL) {
159                                                 frozen_pointer->vertical_ratio = frozen_pointer->vertical_fence->split_ratio;
160                                         }
161                                 }
162                                 break;
163                         case ACTION_NONE:
164                                 break;
165                 }
166         } else {
167                 if (pac == ACTION_FOCUS) {
168                         monitor_t *m = monitor_from_point(pos);
169                         if (m != NULL && m != mon) {
170                                 focus_node(m, m->desk, m->desk->focus);
171                         }
172                 }
173                 frozen_pointer->action = ACTION_NONE;
174         }
175 }
176
177 void track_pointer(int root_x, int root_y)
178 {
179         if (frozen_pointer->action == ACTION_NONE) {
180                 return;
181         }
182
183         int delta_x, delta_y, x = 0, y = 0, w = 1, h = 1;
184
185         pointer_action_t pac = frozen_pointer->action;
186         monitor_t *m = frozen_pointer->monitor;
187         desktop_t *d = frozen_pointer->desktop;
188         node_t *n = frozen_pointer->node;
189         client_t *c = frozen_pointer->client;
190         xcb_window_t win = frozen_pointer->window;
191         xcb_rectangle_t rect = frozen_pointer->rectangle;
192         node_t *vertical_fence = frozen_pointer->vertical_fence;
193         node_t *horizontal_fence = frozen_pointer->horizontal_fence;
194
195         delta_x = root_x - frozen_pointer->position.x;
196         delta_y = root_y - frozen_pointer->position.y;
197
198         switch (pac) {
199                 case ACTION_MOVE:
200                         if (frozen_pointer->is_tiled) {
201                                 xcb_window_t pwin = XCB_NONE;
202                                 query_pointer(&pwin, NULL);
203                                 if (pwin == win) {
204                                         return;
205                                 }
206                                 coordinates_t loc;
207                                 bool is_managed = (pwin == XCB_NONE ? false : locate_window(pwin, &loc));
208                                 if (is_managed && !IS_FLOATING(loc.node->client) && loc.monitor == m) {
209                                         swap_nodes(m, d, n, m, d, loc.node);
210                                 } else {
211                                         if (is_managed && loc.monitor == m) {
212                                                 return;
213                                         } else if (!is_managed) {
214                                                 xcb_point_t pt = (xcb_point_t) {root_x, root_y};
215                                                 monitor_t *pmon = monitor_from_point(pt);
216                                                 if (pmon == NULL || pmon == m) {
217                                                         return;
218                                                 } else {
219                                                         loc.monitor = pmon;
220                                                         loc.desktop = pmon->desk;
221                                                 }
222                                         }
223                                         bool focused = (n == mon->desk->focus);
224                                         transfer_node(m, d, n, loc.monitor, loc.desktop, loc.desktop->focus);
225                                         if (focused) {
226                                                 focus_node(loc.monitor, loc.desktop, n);
227                                         }
228                                         frozen_pointer->monitor = loc.monitor;
229                                         frozen_pointer->desktop = loc.desktop;
230                                 }
231                         } else {
232                                 x = rect.x + delta_x;
233                                 y = rect.y + delta_y;
234                                 window_move(win, x, y);
235                                 c->floating_rectangle.x = x;
236                                 c->floating_rectangle.y = y;
237                                 xcb_point_t pt = (xcb_point_t) {root_x, root_y};
238                                 monitor_t *pmon = monitor_from_point(pt);
239                                 if (pmon == NULL || pmon == m) {
240                                         return;
241                                 }
242                                 bool focused = (n == mon->desk->focus);
243                                 transfer_node(m, d, n, pmon, pmon->desk, pmon->desk->focus);
244                                 if (focused) {
245                                         focus_node(pmon, pmon->desk, n);
246                                 }
247                                 frozen_pointer->monitor = pmon;
248                                 frozen_pointer->desktop = pmon->desk;
249                         }
250                         break;
251                 case ACTION_RESIZE_SIDE:
252                 case ACTION_RESIZE_CORNER:
253                         if (frozen_pointer->is_tiled) {
254                                 if (vertical_fence != NULL) {
255                                         double sr = frozen_pointer->vertical_ratio + (double) delta_x / vertical_fence->rectangle.width;
256                                         sr = MAX(0, sr);
257                                         sr = MIN(1, sr);
258                                         vertical_fence->split_ratio = sr;
259                                 }
260                                 if (horizontal_fence != NULL) {
261                                         double sr = frozen_pointer->horizontal_ratio + (double) delta_y / horizontal_fence->rectangle.height;
262                                         sr = MAX(0, sr);
263                                         sr = MIN(1, sr);
264                                         horizontal_fence->split_ratio = sr;
265                                 }
266                                 arrange(m, d);
267                         } else {
268                                 if (pac == ACTION_RESIZE_SIDE) {
269                                         switch (frozen_pointer->side) {
270                                                 case SIDE_TOP:
271                                                         x = rect.x;
272                                                         y = rect.y + delta_y;
273                                                         w = rect.width;
274                                                         h = rect.height - delta_y;
275                                                         break;
276                                                 case SIDE_RIGHT:
277                                                         x = rect.x;
278                                                         y = rect.y;
279                                                         w = rect.width + delta_x;
280                                                         h = rect.height;
281                                                         break;
282                                                 case SIDE_BOTTOM:
283                                                         x = rect.x;
284                                                         y = rect.y;
285                                                         w = rect.width;
286                                                         h = rect.height + delta_y;
287                                                         break;
288                                                 case SIDE_LEFT:
289                                                         x = rect.x + delta_x;
290                                                         y = rect.y;
291                                                         w = rect.width - delta_x;
292                                                         h = rect.height;
293                                                         break;
294                                         }
295                                 } else if (pac == ACTION_RESIZE_CORNER) {
296                                         switch (frozen_pointer->corner) {
297                                                 case CORNER_TOP_LEFT:
298                                                         x = rect.x + delta_x;
299                                                         y = rect.y + delta_y;
300                                                         w = rect.width - delta_x;
301                                                         h = rect.height - delta_y;
302                                                         break;
303                                                 case CORNER_TOP_RIGHT:
304                                                         x = rect.x;
305                                                         y = rect.y + delta_y;
306                                                         w = rect.width + delta_x;
307                                                         h = rect.height - delta_y;
308                                                         break;
309                                                 case CORNER_BOTTOM_LEFT:
310                                                         x = rect.x + delta_x;
311                                                         y = rect.y;
312                                                         w = rect.width - delta_x;
313                                                         h = rect.height + delta_y;
314                                                         break;
315                                                 case CORNER_BOTTOM_RIGHT:
316                                                         x = rect.x;
317                                                         y = rect.y;
318                                                         w = rect.width + delta_x;
319                                                         h = rect.height + delta_y;
320                                                         break;
321                                         }
322                                 }
323
324                                 int oldw = w, oldh = h;
325                                 restrain_floating_size(c, &w, &h);
326
327                                 if (c->state == STATE_FLOATING) {
328                                         if (oldw == w) {
329                                                 c->floating_rectangle.x = x;
330                                                 c->floating_rectangle.width = w;
331                                         }
332                                         if (oldh == h) {
333                                                 c->floating_rectangle.y = y;
334                                                 c->floating_rectangle.height = h;
335                                         }
336                                         window_move_resize(win, c->floating_rectangle.x,
337                                                                 c->floating_rectangle.y,
338                                                                 c->floating_rectangle.width,
339                                                                 c->floating_rectangle.height);
340                                 } else {
341                                         c->floating_rectangle.width = w;
342                                         c->floating_rectangle.height = h;
343                                         arrange(m, d);
344                                 }
345                         }
346                         break;
347                 case ACTION_FOCUS:
348                 case ACTION_NONE:
349                         break;
350         }
351 }
352
353 void ungrab_pointer(void)
354 {
355         if (frozen_pointer->action != ACTION_NONE) {
356                 xcb_rectangle_t r = get_rectangle(frozen_pointer->monitor, frozen_pointer->desktop, frozen_pointer->node);
357                 put_status(SBSC_MASK_NODE_GEOMETRY, "node_geometry %s %s 0x%X %ux%u+%i+%i\n", frozen_pointer->monitor->name, frozen_pointer->desktop->name, frozen_pointer->window, r.width, r.height, r.x, r.y);
358         }
359         frozen_pointer->action = ACTION_NONE;
360 }