]> icculus.org git repositories - dana/openbox.git/blob - openbox/actions/desktop.c
Provide operations to get the size of a ObClientSet, and get the clients in it. Also...
[dana/openbox.git] / openbox / actions / desktop.c
1 #include "openbox/action.h"
2 #include "openbox/action_list_run.h"
3 #include "openbox/action_value.h"
4 #include "openbox/screen.h"
5 #include "openbox/client.h"
6 #include "openbox/openbox.h"
7 #include "obt/keyboard.h"
8
9 typedef enum {
10     LAST,
11     CURRENT,
12     RELATIVE,
13     ABSOLUTE
14 } SwitchType;
15
16 typedef struct {
17     SwitchType type;
18     union {
19         struct {
20             guint desktop;
21         } abs;
22
23         struct {
24             gboolean linear;
25             gboolean wrap;
26             ObDirection dir;
27         } rel;
28     } u;
29     gboolean send;
30     gboolean follow;
31     gboolean interactive;
32 } Options;
33
34 static gpointer setup_go_func(GHashTable *config,
35                               ObActionIPreFunc *pre,
36                               ObActionIInputFunc *input,
37                               ObActionICancelFunc *cancel,
38                               ObActionIPostFunc *post);
39 static gpointer setup_send_func(GHashTable *config,
40                                 ObActionIPreFunc *pre,
41                                 ObActionIInputFunc *input,
42                                 ObActionICancelFunc *cancel,
43                                 ObActionIPostFunc *post);
44 static void free_func(gpointer o);
45 static gboolean run_func(const ObActionListRun *data, gpointer options);
46
47 static gboolean i_pre_func(guint state, gpointer options);
48 static gboolean i_input_func(guint initial_state,
49                              XEvent *e,
50                              ObtIC *ic,
51                              gpointer options,
52                              gboolean *used);
53 static void i_post_func(gpointer options);
54
55
56 void action_desktop_startup(void)
57 {
58     action_register_i("GoToDesktop", OB_ACTION_DEFAULT_FILTER_EMPTY,
59                       setup_go_func, free_func, run_func);
60     action_register_i("SendToDesktop", OB_ACTION_DEFAULT_FILTER_SINGLE,
61                       setup_send_func, free_func, run_func);
62 }
63
64 static gpointer setup_func(GHashTable *config,
65                            ObActionIPreFunc *pre,
66                            ObActionIInputFunc *input,
67                            ObActionICancelFunc *cancel,
68                            ObActionIPostFunc *post)
69 {
70     ObActionValue *v;
71     Options *o;
72
73     o = g_slice_new0(Options);
74     /* don't go anywhere if there are no options given */
75     o->type = ABSOLUTE;
76     o->u.abs.desktop = screen_desktop;
77     /* wrap by default - it's handy! */
78     o->u.rel.wrap = TRUE;
79
80     v = g_hash_table_lookup(config, "to");
81     if (v && action_value_is_string(v)) {
82         const gchar *s = action_value_string(v);
83         if (!g_ascii_strcasecmp(s, "last"))
84             o->type = LAST;
85         else if (!g_ascii_strcasecmp(s, "current"))
86             o->type = CURRENT;
87         else if (!g_ascii_strcasecmp(s, "next")) {
88             o->type = RELATIVE;
89             o->u.rel.linear = TRUE;
90             o->u.rel.dir = OB_DIRECTION_EAST;
91         }
92         else if (!g_ascii_strcasecmp(s, "previous")) {
93             o->type = RELATIVE;
94             o->u.rel.linear = TRUE;
95             o->u.rel.dir = OB_DIRECTION_WEST;
96         }
97         else if (!g_ascii_strcasecmp(s, "north") ||
98                  !g_ascii_strcasecmp(s, "up")) {
99             o->type = RELATIVE;
100             o->u.rel.dir = OB_DIRECTION_NORTH;
101         }
102         else if (!g_ascii_strcasecmp(s, "south") ||
103                  !g_ascii_strcasecmp(s, "down")) {
104             o->type = RELATIVE;
105             o->u.rel.dir = OB_DIRECTION_SOUTH;
106         }
107         else if (!g_ascii_strcasecmp(s, "west") ||
108                  !g_ascii_strcasecmp(s, "left")) {
109             o->type = RELATIVE;
110             o->u.rel.dir = OB_DIRECTION_WEST;
111         }
112         else if (!g_ascii_strcasecmp(s, "east") ||
113                  !g_ascii_strcasecmp(s, "right")) {
114             o->type = RELATIVE;
115             o->u.rel.dir = OB_DIRECTION_EAST;
116         }
117         else {
118             o->type = ABSOLUTE;
119             o->u.abs.desktop = atoi(s) - 1;
120         }
121     }
122
123     v = g_hash_table_lookup(config, "wrap");
124     if (v && action_value_is_string(v))
125         o->u.rel.wrap = action_value_bool(v);
126
127     return o;
128 }
129
130
131 static gpointer setup_go_func(GHashTable *config,
132                               ObActionIPreFunc *pre,
133                               ObActionIInputFunc *input,
134                               ObActionICancelFunc *cancel,
135                               ObActionIPostFunc *post)
136 {
137     Options *o;
138
139     o = setup_func(config, pre, input, cancel, post);
140     if (o->type == RELATIVE) {
141         o->interactive = TRUE;
142         *pre = i_pre_func;
143         *input = i_input_func;
144         *post = i_post_func;
145     }
146
147     return o;
148 }
149
150 static gpointer setup_send_func(GHashTable *config,
151                                 ObActionIPreFunc *pre,
152                                 ObActionIInputFunc *input,
153                                 ObActionICancelFunc *cancel,
154                                 ObActionIPostFunc *post)
155 {
156     ObActionValue *v;
157     Options *o;
158
159     o = setup_func(config, pre, input, cancel, post);
160     o->send = TRUE;
161     o->follow = TRUE;
162
163     v = g_hash_table_lookup(config, "follow");
164     if (v && action_value_is_string(v))
165         o->follow = action_value_bool(v);
166
167     if (o->type == RELATIVE && o->follow) {
168         o->interactive = TRUE;
169         *pre = i_pre_func;
170         *input = i_input_func;
171         *post = i_post_func;
172     }
173
174     return o;
175 }
176
177 static void free_func(gpointer o)
178 {
179     g_slice_free(Options, o);
180 }
181
182 static gboolean run_func(const ObActionListRun *data, gpointer options)
183 {
184     Options *o = options;
185     guint d;
186
187     switch (o->type) {
188     case LAST:
189         d = screen_last_desktop;
190         break;
191     case CURRENT:
192         d = screen_desktop;
193         break;
194     case ABSOLUTE:
195         d = o->u.abs.desktop;
196         break;
197     case RELATIVE:
198         d = screen_find_desktop(screen_desktop,
199                                 o->u.rel.dir, o->u.rel.wrap, o->u.rel.linear);
200         break;
201     default:
202         g_assert_not_reached();
203     }
204
205     if (d < screen_num_desktops &&
206         (d != screen_desktop ||
207          (data->target && data->target->desktop != screen_desktop))) {
208         gboolean go = TRUE;
209
210         action_client_move(data, TRUE);
211         if (o->send && data->target && client_normal(data->target)) {
212             client_set_desktop(data->target, d, o->follow, FALSE);
213             go = o->follow;
214         }
215
216         if (go) {
217             screen_set_desktop(d, TRUE);
218             if (data->target)
219                 client_bring_helper_windows(data->target);
220         }
221
222         action_client_move(data, FALSE);
223     }
224
225     return o->interactive;
226 }
227
228 static gboolean i_input_func(guint initial_state,
229                              XEvent *e,
230                              ObtIC *ic,
231                              gpointer options,
232                              gboolean *used)
233 {
234     guint mods, initial_mods;
235
236     initial_mods = obt_keyboard_only_modmasks(initial_state);
237     mods = obt_keyboard_only_modmasks(e->xkey.state);
238     if (e->type == KeyRelease) {
239         /* remove from the state the mask of the modifier key being
240            released, if it is a modifier key being released that is */
241         mods &= ~obt_keyboard_keyevent_to_modmask(e);
242     }
243
244     if (e->type == KeyPress) {
245         KeySym sym = obt_keyboard_keypress_to_keysym(e);
246
247         /* Escape cancels no matter what */
248         if (sym == XK_Escape)
249             return FALSE;
250
251         /* There were no modifiers and they pressed enter */
252         else if ((sym == XK_Return || sym == XK_KP_Enter) && !initial_mods)
253             return FALSE;
254     }
255     /* They released the modifiers */
256     else if (e->type == KeyRelease && initial_mods && !(mods & initial_mods))
257     {
258         return FALSE;
259     }
260
261     return TRUE;
262 }
263
264 static gboolean i_pre_func(guint initial_state, gpointer options)
265 {
266     guint initial_mods = obt_keyboard_only_modmasks(initial_state);
267     if (!inital_mods) {
268         Options *o = options;
269         o->interactive = FALSE;
270         return FALSE;
271     }
272     else {
273         screen_show_desktop_popup(screen_desktop, TRUE);
274         return TRUE;
275     }
276 }
277
278 static void i_post_func(gpointer options)
279 {
280     screen_hide_desktop_popup();
281 }