6 #include "framerender.h"
14 Action *action_new(void (*func)(union ActionData *data))
16 Action *a = g_new0(Action, 1);
22 void action_free(Action *a)
24 if (a == NULL) return;
26 /* deal with pointers */
27 if (a->func == action_execute || a->func == action_restart)
28 g_free(a->data.execute.path);
29 else if (a->func == action_showmenu)
30 g_free(a->data.showmenu.name);
35 Action *action_from_string(char *name)
38 if (!g_ascii_strcasecmp(name, "execute")) {
39 a = action_new(action_execute);
40 } else if (!g_ascii_strcasecmp(name, "focus")) {
41 a = action_new(action_focus);
42 } else if (!g_ascii_strcasecmp(name, "unfocus")) {
43 a = action_new(action_unfocus);
44 } else if (!g_ascii_strcasecmp(name, "iconify")) {
45 a = action_new(action_iconify);
46 } else if (!g_ascii_strcasecmp(name, "raise")) {
47 a = action_new(action_raise);
48 } else if (!g_ascii_strcasecmp(name, "lower")) {
49 a = action_new(action_lower);
50 } else if (!g_ascii_strcasecmp(name, "focusraise")) {
51 a = action_new(action_focusraise);
52 } else if (!g_ascii_strcasecmp(name, "close")) {
53 a = action_new(action_close);
54 } else if (!g_ascii_strcasecmp(name, "kill")) {
55 a = action_new(action_kill);
56 } else if (!g_ascii_strcasecmp(name, "shadelower")) {
57 a = action_new(action_shadelower);
58 } else if (!g_ascii_strcasecmp(name, "unshaderaise")) {
59 a = action_new(action_unshaderaise);
60 } else if (!g_ascii_strcasecmp(name, "shade")) {
61 a = action_new(action_shade);
62 } else if (!g_ascii_strcasecmp(name, "unshade")) {
63 a = action_new(action_unshade);
64 } else if (!g_ascii_strcasecmp(name, "toggleshade")) {
65 a = action_new(action_toggle_shade);
66 } else if (!g_ascii_strcasecmp(name, "toggleomnipresent")) {
67 a = action_new(action_toggle_omnipresent);
68 } else if (!g_ascii_strcasecmp(name, "moverelativehorz")) {
69 a = action_new(action_move_relative_horz);
70 } else if (!g_ascii_strcasecmp(name, "moverelativevert")) {
71 a = action_new(action_move_relative_vert);
72 } else if (!g_ascii_strcasecmp(name, "resizerelativehorz")) {
73 a = action_new(action_resize_relative_horz);
74 } else if (!g_ascii_strcasecmp(name, "resizerelativevert")) {
75 a = action_new(action_resize_relative_vert);
76 } else if (!g_ascii_strcasecmp(name, "maximizefull")) {
77 a = action_new(action_maximize_full);
78 } else if (!g_ascii_strcasecmp(name, "unmaximizefull")) {
79 a = action_new(action_unmaximize_full);
80 } else if (!g_ascii_strcasecmp(name, "togglemaximizefull")) {
81 a = action_new(action_toggle_maximize_full);
82 } else if (!g_ascii_strcasecmp(name, "maximizehorz")) {
83 a = action_new(action_maximize_horz);
84 } else if (!g_ascii_strcasecmp(name, "unmaximizehorz")) {
85 a = action_new(action_unmaximize_horz);
86 } else if (!g_ascii_strcasecmp(name, "togglemaximizehorz")) {
87 a = action_new(action_toggle_maximize_horz);
88 } else if (!g_ascii_strcasecmp(name, "maximizevert")) {
89 a = action_new(action_maximize_vert);
90 } else if (!g_ascii_strcasecmp(name, "unmaximizevert")) {
91 a = action_new(action_unmaximize_vert);
92 } else if (!g_ascii_strcasecmp(name, "togglemaximizevert")) {
93 a = action_new(action_toggle_maximize_vert);
94 } else if (!g_ascii_strcasecmp(name, "sendtodesktop")) {
95 a = action_new(action_send_to_desktop);
96 a->data.sendto.follow = TRUE;
97 } else if (!g_ascii_strcasecmp(name, "sendtonextdesktop")) {
98 a = action_new(action_send_to_next_desktop);
99 a->data.sendtonextprev.wrap = FALSE;
100 a->data.sendtonextprev.follow = TRUE;
101 } else if (!g_ascii_strcasecmp(name, "sendtonextdesktopwrap")) {
102 a = action_new(action_send_to_next_desktop);
103 a->data.sendtonextprev.wrap = TRUE;
104 a->data.sendtonextprev.follow = TRUE;
105 } else if (!g_ascii_strcasecmp(name, "sendtopreviousdesktop")) {
106 a = action_new(action_send_to_previous_desktop);
107 a->data.sendtonextprev.wrap = FALSE;
108 a->data.sendtonextprev.follow = TRUE;
109 } else if (!g_ascii_strcasecmp(name, "sendtopreviousdesktopwrap")) {
110 a = action_new(action_send_to_previous_desktop);
111 a->data.sendtonextprev.wrap = TRUE;
112 a->data.sendtonextprev.follow = TRUE;
113 } else if (!g_ascii_strcasecmp(name, "desktop")) {
114 a = action_new(action_desktop);
115 } else if (!g_ascii_strcasecmp(name, "nextdesktop")) {
116 a = action_new(action_next_desktop);
117 a->data.nextprevdesktop.wrap = FALSE;
118 } else if (!g_ascii_strcasecmp(name, "nextdesktopwrap")) {
119 a = action_new(action_next_desktop);
120 a->data.nextprevdesktop.wrap = TRUE;
121 } else if (!g_ascii_strcasecmp(name, "previousdesktop")) {
122 a = action_new(action_previous_desktop);
123 a->data.nextprevdesktop.wrap = FALSE;
124 } else if (!g_ascii_strcasecmp(name, "previousdesktopwrap")) {
125 a = action_new(action_previous_desktop);
126 a->data.nextprevdesktop.wrap = TRUE;
127 } else if (!g_ascii_strcasecmp(name, "nextdesktopcolumn")) {
128 a = action_new(action_next_desktop_column);
129 a->data.nextprevdesktop.wrap = FALSE;
130 } else if (!g_ascii_strcasecmp(name, "nextdesktopcolumnwrap")) {
131 a = action_new(action_next_desktop_column);
132 a->data.nextprevdesktop.wrap = TRUE;
133 } else if (!g_ascii_strcasecmp(name, "previousdesktopcolumn")) {
134 a = action_new(action_previous_desktop_column);
135 a->data.nextprevdesktop.wrap = FALSE;
136 } else if (!g_ascii_strcasecmp(name, "previousdesktopcolumnwrap")) {
137 a = action_new(action_previous_desktop_column);
138 a->data.nextprevdesktop.wrap = TRUE;
139 } else if (!g_ascii_strcasecmp(name, "nextdesktoprow")) {
140 a = action_new(action_next_desktop_row);
141 a->data.nextprevdesktop.wrap = FALSE;
142 } else if (!g_ascii_strcasecmp(name, "nextdesktoprowwrap")) {
143 a = action_new(action_next_desktop_row);
144 a->data.nextprevdesktop.wrap = TRUE;
145 } else if (!g_ascii_strcasecmp(name, "previousdesktoprow")) {
146 a = action_new(action_previous_desktop_row);
147 a->data.nextprevdesktop.wrap = FALSE;
148 } else if (!g_ascii_strcasecmp(name, "previousdesktoprowwrap")) {
149 a = action_new(action_previous_desktop_row);
150 a->data.nextprevdesktop.wrap = TRUE;
151 } else if (!g_ascii_strcasecmp(name, "toggledecorations")) {
152 a = action_new(action_toggle_decorations);
153 } else if (!g_ascii_strcasecmp(name, "move")) {
154 a = action_new(action_move);
155 } else if (!g_ascii_strcasecmp(name, "resize")) {
156 a = action_new(action_resize);
157 } else if (!g_ascii_strcasecmp(name, "restart")) {
158 a = action_new(action_restart);
159 } else if (!g_ascii_strcasecmp(name, "exit")) {
160 a = action_new(action_exit);
161 } else if (!g_ascii_strcasecmp(name, "showmenu")) {
162 a = action_new(action_showmenu);
163 } else if (!g_ascii_strcasecmp(name, "nextwindowlinear")) {
164 a = action_new(action_cycle_windows);
165 a->data.cycle.linear = TRUE;
166 a->data.cycle.forward = TRUE;
167 } else if (!g_ascii_strcasecmp(name, "previouswindowlinear")) {
168 a = action_new(action_cycle_windows);
169 a->data.cycle.linear = TRUE;
170 a->data.cycle.forward = FALSE;
171 } else if (!g_ascii_strcasecmp(name, "nextwindow")) {
172 a = action_new(action_cycle_windows);
173 a->data.cycle.linear = FALSE;
174 a->data.cycle.forward = TRUE;
175 } else if (!g_ascii_strcasecmp(name, "previouswindow")) {
176 a = action_new(action_cycle_windows);
177 a->data.cycle.linear = FALSE;
178 a->data.cycle.forward = FALSE;
184 void action_execute(union ActionData *data)
187 if (data->execute.path)
188 if (!g_spawn_command_line_async(data->execute.path, &e)) {
189 g_warning("failed to execute '%s': %s",
190 data->execute.path, e->message);
194 void action_focus(union ActionData *data)
197 client_focus(data->client.c);
200 void action_unfocus (union ActionData *data)
203 client_unfocus(data->client.c);
206 void action_iconify(union ActionData *data)
209 client_iconify(data->client.c, TRUE, TRUE);
212 void action_focusraise(union ActionData *data)
214 if (data->client.c) {
215 client_focus(data->client.c);
216 stacking_raise(data->client.c);
220 void action_raise(union ActionData *data)
223 stacking_raise(data->client.c);
226 void action_unshaderaise(union ActionData *data)
228 if (data->client.c) {
229 if (data->client.c->shaded)
230 client_shade(data->client.c, FALSE);
232 stacking_raise(data->client.c);
236 void action_shadelower(union ActionData *data)
238 if (data->client.c) {
239 if (data->client.c->shaded)
240 stacking_lower(data->client.c);
242 client_shade(data->client.c, TRUE);
246 void action_lower(union ActionData *data)
249 stacking_lower(data->client.c);
252 void action_close(union ActionData *data)
255 client_close(data->client.c);
258 void action_kill(union ActionData *data)
261 client_kill(data->client.c);
264 void action_shade(union ActionData *data)
267 client_shade(data->client.c, TRUE);
270 void action_unshade(union ActionData *data)
273 client_shade(data->client.c, FALSE);
276 void action_toggle_shade(union ActionData *data)
279 client_shade(data->client.c, !data->client.c->shaded);
282 void action_toggle_omnipresent(union ActionData *data)
285 client_set_desktop(data->client.c,
286 data->client.c->desktop == DESKTOP_ALL ?
287 screen_desktop : DESKTOP_ALL, FALSE);
290 void action_move_relative_horz(union ActionData *data)
292 Client *c = data->relative.c;
294 client_configure(c, Corner_TopLeft,
295 c->area.x + data->relative.delta, c->area.y,
296 c->area.width, c->area.height, TRUE, TRUE);
299 void action_move_relative_vert(union ActionData *data)
301 Client *c = data->relative.c;
303 client_configure(c, Corner_TopLeft,
304 c->area.x, c->area.y + data->relative.delta,
305 c->area.width, c->area.height, TRUE, TRUE);
308 void action_resize_relative_horz(union ActionData *data)
310 Client *c = data->relative.c;
312 client_configure(c, Corner_TopLeft, c->area.x, c->area.y,
313 c->area.width + data->relative.delta,
314 c->area.height, TRUE, TRUE);
317 void action_resize_relative_vert(union ActionData *data)
319 Client *c = data->relative.c;
321 client_configure(c, Corner_TopLeft, c->area.x, c->area.y,
322 c->area.width, c->area.height + data->relative.delta,
326 void action_maximize_full(union ActionData *data)
329 client_maximize(data->client.c, TRUE, 0, TRUE);
332 void action_unmaximize_full(union ActionData *data)
335 client_maximize(data->client.c, FALSE, 0, TRUE);
338 void action_toggle_maximize_full(union ActionData *data)
341 client_maximize(data->client.c,
342 !(data->client.c->max_horz ||
343 data->client.c->max_vert),
347 void action_maximize_horz(union ActionData *data)
350 client_maximize(data->client.c, TRUE, 1, TRUE);
353 void action_unmaximize_horz(union ActionData *data)
356 client_maximize(data->client.c, FALSE, 1, TRUE);
359 void action_toggle_maximize_horz(union ActionData *data)
362 client_maximize(data->client.c, !data->client.c->max_horz, 1, TRUE);
365 void action_maximize_vert(union ActionData *data)
368 client_maximize(data->client.c, TRUE, 2, TRUE);
371 void action_unmaximize_vert(union ActionData *data)
374 client_maximize(data->client.c, FALSE, 2, TRUE);
377 void action_toggle_maximize_vert(union ActionData *data)
380 client_maximize(data->client.c, !data->client.c->max_vert, 2, TRUE);
383 void action_send_to_desktop(union ActionData *data)
385 if (data->sendto.c) {
386 if (data->sendto.desk < screen_num_desktops ||
387 data->sendto.desk == DESKTOP_ALL) {
388 client_set_desktop(data->desktop.c,
389 data->sendto.desk, data->sendto.follow);
390 if (data->sendto.follow) screen_set_desktop(data->sendto.desk);
395 void action_send_to_next_desktop(union ActionData *data)
399 if (!data->sendtonextprev.c) return;
401 d = screen_desktop + 1;
402 if (d >= screen_num_desktops) {
403 if (!data->sendtonextprev.wrap) return;
406 client_set_desktop(data->sendtonextprev.c, d, data->sendtonextprev.follow);
407 if (data->sendtonextprev.follow) screen_set_desktop(d);
410 void action_send_to_previous_desktop(union ActionData *data)
414 if (!data->sendtonextprev.c) return;
416 d = screen_desktop - 1;
417 if (d >= screen_num_desktops) {
418 if (!data->sendtonextprev.wrap) return;
419 d = screen_num_desktops - 1;
421 client_set_desktop(data->sendtonextprev.c, d, data->sendtonextprev.follow);
422 if (data->sendtonextprev.follow) screen_set_desktop(d);
425 void action_desktop(union ActionData *data)
427 if (data->desktop.desk < screen_num_desktops ||
428 data->desktop.desk == DESKTOP_ALL)
429 screen_set_desktop(data->desktop.desk);
432 void action_next_desktop(union ActionData *data)
436 d = screen_desktop + 1;
437 if (d >= screen_num_desktops) {
438 if (!data->nextprevdesktop.wrap) return;
441 screen_set_desktop(d);
444 void action_previous_desktop(union ActionData *data)
448 d = screen_desktop - 1;
449 if (d >= screen_num_desktops) {
450 if (!data->nextprevdesktop.wrap) return;
451 d = screen_num_desktops - 1;
453 screen_set_desktop(d);
456 static void cur_row_col(guint *r, guint *c)
458 switch (screen_desktop_layout.orientation) {
459 case Orientation_Horz:
460 switch (screen_desktop_layout.start_corner) {
462 *r = screen_desktop / screen_desktop_layout.columns;
463 *c = screen_desktop % screen_desktop_layout.columns;
465 case Corner_BottomLeft:
466 *r = screen_desktop_layout.rows - 1 -
467 screen_desktop / screen_desktop_layout.columns;
468 *c = screen_desktop % screen_desktop_layout.columns;
470 case Corner_TopRight:
471 *r = screen_desktop / screen_desktop_layout.columns;
472 *c = screen_desktop_layout.columns - 1 -
473 screen_desktop % screen_desktop_layout.columns;
475 case Corner_BottomRight:
476 *r = screen_desktop_layout.rows - 1 -
477 screen_desktop / screen_desktop_layout.columns;
478 *c = screen_desktop_layout.columns - 1 -
479 screen_desktop % screen_desktop_layout.columns;
483 case Orientation_Vert:
484 switch (screen_desktop_layout.start_corner) {
486 *r = screen_desktop % screen_desktop_layout.rows;
487 *c = screen_desktop / screen_desktop_layout.rows;
489 case Corner_BottomLeft:
490 *r = screen_desktop_layout.rows - 1 -
491 screen_desktop % screen_desktop_layout.rows;
492 *c = screen_desktop / screen_desktop_layout.rows;
494 case Corner_TopRight:
495 *r = screen_desktop % screen_desktop_layout.rows;
496 *c = screen_desktop_layout.columns - 1 -
497 screen_desktop / screen_desktop_layout.rows;
499 case Corner_BottomRight:
500 *r = screen_desktop_layout.rows - 1 -
501 screen_desktop % screen_desktop_layout.rows;
502 *c = screen_desktop_layout.columns - 1 -
503 screen_desktop / screen_desktop_layout.rows;
510 static guint translate_row_col(guint r, guint c)
512 switch (screen_desktop_layout.orientation) {
513 case Orientation_Horz:
514 switch (screen_desktop_layout.start_corner) {
516 return r * screen_desktop_layout.columns + c;
517 case Corner_BottomLeft:
518 return (screen_desktop_layout.rows - 1 - r) *
519 screen_desktop_layout.columns + c;
520 case Corner_TopRight:
521 return r * screen_desktop_layout.columns +
522 (screen_desktop_layout.columns - 1 - c);
523 case Corner_BottomRight:
524 return (screen_desktop_layout.rows - 1 - r) *
525 screen_desktop_layout.columns +
526 (screen_desktop_layout.columns - 1 - c);
528 case Orientation_Vert:
529 switch (screen_desktop_layout.start_corner) {
531 return c * screen_desktop_layout.rows + r;
532 case Corner_BottomLeft:
533 return c * screen_desktop_layout.rows +
534 (screen_desktop_layout.rows - 1 - r);
535 case Corner_TopRight:
536 return (screen_desktop_layout.columns - 1 - c) *
537 screen_desktop_layout.rows + r;
538 case Corner_BottomRight:
539 return (screen_desktop_layout.columns - 1 - c) *
540 screen_desktop_layout.rows +
541 (screen_desktop_layout.rows - 1 - r);
544 g_assert_not_reached();
548 void action_next_desktop_column(union ActionData *data)
554 d = translate_row_col(r, c);
555 if (d >= screen_num_desktops) {
556 if (!data->nextprevdesktop.wrap) return;
559 if (d >= screen_num_desktops)
561 d = translate_row_col(r, c);
562 if (d < screen_num_desktops)
563 screen_set_desktop(d);
566 void action_previous_desktop_column(union ActionData *data)
572 d = translate_row_col(r, c);
573 if (d >= screen_num_desktops) {
574 if (!data->nextprevdesktop.wrap) return;
575 c = screen_desktop_layout.columns - 1;
577 if (d >= screen_num_desktops)
579 d = translate_row_col(r, c);
580 if (d < screen_num_desktops)
581 screen_set_desktop(d);
584 void action_next_desktop_row(union ActionData *data)
590 d = translate_row_col(r, c);
591 if (d >= screen_num_desktops) {
592 if (!data->nextprevdesktop.wrap) return;
595 if (d >= screen_num_desktops)
597 d = translate_row_col(r, c);
598 if (d < screen_num_desktops)
599 screen_set_desktop(d);
602 void action_previous_desktop_row(union ActionData *data)
608 d = translate_row_col(r, c);
609 if (d >= screen_num_desktops) {
610 if (!data->nextprevdesktop.wrap) return;
611 c = screen_desktop_layout.rows - 1;
613 if (d >= screen_num_desktops)
615 d = translate_row_col(r, c);
616 if (d < screen_num_desktops)
617 screen_set_desktop(d);
620 void action_toggle_decorations(union ActionData *data)
622 Client *c = data->client.c;;
626 c->disabled_decorations = c->disabled_decorations ? 0 : ~0;
627 client_setup_decor_and_functions(c);
630 static void popup_coords(char *format, int a, int b, gboolean hide)
632 XSetWindowAttributes attrib;
633 static Window coords = None;
635 if (coords == None) {
636 attrib.override_redirect = TRUE;
637 coords = XCreateWindow(ob_display, ob_root,
638 0, 0, 1, 1, 0, render_depth, InputOutput,
639 render_visual, CWOverrideRedirect, &attrib);
640 g_assert(coords != None);
644 XUnmapWindow(ob_display, coords);
649 text = g_strdup_printf(format, a, b);
650 framerender_size_popup_label(text, &s);
651 XMoveResizeWindow(ob_display, coords,
652 10, 10, s.width, s.height);
653 framerender_popup_label(coords, &s, text);
656 XMapWindow(ob_display, coords);
660 void action_move(union ActionData *data)
662 Client *c = data->move.c;
663 int x = data->move.x;
664 int y = data->move.y;
666 if (!c || !client_normal(c)) return;
668 dispatch_move(c, &x, &y);
670 popup_coords("X: %d Y: %d", x, y, data->move.final);
672 frame_frame_gravity(c->frame, &x, &y); /* get where the client should be */
673 client_configure(c, Corner_TopLeft, x, y, c->area.width, c->area.height,
674 TRUE, data->move.final);
677 void action_resize(union ActionData *data)
679 Client *c = data->resize.c;
680 int w = data->resize.x;
681 int h = data->resize.y;
683 if (!c || c->shaded || !client_normal(c)) return;
685 dispatch_resize(c, &w, &h, data->resize.corner);
687 w -= c->frame->size.left + c->frame->size.right;
688 h -= c->frame->size.top + c->frame->size.bottom;
690 client_configure(c, data->resize.corner, c->area.x, c->area.y, w, h,
691 TRUE, data->resize.final);
693 popup_coords("W: %d H: %d", c->logical_size.width,
694 c->logical_size.height, data->move.final);
697 void action_restart(union ActionData *data)
699 ob_restart_path = data->execute.path;
700 ob_shutdown = ob_restart = TRUE;
703 void action_exit(union ActionData *data)
708 void action_showmenu(union ActionData *data)
710 if (data->showmenu.name) {
711 menu_show(data->showmenu.name, data->showmenu.x, data->showmenu.y,
716 static void popup_cycle(Client *c, gboolean hide)
718 XSetWindowAttributes attrib;
719 static Window coords = None;
721 if (coords == None) {
722 attrib.override_redirect = TRUE;
723 coords = XCreateWindow(ob_display, ob_root,
724 0, 0, 1, 1, 0, render_depth, InputOutput,
725 render_visual, CWOverrideRedirect, &attrib);
726 g_assert(coords != None);
730 XUnmapWindow(ob_display, coords);
735 a = screen_area(c->desktop);
737 framerender_size_popup_label(c->title, &s);
738 XMoveResizeWindow(ob_display, coords,
739 a->x + (a->width - s.width) / 2,
740 a->y + (a->height - s.height) / 2,
742 framerender_popup_label(coords, &s, c->title);
744 XMapWindow(ob_display, coords);
748 void action_cycle_windows(union ActionData *data)
752 c = focus_cycle(data->cycle.forward, data->cycle.linear, data->cycle.final,
754 popup_cycle(c, !c || data->cycle.final || data->cycle.cancel);