]> Dogcows Code - chaz/openbox/blob - openbox/actions/desktop.c
Allow sending windows on other desktops to the current desktop.
[chaz/openbox] / openbox / actions / desktop.c
1 #include "openbox/actions.h"
2 #include "openbox/screen.h"
3 #include "openbox/client.h"
4 #include "openbox/openbox.h"
5 #include "obt/keyboard.h"
6
7 typedef enum {
8 LAST,
9 CURRENT,
10 RELATIVE,
11 ABSOLUTE
12 } SwitchType;
13
14 typedef struct {
15 SwitchType type;
16 union {
17 struct {
18 guint desktop;
19 } abs;
20
21 struct {
22 gboolean linear;
23 gboolean wrap;
24 ObDirection dir;
25 } rel;
26 } u;
27 gboolean send;
28 gboolean follow;
29 gboolean interactive;
30 } Options;
31
32 static gpointer setup_go_func(xmlNodePtr node,
33 ObActionsIPreFunc *pre,
34 ObActionsIInputFunc *input,
35 ObActionsICancelFunc *cancel,
36 ObActionsIPostFunc *post);
37 static gpointer setup_send_func(xmlNodePtr node,
38 ObActionsIPreFunc *pre,
39 ObActionsIInputFunc *input,
40 ObActionsICancelFunc *cancel,
41 ObActionsIPostFunc *post);
42 static void free_func(gpointer o);
43 static gboolean run_func(ObActionsData *data, gpointer options);
44
45 static gboolean i_pre_func(guint state, gpointer options);
46 static gboolean i_input_func(guint initial_state,
47 XEvent *e,
48 ObtIC *ic,
49 gpointer options,
50 gboolean *used);
51 static void i_post_func(gpointer options);
52
53 /* 3.4-compatibility */
54 static gpointer setup_go_last_func(xmlNodePtr node);
55 static gpointer setup_send_last_func(xmlNodePtr node);
56 static gpointer setup_go_abs_func(xmlNodePtr node);
57 static gpointer setup_send_abs_func(xmlNodePtr node);
58 static gpointer setup_go_next_func(xmlNodePtr node,
59 ObActionsIPreFunc *pre,
60 ObActionsIInputFunc *input,
61 ObActionsICancelFunc *cancel,
62 ObActionsIPostFunc *post);
63 static gpointer setup_send_next_func(xmlNodePtr node,
64 ObActionsIPreFunc *pre,
65 ObActionsIInputFunc *input,
66 ObActionsICancelFunc *cancel,
67 ObActionsIPostFunc *post);
68 static gpointer setup_go_prev_func(xmlNodePtr node,
69 ObActionsIPreFunc *pre,
70 ObActionsIInputFunc *input,
71 ObActionsICancelFunc *cancel,
72 ObActionsIPostFunc *post);
73 static gpointer setup_send_prev_func(xmlNodePtr node,
74 ObActionsIPreFunc *pre,
75 ObActionsIInputFunc *input,
76 ObActionsICancelFunc *cancel,
77 ObActionsIPostFunc *post);
78 static gpointer setup_go_left_func(xmlNodePtr node,
79 ObActionsIPreFunc *pre,
80 ObActionsIInputFunc *input,
81 ObActionsICancelFunc *cancel,
82 ObActionsIPostFunc *post);
83 static gpointer setup_send_left_func(xmlNodePtr node,
84 ObActionsIPreFunc *pre,
85 ObActionsIInputFunc *input,
86 ObActionsICancelFunc *cancel,
87 ObActionsIPostFunc *post);
88 static gpointer setup_go_right_func(xmlNodePtr node,
89 ObActionsIPreFunc *pre,
90 ObActionsIInputFunc *input,
91 ObActionsICancelFunc *cancel,
92 ObActionsIPostFunc *post);
93 static gpointer setup_send_right_func(xmlNodePtr node,
94 ObActionsIPreFunc *pre,
95 ObActionsIInputFunc *input,
96 ObActionsICancelFunc *cancel,
97 ObActionsIPostFunc *post);
98 static gpointer setup_go_up_func(xmlNodePtr node,
99 ObActionsIPreFunc *pre,
100 ObActionsIInputFunc *input,
101 ObActionsICancelFunc *cancel,
102 ObActionsIPostFunc *post);
103 static gpointer setup_send_up_func(xmlNodePtr node,
104 ObActionsIPreFunc *pre,
105 ObActionsIInputFunc *input,
106 ObActionsICancelFunc *cancel,
107 ObActionsIPostFunc *post);
108 static gpointer setup_go_down_func(xmlNodePtr node,
109 ObActionsIPreFunc *pre,
110 ObActionsIInputFunc *input,
111 ObActionsICancelFunc *cancel,
112 ObActionsIPostFunc *post);
113 static gpointer setup_send_down_func(xmlNodePtr node,
114 ObActionsIPreFunc *pre,
115 ObActionsIInputFunc *input,
116 ObActionsICancelFunc *cancel,
117 ObActionsIPostFunc *post);
118
119 void action_desktop_startup(void)
120 {
121 actions_register_i("GoToDesktop", setup_go_func, free_func, run_func);
122 actions_register_i("SendToDesktop", setup_send_func, free_func, run_func);
123 /* 3.4-compatibility */
124 actions_register("DesktopLast", setup_go_last_func, free_func, run_func);
125 actions_register("SendToDesktopLast", setup_send_last_func,
126 free_func, run_func);
127 actions_register("Desktop", setup_go_abs_func, free_func, run_func);
128 actions_register("SendToDesktop", setup_send_abs_func,
129 free_func, run_func);
130 actions_register_i("DesktopNext", setup_go_next_func, free_func, run_func);
131 actions_register_i("SendToDesktopNext", setup_send_next_func,
132 free_func, run_func);
133 actions_register_i("DesktopPrevious", setup_go_prev_func,
134 free_func, run_func);
135 actions_register_i("SendToDesktopPrevious", setup_send_prev_func,
136 free_func, run_func);
137 actions_register_i("DesktopLeft", setup_go_left_func, free_func, run_func);
138 actions_register_i("SendToDesktopLeft", setup_send_left_func,
139 free_func, run_func);
140 actions_register_i("DesktopRight", setup_go_right_func,
141 free_func, run_func);
142 actions_register_i("SendToDesktopRight", setup_send_right_func,
143 free_func, run_func);
144 actions_register_i("DesktopUp", setup_go_up_func, free_func, run_func);
145 actions_register_i("SendToDesktopUp", setup_send_up_func,
146 free_func, run_func);
147 actions_register_i("DesktopDown", setup_go_down_func, free_func, run_func);
148 actions_register_i("SendToDesktopDown", setup_send_down_func,
149 free_func, run_func);
150 }
151
152 static gpointer setup_func(xmlNodePtr node,
153 ObActionsIPreFunc *pre,
154 ObActionsIInputFunc *input,
155 ObActionsICancelFunc *cancel,
156 ObActionsIPostFunc *post)
157 {
158 xmlNodePtr n;
159 Options *o;
160
161 o = g_slice_new0(Options);
162 /* don't go anywhere if there are no options given */
163 o->type = ABSOLUTE;
164 o->u.abs.desktop = screen_desktop;
165 /* wrap by default - it's handy! */
166 o->u.rel.wrap = TRUE;
167
168 if ((n = obt_xml_find_node(node, "to"))) {
169 gchar *s = obt_xml_node_string(n);
170 if (!g_ascii_strcasecmp(s, "last"))
171 o->type = LAST;
172 else if (!g_ascii_strcasecmp(s, "current"))
173 o->type = CURRENT;
174 else if (!g_ascii_strcasecmp(s, "next")) {
175 o->type = RELATIVE;
176 o->u.rel.linear = TRUE;
177 o->u.rel.dir = OB_DIRECTION_EAST;
178 }
179 else if (!g_ascii_strcasecmp(s, "previous")) {
180 o->type = RELATIVE;
181 o->u.rel.linear = TRUE;
182 o->u.rel.dir = OB_DIRECTION_WEST;
183 }
184 else if (!g_ascii_strcasecmp(s, "north") ||
185 !g_ascii_strcasecmp(s, "up")) {
186 o->type = RELATIVE;
187 o->u.rel.dir = OB_DIRECTION_NORTH;
188 }
189 else if (!g_ascii_strcasecmp(s, "south") ||
190 !g_ascii_strcasecmp(s, "down")) {
191 o->type = RELATIVE;
192 o->u.rel.dir = OB_DIRECTION_SOUTH;
193 }
194 else if (!g_ascii_strcasecmp(s, "west") ||
195 !g_ascii_strcasecmp(s, "left")) {
196 o->type = RELATIVE;
197 o->u.rel.dir = OB_DIRECTION_WEST;
198 }
199 else if (!g_ascii_strcasecmp(s, "east") ||
200 !g_ascii_strcasecmp(s, "right")) {
201 o->type = RELATIVE;
202 o->u.rel.dir = OB_DIRECTION_EAST;
203 }
204 else {
205 o->type = ABSOLUTE;
206 o->u.abs.desktop = atoi(s) - 1;
207 }
208 g_free(s);
209 }
210
211 if ((n = obt_xml_find_node(node, "wrap")))
212 o->u.rel.wrap = obt_xml_node_bool(n);
213
214 return o;
215 }
216
217
218 static gpointer setup_go_func(xmlNodePtr node,
219 ObActionsIPreFunc *pre,
220 ObActionsIInputFunc *input,
221 ObActionsICancelFunc *cancel,
222 ObActionsIPostFunc *post)
223 {
224 Options *o;
225
226 o = setup_func(node, pre, input, cancel, post);
227 if (o->type == RELATIVE) {
228 o->interactive = TRUE;
229 *pre = i_pre_func;
230 *input = i_input_func;
231 *post = i_post_func;
232 }
233
234 return o;
235 }
236
237 static gpointer setup_send_func(xmlNodePtr node,
238 ObActionsIPreFunc *pre,
239 ObActionsIInputFunc *input,
240 ObActionsICancelFunc *cancel,
241 ObActionsIPostFunc *post)
242 {
243 xmlNodePtr n;
244 Options *o;
245
246 o = setup_func(node, pre, input, cancel, post);
247 o->send = TRUE;
248 o->follow = TRUE;
249
250 if ((n = obt_xml_find_node(node, "follow")))
251 o->follow = obt_xml_node_bool(n);
252
253 if (o->type == RELATIVE && o->follow) {
254 o->interactive = TRUE;
255 *pre = i_pre_func;
256 *input = i_input_func;
257 *post = i_post_func;
258 }
259
260 return o;
261 }
262
263 static void free_func(gpointer o)
264 {
265 g_slice_free(Options, o);
266 }
267
268 /* Always return FALSE because its not interactive */
269 static gboolean run_func(ObActionsData *data, gpointer options)
270 {
271 Options *o = options;
272 guint d;
273
274 switch (o->type) {
275 case LAST:
276 d = screen_last_desktop;
277 break;
278 case CURRENT:
279 d = screen_desktop;
280 break;
281 case ABSOLUTE:
282 d = o->u.abs.desktop;
283 break;
284 case RELATIVE:
285 d = screen_find_desktop(screen_desktop,
286 o->u.rel.dir, o->u.rel.wrap, o->u.rel.linear);
287 break;
288 default:
289 g_assert_not_reached();
290 }
291
292 if (d < screen_num_desktops &&
293 (d != screen_desktop ||
294 (data->client && data->client->desktop != screen_desktop))) {
295 gboolean go = TRUE;
296
297 actions_client_move(data, TRUE);
298 if (o->send && data->client && client_normal(data->client)) {
299 client_set_desktop(data->client, d, o->follow, FALSE);
300 go = o->follow;
301 }
302
303 if (go) {
304 screen_set_desktop(d, TRUE);
305 if (data->client)
306 client_bring_helper_windows(data->client);
307 }
308
309 actions_client_move(data, FALSE);
310 }
311
312 return o->interactive;
313 }
314
315 static gboolean i_input_func(guint initial_state,
316 XEvent *e,
317 ObtIC *ic,
318 gpointer options,
319 gboolean *used)
320 {
321 guint mods;
322
323 mods = obt_keyboard_only_modmasks(e->xkey.state);
324 if (e->type == KeyRelease) {
325 /* remove from the state the mask of the modifier key being
326 released, if it is a modifier key being released that is */
327 mods &= ~obt_keyboard_keyevent_to_modmask(e);
328 }
329
330 if (e->type == KeyPress) {
331 KeySym sym = obt_keyboard_keypress_to_keysym(e);
332
333 /* Escape cancels no matter what */
334 if (sym == XK_Escape)
335 return FALSE;
336
337 /* There were no modifiers and they pressed enter */
338 else if ((sym == XK_Return || sym == XK_KP_Enter) && !initial_state)
339 return FALSE;
340 }
341 /* They released the modifiers */
342 else if (e->type == KeyRelease && initial_state && !(mods & initial_state))
343 {
344 return FALSE;
345 }
346
347 return TRUE;
348 }
349
350 static gboolean i_pre_func(guint initial_state, gpointer options)
351 {
352 if (!initial_state) {
353 Options *o = options;
354 o->interactive = FALSE;
355 return FALSE;
356 }
357 else {
358 screen_show_desktop_popup(screen_desktop, TRUE);
359 return TRUE;
360 }
361 }
362
363 static void i_post_func(gpointer options)
364 {
365 screen_hide_desktop_popup();
366 }
367
368 /* 3.4-compatilibity */
369 static gpointer setup_follow(xmlNodePtr node)
370 {
371 xmlNodePtr n;
372 Options *o = g_slice_new0(Options);
373 o->send = TRUE;
374 o->follow = TRUE;
375 if ((n = obt_xml_find_node(node, "follow")))
376 o->follow = obt_xml_node_bool(n);
377 return o;
378 }
379
380 static gpointer setup_go_last_func(xmlNodePtr node)
381 {
382 Options *o = g_slice_new0(Options);
383 o->type = LAST;
384 return o;
385 }
386
387 static gpointer setup_send_last_func(xmlNodePtr node)
388 {
389 Options *o = setup_follow(node);
390 o->type = LAST;
391 return o;
392 }
393
394 static gpointer setup_go_abs_func(xmlNodePtr node)
395 {
396 xmlNodePtr n;
397 Options *o = g_slice_new0(Options);
398 o->type = ABSOLUTE;
399 if ((n = obt_xml_find_node(node, "desktop")))
400 o->u.abs.desktop = obt_xml_node_int(n) - 1;
401 else
402 o->u.abs.desktop = screen_desktop;
403 return o;
404 }
405
406 static gpointer setup_send_abs_func(xmlNodePtr node)
407 {
408 xmlNodePtr n;
409 Options *o = setup_follow(node);
410 o->type = ABSOLUTE;
411 if ((n = obt_xml_find_node(node, "desktop")))
412 o->u.abs.desktop = obt_xml_node_int(n) - 1;
413 else
414 o->u.abs.desktop = screen_desktop;
415 return o;
416 }
417
418 static void setup_rel(Options *o, xmlNodePtr node, gboolean lin,
419 ObDirection dir,
420 ObActionsIPreFunc *pre,
421 ObActionsIInputFunc *input,
422 ObActionsIPostFunc *post)
423 {
424 xmlNodePtr n;
425
426 o->type = RELATIVE;
427 o->u.rel.linear = lin;
428 o->u.rel.dir = dir;
429 o->u.rel.wrap = TRUE;
430
431 if ((n = obt_xml_find_node(node, "wrap")))
432 o->u.rel.wrap = obt_xml_node_bool(n);
433
434 if (input) {
435 o->interactive = TRUE;
436 *pre = i_pre_func;
437 *input = i_input_func;
438 *post = i_post_func;
439 }
440 }
441
442 static gpointer setup_go_next_func(xmlNodePtr node,
443 ObActionsIPreFunc *pre,
444 ObActionsIInputFunc *input,
445 ObActionsICancelFunc *cancel,
446 ObActionsIPostFunc *post)
447 {
448 Options *o = g_slice_new0(Options);
449 setup_rel(o, node, TRUE, OB_DIRECTION_EAST, pre, input, post);
450 return o;
451 }
452
453 static gpointer setup_send_next_func(xmlNodePtr node,
454 ObActionsIPreFunc *pre,
455 ObActionsIInputFunc *input,
456 ObActionsICancelFunc *cancel,
457 ObActionsIPostFunc *post)
458 {
459 Options *o = setup_follow(node);
460 setup_rel(o, node, TRUE, OB_DIRECTION_EAST,
461 pre, (o->follow ? input : NULL), post);
462 return o;
463 }
464
465 static gpointer setup_go_prev_func(xmlNodePtr node,
466 ObActionsIPreFunc *pre,
467 ObActionsIInputFunc *input,
468 ObActionsICancelFunc *cancel,
469 ObActionsIPostFunc *post)
470 {
471 Options *o = g_slice_new0(Options);
472 setup_rel(o, node, TRUE, OB_DIRECTION_WEST, pre, input, post);
473 return o;
474 }
475
476 static gpointer setup_send_prev_func(xmlNodePtr node,
477 ObActionsIPreFunc *pre,
478 ObActionsIInputFunc *input,
479 ObActionsICancelFunc *cancel,
480 ObActionsIPostFunc *post)
481 {
482 Options *o = setup_follow(node);
483 setup_rel(o, node, TRUE, OB_DIRECTION_WEST,
484 pre, (o->follow ? input : NULL), post);
485 return o;
486 }
487
488 static gpointer setup_go_left_func(xmlNodePtr node,
489 ObActionsIPreFunc *pre,
490 ObActionsIInputFunc *input,
491 ObActionsICancelFunc *cancel,
492 ObActionsIPostFunc *post)
493 {
494 Options *o = g_slice_new0(Options);
495 setup_rel(o, node, FALSE, OB_DIRECTION_WEST, pre, input, post);
496 return o;
497 }
498
499 static gpointer setup_send_left_func(xmlNodePtr node,
500 ObActionsIPreFunc *pre,
501 ObActionsIInputFunc *input,
502 ObActionsICancelFunc *cancel,
503 ObActionsIPostFunc *post)
504 {
505 Options *o = setup_follow(node);
506 setup_rel(o, node, FALSE, OB_DIRECTION_WEST,
507 pre, (o->follow ? input : NULL), post);
508 return o;
509 }
510
511 static gpointer setup_go_right_func(xmlNodePtr node,
512 ObActionsIPreFunc *pre,
513 ObActionsIInputFunc *input,
514 ObActionsICancelFunc *cancel,
515 ObActionsIPostFunc *post)
516 {
517 Options *o = g_slice_new0(Options);
518 setup_rel(o, node, FALSE, OB_DIRECTION_EAST, pre, input, post);
519 return o;
520 }
521
522 static gpointer setup_send_right_func(xmlNodePtr node,
523 ObActionsIPreFunc *pre,
524 ObActionsIInputFunc *input,
525 ObActionsICancelFunc *cancel,
526 ObActionsIPostFunc *post)
527 {
528 Options *o = setup_follow(node);
529 setup_rel(o, node, FALSE, OB_DIRECTION_EAST,
530 pre, (o->follow ? input : NULL), post);
531 return o;
532 }
533
534 static gpointer setup_go_up_func(xmlNodePtr node,
535 ObActionsIPreFunc *pre,
536 ObActionsIInputFunc *input,
537 ObActionsICancelFunc *cancel,
538 ObActionsIPostFunc *post)
539 {
540 Options *o = g_slice_new0(Options);
541 setup_rel(o, node, FALSE, OB_DIRECTION_NORTH, pre, input, post);
542 return o;
543 }
544
545 static gpointer setup_send_up_func(xmlNodePtr node,
546 ObActionsIPreFunc *pre,
547 ObActionsIInputFunc *input,
548 ObActionsICancelFunc *cancel,
549 ObActionsIPostFunc *post)
550 {
551 Options *o = setup_follow(node);
552 setup_rel(o, node, FALSE, OB_DIRECTION_NORTH,
553 pre, (o->follow ? input : NULL), post);
554 return o;
555 }
556
557 static gpointer setup_go_down_func(xmlNodePtr node,
558 ObActionsIPreFunc *pre,
559 ObActionsIInputFunc *input,
560 ObActionsICancelFunc *cancel,
561 ObActionsIPostFunc *post)
562 {
563 Options *o = g_slice_new0(Options);
564 setup_rel(o, node, FALSE, OB_DIRECTION_SOUTH, pre, input, post);
565 return o;
566 }
567
568 static gpointer setup_send_down_func(xmlNodePtr node,
569 ObActionsIPreFunc *pre,
570 ObActionsIInputFunc *input,
571 ObActionsICancelFunc *cancel,
572 ObActionsIPostFunc *post)
573 {
574 Options *o = setup_follow(node);
575 setup_rel(o, node, FALSE, OB_DIRECTION_SOUTH,
576 pre, (o->follow ? input : NULL), post);
577 return o;
578 }
This page took 0.059136 seconds and 5 git commands to generate.