7 #include "framerender.h"
15 Action *action_new(void (*func)(union ActionData *data))
17 Action *a = g_new0(Action, 1);
23 void action_free(Action *a)
25 if (a == NULL) return;
27 /* deal with pointers */
28 if (a->func == action_execute || a->func == action_restart)
29 g_free(a->data.execute.path);
30 else if (a->func == action_showmenu)
31 g_free(a->data.showmenu.name);
36 Action *action_from_string(char *name)
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;
185 void action_execute(union ActionData *data)
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);
195 void action_focus(union ActionData *data)
198 client_focus(data->client.c);
201 void action_unfocus (union ActionData *data)
204 client_unfocus(data->client.c);
207 void action_iconify(union ActionData *data)
210 client_iconify(data->client.c, TRUE, TRUE);
213 void action_focusraise(union ActionData *data)
215 if (data->client.c) {
216 client_focus(data->client.c);
217 stacking_raise(data->client.c);
221 void action_raise(union ActionData *data)
224 stacking_raise(data->client.c);
227 void action_unshaderaise(union ActionData *data)
229 if (data->client.c) {
230 if (data->client.c->shaded)
231 client_shade(data->client.c, FALSE);
233 stacking_raise(data->client.c);
237 void action_shadelower(union ActionData *data)
239 if (data->client.c) {
240 if (data->client.c->shaded)
241 stacking_lower(data->client.c);
243 client_shade(data->client.c, TRUE);
247 void action_lower(union ActionData *data)
250 stacking_lower(data->client.c);
253 void action_close(union ActionData *data)
256 client_close(data->client.c);
259 void action_kill(union ActionData *data)
262 client_kill(data->client.c);
265 void action_shade(union ActionData *data)
268 client_shade(data->client.c, TRUE);
271 void action_unshade(union ActionData *data)
274 client_shade(data->client.c, FALSE);
277 void action_toggle_shade(union ActionData *data)
280 client_shade(data->client.c, !data->client.c->shaded);
283 void action_toggle_omnipresent(union ActionData *data)
286 client_set_desktop(data->client.c,
287 data->client.c->desktop == DESKTOP_ALL ?
288 screen_desktop : DESKTOP_ALL, FALSE);
291 void action_move_relative_horz(union ActionData *data)
293 Client *c = data->relative.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);
300 void action_move_relative_vert(union ActionData *data)
302 Client *c = data->relative.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);
309 void action_resize_relative_horz(union ActionData *data)
311 Client *c = data->relative.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);
318 void action_resize_relative_vert(union ActionData *data)
320 Client *c = data->relative.c;
322 client_configure(c, Corner_TopLeft, c->area.x, c->area.y,
323 c->area.width, c->area.height + data->relative.delta,
327 void action_maximize_full(union ActionData *data)
330 client_maximize(data->client.c, TRUE, 0, TRUE);
333 void action_unmaximize_full(union ActionData *data)
336 client_maximize(data->client.c, FALSE, 0, TRUE);
339 void action_toggle_maximize_full(union ActionData *data)
342 client_maximize(data->client.c,
343 !(data->client.c->max_horz ||
344 data->client.c->max_vert),
348 void action_maximize_horz(union ActionData *data)
351 client_maximize(data->client.c, TRUE, 1, TRUE);
354 void action_unmaximize_horz(union ActionData *data)
357 client_maximize(data->client.c, FALSE, 1, TRUE);
360 void action_toggle_maximize_horz(union ActionData *data)
363 client_maximize(data->client.c, !data->client.c->max_horz, 1, TRUE);
366 void action_maximize_vert(union ActionData *data)
369 client_maximize(data->client.c, TRUE, 2, TRUE);
372 void action_unmaximize_vert(union ActionData *data)
375 client_maximize(data->client.c, FALSE, 2, TRUE);
378 void action_toggle_maximize_vert(union ActionData *data)
381 client_maximize(data->client.c, !data->client.c->max_vert, 2, TRUE);
384 void action_send_to_desktop(union ActionData *data)
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);
396 void action_send_to_next_desktop(union ActionData *data)
400 if (!data->sendtonextprev.c) return;
402 d = screen_desktop + 1;
403 if (d >= screen_num_desktops) {
404 if (!data->sendtonextprev.wrap) return;
407 client_set_desktop(data->sendtonextprev.c, d, data->sendtonextprev.follow);
408 if (data->sendtonextprev.follow) screen_set_desktop(d);
411 void action_send_to_previous_desktop(union ActionData *data)
415 if (!data->sendtonextprev.c) return;
417 d = screen_desktop - 1;
418 if (d >= screen_num_desktops) {
419 if (!data->sendtonextprev.wrap) return;
420 d = screen_num_desktops - 1;
422 client_set_desktop(data->sendtonextprev.c, d, data->sendtonextprev.follow);
423 if (data->sendtonextprev.follow) screen_set_desktop(d);
426 void action_desktop(union ActionData *data)
428 if (data->desktop.desk < screen_num_desktops ||
429 data->desktop.desk == DESKTOP_ALL)
430 screen_set_desktop(data->desktop.desk);
433 void action_next_desktop(union ActionData *data)
437 d = screen_desktop + 1;
438 if (d >= screen_num_desktops) {
439 if (!data->nextprevdesktop.wrap) return;
442 screen_set_desktop(d);
445 void action_previous_desktop(union ActionData *data)
449 d = screen_desktop - 1;
450 if (d >= screen_num_desktops) {
451 if (!data->nextprevdesktop.wrap) return;
452 d = screen_num_desktops - 1;
454 screen_set_desktop(d);
457 static void cur_row_col(guint *r, guint *c)
459 switch (screen_desktop_layout.orientation) {
460 case Orientation_Horz:
461 switch (screen_desktop_layout.start_corner) {
463 *r = screen_desktop / screen_desktop_layout.columns;
464 *c = screen_desktop % screen_desktop_layout.columns;
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;
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;
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;
484 case Orientation_Vert:
485 switch (screen_desktop_layout.start_corner) {
487 *r = screen_desktop % screen_desktop_layout.rows;
488 *c = screen_desktop / screen_desktop_layout.rows;
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;
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;
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;
511 static guint translate_row_col(guint r, guint c)
513 switch (screen_desktop_layout.orientation) {
514 case Orientation_Horz:
515 switch (screen_desktop_layout.start_corner) {
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);
529 case Orientation_Vert:
530 switch (screen_desktop_layout.start_corner) {
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);
545 g_assert_not_reached();
549 void action_next_desktop_column(union ActionData *data)
555 d = translate_row_col(r, c);
556 if (d >= screen_num_desktops) {
557 if (!data->nextprevdesktop.wrap) return;
560 if (d >= screen_num_desktops)
562 d = translate_row_col(r, c);
563 if (d < screen_num_desktops)
564 screen_set_desktop(d);
567 void action_previous_desktop_column(union ActionData *data)
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;
578 if (d >= screen_num_desktops)
580 d = translate_row_col(r, c);
581 if (d < screen_num_desktops)
582 screen_set_desktop(d);
585 void action_next_desktop_row(union ActionData *data)
591 d = translate_row_col(r, c);
592 if (d >= screen_num_desktops) {
593 if (!data->nextprevdesktop.wrap) return;
596 if (d >= screen_num_desktops)
598 d = translate_row_col(r, c);
599 if (d < screen_num_desktops)
600 screen_set_desktop(d);
603 void action_previous_desktop_row(union ActionData *data)
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;
614 if (d >= screen_num_desktops)
616 d = translate_row_col(r, c);
617 if (d < screen_num_desktops)
618 screen_set_desktop(d);
621 void action_toggle_decorations(union ActionData *data)
623 Client *c = data->client.c;;
627 c->disabled_decorations = c->disabled_decorations ? 0 : ~0;
628 client_setup_decor_and_functions(c);
631 static void popup_coords(char *format, Cursor cur, int a, int b, gboolean hide)
633 XSetWindowAttributes attrib;
634 static Window coords = None;
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);
643 grab_pointer(TRUE, cur);
645 XMapWindow(ob_display, coords);
649 XDestroyWindow(ob_display, coords);
652 grab_pointer(FALSE, None);
657 text = g_strdup_printf(format, a, b);
658 framerender_size_popup_label(text, &s);
659 XMoveResizeWindow(ob_display, coords,
660 10, 10, s.width, s.height);
661 framerender_popup_label(coords, &s, text);
666 void action_move(union ActionData *data)
668 Client *c = data->move.c;
669 int x = data->move.x;
670 int y = data->move.y;
672 if (!c || !client_normal(c)) return;
674 dispatch_move(c, &x, &y);
676 popup_coords("X: %d Y: %d", ob_cursors.move, x, y, data->move.final);
678 frame_frame_gravity(c->frame, &x, &y); /* get where the client should be */
679 client_configure(c, Corner_TopLeft, x, y, c->area.width, c->area.height,
680 TRUE, data->move.final);
683 void action_resize(union ActionData *data)
685 Client *c = data->resize.c;
687 int w = data->resize.x;
688 int h = data->resize.y;
690 if (!c || c->shaded || !client_normal(c)) return;
692 dispatch_resize(c, &w, &h, data->resize.corner);
694 w -= c->frame->size.left + c->frame->size.right;
695 h -= c->frame->size.top + c->frame->size.bottom;
697 client_configure(c, data->resize.corner, c->area.x, c->area.y, w, h,
698 TRUE, data->resize.final);
700 switch (data->resize.corner) {
704 case Corner_TopRight:
707 case Corner_BottomLeft:
710 case Corner_BottomRight:
715 popup_coords("W: %d H: %d", cur, c->logical_size.width,
716 c->logical_size.height, data->move.final);
719 void action_restart(union ActionData *data)
721 ob_restart_path = data->execute.path;
722 ob_shutdown = ob_restart = TRUE;
725 void action_exit(union ActionData *data)
730 void action_showmenu(union ActionData *data)
732 if (data->showmenu.name) {
733 menu_show(data->showmenu.name, data->showmenu.x, data->showmenu.y,
738 static void popup_cycle(Client *c, gboolean hide)
740 XSetWindowAttributes attrib;
741 static Window coords = None;
743 if (coords == None) {
744 attrib.override_redirect = TRUE;
745 coords = XCreateWindow(ob_display, ob_root,
746 0, 0, 1, 1, 0, render_depth, InputOutput,
747 render_visual, CWOverrideRedirect, &attrib);
748 g_assert(coords != None);
750 grab_pointer(TRUE, None);
752 XMapWindow(ob_display, coords);
756 XDestroyWindow(ob_display, coords);
759 grab_pointer(FALSE, None);
764 a = screen_area(c->desktop);
766 framerender_size_popup_label(c->title, &s);
767 XMoveResizeWindow(ob_display, coords,
768 a->x + (a->width - s.width) / 2,
769 a->y + (a->height - s.height) / 2,
771 framerender_popup_label(coords, &s, c->title);
775 void action_cycle_windows(union ActionData *data)
779 c = focus_cycle(data->cycle.forward, data->cycle.linear, data->cycle.final,
781 popup_cycle(c, !c || data->cycle.final || data->cycle.cancel);