Mac OS X-style window cycling.
[wmaker-crm.git] / src / cycling.c
1 /* cycling.c- window cycling
2  *
3  *  Window Maker window manager
4  *
5  *  Copyright (c) 2000-2003 Alfredo K. Kojima
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License as published by
9  *  the Free Software Foundation; either version 2 of the License, or
10  *  (at your option) any later version.
11  *
12  *  This program is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU General Public License for more details.
16  *
17  *  You should have received a copy of the GNU General Public License
18  *  along with this program; if not, write to the Free Software
19  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
20  *  USA.
21  */
22
23 #include "wconfig.h"
24
25 #include <stdlib.h>
26 #include <X11/Xlib.h>
27 #include <X11/Xutil.h>
28 #include <X11/keysym.h>
29
30 #include "WindowMaker.h"
31 #include "GNUstep.h"
32 #include "screen.h"
33 #include "wcore.h"
34 #include "window.h"
35 #include "framewin.h"
36 #include "keybind.h"
37 #include "actions.h"
38 #include "stacking.h"
39 #include "funcs.h"
40 #include "xinerama.h"
41 #include "switchpanel.h"
42
43 /* Globals */
44 extern WPreferences wPreferences;
45
46 extern WShortKey wKeyBindings[WKBD_LAST];
47
48 static void raiseWindow(WSwitchPanel * swpanel, WWindow * wwin)
49 {
50         Window swwin = wSwitchPanelGetWindow(swpanel);
51
52         if (wwin->flags.mapped) {
53                 if (swwin != None) {
54                         Window win[2];
55
56                         win[0] = swwin;
57                         win[1] = wwin->frame->core->window;
58
59                         XRestackWindows(dpy, win, 2);
60                 } else
61                         XRaiseWindow(dpy, wwin->frame->core->window);
62         }
63 }
64
65 static WWindow *change_focus_and_raise(WWindow *newFocused, WWindow *oldFocused,
66                                        WSwitchPanel *swpanel, WScreen *scr, Bool esc_cancel)
67 {
68         if (!newFocused)
69                 return oldFocused;
70
71         wWindowFocus(newFocused, oldFocused);
72         oldFocused = newFocused;
73
74         if (wPreferences.circ_raise) {
75                 CommitStacking(scr);
76
77                 if (!esc_cancel)
78                         raiseWindow(swpanel, newFocused);
79         }
80
81         return oldFocused;
82 }
83
84 void StartWindozeCycle(WWindow * wwin, XEvent * event, Bool next, Bool class_only)
85 {
86
87         XModifierKeymap *keymap        = NULL;
88         WSwitchPanel    *swpanel       = NULL;
89         WScreen         *scr           = wScreenForRootWindow(event->xkey.root);
90         KeyCode         leftKey        = XKeysymToKeycode(dpy, XK_Left);
91         KeyCode         rightKey       = XKeysymToKeycode(dpy, XK_Right);
92         KeyCode         homeKey        = XKeysymToKeycode(dpy, XK_Home);
93         KeyCode         endKey         = XKeysymToKeycode(dpy, XK_End);
94         KeyCode         shiftLKey      = XKeysymToKeycode(dpy, XK_Shift_L);
95         KeyCode         shiftRKey      = XKeysymToKeycode(dpy, XK_Shift_R);
96         KeyCode         escapeKey      = XKeysymToKeycode(dpy, XK_Escape);
97         Bool            esc_cancel     = False;
98         Bool            somethingElse  = False;
99         Bool            done           = False;
100         Bool            hasModifier;
101         int             modifiers;
102         WWindow         *newFocused;
103         WWindow         *oldFocused;
104         XEvent          ev;
105
106
107         if (!wwin)
108                 return;
109
110         if (next)
111                 hasModifier = (wKeyBindings[WKBD_FOCUSNEXT].modifier != 0);
112         else
113                 hasModifier = (wKeyBindings[WKBD_FOCUSPREV].modifier != 0);
114
115         if (hasModifier) {
116                 keymap = XGetModifierMapping(dpy);
117
118 #ifdef DEBUG
119                 printf("Grabbing keyboard\n");
120 #endif
121                 XGrabKeyboard(dpy, scr->root_win, False, GrabModeAsync, GrabModeAsync, CurrentTime);
122         }
123
124         scr->flags.doing_alt_tab = 1;
125
126         swpanel = wInitSwitchPanel(scr, wwin, scr->current_workspace, class_only);
127         oldFocused = wwin;
128
129         if (swpanel) {
130
131                 if (wwin->flags.mapped)
132                         newFocused = wSwitchPanelSelectNext(swpanel, !next);
133                 else
134                         newFocused = wSwitchPanelSelectFirst(swpanel, False);
135
136                 oldFocused = change_focus_and_raise(newFocused, oldFocused, swpanel, scr, False);
137         } else {
138                 if (wwin->frame->workspace == scr->current_workspace)
139                         newFocused = wwin;
140                 else
141                         newFocused = NULL;
142         }
143
144         while (hasModifier && !done) {
145                 int i;
146
147                 WMMaskEvent(dpy, KeyPressMask | KeyReleaseMask | ExposureMask
148                             | PointerMotionMask | ButtonReleaseMask | EnterWindowMask, &ev);
149
150                 /* ignore CapsLock */
151                 modifiers = ev.xkey.state & ValidModMask;
152
153                 if (!swpanel)
154                         done = True;
155
156                 switch (ev.type) {
157
158                 case KeyPress:
159
160                         if ((wKeyBindings[WKBD_FOCUSNEXT].keycode == ev.xkey.keycode
161                              && wKeyBindings[WKBD_FOCUSNEXT].modifier == modifiers)
162                             || (wKeyBindings[WKBD_GROUPNEXT].keycode == ev.xkey.keycode
163                             && wKeyBindings[WKBD_GROUPNEXT].modifier == modifiers)
164                             || ev.xkey.keycode == rightKey) {
165
166                                 newFocused = wSwitchPanelSelectNext(swpanel, False);
167                                 oldFocused = change_focus_and_raise(newFocused, oldFocused, swpanel, scr, False);
168
169                         } else if ((wKeyBindings[WKBD_FOCUSPREV].keycode == ev.xkey.keycode
170                                     && wKeyBindings[WKBD_FOCUSPREV].modifier == modifiers)
171                             || (wKeyBindings[WKBD_GROUPPREV].keycode == ev.xkey.keycode
172                             && wKeyBindings[WKBD_GROUPPREV].modifier == modifiers)
173                                    || ev.xkey.keycode == leftKey) {
174
175                                 newFocused = wSwitchPanelSelectNext(swpanel, True);
176                                 oldFocused = change_focus_and_raise(newFocused, oldFocused, swpanel, scr, False);
177
178                         } else if (ev.xkey.keycode == homeKey || ev.xkey.keycode == endKey) {
179
180                                 newFocused = wSwitchPanelSelectFirst(swpanel, ev.xkey.keycode != homeKey);
181                                 oldFocused = change_focus_and_raise(newFocused, oldFocused, swpanel, scr, False);
182
183                         } else if (ev.xkey.keycode == escapeKey) {
184
185                                 /* Focus the first window of the swpanel, despite the 'False' */
186                                 newFocused = wSwitchPanelSelectFirst(swpanel, False);
187                                 oldFocused = change_focus_and_raise(newFocused, oldFocused, swpanel, scr, True);
188                                 esc_cancel = True;
189                                 done = True;
190
191                         } else if (ev.xkey.keycode != shiftLKey && ev.xkey.keycode != shiftRKey) {
192
193                                 somethingElse = True;
194                                 done = True;
195                         }
196                         break;
197
198                 case KeyRelease:
199
200                         for (i = 0; i < 8 * keymap->max_keypermod; i++) {
201
202                                 int mask = 1 << (i / keymap->max_keypermod);
203
204                                 if (keymap->modifiermap[i] == ev.xkey.keycode &&
205                                     ((wKeyBindings[WKBD_FOCUSNEXT].modifier & mask)
206                                      || (wKeyBindings[WKBD_FOCUSPREV].modifier & mask)
207                                      || (wKeyBindings[WKBD_GROUPNEXT].modifier & mask)
208                                      || (wKeyBindings[WKBD_GROUPPREV].modifier & mask))) {
209                                         done = True;
210                                         break;
211                                 }
212                         }
213                         break;
214
215                 case EnterNotify:
216
217                         /* ignore unwanted EnterNotify's */
218                         break;
219
220                 case LeaveNotify:
221                 case MotionNotify:
222
223                 case ButtonRelease:
224
225                         newFocused = wSwitchPanelHandleEvent(swpanel, &ev);
226                         oldFocused = change_focus_and_raise(newFocused, oldFocused, swpanel, scr, False);
227
228                         if (ev.type == ButtonRelease)
229                                 done = True;
230                         break;
231
232                 default:
233                         WMHandleEvent(&ev);
234                         break;
235                 }
236         }
237         if (keymap)
238                 XFreeModifiermap(keymap);
239
240         if (hasModifier) {
241
242                 XUngrabKeyboard(dpy, CurrentTime);
243         }
244
245         if (swpanel)
246                 wSwitchPanelDestroy(swpanel);
247
248         if (newFocused && !esc_cancel) {
249                 wRaiseFrame(newFocused->frame->core);
250                 CommitStacking(scr);
251                 if (!newFocused->flags.mapped)
252                         wMakeWindowVisible(newFocused);
253                 wSetFocusTo(scr, newFocused);
254         }
255
256         scr->flags.doing_alt_tab = 0;
257
258         if (somethingElse)
259                 WMHandleEvent(&ev);
260 }