wmaker: Added 'const' attribute to local variables
[wmaker-crm.git] / src / event.c
blobc6951bb4796b0a299bc7fffce617137a2cc729ca
1 /* event.c- event loop and handling
3 * Window Maker window manager
5 * Copyright (c) 1997-2003 Alfredo K. Kojima
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.
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.
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 #include "wconfig.h"
24 #ifdef HAVE_INOTIFY
25 #include <sys/select.h>
26 #include <sys/inotify.h>
27 #endif
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <unistd.h>
32 #include <string.h>
33 #include <strings.h>
34 #include <time.h>
36 #include <X11/Xlib.h>
37 #include <X11/Xutil.h>
38 #ifdef SHAPE
39 # include <X11/extensions/shape.h>
40 #endif
41 #ifdef XDND
42 #include "xdnd.h"
43 #endif
45 #ifdef HAVE_XRANDR
46 #include <X11/extensions/Xrandr.h>
47 #endif
49 #ifdef KEEP_XKB_LOCK_STATUS
50 #include <X11/XKBlib.h>
51 #endif /* KEEP_XKB_LOCK_STATUS */
53 #include "WindowMaker.h"
54 #include "window.h"
55 #include "actions.h"
56 #include "client.h"
57 #include "main.h"
58 #include "cycling.h"
59 #include "keybind.h"
60 #include "application.h"
61 #include "stacking.h"
62 #include "defaults.h"
63 #include "workspace.h"
64 #include "dock.h"
65 #include "framewin.h"
66 #include "properties.h"
67 #include "balloon.h"
68 #include "xinerama.h"
69 #include "wmspec.h"
70 #include "rootmenu.h"
71 #include "colormap.h"
72 #include "screen.h"
73 #include "shutdown.h"
74 #include "misc.h"
75 #include "event.h"
76 #include "winmenu.h"
77 #include "switchmenu.h"
79 /******** Global Variables **********/
80 extern XContext wWinContext;
81 extern XContext wVEdgeContext;
83 extern Cursor wCursor[WCUR_LAST];
85 extern WShortKey wKeyBindings[WKBD_LAST];
86 extern int wScreenCount;
87 extern Time LastTimestamp;
88 extern Time LastFocusChange;
90 extern WPreferences wPreferences;
92 #define MOD_MASK wPreferences.modifier_mask
94 extern Atom _XA_WM_COLORMAP_NOTIFY;
96 extern Atom _XA_WM_CHANGE_STATE;
97 extern Atom _XA_WM_DELETE_WINDOW;
98 extern Atom _XA_GNUSTEP_WM_ATTR;
99 extern Atom _XA_GNUSTEP_WM_MINIATURIZE_WINDOW;
100 extern Atom _XA_GNUSTEP_TITLEBAR_STATE;
101 extern Atom _XA_WINDOWMAKER_WM_FUNCTION;
102 extern Atom _XA_WINDOWMAKER_COMMAND;
103 extern Atom _XA_WM_IGNORE_FOCUS_EVENTS;
105 #ifdef SHAPE
106 extern Bool wShapeSupported;
107 extern int wShapeEventBase;
108 #endif
110 #ifdef KEEP_XKB_LOCK_STATUS
111 extern int wXkbEventBase;
112 #endif
114 /************ Local stuff ***********/
116 static void saveTimestamp(XEvent *event);
117 static void handleColormapNotify(XEvent *event);
118 static void handleMapNotify(XEvent *event);
119 static void handleUnmapNotify(XEvent *event);
120 static void handleButtonPress(XEvent *event);
121 static void handleExpose(XEvent *event);
122 static void handleDestroyNotify(XEvent *event);
123 static void handleConfigureRequest(XEvent *event);
124 static void handleMapRequest(XEvent *event);
125 static void handlePropertyNotify(XEvent *event);
126 static void handleEnterNotify(XEvent *event);
127 static void handleLeaveNotify(XEvent *event);
128 static void handleExtensions(XEvent *event);
129 static void handleClientMessage(XEvent *event);
130 static void handleKeyPress(XEvent *event);
131 static void handleFocusIn(XEvent *event);
132 static void handleMotionNotify(XEvent *event);
133 static void handleVisibilityNotify(XEvent *event);
134 static void handle_inotify_events(int fd, int wd);
135 static void wdelete_death_handler(WMagicNumber id);
138 #ifdef SHAPE
139 static void handleShapeNotify(XEvent *event);
140 #endif
142 #ifdef KEEP_XKB_LOCK_STATUS
143 static void handleXkbIndicatorStateNotify(XEvent *event);
144 #endif
146 /* called from the signal handler */
147 void NotifyDeadProcess(pid_t pid, unsigned char status);
149 /* real dead process handler */
150 static void handleDeadProcess(void *foo);
152 typedef struct DeadProcesses {
153 pid_t pid;
154 unsigned char exit_status;
155 } DeadProcesses;
157 /* stack of dead processes */
158 static DeadProcesses deadProcesses[MAX_DEAD_PROCESSES];
159 static int deadProcessPtr = 0;
161 typedef struct DeathHandler {
162 WDeathHandler *callback;
163 pid_t pid;
164 void *client_data;
165 } DeathHandler;
167 static WMArray *deathHandlers = NULL;
169 WMagicNumber wAddDeathHandler(pid_t pid, WDeathHandler * callback, void *cdata)
171 DeathHandler *handler;
173 handler = malloc(sizeof(DeathHandler));
174 if (!handler)
175 return 0;
177 handler->pid = pid;
178 handler->callback = callback;
179 handler->client_data = cdata;
181 if (!deathHandlers)
182 deathHandlers = WMCreateArrayWithDestructor(8, free);
184 WMAddToArray(deathHandlers, handler);
186 return handler;
189 static void wdelete_death_handler(WMagicNumber id)
191 DeathHandler *handler = (DeathHandler *) id;
193 if (!handler || !deathHandlers)
194 return;
196 /* array destructor will call free(handler) */
197 WMRemoveFromArray(deathHandlers, handler);
200 void DispatchEvent(XEvent * event)
202 if (deathHandlers)
203 handleDeadProcess(NULL);
205 if (WCHECK_STATE(WSTATE_NEED_EXIT)) {
206 WCHANGE_STATE(WSTATE_EXITING);
207 /* received SIGTERM */
209 * WMHandleEvent() can't be called from anything
210 * executed inside here, or we can get in a infinite
211 * recursive loop.
213 Shutdown(WSExitMode);
215 } else if (WCHECK_STATE(WSTATE_NEED_RESTART)) {
216 WCHANGE_STATE(WSTATE_RESTARTING);
218 Shutdown(WSRestartPreparationMode);
219 /* received SIGHUP */
220 Restart(NULL, True);
221 } else if (WCHECK_STATE(WSTATE_NEED_REREAD)) {
222 WCHANGE_STATE(WSTATE_NORMAL);
223 wDefaultsCheckDomains(NULL);
226 /* for the case that all that is wanted to be dispatched is
227 * the stuff above */
228 if (!event)
229 return;
231 saveTimestamp(event);
232 switch (event->type) {
233 case MapRequest:
234 handleMapRequest(event);
235 break;
237 case KeyPress:
238 handleKeyPress(event);
239 break;
241 case MotionNotify:
242 handleMotionNotify(event);
243 break;
245 case ConfigureRequest:
246 handleConfigureRequest(event);
247 break;
249 case DestroyNotify:
250 handleDestroyNotify(event);
251 break;
253 case MapNotify:
254 handleMapNotify(event);
255 break;
257 case UnmapNotify:
258 handleUnmapNotify(event);
259 break;
261 case ButtonPress:
262 handleButtonPress(event);
263 break;
265 case Expose:
266 handleExpose(event);
267 break;
269 case PropertyNotify:
270 handlePropertyNotify(event);
271 break;
273 case EnterNotify:
274 handleEnterNotify(event);
275 break;
277 case LeaveNotify:
278 handleLeaveNotify(event);
279 break;
281 case ClientMessage:
282 handleClientMessage(event);
283 break;
285 case ColormapNotify:
286 handleColormapNotify(event);
287 break;
289 case MappingNotify:
290 if (event->xmapping.request == MappingKeyboard || event->xmapping.request == MappingModifier)
291 XRefreshKeyboardMapping(&event->xmapping);
292 break;
294 case FocusIn:
295 handleFocusIn(event);
296 break;
298 case VisibilityNotify:
299 handleVisibilityNotify(event);
300 break;
302 case ConfigureNotify:
303 #ifdef HAVE_XRANDR
304 if (event->xconfigure.window == DefaultRootWindow(dpy))
305 XRRUpdateConfiguration(event);
306 #endif
307 break;
309 default:
310 handleExtensions(event);
311 break;
315 #ifdef HAVE_INOTIFY
317 *----------------------------------------------------------------------
318 * handle_inotify_events-
319 * Check for inotify events
321 * Returns:
322 * After reading events for the given file descriptor (fd) and
323 * watch descriptor (wd)
325 * Side effects:
326 * Calls wDefaultsCheckDomains if config database is updated
327 *----------------------------------------------------------------------
329 /* allow 5 simultaneous events, with path + filenames up to 64 chars */
330 #define BUFF_SIZE ((sizeof(struct inotify_event) + 64)*5)
331 static void handle_inotify_events(int fd, int wd)
333 ssize_t eventQLength, i = 0;
334 char buff[BUFF_SIZE] = { 0 };
335 /* Check config only once per read of the event queue */
336 int oneShotFlag = 0;
339 * Read off the queued events
340 * queue overflow is not checked (IN_Q_OVERFLOW). In practise this should
341 * not occur; the block is on Xevents, but a config file change will normally
342 * occur as a result of an Xevent - so the event queue should never have more than
343 * a few entries before a read().
345 eventQLength = read(fd, buff, BUFF_SIZE);
347 /* check what events occured */
348 /* Should really check wd here too, but for now we only have one watch! */
349 while (i < eventQLength) {
350 struct inotify_event *pevent = (struct inotify_event *)&buff[i];
353 * see inotify.h for event types.
355 if (pevent->mask & IN_DELETE_SELF) {
356 wwarning(_("the defaults database has been deleted!"
357 " Restart Window Maker to create the database" " with the default settings"));
358 close(fd);
360 if (pevent->mask & IN_UNMOUNT) {
361 wwarning(_("the unit containing the defaults database has"
362 " been unmounted. Setting --static mode." " Any changes will not be saved."));
363 close(fd);
364 wPreferences.flags.noupdates = 1;
366 if ((pevent->mask & IN_MODIFY) && oneShotFlag == 0) {
367 wwarning(_("Inotify: Reading config files in defaults database."));
368 wDefaultsCheckDomains(NULL);
371 /* move to next event in the buffer */
372 i += sizeof(struct inotify_event) + pevent->len;
375 #endif /* HAVE_INOTIFY */
378 *----------------------------------------------------------------------
379 * EventLoop-
380 * Processes X and internal events indefinitely.
382 * Returns:
383 * Never returns
385 * Side effects:
386 * The LastTimestamp global variable is updated.
387 * Calls inotifyGetEvents if defaults database changes.
388 *----------------------------------------------------------------------
390 noreturn void EventLoop(void)
392 XEvent event;
393 #ifdef HAVE_INOTIFY
394 extern int inotifyFD;
395 extern int inotifyWD;
396 struct timeval time;
397 fd_set rfds;
398 int retVal = 0;
400 if (inotifyFD < 0 || inotifyWD < 0)
401 retVal = -1;
402 #endif
404 for (;;) {
406 WMNextEvent(dpy, &event); /* Blocks here */
407 WMHandleEvent(&event);
408 #ifdef HAVE_INOTIFY
409 if (retVal != -1) {
410 time.tv_sec = 0;
411 time.tv_usec = 0;
412 FD_ZERO(&rfds);
413 FD_SET(inotifyFD, &rfds);
415 /* check for available read data from inotify - don't block! */
416 retVal = select(inotifyFD + 1, &rfds, NULL, NULL, &time);
418 if (retVal < 0) { /* an error has occured */
419 wwarning(_("select failed. The inotify instance will be closed."
420 " Changes to the defaults database will require"
421 " a restart to take effect."));
422 close(inotifyFD);
423 continue;
425 if (FD_ISSET(inotifyFD, &rfds))
426 handle_inotify_events(inotifyFD, inotifyWD);
428 #endif
433 *----------------------------------------------------------------------
434 * ProcessPendingEvents --
435 * Processes the events that are currently pending (at the time
436 * this function is called) in the display's queue.
438 * Returns:
439 * After the pending events that were present at the function call
440 * are processed.
442 * Side effects:
443 * Many -- whatever handling events may involve.
445 *----------------------------------------------------------------------
447 void ProcessPendingEvents(void)
449 XEvent event;
450 int count;
452 XSync(dpy, False);
454 /* Take a snapshot of the event count in the queue */
455 count = XPending(dpy);
457 while (count > 0 && XPending(dpy)) {
458 WMNextEvent(dpy, &event);
459 WMHandleEvent(&event);
460 count--;
464 Bool IsDoubleClick(WScreen * scr, XEvent * event)
466 if ((scr->last_click_time > 0) &&
467 (event->xbutton.time - scr->last_click_time <= wPreferences.dblclick_time)
468 && (event->xbutton.button == scr->last_click_button)
469 && (event->xbutton.window == scr->last_click_window)) {
471 scr->flags.next_click_is_not_double = 1;
472 scr->last_click_time = 0;
473 scr->last_click_window = event->xbutton.window;
475 return True;
477 return False;
480 void NotifyDeadProcess(pid_t pid, unsigned char status)
482 if (deadProcessPtr >= MAX_DEAD_PROCESSES - 1) {
483 wwarning("stack overflow: too many dead processes");
484 return;
486 /* stack the process to be handled later,
487 * as this is called from the signal handler */
488 deadProcesses[deadProcessPtr].pid = pid;
489 deadProcesses[deadProcessPtr].exit_status = status;
490 deadProcessPtr++;
493 static void handleDeadProcess(void *foo)
495 DeathHandler *tmp;
496 int i;
498 for (i = 0; i < deadProcessPtr; i++) {
499 wWindowDeleteSavedStatesForPID(deadProcesses[i].pid);
502 if (!deathHandlers) {
503 deadProcessPtr = 0;
504 return;
507 /* get the pids on the queue and call handlers */
508 while (deadProcessPtr > 0) {
509 deadProcessPtr--;
511 for (i = WMGetArrayItemCount(deathHandlers) - 1; i >= 0; i--) {
512 tmp = WMGetFromArray(deathHandlers, i);
513 if (!tmp)
514 continue;
516 if (tmp->pid == deadProcesses[deadProcessPtr].pid) {
517 (*tmp->callback) (tmp->pid,
518 deadProcesses[deadProcessPtr].exit_status, tmp->client_data);
519 wdelete_death_handler(tmp);
525 static void saveTimestamp(XEvent * event)
528 * Never save CurrentTime as LastTimestamp because CurrentTime
529 * it's not a real timestamp (it's the 0L constant)
532 switch (event->type) {
533 case ButtonRelease:
534 case ButtonPress:
535 LastTimestamp = event->xbutton.time;
536 break;
537 case KeyPress:
538 case KeyRelease:
539 LastTimestamp = event->xkey.time;
540 break;
541 case MotionNotify:
542 LastTimestamp = event->xmotion.time;
543 break;
544 case PropertyNotify:
545 LastTimestamp = event->xproperty.time;
546 break;
547 case EnterNotify:
548 case LeaveNotify:
549 LastTimestamp = event->xcrossing.time;
550 break;
551 case SelectionClear:
552 LastTimestamp = event->xselectionclear.time;
553 break;
554 case SelectionRequest:
555 LastTimestamp = event->xselectionrequest.time;
556 break;
557 case SelectionNotify:
558 LastTimestamp = event->xselection.time;
559 #ifdef XDND
560 wXDNDProcessSelection(event);
561 #endif
562 break;
566 static int matchWindow(const void *item, const void *cdata)
568 return (((WFakeGroupLeader *) item)->origLeader == (Window) cdata);
571 static void handleExtensions(XEvent * event)
573 #ifdef KEEP_XKB_LOCK_STATUS
574 XkbEvent *xkbevent;
575 xkbevent = (XkbEvent *) event;
576 #endif /*KEEP_XKB_LOCK_STATUS */
577 #ifdef SHAPE
578 if (wShapeSupported && event->type == (wShapeEventBase + ShapeNotify)) {
579 handleShapeNotify(event);
581 #endif
582 #ifdef KEEP_XKB_LOCK_STATUS
583 if (wPreferences.modelock && (xkbevent->type == wXkbEventBase)) {
584 handleXkbIndicatorStateNotify(event);
586 #endif /*KEEP_XKB_LOCK_STATUS */
587 #ifdef HAVE_XRANDR
588 if (has_randr && event->type == (randr_event_base + RRScreenChangeNotify)) {
589 /* From xrandr man page: "Clients must call back into Xlib using
590 * XRRUpdateConfiguration when screen configuration change notify
591 * events are generated */
592 XRRUpdateConfiguration(event);
593 WCHANGE_STATE(WSTATE_RESTARTING);
594 Shutdown(WSRestartPreparationMode);
595 Restart(NULL,True);
597 #endif
600 static void handleMapRequest(XEvent * ev)
602 WWindow *wwin;
603 WScreen *scr = NULL;
604 Window window = ev->xmaprequest.window;
606 if ((wwin = wWindowFor(window))) {
607 if (wwin->flags.shaded) {
608 wUnshadeWindow(wwin);
610 /* deiconify window */
611 if (wwin->flags.miniaturized) {
612 wDeiconifyWindow(wwin);
613 } else if (wwin->flags.hidden) {
614 WApplication *wapp = wApplicationOf(wwin->main_window);
615 /* go to the last workspace that the user worked on the app */
616 if (wapp) {
617 wWorkspaceChange(wwin->screen_ptr, wapp->last_workspace);
619 wUnhideApplication(wapp, False, False);
621 return;
624 scr = wScreenForRootWindow(ev->xmaprequest.parent);
626 wwin = wManageWindow(scr, window);
629 * This is to let the Dock know that the application it launched
630 * has already been mapped (eg: it has finished launching).
631 * It is not necessary for normally docked apps, but is needed for
632 * apps that were forcedly docked (like with dockit).
634 if (scr->last_dock) {
635 if (wwin && wwin->main_window != None && wwin->main_window != window)
636 wDockTrackWindowLaunch(scr->last_dock, wwin->main_window);
637 else
638 wDockTrackWindowLaunch(scr->last_dock, window);
641 if (wwin) {
642 wClientSetState(wwin, NormalState, None);
643 if (wwin->flags.maximized) {
644 wMaximizeWindow(wwin, wwin->flags.maximized);
646 if (wwin->flags.shaded) {
647 wwin->flags.shaded = 0;
648 wwin->flags.skip_next_animation = 1;
649 wShadeWindow(wwin);
651 if (wwin->flags.miniaturized) {
652 wwin->flags.miniaturized = 0;
653 wwin->flags.skip_next_animation = 1;
654 wIconifyWindow(wwin);
656 if (wwin->flags.fullscreen) {
657 wwin->flags.fullscreen = 0;
658 wFullscreenWindow(wwin);
660 if (wwin->flags.hidden) {
661 WApplication *wapp = wApplicationOf(wwin->main_window);
663 wwin->flags.hidden = 0;
664 wwin->flags.skip_next_animation = 1;
665 if (wapp) {
666 wHideApplication(wapp);
672 static void handleDestroyNotify(XEvent * event)
674 WWindow *wwin;
675 WApplication *app;
676 Window window = event->xdestroywindow.window;
677 WScreen *scr = wScreenForRootWindow(event->xdestroywindow.event);
678 int widx;
680 wwin = wWindowFor(window);
681 if (wwin) {
682 wUnmanageWindow(wwin, False, True);
685 if (scr != NULL) {
686 while ((widx = WMFindInArray(scr->fakeGroupLeaders, matchWindow, (void *)window)) != WANotFound) {
687 WFakeGroupLeader *fPtr;
689 fPtr = WMGetFromArray(scr->fakeGroupLeaders, widx);
690 if (fPtr->retainCount > 0) {
691 fPtr->retainCount--;
692 if (fPtr->retainCount == 0 && fPtr->leader != None) {
693 XDestroyWindow(dpy, fPtr->leader);
694 fPtr->leader = None;
695 XFlush(dpy);
698 fPtr->origLeader = None;
702 app = wApplicationOf(window);
703 if (app) {
704 if (window == app->main_window) {
705 app->refcount = 0;
706 wwin = app->main_window_desc->screen_ptr->focused_window;
707 while (wwin) {
708 if (wwin->main_window == window) {
709 wwin->main_window = None;
711 wwin = wwin->prev;
714 wApplicationDestroy(app);
718 static void handleExpose(XEvent * event)
720 WObjDescriptor *desc;
721 XEvent ev;
723 while (XCheckTypedWindowEvent(dpy, event->xexpose.window, Expose, &ev)) ;
725 if (XFindContext(dpy, event->xexpose.window, wWinContext, (XPointer *) & desc) == XCNOENT) {
726 return;
729 if (desc->handle_expose) {
730 (*desc->handle_expose) (desc, event);
734 static void executeButtonAction(WScreen * scr, XEvent * event, int action)
736 switch (action) {
737 case WA_SELECT_WINDOWS:
738 wUnselectWindows(scr);
739 wSelectWindows(scr, event);
740 break;
741 case WA_OPEN_APPMENU:
742 OpenRootMenu(scr, event->xbutton.x_root, event->xbutton.y_root, False);
743 /* ugly hack */
744 if (scr->root_menu) {
745 if (scr->root_menu->brother->flags.mapped)
746 event->xbutton.window = scr->root_menu->brother->frame->core->window;
747 else
748 event->xbutton.window = scr->root_menu->frame->core->window;
750 break;
751 case WA_OPEN_WINLISTMENU:
752 OpenSwitchMenu(scr, event->xbutton.x_root, event->xbutton.y_root, False);
753 if (scr->switch_menu) {
754 if (scr->switch_menu->brother->flags.mapped)
755 event->xbutton.window = scr->switch_menu->brother->frame->core->window;
756 else
757 event->xbutton.window = scr->switch_menu->frame->core->window;
759 break;
760 default:
761 break;
765 /* bindable */
766 static void handleButtonPress(XEvent * event)
768 WObjDescriptor *desc;
769 WScreen *scr;
771 scr = wScreenForRootWindow(event->xbutton.root);
773 #ifdef BALLOON_TEXT
774 wBalloonHide(scr);
775 #endif
777 if (event->xbutton.window == scr->root_win) {
778 if (event->xbutton.button == Button1 && wPreferences.mouse_button1 != WA_NONE) {
779 executeButtonAction(scr, event, wPreferences.mouse_button1);
780 } else if (event->xbutton.button == Button2 && wPreferences.mouse_button2 != WA_NONE) {
781 executeButtonAction(scr, event, wPreferences.mouse_button2);
782 } else if (event->xbutton.button == Button3 && wPreferences.mouse_button3 != WA_NONE) {
783 executeButtonAction(scr, event, wPreferences.mouse_button3);
784 } else if (event->xbutton.button == Button4 && wPreferences.mouse_wheel != WA_NONE) {
785 wWorkspaceRelativeChange(scr, 1);
786 } else if (event->xbutton.button == Button5 && wPreferences.mouse_wheel != WA_NONE) {
787 wWorkspaceRelativeChange(scr, -1);
791 desc = NULL;
792 if (XFindContext(dpy, event->xbutton.subwindow, wWinContext, (XPointer *) & desc) == XCNOENT) {
793 if (XFindContext(dpy, event->xbutton.window, wWinContext, (XPointer *) & desc) == XCNOENT) {
794 return;
798 if (desc->parent_type == WCLASS_WINDOW) {
799 XSync(dpy, 0);
801 if (event->xbutton.state & ( MOD_MASK | ControlMask )) {
802 XAllowEvents(dpy, AsyncPointer, CurrentTime);
803 } else {
804 /* if (wPreferences.focus_mode == WKF_CLICK) { */
805 if (wPreferences.ignore_focus_click) {
806 XAllowEvents(dpy, AsyncPointer, CurrentTime);
808 XAllowEvents(dpy, ReplayPointer, CurrentTime);
809 /* } */
811 XSync(dpy, 0);
812 } else if (desc->parent_type == WCLASS_APPICON
813 || desc->parent_type == WCLASS_MINIWINDOW || desc->parent_type == WCLASS_DOCK_ICON) {
814 if (event->xbutton.state & MOD_MASK) {
815 XSync(dpy, 0);
816 XAllowEvents(dpy, AsyncPointer, CurrentTime);
817 XSync(dpy, 0);
821 if (desc->handle_mousedown != NULL) {
822 (*desc->handle_mousedown) (desc, event);
825 /* save double-click information */
826 if (scr->flags.next_click_is_not_double) {
827 scr->flags.next_click_is_not_double = 0;
828 } else {
829 scr->last_click_time = event->xbutton.time;
830 scr->last_click_button = event->xbutton.button;
831 scr->last_click_window = event->xbutton.window;
835 static void handleMapNotify(XEvent * event)
837 WWindow *wwin;
839 wwin = wWindowFor(event->xmap.event);
840 if (wwin && wwin->client_win == event->xmap.event) {
841 if (wwin->flags.miniaturized) {
842 wDeiconifyWindow(wwin);
843 } else {
844 XGrabServer(dpy);
845 wWindowMap(wwin);
846 wClientSetState(wwin, NormalState, None);
847 XUngrabServer(dpy);
852 static void handleUnmapNotify(XEvent * event)
854 WWindow *wwin;
855 XEvent ev;
856 Bool withdraw = False;
858 /* only process windows with StructureNotify selected
859 * (ignore SubstructureNotify) */
860 wwin = wWindowFor(event->xunmap.window);
861 if (!wwin)
862 return;
864 /* whether the event is a Withdrawal request */
865 if (event->xunmap.event == wwin->screen_ptr->root_win && event->xunmap.send_event)
866 withdraw = True;
868 if (wwin->client_win != event->xunmap.event && !withdraw)
869 return;
871 if (!wwin->flags.mapped && !withdraw
872 && wwin->frame->workspace == wwin->screen_ptr->current_workspace
873 && !wwin->flags.miniaturized && !wwin->flags.hidden)
874 return;
876 XGrabServer(dpy);
877 XUnmapWindow(dpy, wwin->frame->core->window);
878 wwin->flags.mapped = 0;
879 XSync(dpy, 0);
880 /* check if the window was destroyed */
881 if (XCheckTypedWindowEvent(dpy, wwin->client_win, DestroyNotify, &ev)) {
882 DispatchEvent(&ev);
883 } else {
884 Bool reparented = False;
886 if (XCheckTypedWindowEvent(dpy, wwin->client_win, ReparentNotify, &ev))
887 reparented = True;
889 /* withdraw window */
890 wwin->flags.mapped = 0;
891 if (!reparented)
892 wClientSetState(wwin, WithdrawnState, None);
894 /* if the window was reparented, do not reparent it back to the
895 * root window */
896 wUnmanageWindow(wwin, !reparented, False);
898 XUngrabServer(dpy);
901 static void handleConfigureRequest(XEvent * event)
903 WWindow *wwin;
905 if (!(wwin = wWindowFor(event->xconfigurerequest.window))) {
907 * Configure request for unmapped window
909 wClientConfigure(NULL, &(event->xconfigurerequest));
910 } else {
911 wClientConfigure(wwin, &(event->xconfigurerequest));
915 static void handlePropertyNotify(XEvent * event)
917 WWindow *wwin;
918 WApplication *wapp;
919 Window jr;
920 int ji;
921 unsigned int ju;
923 wwin = wWindowFor(event->xproperty.window);
924 if (wwin) {
925 if (!XGetGeometry(dpy, wwin->client_win, &jr, &ji, &ji, &ju, &ju, &ju, &ju)) {
926 return;
928 wClientCheckProperty(wwin, &event->xproperty);
930 wapp = wApplicationOf(event->xproperty.window);
931 if (wapp) {
932 wClientCheckProperty(wapp->main_window_desc, &event->xproperty);
936 static void handleClientMessage(XEvent * event)
938 WWindow *wwin;
939 WObjDescriptor *desc;
941 /* handle transition from Normal to Iconic state */
942 if (event->xclient.message_type == _XA_WM_CHANGE_STATE
943 && event->xclient.format == 32 && event->xclient.data.l[0] == IconicState) {
945 wwin = wWindowFor(event->xclient.window);
946 if (!wwin)
947 return;
948 if (!wwin->flags.miniaturized)
949 wIconifyWindow(wwin);
950 } else if (event->xclient.message_type == _XA_WM_COLORMAP_NOTIFY && event->xclient.format == 32) {
951 WScreen *scr = wScreenForRootWindow(event->xclient.window);
953 if (!scr)
954 return;
956 if (event->xclient.data.l[1] == 1) { /* starting */
957 wColormapAllowClientInstallation(scr, True);
958 } else { /* stopping */
959 wColormapAllowClientInstallation(scr, False);
961 } else if (event->xclient.message_type == _XA_WINDOWMAKER_COMMAND) {
963 char *command;
964 size_t len;
966 len = sizeof(event->xclient.data.b) + 1;
967 command = wmalloc(len);
968 strncpy(command, event->xclient.data.b, sizeof(event->xclient.data.b));
970 if (strncmp(command, "Reconfigure", sizeof("Reconfigure")) == 0) {
971 wwarning(_("Got Reconfigure command"));
972 wDefaultsCheckDomains(NULL);
973 } else {
974 wwarning(_("Got unknown command %s"), command);
977 wfree(command);
979 } else if (event->xclient.message_type == _XA_WINDOWMAKER_WM_FUNCTION) {
980 WApplication *wapp;
981 int done = 0;
982 wapp = wApplicationOf(event->xclient.window);
983 if (wapp) {
984 switch (event->xclient.data.l[0]) {
985 case WMFHideOtherApplications:
986 wHideOtherApplications(wapp->main_window_desc);
987 done = 1;
988 break;
990 case WMFHideApplication:
991 wHideApplication(wapp);
992 done = 1;
993 break;
996 if (!done) {
997 wwin = wWindowFor(event->xclient.window);
998 if (wwin) {
999 switch (event->xclient.data.l[0]) {
1000 case WMFHideOtherApplications:
1001 wHideOtherApplications(wwin);
1002 break;
1004 case WMFHideApplication:
1005 wHideApplication(wApplicationOf(wwin->main_window));
1006 break;
1010 } else if (event->xclient.message_type == _XA_GNUSTEP_WM_ATTR) {
1011 wwin = wWindowFor(event->xclient.window);
1012 if (!wwin)
1013 return;
1014 switch (event->xclient.data.l[0]) {
1015 case GSWindowLevelAttr:
1017 int level = (int)event->xclient.data.l[1];
1019 if (WINDOW_LEVEL(wwin) != level) {
1020 ChangeStackingLevel(wwin->frame->core, level);
1023 break;
1025 } else if (event->xclient.message_type == _XA_GNUSTEP_TITLEBAR_STATE) {
1026 wwin = wWindowFor(event->xclient.window);
1027 if (!wwin)
1028 return;
1029 switch (event->xclient.data.l[0]) {
1030 case WMTitleBarNormal:
1031 wFrameWindowChangeState(wwin->frame, WS_UNFOCUSED);
1032 break;
1033 case WMTitleBarMain:
1034 wFrameWindowChangeState(wwin->frame, WS_PFOCUSED);
1035 break;
1036 case WMTitleBarKey:
1037 wFrameWindowChangeState(wwin->frame, WS_FOCUSED);
1038 break;
1040 } else if (event->xclient.message_type == _XA_WM_IGNORE_FOCUS_EVENTS) {
1041 WScreen *scr = wScreenForRootWindow(event->xclient.window);
1042 if (!scr)
1043 return;
1044 scr->flags.ignore_focus_events = event->xclient.data.l[0] ? 1 : 0;
1045 } else if (wNETWMProcessClientMessage(&event->xclient)) {
1046 /* do nothing */
1047 #ifdef XDND
1048 } else if (wXDNDProcessClientMessage(&event->xclient)) {
1049 /* do nothing */
1050 #endif /* XDND */
1051 } else {
1053 * Non-standard thing, but needed by OffiX DND.
1054 * For when the icon frame gets a ClientMessage
1055 * that should have gone to the icon_window.
1057 if (XFindContext(dpy, event->xbutton.window, wWinContext, (XPointer *) & desc) != XCNOENT) {
1058 struct WIcon *icon = NULL;
1060 if (desc->parent_type == WCLASS_MINIWINDOW) {
1061 icon = (WIcon *) desc->parent;
1062 } else if (desc->parent_type == WCLASS_DOCK_ICON || desc->parent_type == WCLASS_APPICON) {
1063 icon = ((WAppIcon *) desc->parent)->icon;
1065 if (icon && (wwin = icon->owner)) {
1066 if (wwin->client_win != event->xclient.window) {
1067 event->xclient.window = wwin->client_win;
1068 XSendEvent(dpy, wwin->client_win, False, NoEventMask, event);
1075 static void raiseWindow(WScreen * scr)
1077 WWindow *wwin;
1079 scr->autoRaiseTimer = NULL;
1081 wwin = wWindowFor(scr->autoRaiseWindow);
1082 if (!wwin)
1083 return;
1085 if (!wwin->flags.destroyed && wwin->flags.focused) {
1086 wRaiseFrame(wwin->frame->core);
1087 /* this is needed or a race condition will occur */
1088 XSync(dpy, False);
1092 static void handleEnterNotify(XEvent * event)
1094 WWindow *wwin;
1095 WObjDescriptor *desc = NULL;
1096 XEvent ev;
1097 WScreen *scr = wScreenForRootWindow(event->xcrossing.root);
1099 if (XCheckTypedWindowEvent(dpy, event->xcrossing.window, LeaveNotify, &ev)) {
1100 /* already left the window... */
1101 saveTimestamp(&ev);
1102 if (ev.xcrossing.mode == event->xcrossing.mode && ev.xcrossing.detail == event->xcrossing.detail) {
1103 return;
1107 if (XFindContext(dpy, event->xcrossing.window, wWinContext, (XPointer *) & desc) != XCNOENT) {
1108 if (desc->handle_enternotify)
1109 (*desc->handle_enternotify) (desc, event);
1112 /* enter to window */
1113 wwin = wWindowFor(event->xcrossing.window);
1114 if (!wwin) {
1115 if (wPreferences.colormap_mode == WCM_POINTER) {
1116 wColormapInstallForWindow(scr, NULL);
1118 if (scr->autoRaiseTimer && event->xcrossing.root == event->xcrossing.window) {
1119 WMDeleteTimerHandler(scr->autoRaiseTimer);
1120 scr->autoRaiseTimer = NULL;
1122 } else {
1123 /* set auto raise timer even if in focus-follows-mouse mode
1124 * and the event is for the frame window, even if the window
1125 * has focus already. useful if you move the pointer from a focused
1126 * window to the root window and back pretty fast
1128 * set focus if in focus-follows-mouse mode and the event
1129 * is for the frame window and window doesn't have focus yet */
1130 if (wPreferences.focus_mode == WKF_SLOPPY
1131 && wwin->frame->core->window == event->xcrossing.window && !scr->flags.doing_alt_tab) {
1133 if (!wwin->flags.focused && !WFLAGP(wwin, no_focusable))
1134 wSetFocusTo(scr, wwin);
1136 if (scr->autoRaiseTimer)
1137 WMDeleteTimerHandler(scr->autoRaiseTimer);
1138 scr->autoRaiseTimer = NULL;
1140 if (wPreferences.raise_delay && !WFLAGP(wwin, no_focusable)) {
1141 scr->autoRaiseWindow = wwin->frame->core->window;
1142 scr->autoRaiseTimer
1143 = WMAddTimerHandler(wPreferences.raise_delay, (WMCallback *) raiseWindow, scr);
1146 /* Install colormap for window, if the colormap installation mode
1147 * is colormap_follows_mouse */
1148 if (wPreferences.colormap_mode == WCM_POINTER) {
1149 if (wwin->client_win == event->xcrossing.window)
1150 wColormapInstallForWindow(scr, wwin);
1151 else
1152 wColormapInstallForWindow(scr, NULL);
1156 if (event->xcrossing.window == event->xcrossing.root
1157 && event->xcrossing.detail == NotifyNormal
1158 && event->xcrossing.detail != NotifyInferior && wPreferences.focus_mode != WKF_CLICK) {
1160 wSetFocusTo(scr, scr->focused_window);
1162 #ifdef BALLOON_TEXT
1163 wBalloonEnteredObject(scr, desc);
1164 #endif
1167 static void handleLeaveNotify(XEvent * event)
1169 WObjDescriptor *desc = NULL;
1171 if (XFindContext(dpy, event->xcrossing.window, wWinContext, (XPointer *) & desc) != XCNOENT) {
1172 if (desc->handle_leavenotify)
1173 (*desc->handle_leavenotify) (desc, event);
1177 #ifdef SHAPE
1178 static void handleShapeNotify(XEvent * event)
1180 XShapeEvent *shev = (XShapeEvent *) event;
1181 WWindow *wwin;
1182 union {
1183 XEvent xevent;
1184 XShapeEvent xshape;
1185 } ev;
1187 while (XCheckTypedWindowEvent(dpy, shev->window, event->type, &ev.xevent)) {
1188 if (ev.xshape.kind == ShapeBounding) {
1189 if (ev.xshape.shaped == shev->shaped) {
1190 *shev = ev.xshape;
1191 } else {
1192 XPutBackEvent(dpy, &ev.xevent);
1193 break;
1198 wwin = wWindowFor(shev->window);
1199 if (!wwin || shev->kind != ShapeBounding)
1200 return;
1202 if (!shev->shaped && wwin->flags.shaped) {
1204 wwin->flags.shaped = 0;
1205 wWindowClearShape(wwin);
1207 } else if (shev->shaped) {
1209 wwin->flags.shaped = 1;
1210 wWindowSetShape(wwin);
1213 #endif /* SHAPE */
1215 #ifdef KEEP_XKB_LOCK_STATUS
1216 /* please help ]d if you know what to do */
1217 static void handleXkbIndicatorStateNotify(XEvent *event)
1219 WWindow *wwin;
1220 WScreen *scr;
1221 XkbStateRec staterec;
1222 int i;
1224 for (i = 0; i < wScreenCount; i++) {
1225 scr = wScreenWithNumber(i);
1226 wwin = scr->focused_window;
1227 if (wwin && wwin->flags.focused) {
1228 XkbGetState(dpy, XkbUseCoreKbd, &staterec);
1229 if (wwin->frame->languagemode != staterec.group) {
1230 wwin->frame->last_languagemode = wwin->frame->languagemode;
1231 wwin->frame->languagemode = staterec.group;
1233 #ifdef XKB_BUTTON_HINT
1234 if (wwin->frame->titlebar) {
1235 wFrameWindowPaint(wwin->frame);
1237 #endif
1241 #endif /*KEEP_XKB_LOCK_STATUS */
1243 static void handleColormapNotify(XEvent * event)
1245 WWindow *wwin;
1246 WScreen *scr;
1247 Bool reinstall = False;
1249 wwin = wWindowFor(event->xcolormap.window);
1250 if (!wwin)
1251 return;
1253 scr = wwin->screen_ptr;
1255 do {
1256 if (wwin) {
1257 if (event->xcolormap.new) {
1258 XWindowAttributes attr;
1260 XGetWindowAttributes(dpy, wwin->client_win, &attr);
1262 if (wwin == scr->cmap_window && wwin->cmap_window_no == 0)
1263 scr->current_colormap = attr.colormap;
1265 reinstall = True;
1266 } else if (event->xcolormap.state == ColormapUninstalled &&
1267 scr->current_colormap == event->xcolormap.colormap) {
1269 /* some bastard app (like XV) removed our colormap */
1271 * can't enforce or things like xscreensaver wont work
1272 * reinstall = True;
1274 } else if (event->xcolormap.state == ColormapInstalled &&
1275 scr->current_colormap == event->xcolormap.colormap) {
1277 /* someone has put our colormap back */
1278 reinstall = False;
1281 } while (XCheckTypedEvent(dpy, ColormapNotify, event)
1282 && ((wwin = wWindowFor(event->xcolormap.window)) || 1));
1284 if (reinstall && scr->current_colormap != None) {
1285 if (!scr->flags.colormap_stuff_blocked)
1286 XInstallColormap(dpy, scr->current_colormap);
1290 static void handleFocusIn(XEvent * event)
1292 WWindow *wwin;
1295 * For applications that like stealing the focus.
1297 while (XCheckTypedEvent(dpy, FocusIn, event)) ;
1298 saveTimestamp(event);
1299 if (event->xfocus.mode == NotifyUngrab
1300 || event->xfocus.mode == NotifyGrab || event->xfocus.detail > NotifyNonlinearVirtual) {
1301 return;
1304 wwin = wWindowFor(event->xfocus.window);
1305 if (wwin && !wwin->flags.focused) {
1306 if (wwin->flags.mapped)
1307 wSetFocusTo(wwin->screen_ptr, wwin);
1308 else
1309 wSetFocusTo(wwin->screen_ptr, NULL);
1310 } else if (!wwin) {
1311 WScreen *scr = wScreenForWindow(event->xfocus.window);
1312 if (scr)
1313 wSetFocusTo(scr, NULL);
1317 static WWindow *windowUnderPointer(WScreen * scr)
1319 unsigned int mask;
1320 int foo;
1321 Window bar, win;
1323 if (XQueryPointer(dpy, scr->root_win, &bar, &win, &foo, &foo, &foo, &foo, &mask))
1324 return wWindowFor(win);
1325 return NULL;
1328 static int CheckFullScreenWindowFocused(WScreen * scr)
1330 if (scr->focused_window && scr->focused_window->flags.fullscreen)
1331 return 1;
1332 else
1333 return 0;
1336 static void handleKeyPress(XEvent * event)
1338 WScreen *scr = wScreenForRootWindow(event->xkey.root);
1339 WWindow *wwin = scr->focused_window;
1340 short i, widx;
1341 int modifiers;
1342 int command = -1;
1343 #ifdef KEEP_XKB_LOCK_STATUS
1344 XkbStateRec staterec;
1345 #endif /*KEEP_XKB_LOCK_STATUS */
1347 /* ignore CapsLock */
1348 modifiers = event->xkey.state & ValidModMask;
1350 for (i = 0; i < WKBD_LAST; i++) {
1351 if (wKeyBindings[i].keycode == 0)
1352 continue;
1354 if (wKeyBindings[i].keycode == event->xkey.keycode && ( /*wKeyBindings[i].modifier==0
1355 || */ wKeyBindings[i].modifier ==
1356 modifiers)) {
1357 command = i;
1358 break;
1362 if (command < 0) {
1364 if (!wRootMenuPerformShortcut(event)) {
1365 static int dontLoop = 0;
1367 if (dontLoop > 10) {
1368 wwarning("problem with key event processing code");
1369 return;
1371 dontLoop++;
1372 /* if the focused window is an internal window, try redispatching
1373 * the event to the managed window, as it can be a WINGs window */
1374 if (wwin && wwin->flags.internal_window && wwin->client_leader != None) {
1375 /* client_leader contains the WINGs toplevel */
1376 event->xany.window = wwin->client_leader;
1377 WMHandleEvent(event);
1379 dontLoop--;
1381 return;
1383 #define ISMAPPED(w) ((w) && !(w)->flags.miniaturized && ((w)->flags.mapped || (w)->flags.shaded))
1384 #define ISFOCUSED(w) ((w) && (w)->flags.focused)
1386 switch (command) {
1388 case WKBD_ROOTMENU:
1389 /*OpenRootMenu(scr, event->xkey.x_root, event->xkey.y_root, True); */
1390 if (!CheckFullScreenWindowFocused(scr)) {
1391 WMRect rect = wGetRectForHead(scr, wGetHeadForPointerLocation(scr));
1392 OpenRootMenu(scr, rect.pos.x + rect.size.width / 2, rect.pos.y + rect.size.height / 2,
1393 True);
1395 break;
1396 case WKBD_WINDOWLIST:
1397 if (!CheckFullScreenWindowFocused(scr)) {
1398 WMRect rect = wGetRectForHead(scr, wGetHeadForPointerLocation(scr));
1399 OpenSwitchMenu(scr, rect.pos.x + rect.size.width / 2, rect.pos.y + rect.size.height / 2,
1400 True);
1402 break;
1404 case WKBD_WINDOWMENU:
1405 if (ISMAPPED(wwin) && ISFOCUSED(wwin))
1406 OpenWindowMenu(wwin, wwin->frame_x, wwin->frame_y + wwin->frame->top_width, True);
1407 break;
1408 case WKBD_MINIMIZEALL:
1409 CloseWindowMenu(scr);
1410 wHideAll(scr);
1411 break;
1412 case WKBD_MINIATURIZE:
1413 if (ISMAPPED(wwin) && ISFOCUSED(wwin)
1414 && !WFLAGP(wwin, no_miniaturizable)) {
1415 CloseWindowMenu(scr);
1417 if (wwin->protocols.MINIATURIZE_WINDOW)
1418 wClientSendProtocol(wwin, _XA_GNUSTEP_WM_MINIATURIZE_WINDOW, event->xbutton.time);
1419 else {
1420 wIconifyWindow(wwin);
1423 break;
1424 case WKBD_HIDE:
1425 if (ISMAPPED(wwin) && ISFOCUSED(wwin)) {
1426 WApplication *wapp = wApplicationOf(wwin->main_window);
1427 CloseWindowMenu(scr);
1429 if (wapp && !WFLAGP(wapp->main_window_desc, no_appicon)) {
1430 wHideApplication(wapp);
1433 break;
1434 case WKBD_HIDE_OTHERS:
1435 if (ISMAPPED(wwin) && ISFOCUSED(wwin)) {
1436 CloseWindowMenu(scr);
1438 wHideOtherApplications(wwin);
1440 break;
1441 case WKBD_MAXIMIZE:
1442 if (ISMAPPED(wwin) && ISFOCUSED(wwin) && IS_RESIZABLE(wwin)) {
1443 CloseWindowMenu(scr);
1445 handleMaximize(wwin, MAX_VERTICAL | MAX_HORIZONTAL | MAX_KEYBOARD);
1447 break;
1448 case WKBD_VMAXIMIZE:
1449 if (ISMAPPED(wwin) && ISFOCUSED(wwin) && IS_RESIZABLE(wwin)) {
1450 CloseWindowMenu(scr);
1452 handleMaximize(wwin, MAX_VERTICAL | MAX_KEYBOARD);
1454 break;
1455 case WKBD_HMAXIMIZE:
1456 if (ISMAPPED(wwin) && ISFOCUSED(wwin) && IS_RESIZABLE(wwin)) {
1457 CloseWindowMenu(scr);
1459 handleMaximize(wwin, MAX_HORIZONTAL | MAX_KEYBOARD);
1461 break;
1462 case WKBD_LHMAXIMIZE:
1463 if (ISMAPPED(wwin) && ISFOCUSED(wwin) && IS_RESIZABLE(wwin)) {
1464 CloseWindowMenu(scr);
1466 handleMaximize(wwin, MAX_VERTICAL | MAX_LEFTHALF | MAX_KEYBOARD);
1468 break;
1469 case WKBD_RHMAXIMIZE:
1470 if (ISMAPPED(wwin) && ISFOCUSED(wwin) && IS_RESIZABLE(wwin)) {
1471 CloseWindowMenu(scr);
1473 handleMaximize(wwin, MAX_VERTICAL | MAX_RIGHTHALF | MAX_KEYBOARD);
1475 break;
1476 case WKBD_THMAXIMIZE:
1477 if (ISMAPPED(wwin) && ISFOCUSED(wwin) && IS_RESIZABLE(wwin)) {
1478 CloseWindowMenu(scr);
1480 handleMaximize(wwin, MAX_HORIZONTAL | MAX_TOPHALF | MAX_KEYBOARD);
1482 break;
1483 case WKBD_BHMAXIMIZE:
1484 if (ISMAPPED(wwin) && ISFOCUSED(wwin) && IS_RESIZABLE(wwin)) {
1485 CloseWindowMenu(scr);
1487 handleMaximize(wwin, MAX_HORIZONTAL | MAX_BOTTOMHALF | MAX_KEYBOARD);
1489 break;
1490 case WKBD_LTCMAXIMIZE:
1491 if (ISMAPPED(wwin) && ISFOCUSED(wwin) && IS_RESIZABLE(wwin)) {
1492 CloseWindowMenu(scr);
1494 handleMaximize(wwin, MAX_LEFTHALF | MAX_TOPHALF | MAX_KEYBOARD);
1496 break;
1497 case WKBD_RTCMAXIMIZE:
1498 if (ISMAPPED(wwin) && ISFOCUSED(wwin) && IS_RESIZABLE(wwin)) {
1499 CloseWindowMenu(scr);
1501 handleMaximize(wwin, MAX_RIGHTHALF | MAX_TOPHALF | MAX_KEYBOARD);
1503 break;
1504 case WKBD_LBCMAXIMIZE:
1505 if (ISMAPPED(wwin) && ISFOCUSED(wwin) && IS_RESIZABLE(wwin)) {
1506 CloseWindowMenu(scr);
1508 handleMaximize(wwin, MAX_LEFTHALF | MAX_BOTTOMHALF | MAX_KEYBOARD);
1510 break;
1511 case WKBD_RBCMAXIMIZE:
1512 if (ISMAPPED(wwin) && ISFOCUSED(wwin) && IS_RESIZABLE(wwin)) {
1513 CloseWindowMenu(scr);
1515 handleMaximize(wwin, MAX_RIGHTHALF | MAX_BOTTOMHALF | MAX_KEYBOARD);
1517 break;
1518 case WKBD_MAXIMUS:
1519 if (ISMAPPED(wwin) && ISFOCUSED(wwin) && IS_RESIZABLE(wwin)) {
1520 CloseWindowMenu(scr);
1522 handleMaximize(wwin, MAX_MAXIMUS | MAX_KEYBOARD);
1524 break;
1525 case WKBD_RAISE:
1526 if (ISMAPPED(wwin) && ISFOCUSED(wwin)) {
1527 CloseWindowMenu(scr);
1529 wRaiseFrame(wwin->frame->core);
1531 break;
1532 case WKBD_LOWER:
1533 if (ISMAPPED(wwin) && ISFOCUSED(wwin)) {
1534 CloseWindowMenu(scr);
1536 wLowerFrame(wwin->frame->core);
1538 break;
1539 case WKBD_RAISELOWER:
1540 /* raise or lower the window under the pointer, not the
1541 * focused one
1543 wwin = windowUnderPointer(scr);
1544 if (wwin)
1545 wRaiseLowerFrame(wwin->frame->core);
1546 break;
1547 case WKBD_SHADE:
1548 if (ISMAPPED(wwin) && ISFOCUSED(wwin) && !WFLAGP(wwin, no_shadeable)) {
1549 if (wwin->flags.shaded)
1550 wUnshadeWindow(wwin);
1551 else
1552 wShadeWindow(wwin);
1554 break;
1555 case WKBD_MOVERESIZE:
1556 if (ISMAPPED(wwin) && ISFOCUSED(wwin) && (IS_RESIZABLE(wwin) || IS_MOVABLE(wwin))) {
1557 CloseWindowMenu(scr);
1559 wKeyboardMoveResizeWindow(wwin);
1561 break;
1562 case WKBD_CLOSE:
1563 if (ISMAPPED(wwin) && ISFOCUSED(wwin) && !WFLAGP(wwin, no_closable)) {
1564 CloseWindowMenu(scr);
1565 if (wwin->protocols.DELETE_WINDOW)
1566 wClientSendProtocol(wwin, _XA_WM_DELETE_WINDOW, event->xkey.time);
1568 break;
1569 case WKBD_SELECT:
1570 if (ISMAPPED(wwin) && ISFOCUSED(wwin)) {
1571 wSelectWindow(wwin, !wwin->flags.selected);
1573 break;
1574 case WKBD_FOCUSNEXT:
1575 StartWindozeCycle(wwin, event, True, False);
1576 break;
1578 case WKBD_FOCUSPREV:
1579 StartWindozeCycle(wwin, event, False, False);
1580 break;
1582 case WKBD_GROUPNEXT:
1583 StartWindozeCycle(wwin, event, True, True);
1584 break;
1586 case WKBD_GROUPPREV:
1587 StartWindozeCycle(wwin, event, False, True);
1588 break;
1590 case WKBD_WORKSPACE1 ... WKBD_WORKSPACE10:
1591 widx = command - WKBD_WORKSPACE1;
1592 i = (scr->current_workspace / 10) * 10 + widx;
1593 if (wPreferences.ws_advance || i < scr->workspace_count)
1594 wWorkspaceChange(scr, i);
1595 break;
1597 case WKBD_NEXTWORKSPACE:
1598 wWorkspaceRelativeChange(scr, 1);
1599 break;
1600 case WKBD_PREVWORKSPACE:
1601 wWorkspaceRelativeChange(scr, -1);
1602 break;
1603 case WKBD_LASTWORKSPACE:
1604 wWorkspaceChange(scr, scr->last_workspace);
1605 break;
1607 case WKBD_MOVE_WORKSPACE1 ... WKBD_MOVE_WORKSPACE10:
1608 widx = command - WKBD_MOVE_WORKSPACE1;
1609 i = (scr->current_workspace / 10) * 10 + widx;
1610 if (wwin && (wPreferences.ws_advance || i < scr->workspace_count))
1611 wWindowChangeWorkspace(wwin, i);
1612 break;
1614 case WKBD_MOVE_NEXTWORKSPACE:
1615 if (wwin)
1616 wWindowChangeWorkspaceRelative(wwin, 1);
1617 break;
1618 case WKBD_MOVE_PREVWORKSPACE:
1619 if (wwin)
1620 wWindowChangeWorkspaceRelative(wwin, -1);
1621 break;
1622 case WKBD_MOVE_LASTWORKSPACE:
1623 if (wwin)
1624 wWindowChangeWorkspace(wwin, scr->last_workspace);
1625 break;
1627 case WKBD_MOVE_NEXTWSLAYER:
1628 case WKBD_MOVE_PREVWSLAYER:
1630 if (wwin) {
1631 int row, column;
1633 row = scr->current_workspace / 10;
1634 column = scr->current_workspace % 10;
1636 if (command == WKBD_MOVE_NEXTWSLAYER) {
1637 if ((row + 1) * 10 < scr->workspace_count)
1638 wWindowChangeWorkspace(wwin, column + (row + 1) * 10);
1639 } else {
1640 if (row > 0)
1641 wWindowChangeWorkspace(wwin, column + (row - 1) * 10);
1645 break;
1647 case WKBD_WINDOW1:
1648 case WKBD_WINDOW2:
1649 case WKBD_WINDOW3:
1650 case WKBD_WINDOW4:
1651 case WKBD_WINDOW5:
1652 case WKBD_WINDOW6:
1653 case WKBD_WINDOW7:
1654 case WKBD_WINDOW8:
1655 case WKBD_WINDOW9:
1656 case WKBD_WINDOW10:
1658 widx = command - WKBD_WINDOW1;
1660 if (scr->shortcutWindows[widx]) {
1661 WMArray *list = scr->shortcutWindows[widx];
1662 int cw;
1663 int count = WMGetArrayItemCount(list);
1664 WWindow *twin;
1665 WMArrayIterator iter;
1666 WWindow *wwin;
1668 wUnselectWindows(scr);
1669 cw = scr->current_workspace;
1671 WM_ETARETI_ARRAY(list, wwin, iter) {
1672 if (count > 1)
1673 wWindowChangeWorkspace(wwin, cw);
1675 wMakeWindowVisible(wwin);
1677 if (count > 1)
1678 wSelectWindow(wwin, True);
1681 /* rotate the order of windows, to create a cycling effect */
1682 twin = WMGetFromArray(list, 0);
1683 WMDeleteFromArray(list, 0);
1684 WMAddToArray(list, twin);
1686 } else if (wwin && ISMAPPED(wwin) && ISFOCUSED(wwin)) {
1687 if (scr->shortcutWindows[widx]) {
1688 WMFreeArray(scr->shortcutWindows[widx]);
1689 scr->shortcutWindows[widx] = NULL;
1692 if (wwin->flags.selected && scr->selected_windows) {
1693 scr->shortcutWindows[widx] = WMDuplicateArray(scr->selected_windows);
1694 /*WMRemoveFromArray(scr->shortcutWindows[index], wwin);
1695 WMInsertInArray(scr->shortcutWindows[index], 0, wwin); */
1696 } else {
1697 scr->shortcutWindows[widx] = WMCreateArray(4);
1698 WMAddToArray(scr->shortcutWindows[widx], wwin);
1701 wSelectWindow(wwin, !wwin->flags.selected);
1702 XFlush(dpy);
1703 wusleep(3000);
1704 wSelectWindow(wwin, !wwin->flags.selected);
1705 XFlush(dpy);
1707 } else if (scr->selected_windows && WMGetArrayItemCount(scr->selected_windows)) {
1709 if (wwin->flags.selected && scr->selected_windows) {
1710 if (scr->shortcutWindows[widx]) {
1711 WMFreeArray(scr->shortcutWindows[widx]);
1713 scr->shortcutWindows[widx] = WMDuplicateArray(scr->selected_windows);
1717 break;
1719 case WKBD_RELAUNCH:
1720 if (ISMAPPED(wwin) && ISFOCUSED(wwin))
1721 (void) RelaunchWindow(wwin);
1723 break;
1725 case WKBD_SWITCH_SCREEN:
1726 if (wScreenCount > 1) {
1727 WScreen *scr2;
1728 int i;
1730 /* find index of this screen */
1731 for (i = 0; i < wScreenCount; i++) {
1732 if (wScreenWithNumber(i) == scr)
1733 break;
1735 i++;
1736 if (i >= wScreenCount) {
1737 i = 0;
1739 scr2 = wScreenWithNumber(i);
1741 if (scr2) {
1742 XWarpPointer(dpy, scr->root_win, scr2->root_win, 0, 0, 0, 0,
1743 scr2->scr_width / 2, scr2->scr_height / 2);
1746 break;
1748 case WKBD_NEXTWSLAYER:
1749 case WKBD_PREVWSLAYER:
1751 int row, column;
1753 row = scr->current_workspace / 10;
1754 column = scr->current_workspace % 10;
1756 if (command == WKBD_NEXTWSLAYER) {
1757 if ((row + 1) * 10 < scr->workspace_count)
1758 wWorkspaceChange(scr, column + (row + 1) * 10);
1759 } else {
1760 if (row > 0)
1761 wWorkspaceChange(scr, column + (row - 1) * 10);
1764 break;
1765 case WKBD_CLIPRAISELOWER:
1766 if (!wPreferences.flags.noclip)
1767 wDockRaiseLower(scr->workspaces[scr->current_workspace]->clip);
1768 break;
1769 case WKBD_DOCKRAISELOWER:
1770 if (!wPreferences.flags.nodock)
1771 wDockRaiseLower(scr->dock);
1772 break;
1773 #ifdef KEEP_XKB_LOCK_STATUS
1774 case WKBD_TOGGLE:
1775 if (wPreferences.modelock) {
1776 /*toggle */
1777 wwin = scr->focused_window;
1779 if (wwin && wwin->flags.mapped
1780 && wwin->frame->workspace == wwin->screen_ptr->current_workspace
1781 && !wwin->flags.miniaturized && !wwin->flags.hidden) {
1782 XkbGetState(dpy, XkbUseCoreKbd, &staterec);
1784 wwin->frame->languagemode = wwin->frame->last_languagemode;
1785 wwin->frame->last_languagemode = staterec.group;
1786 XkbLockGroup(dpy, XkbUseCoreKbd, wwin->frame->languagemode);
1790 break;
1791 #endif /* KEEP_XKB_LOCK_STATUS */
1795 static void handleMotionNotify(XEvent * event)
1797 WScreen *scr = wScreenForRootWindow(event->xmotion.root);
1799 if (wPreferences.scrollable_menus) {
1800 WMPoint p = wmkpoint(event->xmotion.x_root, event->xmotion.y_root);
1801 WMRect rect = wGetRectForHead(scr, wGetHeadForPoint(scr, p));
1803 if (scr->flags.jump_back_pending ||
1804 p.x <= (rect.pos.x + 1) ||
1805 p.x >= (rect.pos.x + rect.size.width - 2) ||
1806 p.y <= (rect.pos.y + 1) || p.y >= (rect.pos.y + rect.size.height - 2)) {
1807 WMenu *menu;
1809 menu = wMenuUnderPointer(scr);
1810 if (menu != NULL)
1811 wMenuScroll(menu, event);
1816 static void handleVisibilityNotify(XEvent * event)
1818 WWindow *wwin;
1820 wwin = wWindowFor(event->xvisibility.window);
1821 if (!wwin)
1822 return;
1823 wwin->flags.obscured = (event->xvisibility.state == VisibilityFullyObscured);