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