move the openbox engine into librender and the kernel. the theme is loaded and stored...
[dana/openbox.git] / openbox / action.c
1 #include "client.h"
2 #include "focus.h"
3 #include "stacking.h"
4 #include "frame.h"
5 #include "framerender.h"
6 #include "screen.h"
7 #include "action.h"
8 #include "dispatch.h"
9 #include "openbox.h"
10
11 #include <glib.h>
12
13 Action *action_new(void (*func)(union ActionData *data))
14 {
15     Action *a = g_new0(Action, 1);
16     a->func = func;
17
18     /* deal with pointers */
19     if (func == action_execute)
20         a->data.execute.path = NULL;
21
22     return a;
23 }
24
25 void action_free(Action *a)
26 {
27     if (a == NULL) return;
28
29     /* deal with pointers */
30     if (a->func == action_execute || a->func == action_restart)
31         g_free(a->data.execute.path);
32
33     g_free(a);
34 }
35
36 Action *action_from_string(char *name)
37 {
38     Action *a = NULL;
39     if (!g_ascii_strcasecmp(name, "execute")) {
40         a = action_new(action_execute);
41     } else if (!g_ascii_strcasecmp(name, "focus")) {
42         a = action_new(action_focus);
43     } else if (!g_ascii_strcasecmp(name, "unfocus")) {
44         a = action_new(action_unfocus);
45     } else if (!g_ascii_strcasecmp(name, "iconify")) {
46         a = action_new(action_iconify);
47     } else if (!g_ascii_strcasecmp(name, "raise")) {
48         a = action_new(action_raise);
49     } else if (!g_ascii_strcasecmp(name, "lower")) {
50         a = action_new(action_lower);
51     } else if (!g_ascii_strcasecmp(name, "focusraise")) {
52         a = action_new(action_focusraise);
53     } else if (!g_ascii_strcasecmp(name, "close")) {
54         a = action_new(action_close);
55     } else if (!g_ascii_strcasecmp(name, "kill")) {
56         a = action_new(action_kill);
57     } else if (!g_ascii_strcasecmp(name, "shadelower")) {
58         a = action_new(action_shadelower);
59     } else if (!g_ascii_strcasecmp(name, "unshaderaise")) {
60         a = action_new(action_unshaderaise);
61     } else if (!g_ascii_strcasecmp(name, "shade")) {
62         a = action_new(action_shade);
63     } else if (!g_ascii_strcasecmp(name, "unshade")) {
64         a = action_new(action_unshade);
65     } else if (!g_ascii_strcasecmp(name, "toggleshade")) {
66         a = action_new(action_toggle_shade);
67     } else if (!g_ascii_strcasecmp(name, "toggleomnipresent")) {
68         a = action_new(action_toggle_omnipresent);
69     } else if (!g_ascii_strcasecmp(name, "moverelativehorz")) {
70         a = action_new(action_move_relative_horz);
71     } else if (!g_ascii_strcasecmp(name, "moverelativevert")) {
72         a = action_new(action_move_relative_vert);
73     } else if (!g_ascii_strcasecmp(name, "resizerelativehorz")) {
74         a = action_new(action_resize_relative_horz);
75     } else if (!g_ascii_strcasecmp(name, "resizerelativevert")) {
76         a = action_new(action_resize_relative_vert);
77     } else if (!g_ascii_strcasecmp(name, "maximizefull")) {
78         a = action_new(action_maximize_full);
79     } else if (!g_ascii_strcasecmp(name, "unmaximizefull")) {
80         a = action_new(action_unmaximize_full);
81     } else if (!g_ascii_strcasecmp(name, "togglemaximizefull")) {
82         a = action_new(action_toggle_maximize_full);
83     } else if (!g_ascii_strcasecmp(name, "maximizehorz")) {
84         a = action_new(action_maximize_horz);
85     } else if (!g_ascii_strcasecmp(name, "unmaximizehorz")) {
86         a = action_new(action_unmaximize_horz);
87     } else if (!g_ascii_strcasecmp(name, "togglemaximizehorz")) {
88         a = action_new(action_toggle_maximize_horz);
89     } else if (!g_ascii_strcasecmp(name, "maximizevert")) {
90         a = action_new(action_maximize_vert);
91     } else if (!g_ascii_strcasecmp(name, "unmaximizevert")) {
92         a = action_new(action_unmaximize_vert);
93     } else if (!g_ascii_strcasecmp(name, "togglemaximizevert")) {
94         a = action_new(action_toggle_maximize_vert);
95     } else if (!g_ascii_strcasecmp(name, "sendtodesktop")) {
96         a = action_new(action_send_to_desktop);
97         a->data.sendto.follow = TRUE;
98     } else if (!g_ascii_strcasecmp(name, "sendtonextdesktop")) {
99         a = action_new(action_send_to_next_desktop);
100         a->data.sendtonextprev.wrap = FALSE;
101         a->data.sendtonextprev.follow = TRUE;
102     } else if (!g_ascii_strcasecmp(name, "sendtonextdesktopwrap")) {
103         a = action_new(action_send_to_next_desktop);
104         a->data.sendtonextprev.wrap = TRUE;
105         a->data.sendtonextprev.follow = TRUE;
106     } else if (!g_ascii_strcasecmp(name, "sendtopreviousdesktop")) {
107         a = action_new(action_send_to_previous_desktop);
108         a->data.sendtonextprev.wrap = FALSE;
109         a->data.sendtonextprev.follow = TRUE;
110     } else if (!g_ascii_strcasecmp(name, "sendtopreviousdesktopwrap")) {
111         a = action_new(action_send_to_previous_desktop);
112         a->data.sendtonextprev.wrap = TRUE;
113         a->data.sendtonextprev.follow = TRUE;
114     } else if (!g_ascii_strcasecmp(name, "desktop")) {
115         a = action_new(action_desktop);
116     } else if (!g_ascii_strcasecmp(name, "nextdesktop")) {
117         a = action_new(action_next_desktop);
118         a->data.nextprevdesktop.wrap = FALSE;
119     } else if (!g_ascii_strcasecmp(name, "nextdesktopwrap")) {
120         a = action_new(action_next_desktop);
121         a->data.nextprevdesktop.wrap = TRUE;
122     } else if (!g_ascii_strcasecmp(name, "previousdesktop")) {
123         a = action_new(action_previous_desktop);
124         a->data.nextprevdesktop.wrap = FALSE;
125     } else if (!g_ascii_strcasecmp(name, "previousdesktopwrap")) {
126         a = action_new(action_previous_desktop);
127         a->data.nextprevdesktop.wrap = TRUE;
128     } else if (!g_ascii_strcasecmp(name, "nextdesktopcolumn")) {
129         a = action_new(action_next_desktop_column);
130         a->data.nextprevdesktop.wrap = FALSE;
131     } else if (!g_ascii_strcasecmp(name, "nextdesktopcolumnwrap")) {
132         a = action_new(action_next_desktop_column);
133         a->data.nextprevdesktop.wrap = TRUE;
134     } else if (!g_ascii_strcasecmp(name, "previousdesktopcolumn")) {
135         a = action_new(action_previous_desktop_column);
136         a->data.nextprevdesktop.wrap = FALSE;
137     } else if (!g_ascii_strcasecmp(name, "previousdesktopcolumnwrap")) {
138         a = action_new(action_previous_desktop_column);
139         a->data.nextprevdesktop.wrap = TRUE;
140     } else if (!g_ascii_strcasecmp(name, "nextdesktoprow")) {
141         a = action_new(action_next_desktop_row);
142         a->data.nextprevdesktop.wrap = FALSE;
143     } else if (!g_ascii_strcasecmp(name, "nextdesktoprowwrap")) {
144         a = action_new(action_next_desktop_row);
145         a->data.nextprevdesktop.wrap = TRUE;
146     } else if (!g_ascii_strcasecmp(name, "previousdesktoprow")) {
147         a = action_new(action_previous_desktop_row);
148         a->data.nextprevdesktop.wrap = FALSE;
149     } else if (!g_ascii_strcasecmp(name, "previousdesktoprowwrap")) {
150         a = action_new(action_previous_desktop_row);
151         a->data.nextprevdesktop.wrap = TRUE;
152     } else if (!g_ascii_strcasecmp(name, "toggledecorations")) {
153         a = action_new(action_toggle_decorations);
154     } else if (!g_ascii_strcasecmp(name, "move")) {
155         a = action_new(action_move);
156     } else if (!g_ascii_strcasecmp(name, "resize")) {
157         a = action_new(action_resize);
158     } else if (!g_ascii_strcasecmp(name, "restart")) {
159         a = action_new(action_restart);
160     } else if (!g_ascii_strcasecmp(name, "exit")) {
161         a = action_new(action_exit);
162     } else if (!g_ascii_strcasecmp(name, "showmenu")) {
163         a = action_new(action_showmenu);
164     } else if (!g_ascii_strcasecmp(name, "nextwindowlinear")) {
165         a = action_new(action_cycle_windows);
166         a->data.cycle.linear = TRUE;
167         a->data.cycle.forward = TRUE;
168     } else if (!g_ascii_strcasecmp(name, "previouswindowlinear")) {
169         a = action_new(action_cycle_windows);
170         a->data.cycle.linear = TRUE;
171         a->data.cycle.forward = FALSE;
172     } else if (!g_ascii_strcasecmp(name, "nextwindow")) {
173         a = action_new(action_cycle_windows);
174         a->data.cycle.linear = FALSE;
175         a->data.cycle.forward = TRUE;
176     } else if (!g_ascii_strcasecmp(name, "previouswindow")) {
177         a = action_new(action_cycle_windows);
178         a->data.cycle.linear = FALSE;
179         a->data.cycle.forward = FALSE;
180     }
181     
182     return a;
183 }
184
185 void action_execute(union ActionData *data)
186 {
187     GError *e = NULL;
188     if (data->execute.path)
189         if (!g_spawn_command_line_async(data->execute.path, &e)) {
190             g_warning("failed to execute '%s': %s",
191                       data->execute.path, e->message);
192         }
193 }
194
195 void action_focus(union ActionData *data)
196 {
197     if (data->client.c)
198         client_focus(data->client.c);
199 }
200
201 void action_unfocus (union ActionData *data)
202 {
203     if (data->client.c)
204         client_unfocus(data->client.c);
205 }
206
207 void action_iconify(union ActionData *data)
208 {
209     if (data->client.c)
210         client_iconify(data->client.c, TRUE, TRUE);
211 }
212
213 void action_focusraise(union ActionData *data)
214 {
215     if (data->client.c) {
216         client_focus(data->client.c);
217         stacking_raise(data->client.c);
218     }
219 }
220
221 void action_raise(union ActionData *data)
222 {
223     if (data->client.c)
224         stacking_raise(data->client.c);
225 }
226
227 void action_unshaderaise(union ActionData *data)
228 {
229     if (data->client.c) {
230         if (data->client.c->shaded)
231             client_shade(data->client.c, FALSE);
232         else
233             stacking_raise(data->client.c);
234     }
235 }
236
237 void action_shadelower(union ActionData *data)
238 {
239     if (data->client.c) {
240         if (data->client.c->shaded)
241             stacking_lower(data->client.c);
242         else
243             client_shade(data->client.c, TRUE);
244     }
245 }
246
247 void action_lower(union ActionData *data)
248 {
249     if (data->client.c)
250         stacking_lower(data->client.c);
251 }
252
253 void action_close(union ActionData *data)
254 {
255     if (data->client.c)
256         client_close(data->client.c);
257 }
258
259 void action_kill(union ActionData *data)
260 {
261     if (data->client.c)
262         client_kill(data->client.c);
263 }
264
265 void action_shade(union ActionData *data)
266 {
267     if (data->client.c)
268         client_shade(data->client.c, TRUE);
269 }
270
271 void action_unshade(union ActionData *data)
272 {
273     if (data->client.c)
274         client_shade(data->client.c, FALSE);
275 }
276
277 void action_toggle_shade(union ActionData *data)
278 {
279     if (data->client.c)
280         client_shade(data->client.c, !data->client.c->shaded);
281 }
282
283 void action_toggle_omnipresent(union ActionData *data)
284
285     if (data->client.c)
286         client_set_desktop(data->client.c,
287                            data->client.c->desktop == DESKTOP_ALL ?
288                            screen_desktop : DESKTOP_ALL, FALSE);
289 }
290
291 void action_move_relative_horz(union ActionData *data)
292 {
293     Client *c = data->relative.c;
294     if (c)
295         client_configure(c, Corner_TopLeft,
296                          c->area.x + data->relative.delta, c->area.y,
297                          c->area.width, c->area.height, TRUE, TRUE);
298 }
299
300 void action_move_relative_vert(union ActionData *data)
301 {
302     Client *c = data->relative.c;
303     if (c)
304         client_configure(c, Corner_TopLeft,
305                          c->area.x, c->area.y + data->relative.delta,
306                          c->area.width, c->area.height, TRUE, TRUE);
307 }
308
309 void action_resize_relative_horz(union ActionData *data)
310 {
311     Client *c = data->relative.c;
312     if (c)
313         client_configure(c, Corner_TopLeft, c->area.x, c->area.y,
314                          c->area.width + data->relative.delta,
315                          c->area.height, TRUE, TRUE);
316 }
317
318 void action_resize_relative_vert(union ActionData *data)
319 {
320     Client *c = data->relative.c;
321     if (c && !c->shaded)
322         client_configure(c, Corner_TopLeft, c->area.x, c->area.y,
323                          c->area.width, c->area.height + data->relative.delta,
324                          TRUE, TRUE);
325 }
326
327 void action_maximize_full(union ActionData *data)
328 {
329     if (data->client.c)
330         client_maximize(data->client.c, TRUE, 0, TRUE);
331 }
332
333 void action_unmaximize_full(union ActionData *data)
334 {
335     if (data->client.c)
336         client_maximize(data->client.c, FALSE, 0, TRUE);
337 }
338
339 void action_toggle_maximize_full(union ActionData *data)
340 {
341     if (data->client.c)
342         client_maximize(data->client.c,
343                         !(data->client.c->max_horz ||
344                           data->client.c->max_vert),
345                         0, TRUE);
346 }
347
348 void action_maximize_horz(union ActionData *data)
349 {
350     if (data->client.c)
351         client_maximize(data->client.c, TRUE, 1, TRUE);
352 }
353
354 void action_unmaximize_horz(union ActionData *data)
355 {
356     if (data->client.c)
357         client_maximize(data->client.c, FALSE, 1, TRUE);
358 }
359
360 void action_toggle_maximize_horz(union ActionData *data)
361 {
362     if (data->client.c)
363         client_maximize(data->client.c, !data->client.c->max_horz, 1, TRUE);
364 }
365
366 void action_maximize_vert(union ActionData *data)
367 {
368     if (data->client.c)
369         client_maximize(data->client.c, TRUE, 2, TRUE);
370 }
371
372 void action_unmaximize_vert(union ActionData *data)
373 {
374     if (data->client.c)
375         client_maximize(data->client.c, FALSE, 2, TRUE);
376 }
377
378 void action_toggle_maximize_vert(union ActionData *data)
379 {
380     if (data->client.c)
381         client_maximize(data->client.c, !data->client.c->max_vert, 2, TRUE);
382 }
383
384 void action_send_to_desktop(union ActionData *data)
385 {
386     if (data->sendto.c) {
387         if (data->sendto.desk < screen_num_desktops ||
388             data->sendto.desk == DESKTOP_ALL) {
389             client_set_desktop(data->desktop.c,
390                                data->sendto.desk, data->sendto.follow);
391             if (data->sendto.follow) screen_set_desktop(data->sendto.desk);
392         }
393     }
394 }
395
396 void action_send_to_next_desktop(union ActionData *data)
397 {
398     guint d;
399
400     if (!data->sendtonextprev.c) return;
401
402     d = screen_desktop + 1;
403     if (d >= screen_num_desktops) {
404         if (!data->sendtonextprev.wrap) return;
405         d = 0;
406     }
407     client_set_desktop(data->sendtonextprev.c, d, data->sendtonextprev.follow);
408     if (data->sendtonextprev.follow) screen_set_desktop(d);
409 }
410
411 void action_send_to_previous_desktop(union ActionData *data)
412 {
413     guint d;
414
415     if (!data->sendtonextprev.c) return;
416
417     d = screen_desktop - 1;
418     if (d >= screen_num_desktops) {
419         if (!data->sendtonextprev.wrap) return;
420         d = screen_num_desktops - 1;
421     }
422     client_set_desktop(data->sendtonextprev.c, d, data->sendtonextprev.follow);
423     if (data->sendtonextprev.follow) screen_set_desktop(d);
424 }
425
426 void action_desktop(union ActionData *data)
427 {
428     if (data->desktop.desk < screen_num_desktops ||
429         data->desktop.desk == DESKTOP_ALL)
430         screen_set_desktop(data->desktop.desk);
431 }
432
433 void action_next_desktop(union ActionData *data)
434 {
435     guint d;
436
437     d = screen_desktop + 1;
438     if (d >= screen_num_desktops) {
439         if (!data->nextprevdesktop.wrap) return;
440         d = 0;
441     }
442     screen_set_desktop(d);
443 }
444
445 void action_previous_desktop(union ActionData *data)
446 {
447     guint d;
448
449     d = screen_desktop - 1;
450     if (d >= screen_num_desktops) {
451         if (!data->nextprevdesktop.wrap) return;
452         d = screen_num_desktops - 1;
453     }
454     screen_set_desktop(d);
455 }
456
457 static void cur_row_col(guint *r, guint *c)
458 {
459     switch (screen_desktop_layout.orientation) {
460     case Orientation_Horz:
461         switch (screen_desktop_layout.start_corner) {
462         case Corner_TopLeft:
463             *r = screen_desktop / screen_desktop_layout.columns;
464             *c = screen_desktop % screen_desktop_layout.columns;
465             break;
466         case Corner_BottomLeft:
467             *r = screen_desktop_layout.rows - 1 -
468                 screen_desktop / screen_desktop_layout.columns;
469             *c = screen_desktop % screen_desktop_layout.columns;
470             break;
471         case Corner_TopRight:
472             *r = screen_desktop / screen_desktop_layout.columns;
473             *c = screen_desktop_layout.columns - 1 -
474                 screen_desktop % screen_desktop_layout.columns;
475             break;
476         case Corner_BottomRight:
477             *r = screen_desktop_layout.rows - 1 -
478                 screen_desktop / screen_desktop_layout.columns;
479             *c = screen_desktop_layout.columns - 1 -
480                 screen_desktop % screen_desktop_layout.columns;
481             break;
482         }
483         break;
484     case Orientation_Vert:
485         switch (screen_desktop_layout.start_corner) {
486         case Corner_TopLeft:
487             *r = screen_desktop % screen_desktop_layout.rows;
488             *c = screen_desktop / screen_desktop_layout.rows;
489             break;
490         case Corner_BottomLeft:
491             *r = screen_desktop_layout.rows - 1 -
492                 screen_desktop % screen_desktop_layout.rows;
493             *c = screen_desktop / screen_desktop_layout.rows;
494             break;
495         case Corner_TopRight:
496             *r = screen_desktop % screen_desktop_layout.rows;
497             *c = screen_desktop_layout.columns - 1 -
498                 screen_desktop / screen_desktop_layout.rows;
499             break;
500         case Corner_BottomRight:
501             *r = screen_desktop_layout.rows - 1 -
502                 screen_desktop % screen_desktop_layout.rows;
503             *c = screen_desktop_layout.columns - 1 -
504                 screen_desktop / screen_desktop_layout.rows;
505             break;
506         }
507         break;
508     }
509 }
510
511 static guint translate_row_col(guint r, guint c)
512 {
513     switch (screen_desktop_layout.orientation) {
514     case Orientation_Horz:
515         switch (screen_desktop_layout.start_corner) {
516         case Corner_TopLeft:
517             return r * screen_desktop_layout.columns + c;
518         case Corner_BottomLeft:
519             return (screen_desktop_layout.rows - 1 - r) *
520                 screen_desktop_layout.columns + c;
521         case Corner_TopRight:
522             return r * screen_desktop_layout.columns +
523                 (screen_desktop_layout.columns - 1 - c);
524         case Corner_BottomRight:
525             return (screen_desktop_layout.rows - 1 - r) *
526                 screen_desktop_layout.columns +
527                 (screen_desktop_layout.columns - 1 - c);
528         }
529     case Orientation_Vert:
530         switch (screen_desktop_layout.start_corner) {
531         case Corner_TopLeft:
532             return c * screen_desktop_layout.rows + r;
533         case Corner_BottomLeft:
534             return c * screen_desktop_layout.rows +
535                 (screen_desktop_layout.rows - 1 - r);
536         case Corner_TopRight:
537             return (screen_desktop_layout.columns - 1 - c) *
538                 screen_desktop_layout.rows + r;
539         case Corner_BottomRight:
540             return (screen_desktop_layout.columns - 1 - c) *
541                 screen_desktop_layout.rows +
542                 (screen_desktop_layout.rows - 1 - r);
543         }
544     }
545     g_assert_not_reached();
546     return 0;
547 }
548
549 void action_next_desktop_column(union ActionData *data)
550 {
551     guint r, c, d;
552
553     cur_row_col(&r, &c);
554     ++c;
555     d = translate_row_col(r, c);
556     if (d >= screen_num_desktops) {
557         if (!data->nextprevdesktop.wrap) return;
558         c = 0;
559     }
560     if (d >= screen_num_desktops)
561         ++c;
562     d = translate_row_col(r, c);
563     if (d < screen_num_desktops)
564         screen_set_desktop(d);
565 }
566
567 void action_previous_desktop_column(union ActionData *data)
568 {
569     guint r, c, d;
570
571     cur_row_col(&r, &c);
572     --c;
573     d = translate_row_col(r, c);
574     if (d >= screen_num_desktops) {
575         if (!data->nextprevdesktop.wrap) return;
576         c = screen_desktop_layout.columns - 1;
577     }
578     if (d >= screen_num_desktops)
579         --c;
580     d = translate_row_col(r, c);
581     if (d < screen_num_desktops)
582         screen_set_desktop(d);
583 }
584
585 void action_next_desktop_row(union ActionData *data)
586 {
587     guint r, c, d;
588
589     cur_row_col(&r, &c);
590     ++r;
591     d = translate_row_col(r, c);
592     if (d >= screen_num_desktops) {
593         if (!data->nextprevdesktop.wrap) return;
594         r = 0;
595     }
596     if (d >= screen_num_desktops)
597         ++r;
598     d = translate_row_col(r, c);
599     if (d < screen_num_desktops)
600         screen_set_desktop(d);
601 }
602
603 void action_previous_desktop_row(union ActionData *data)
604 {
605     guint r, c, d;
606
607     cur_row_col(&r, &c);
608     --r;
609     d = translate_row_col(r, c);
610     if (d >= screen_num_desktops) {
611         if (!data->nextprevdesktop.wrap) return;
612         c = screen_desktop_layout.rows - 1;
613     }
614     if (d >= screen_num_desktops)
615         --r;
616     d = translate_row_col(r, c);
617     if (d < screen_num_desktops)
618         screen_set_desktop(d);
619 }
620
621 void action_toggle_decorations(union ActionData *data)
622 {
623     Client *c = data->client.c;;
624
625     if (!c) return;
626
627     c->disabled_decorations = c->disabled_decorations ? 0 : ~0;
628     client_setup_decor_and_functions(c);
629 }
630
631 static void popup_coords(char *format, int a, int b, gboolean hide)
632 {
633     XSetWindowAttributes attrib;
634     static Window coords = None;
635
636     if (coords == None) {
637         attrib.override_redirect = TRUE;
638         coords = XCreateWindow(ob_display, ob_root,
639                                0, 0, 1, 1, 0, render_depth, InputOutput,
640                                render_visual, CWOverrideRedirect, &attrib);
641         g_assert(coords != None);
642     }
643
644     if (hide)
645         XUnmapWindow(ob_display, coords);
646     else {
647         Size s;
648         char *text;
649
650         text = g_strdup_printf(format, a, b);
651         framerender_size_popup_label(text, &s);
652         XMoveResizeWindow(ob_display, coords,
653                           10, 10, s.width, s.height);
654         framerender_popup_label(coords, &s, text);
655         g_free(text);
656
657         XMapWindow(ob_display, coords);
658     }
659 }
660
661 void action_move(union ActionData *data)
662 {
663     Client *c = data->move.c;
664     int x = data->move.x;
665     int y = data->move.y;
666
667     if (!c || !client_normal(c)) return;
668
669     dispatch_move(c, &x, &y);
670
671     popup_coords("X:  %d  Y:  %d", x, y, data->move.final);
672
673     frame_frame_gravity(c->frame, &x, &y); /* get where the client should be */
674     client_configure(c, Corner_TopLeft, x, y, c->area.width, c->area.height,
675                      TRUE, data->move.final);
676 }
677
678 void action_resize(union ActionData *data)
679 {
680     Client *c = data->resize.c;
681     int w = data->resize.x;
682     int h = data->resize.y;
683  
684     if (!c || c->shaded || !client_normal(c)) return;
685
686     dispatch_resize(c, &w, &h, data->resize.corner);
687
688     w -= c->frame->size.left + c->frame->size.right;
689     h -= c->frame->size.top + c->frame->size.bottom;
690     
691     client_configure(c, data->resize.corner, c->area.x, c->area.y, w, h,
692                      TRUE, data->resize.final);
693
694     popup_coords("W:  %d  H:  %d", c->logical_size.width,
695                  c->logical_size.height, data->move.final);
696 }
697
698 void action_restart(union ActionData *data)
699 {
700     ob_restart_path = data->execute.path;
701     ob_shutdown = ob_restart = TRUE;
702 }
703
704 void action_exit(union ActionData *data)
705 {
706     ob_shutdown = TRUE;
707 }
708
709 void action_showmenu(union ActionData *data)
710 {
711     g_message(__FUNCTION__);
712 }
713
714 static void popup_cycle(Client *c, gboolean hide)
715 {
716     XSetWindowAttributes attrib;
717     static Window coords = None;
718
719     if (coords == None) {
720         attrib.override_redirect = TRUE;
721         coords = XCreateWindow(ob_display, ob_root,
722                                0, 0, 1, 1, 0, render_depth, InputOutput,
723                                render_visual, CWOverrideRedirect, &attrib);
724         g_assert(coords != None);
725     }
726
727     if (hide)
728         XUnmapWindow(ob_display, coords);
729     else {
730         Rect *a;
731         Size s;
732
733         a = screen_area(c->desktop);
734
735         framerender_size_popup_label(c->title, &s);
736         XMoveResizeWindow(ob_display, coords,
737                           a->x + (a->width - s.width) / 2,
738                           a->y + (a->height - s.height) / 2,
739                           s.width, s.height);
740         framerender_popup_label(coords, &s, c->title);
741
742         XMapWindow(ob_display, coords);
743     }
744 }
745
746 void action_cycle_windows(union ActionData *data)
747 {
748     Client *c;
749     
750     c = focus_cycle(data->cycle.forward, data->cycle.linear, data->cycle.final,
751                     data->cycle.cancel);
752     popup_cycle(c, !c || data->cycle.final || data->cycle.cancel);
753 }
754