wmaker: fix signedness of variable (Coverity #50082, #50222)
[wmaker-crm.git] / src / event.c
blob9a8a3e3fea620ff8379fbb465ea5c2a891aa6c1d
1 /* event.c- event loop and handling
3 * Window Maker window manager
5 * Copyright (c) 1997-2003 Alfredo K. Kojima
6 * Copyright (c) 2014 Window Maker Team
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License along
19 * with this program; if not, write to the Free Software Foundation, Inc.,
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 #include "wconfig.h"
25 #ifdef HAVE_INOTIFY
26 #include <sys/select.h>
27 #include <sys/inotify.h>
28 #endif
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <unistd.h>
33 #include <string.h>
34 #include <strings.h>
35 #include <time.h>
36 #include <errno.h>
38 #include <X11/Xlib.h>
39 #include <X11/Xutil.h>
40 #ifdef USE_XSHAPE
41 # include <X11/extensions/shape.h>
42 #endif
43 #ifdef XDND
44 #include "xdnd.h"
45 #endif
47 #ifdef USE_RANDR
48 #include <X11/extensions/Xrandr.h>
49 #endif
51 #ifdef KEEP_XKB_LOCK_STATUS
52 #include <X11/XKBlib.h>
53 #endif /* KEEP_XKB_LOCK_STATUS */
55 #include "WindowMaker.h"
56 #include "window.h"
57 #include "actions.h"
58 #include "client.h"
59 #include "main.h"
60 #include "cycling.h"
61 #include "keybind.h"
62 #include "application.h"
63 #include "stacking.h"
64 #include "defaults.h"
65 #include "workspace.h"
66 #include "dock.h"
67 #include "framewin.h"
68 #include "properties.h"
69 #include "balloon.h"
70 #include "xinerama.h"
71 #include "wmspec.h"
72 #include "rootmenu.h"
73 #include "colormap.h"
74 #include "screen.h"
75 #include "shutdown.h"
76 #include "misc.h"
77 #include "event.h"
78 #include "winmenu.h"
79 #include "switchmenu.h"
82 #define MOD_MASK wPreferences.modifier_mask
84 /************ Local stuff ***********/
86 static void saveTimestamp(XEvent *event);
87 static void handleColormapNotify(XEvent *event);
88 static void handleMapNotify(XEvent *event);
89 static void handleUnmapNotify(XEvent *event);
90 static void handleButtonPress(XEvent *event);
91 static void handleExpose(XEvent *event);
92 static void handleDestroyNotify(XEvent *event);
93 static void handleConfigureRequest(XEvent *event);
94 static void handleMapRequest(XEvent *event);
95 static void handlePropertyNotify(XEvent *event);
96 static void handleEnterNotify(XEvent *event);
97 static void handleLeaveNotify(XEvent *event);
98 static void handleExtensions(XEvent *event);
99 static void handleClientMessage(XEvent *event);
100 static void handleKeyPress(XEvent *event);
101 static void handleFocusIn(XEvent *event);
102 static void handleMotionNotify(XEvent *event);
103 static void handleVisibilityNotify(XEvent *event);
104 static void handle_inotify_events(void);
105 static void wdelete_death_handler(WMagicNumber id);
108 #ifdef USE_XSHAPE
109 static void handleShapeNotify(XEvent *event);
110 #endif
112 #ifdef KEEP_XKB_LOCK_STATUS
113 static void handleXkbIndicatorStateNotify(XkbEvent *event);
114 #endif
116 /* real dead process handler */
117 static void handleDeadProcess(void);
119 typedef struct DeadProcesses {
120 pid_t pid;
121 unsigned char exit_status;
122 } DeadProcesses;
124 /* stack of dead processes */
125 static DeadProcesses deadProcesses[MAX_DEAD_PROCESSES];
126 static int deadProcessPtr = 0;
128 typedef struct DeathHandler {
129 WDeathHandler *callback;
130 pid_t pid;
131 void *client_data;
132 } DeathHandler;
134 static WMArray *deathHandlers = NULL;
136 WMagicNumber wAddDeathHandler(pid_t pid, WDeathHandler * callback, void *cdata)
138 DeathHandler *handler;
140 handler = malloc(sizeof(DeathHandler));
141 if (!handler)
142 return 0;
144 handler->pid = pid;
145 handler->callback = callback;
146 handler->client_data = cdata;
148 if (!deathHandlers)
149 deathHandlers = WMCreateArrayWithDestructor(8, free);
151 WMAddToArray(deathHandlers, handler);
153 return handler;
156 static void wdelete_death_handler(WMagicNumber id)
158 DeathHandler *handler = (DeathHandler *) id;
160 if (!handler || !deathHandlers)
161 return;
163 /* array destructor will call free(handler) */
164 WMRemoveFromArray(deathHandlers, handler);
167 void DispatchEvent(XEvent * event)
169 if (deathHandlers)
170 handleDeadProcess();
172 if (WCHECK_STATE(WSTATE_NEED_EXIT)) {
173 WCHANGE_STATE(WSTATE_EXITING);
174 /* received SIGTERM */
176 * WMHandleEvent() can't be called from anything
177 * executed inside here, or we can get in a infinite
178 * recursive loop.
180 Shutdown(WSExitMode);
182 } else if (WCHECK_STATE(WSTATE_NEED_RESTART)) {
183 WCHANGE_STATE(WSTATE_RESTARTING);
185 Shutdown(WSRestartPreparationMode);
186 /* received SIGHUP */
187 Restart(NULL, True);
188 } else if (WCHECK_STATE(WSTATE_NEED_REREAD)) {
189 WCHANGE_STATE(WSTATE_NORMAL);
190 wDefaultsCheckDomains(NULL);
193 /* for the case that all that is wanted to be dispatched is
194 * the stuff above */
195 if (!event)
196 return;
198 saveTimestamp(event);
199 switch (event->type) {
200 case MapRequest:
201 handleMapRequest(event);
202 break;
204 case KeyPress:
205 handleKeyPress(event);
206 break;
208 case MotionNotify:
209 handleMotionNotify(event);
210 break;
212 case ConfigureRequest:
213 handleConfigureRequest(event);
214 break;
216 case DestroyNotify:
217 handleDestroyNotify(event);
218 break;
220 case MapNotify:
221 handleMapNotify(event);
222 break;
224 case UnmapNotify:
225 handleUnmapNotify(event);
226 break;
228 case ButtonPress:
229 handleButtonPress(event);
230 break;
232 case Expose:
233 handleExpose(event);
234 break;
236 case PropertyNotify:
237 handlePropertyNotify(event);
238 break;
240 case EnterNotify:
241 handleEnterNotify(event);
242 break;
244 case LeaveNotify:
245 handleLeaveNotify(event);
246 break;
248 case ClientMessage:
249 handleClientMessage(event);
250 break;
252 case ColormapNotify:
253 handleColormapNotify(event);
254 break;
256 case MappingNotify:
257 if (event->xmapping.request == MappingKeyboard || event->xmapping.request == MappingModifier)
258 XRefreshKeyboardMapping(&event->xmapping);
259 break;
261 case FocusIn:
262 handleFocusIn(event);
263 break;
265 case VisibilityNotify:
266 handleVisibilityNotify(event);
267 break;
269 case ConfigureNotify:
270 #ifdef USE_RANDR
271 if (event->xconfigure.window == DefaultRootWindow(dpy))
272 XRRUpdateConfiguration(event);
273 #endif
274 break;
276 default:
277 handleExtensions(event);
278 break;
282 #ifdef HAVE_INOTIFY
284 *----------------------------------------------------------------------
285 * handle_inotify_events-
286 * Check for inotify events
288 * Returns:
289 * After reading events for the given file descriptor (fd) and
290 * watch descriptor (wd)
292 * Side effects:
293 * Calls wDefaultsCheckDomains if config database is updated
294 *----------------------------------------------------------------------
296 static void handle_inotify_events(void)
298 ssize_t eventQLength;
299 size_t i = 0;
300 /* Make room for at lease 5 simultaneous events, with path + filenames */
301 char buff[ (sizeof(struct inotify_event) + NAME_MAX + 1) * 5 ];
302 /* Check config only once per read of the event queue */
303 int oneShotFlag = 0;
306 * Read off the queued events
307 * queue overflow is not checked (IN_Q_OVERFLOW). In practise this should
308 * not occur; the block is on Xevents, but a config file change will normally
309 * occur as a result of an Xevent - so the event queue should never have more than
310 * a few entries before a read().
312 eventQLength = read(w_global.inotify.fd_event_queue,
313 buff, sizeof(buff) );
315 if (eventQLength < 0) {
316 wwarning(_("read problem when trying to get INotify event: %s"), strerror(errno));
317 return;
320 /* check what events occured */
321 /* Should really check wd here too, but for now we only have one watch! */
322 while (i < eventQLength) {
323 struct inotify_event *pevent = (struct inotify_event *)&buff[i];
326 * see inotify.h for event types.
328 if (pevent->mask & IN_DELETE_SELF) {
329 wwarning(_("the defaults database has been deleted!"
330 " Restart Window Maker to create the database" " with the default settings"));
332 if (w_global.inotify.fd_event_queue >= 0) {
333 close(w_global.inotify.fd_event_queue);
334 w_global.inotify.fd_event_queue = -1;
337 if (pevent->mask & IN_UNMOUNT) {
338 wwarning(_("the unit containing the defaults database has"
339 " been unmounted. Setting --static mode." " Any changes will not be saved."));
341 if (w_global.inotify.fd_event_queue >= 0) {
342 close(w_global.inotify.fd_event_queue);
343 w_global.inotify.fd_event_queue = -1;
346 wPreferences.flags.noupdates = 1;
348 if ((pevent->mask & IN_MODIFY) && oneShotFlag == 0) {
349 wwarning(_("Inotify: Reading config files in defaults database."));
350 wDefaultsCheckDomains(NULL);
351 oneShotFlag = 1;
354 /* move to next event in the buffer */
355 i += sizeof(struct inotify_event) + pevent->len;
358 #endif /* HAVE_INOTIFY */
361 *----------------------------------------------------------------------
362 * EventLoop-
363 * Processes X and internal events indefinitely.
365 * Returns:
366 * Never returns
368 * Side effects:
369 * The LastTimestamp global variable is updated.
370 * Calls inotifyGetEvents if defaults database changes.
371 *----------------------------------------------------------------------
373 noreturn void EventLoop(void)
375 XEvent event;
376 #ifdef HAVE_INOTIFY
377 struct timeval time;
378 fd_set rfds;
379 int retVal = 0;
381 if (w_global.inotify.fd_event_queue < 0 || w_global.inotify.wd_defaults < 0)
382 retVal = -1;
383 #endif
385 for (;;) {
387 WMNextEvent(dpy, &event); /* Blocks here */
388 WMHandleEvent(&event);
389 #ifdef HAVE_INOTIFY
390 if (retVal != -1) {
391 time.tv_sec = 0;
392 time.tv_usec = 0;
393 FD_ZERO(&rfds);
394 FD_SET(w_global.inotify.fd_event_queue, &rfds);
396 /* check for available read data from inotify - don't block! */
397 retVal = select(w_global.inotify.fd_event_queue + 1, &rfds, NULL, NULL, &time);
399 if (retVal < 0) { /* an error has occured */
400 wwarning(_("select failed. The inotify instance will be closed."
401 " Changes to the defaults database will require"
402 " a restart to take effect."));
403 close(w_global.inotify.fd_event_queue);
404 w_global.inotify.fd_event_queue = -1;
405 continue;
407 if (FD_ISSET(w_global.inotify.fd_event_queue, &rfds))
408 handle_inotify_events();
410 #endif
415 *----------------------------------------------------------------------
416 * ProcessPendingEvents --
417 * Processes the events that are currently pending (at the time
418 * this function is called) in the display's queue.
420 * Returns:
421 * After the pending events that were present at the function call
422 * are processed.
424 * Side effects:
425 * Many -- whatever handling events may involve.
427 *----------------------------------------------------------------------
429 void ProcessPendingEvents(void)
431 XEvent event;
432 int count;
434 XSync(dpy, False);
436 /* Take a snapshot of the event count in the queue */
437 count = XPending(dpy);
439 while (count > 0 && XPending(dpy)) {
440 WMNextEvent(dpy, &event);
441 WMHandleEvent(&event);
442 count--;
446 Bool IsDoubleClick(WScreen * scr, XEvent * event)
448 if ((scr->last_click_time > 0) &&
449 (event->xbutton.time - scr->last_click_time <= wPreferences.dblclick_time)
450 && (event->xbutton.button == scr->last_click_button)
451 && (event->xbutton.window == scr->last_click_window)) {
453 scr->flags.next_click_is_not_double = 1;
454 scr->last_click_time = 0;
455 scr->last_click_window = event->xbutton.window;
457 return True;
459 return False;
462 void NotifyDeadProcess(pid_t pid, unsigned char status)
464 if (deadProcessPtr >= MAX_DEAD_PROCESSES - 1) {
465 wwarning("stack overflow: too many dead processes");
466 return;
468 /* stack the process to be handled later,
469 * as this is called from the signal handler */
470 deadProcesses[deadProcessPtr].pid = pid;
471 deadProcesses[deadProcessPtr].exit_status = status;
472 deadProcessPtr++;
475 static void handleDeadProcess(void)
477 DeathHandler *tmp;
478 int i;
480 for (i = 0; i < deadProcessPtr; i++) {
481 wWindowDeleteSavedStatesForPID(deadProcesses[i].pid);
484 if (!deathHandlers) {
485 deadProcessPtr = 0;
486 return;
489 /* get the pids on the queue and call handlers */
490 while (deadProcessPtr > 0) {
491 deadProcessPtr--;
493 for (i = WMGetArrayItemCount(deathHandlers) - 1; i >= 0; i--) {
494 tmp = WMGetFromArray(deathHandlers, i);
495 if (!tmp)
496 continue;
498 if (tmp->pid == deadProcesses[deadProcessPtr].pid) {
499 (*tmp->callback) (tmp->pid,
500 deadProcesses[deadProcessPtr].exit_status, tmp->client_data);
501 wdelete_death_handler(tmp);
507 static void saveTimestamp(XEvent * event)
510 * Never save CurrentTime as LastTimestamp because CurrentTime
511 * it's not a real timestamp (it's the 0L constant)
514 switch (event->type) {
515 case ButtonRelease:
516 case ButtonPress:
517 w_global.timestamp.last_event = event->xbutton.time;
518 break;
519 case KeyPress:
520 case KeyRelease:
521 w_global.timestamp.last_event = event->xkey.time;
522 break;
523 case MotionNotify:
524 w_global.timestamp.last_event = event->xmotion.time;
525 break;
526 case PropertyNotify:
527 w_global.timestamp.last_event = event->xproperty.time;
528 break;
529 case EnterNotify:
530 case LeaveNotify:
531 w_global.timestamp.last_event = event->xcrossing.time;
532 break;
533 case SelectionClear:
534 w_global.timestamp.last_event = event->xselectionclear.time;
535 break;
536 case SelectionRequest:
537 w_global.timestamp.last_event = event->xselectionrequest.time;
538 break;
539 case SelectionNotify:
540 w_global.timestamp.last_event = event->xselection.time;
541 #ifdef XDND
542 wXDNDProcessSelection(event);
543 #endif
544 break;
548 static int matchWindow(const void *item, const void *cdata)
550 return (((WFakeGroupLeader *) item)->origLeader == (Window) cdata);
553 static void handleExtensions(XEvent * event)
555 #ifdef USE_XSHAPE
556 if (w_global.xext.shape.supported && event->type == (w_global.xext.shape.event_base + ShapeNotify)) {
557 handleShapeNotify(event);
559 #endif
560 #ifdef KEEP_XKB_LOCK_STATUS
561 if (wPreferences.modelock && (event->type == w_global.xext.xkb.event_base)) {
562 handleXkbIndicatorStateNotify((XkbEvent *) event);
564 #endif /*KEEP_XKB_LOCK_STATUS */
565 #ifdef USE_RANDR
566 if (w_global.xext.randr.supported && event->type == (w_global.xext.randr.event_base + RRScreenChangeNotify)) {
567 /* From xrandr man page: "Clients must call back into Xlib using
568 * XRRUpdateConfiguration when screen configuration change notify
569 * events are generated */
570 XRRUpdateConfiguration(event);
571 WCHANGE_STATE(WSTATE_RESTARTING);
572 Shutdown(WSRestartPreparationMode);
573 Restart(NULL,True);
575 #endif
578 static void handleMapRequest(XEvent * ev)
580 WWindow *wwin;
581 WScreen *scr = NULL;
582 Window window = ev->xmaprequest.window;
584 if ((wwin = wWindowFor(window))) {
585 if (wwin->flags.shaded) {
586 wUnshadeWindow(wwin);
588 /* deiconify window */
589 if (wwin->flags.miniaturized) {
590 wDeiconifyWindow(wwin);
591 } else if (wwin->flags.hidden) {
592 WApplication *wapp = wApplicationOf(wwin->main_window);
593 /* go to the last workspace that the user worked on the app */
594 if (wapp) {
595 wWorkspaceChange(wwin->screen_ptr, wapp->last_workspace);
597 wUnhideApplication(wapp, False, False);
599 return;
602 scr = wScreenForRootWindow(ev->xmaprequest.parent);
604 wwin = wManageWindow(scr, window);
607 * This is to let the Dock know that the application it launched
608 * has already been mapped (eg: it has finished launching).
609 * It is not necessary for normally docked apps, but is needed for
610 * apps that were forcedly docked (like with dockit).
612 if (scr->last_dock) {
613 if (wwin && wwin->main_window != None && wwin->main_window != window)
614 wDockTrackWindowLaunch(scr->last_dock, wwin->main_window);
615 else
616 wDockTrackWindowLaunch(scr->last_dock, window);
619 if (wwin) {
620 wClientSetState(wwin, NormalState, None);
621 if (wwin->flags.maximized) {
622 wMaximizeWindow(wwin, wwin->flags.maximized);
624 if (wwin->flags.shaded) {
625 wwin->flags.shaded = 0;
626 wwin->flags.skip_next_animation = 1;
627 wShadeWindow(wwin);
629 if (wwin->flags.miniaturized) {
630 wwin->flags.miniaturized = 0;
631 wwin->flags.skip_next_animation = 1;
632 wIconifyWindow(wwin);
634 if (wwin->flags.fullscreen) {
635 wwin->flags.fullscreen = 0;
636 wFullscreenWindow(wwin);
638 if (wwin->flags.hidden) {
639 WApplication *wapp = wApplicationOf(wwin->main_window);
641 wwin->flags.hidden = 0;
642 wwin->flags.skip_next_animation = 1;
643 if (wapp) {
644 wHideApplication(wapp);
650 static void handleDestroyNotify(XEvent * event)
652 WWindow *wwin;
653 WApplication *app;
654 Window window = event->xdestroywindow.window;
655 WScreen *scr = wScreenForRootWindow(event->xdestroywindow.event);
656 int widx;
658 wwin = wWindowFor(window);
659 if (wwin) {
660 wUnmanageWindow(wwin, False, True);
663 if (scr != NULL) {
664 while ((widx = WMFindInArray(scr->fakeGroupLeaders, matchWindow, (void *)window)) != WANotFound) {
665 WFakeGroupLeader *fPtr;
667 fPtr = WMGetFromArray(scr->fakeGroupLeaders, widx);
668 if (fPtr->retainCount > 0) {
669 fPtr->retainCount--;
670 if (fPtr->retainCount == 0 && fPtr->leader != None) {
671 XDestroyWindow(dpy, fPtr->leader);
672 fPtr->leader = None;
673 XFlush(dpy);
676 fPtr->origLeader = None;
680 app = wApplicationOf(window);
681 if (app) {
682 if (window == app->main_window) {
683 app->refcount = 0;
684 wwin = app->main_window_desc->screen_ptr->focused_window;
685 while (wwin) {
686 if (wwin->main_window == window) {
687 wwin->main_window = None;
689 wwin = wwin->prev;
692 wApplicationDestroy(app);
696 static void handleExpose(XEvent * event)
698 WObjDescriptor *desc;
699 XEvent ev;
701 while (XCheckTypedWindowEvent(dpy, event->xexpose.window, Expose, &ev)) ;
703 if (XFindContext(dpy, event->xexpose.window, w_global.context.client_win, (XPointer *) & desc) == XCNOENT) {
704 return;
707 if (desc->handle_expose) {
708 (*desc->handle_expose) (desc, event);
712 static void executeWheelAction(WScreen *scr, XEvent *event, int action)
714 WWindow *wwin;
715 Bool next_direction;
717 if (event->xbutton.button == Button5 || event->xbutton.button == Button6)
718 next_direction = False;
719 else
720 next_direction = True;
722 switch (action) {
723 case WA_SWITCH_WORKSPACES:
724 if (next_direction)
725 wWorkspaceRelativeChange(scr, 1);
726 else
727 wWorkspaceRelativeChange(scr, -1);
728 break;
730 case WA_SWITCH_WINDOWS:
731 wwin = scr->focused_window;
732 if (next_direction)
733 wWindowFocusNext(wwin, True);
734 else
735 wWindowFocusPrev(wwin, True);
736 break;
740 static void executeButtonAction(WScreen *scr, XEvent *event, int action)
742 WWindow *wwin;
744 switch (action) {
745 case WA_SELECT_WINDOWS:
746 wUnselectWindows(scr);
747 wSelectWindows(scr, event);
748 break;
749 case WA_OPEN_APPMENU:
750 OpenRootMenu(scr, event->xbutton.x_root, event->xbutton.y_root, False);
751 /* ugly hack */
752 if (scr->root_menu) {
753 if (scr->root_menu->brother->flags.mapped)
754 event->xbutton.window = scr->root_menu->brother->frame->core->window;
755 else
756 event->xbutton.window = scr->root_menu->frame->core->window;
758 break;
759 case WA_OPEN_WINLISTMENU:
760 OpenSwitchMenu(scr, event->xbutton.x_root, event->xbutton.y_root, False);
761 if (scr->switch_menu) {
762 if (scr->switch_menu->brother->flags.mapped)
763 event->xbutton.window = scr->switch_menu->brother->frame->core->window;
764 else
765 event->xbutton.window = scr->switch_menu->frame->core->window;
767 break;
768 case WA_MOVE_PREVWORKSPACE:
769 wWorkspaceRelativeChange(scr, -1);
770 break;
771 case WA_MOVE_NEXTWORKSPACE:
772 wWorkspaceRelativeChange(scr, 1);
773 break;
774 case WA_MOVE_PREVWINDOW:
775 wwin = scr->focused_window;
776 wWindowFocusPrev(wwin, True);
777 break;
778 case WA_MOVE_NEXTWINDOW:
779 wwin = scr->focused_window;
780 wWindowFocusNext(wwin, True);
781 break;
785 /* bindable */
786 static void handleButtonPress(XEvent * event)
788 WObjDescriptor *desc;
789 WScreen *scr;
791 scr = wScreenForRootWindow(event->xbutton.root);
793 #ifdef BALLOON_TEXT
794 wBalloonHide(scr);
795 #endif
797 if (!wPreferences.disable_root_mouse && event->xbutton.window == scr->root_win) {
798 if (event->xbutton.button == Button1 && wPreferences.mouse_button1 != WA_NONE) {
799 executeButtonAction(scr, event, wPreferences.mouse_button1);
800 } else if (event->xbutton.button == Button2 && wPreferences.mouse_button2 != WA_NONE) {
801 executeButtonAction(scr, event, wPreferences.mouse_button2);
802 } else if (event->xbutton.button == Button3 && wPreferences.mouse_button3 != WA_NONE) {
803 executeButtonAction(scr, event, wPreferences.mouse_button3);
804 } else if (event->xbutton.button == Button8 && wPreferences.mouse_button8 != WA_NONE) {
805 executeButtonAction(scr, event, wPreferences.mouse_button8);
806 }else if (event->xbutton.button == Button9 && wPreferences.mouse_button9 != WA_NONE) {
807 executeButtonAction(scr, event, wPreferences.mouse_button9);
808 } else if (event->xbutton.button == Button4 && wPreferences.mouse_wheel_scroll != WA_NONE) {
809 executeWheelAction(scr, event, wPreferences.mouse_wheel_scroll);
810 } else if (event->xbutton.button == Button5 && wPreferences.mouse_wheel_scroll != WA_NONE) {
811 executeWheelAction(scr, event, wPreferences.mouse_wheel_scroll);
812 } else if (event->xbutton.button == Button6 && wPreferences.mouse_wheel_tilt != WA_NONE) {
813 executeWheelAction(scr, event, wPreferences.mouse_wheel_tilt);
814 } else if (event->xbutton.button == Button7 && wPreferences.mouse_wheel_tilt != WA_NONE) {
815 executeWheelAction(scr, event, wPreferences.mouse_wheel_tilt);
819 desc = NULL;
820 if (XFindContext(dpy, event->xbutton.subwindow, w_global.context.client_win, (XPointer *) & desc) == XCNOENT) {
821 if (XFindContext(dpy, event->xbutton.window, w_global.context.client_win, (XPointer *) & desc) == XCNOENT) {
822 return;
826 if (desc->parent_type == WCLASS_WINDOW) {
827 XSync(dpy, 0);
829 if (event->xbutton.state & ( MOD_MASK | ControlMask )) {
830 XAllowEvents(dpy, AsyncPointer, CurrentTime);
831 } else {
832 /* if (wPreferences.focus_mode == WKF_CLICK) { */
833 if (wPreferences.ignore_focus_click) {
834 XAllowEvents(dpy, AsyncPointer, CurrentTime);
836 XAllowEvents(dpy, ReplayPointer, CurrentTime);
837 /* } */
839 XSync(dpy, 0);
840 } else if (desc->parent_type == WCLASS_APPICON
841 || desc->parent_type == WCLASS_MINIWINDOW || desc->parent_type == WCLASS_DOCK_ICON) {
842 if (event->xbutton.state & MOD_MASK) {
843 XSync(dpy, 0);
844 XAllowEvents(dpy, AsyncPointer, CurrentTime);
845 XSync(dpy, 0);
849 if (desc->handle_mousedown != NULL) {
850 (*desc->handle_mousedown) (desc, event);
853 /* save double-click information */
854 if (scr->flags.next_click_is_not_double) {
855 scr->flags.next_click_is_not_double = 0;
856 } else {
857 scr->last_click_time = event->xbutton.time;
858 scr->last_click_button = event->xbutton.button;
859 scr->last_click_window = event->xbutton.window;
863 static void handleMapNotify(XEvent * event)
865 WWindow *wwin;
867 wwin = wWindowFor(event->xmap.event);
868 if (wwin && wwin->client_win == event->xmap.event) {
869 if (wwin->flags.miniaturized) {
870 wDeiconifyWindow(wwin);
871 } else {
872 XGrabServer(dpy);
873 wWindowMap(wwin);
874 wClientSetState(wwin, NormalState, None);
875 XUngrabServer(dpy);
880 static void handleUnmapNotify(XEvent * event)
882 WWindow *wwin;
883 XEvent ev;
884 Bool withdraw = False;
886 /* only process windows with StructureNotify selected
887 * (ignore SubstructureNotify) */
888 wwin = wWindowFor(event->xunmap.window);
889 if (!wwin)
890 return;
892 /* whether the event is a Withdrawal request */
893 if (event->xunmap.event == wwin->screen_ptr->root_win && event->xunmap.send_event)
894 withdraw = True;
896 if (wwin->client_win != event->xunmap.event && !withdraw)
897 return;
899 if (!wwin->flags.mapped && !withdraw
900 && wwin->frame->workspace == wwin->screen_ptr->current_workspace
901 && !wwin->flags.miniaturized && !wwin->flags.hidden)
902 return;
904 XGrabServer(dpy);
905 XUnmapWindow(dpy, wwin->frame->core->window);
906 wwin->flags.mapped = 0;
907 XSync(dpy, 0);
908 /* check if the window was destroyed */
909 if (XCheckTypedWindowEvent(dpy, wwin->client_win, DestroyNotify, &ev)) {
910 DispatchEvent(&ev);
911 } else {
912 Bool reparented = False;
914 if (XCheckTypedWindowEvent(dpy, wwin->client_win, ReparentNotify, &ev))
915 reparented = True;
917 /* withdraw window */
918 wwin->flags.mapped = 0;
919 if (!reparented)
920 wClientSetState(wwin, WithdrawnState, None);
922 /* if the window was reparented, do not reparent it back to the
923 * root window */
924 wUnmanageWindow(wwin, !reparented, False);
926 XUngrabServer(dpy);
929 static void handleConfigureRequest(XEvent * event)
931 WWindow *wwin;
933 if (!(wwin = wWindowFor(event->xconfigurerequest.window))) {
935 * Configure request for unmapped window
937 wClientConfigure(NULL, &(event->xconfigurerequest));
938 } else {
939 wClientConfigure(wwin, &(event->xconfigurerequest));
943 static void handlePropertyNotify(XEvent * event)
945 WWindow *wwin;
946 WApplication *wapp;
947 Window jr;
948 int ji;
949 unsigned int ju;
951 wwin = wWindowFor(event->xproperty.window);
952 if (wwin) {
953 if (!XGetGeometry(dpy, wwin->client_win, &jr, &ji, &ji, &ju, &ju, &ju, &ju)) {
954 return;
956 wClientCheckProperty(wwin, &event->xproperty);
958 wapp = wApplicationOf(event->xproperty.window);
959 if (wapp) {
960 wClientCheckProperty(wapp->main_window_desc, &event->xproperty);
964 static void handleClientMessage(XEvent * event)
966 WWindow *wwin;
967 WObjDescriptor *desc;
969 /* handle transition from Normal to Iconic state */
970 if (event->xclient.message_type == w_global.atom.wm.change_state
971 && event->xclient.format == 32 && event->xclient.data.l[0] == IconicState) {
973 wwin = wWindowFor(event->xclient.window);
974 if (!wwin)
975 return;
976 if (!wwin->flags.miniaturized)
977 wIconifyWindow(wwin);
978 } else if (event->xclient.message_type == w_global.atom.wm.colormap_notify && event->xclient.format == 32) {
979 WScreen *scr = wScreenForRootWindow(event->xclient.window);
981 if (!scr)
982 return;
984 if (event->xclient.data.l[1] == 1) { /* starting */
985 wColormapAllowClientInstallation(scr, True);
986 } else { /* stopping */
987 wColormapAllowClientInstallation(scr, False);
989 } else if (event->xclient.message_type == w_global.atom.wmaker.command) {
991 char *command;
992 size_t len;
994 len = sizeof(event->xclient.data.b) + 1;
995 command = wmalloc(len);
996 strncpy(command, event->xclient.data.b, sizeof(event->xclient.data.b));
998 if (strncmp(command, "Reconfigure", sizeof("Reconfigure")) == 0) {
999 wwarning(_("Got Reconfigure command"));
1000 wDefaultsCheckDomains(NULL);
1001 } else {
1002 wwarning(_("Got unknown command %s"), command);
1005 wfree(command);
1007 } else if (event->xclient.message_type == w_global.atom.wmaker.wm_function) {
1008 WApplication *wapp;
1009 int done = 0;
1010 wapp = wApplicationOf(event->xclient.window);
1011 if (wapp) {
1012 switch (event->xclient.data.l[0]) {
1013 case WMFHideOtherApplications:
1014 wHideOtherApplications(wapp->main_window_desc);
1015 done = 1;
1016 break;
1018 case WMFHideApplication:
1019 wHideApplication(wapp);
1020 done = 1;
1021 break;
1024 if (!done) {
1025 wwin = wWindowFor(event->xclient.window);
1026 if (wwin) {
1027 switch (event->xclient.data.l[0]) {
1028 case WMFHideOtherApplications:
1029 wHideOtherApplications(wwin);
1030 break;
1032 case WMFHideApplication:
1033 wHideApplication(wApplicationOf(wwin->main_window));
1034 break;
1038 } else if (event->xclient.message_type == w_global.atom.gnustep.wm_attr) {
1039 wwin = wWindowFor(event->xclient.window);
1040 if (!wwin)
1041 return;
1042 switch (event->xclient.data.l[0]) {
1043 case GSWindowLevelAttr:
1045 int level = (int)event->xclient.data.l[1];
1047 if (WINDOW_LEVEL(wwin) != level) {
1048 ChangeStackingLevel(wwin->frame->core, level);
1051 break;
1053 } else if (event->xclient.message_type == w_global.atom.gnustep.titlebar_state) {
1054 wwin = wWindowFor(event->xclient.window);
1055 if (!wwin)
1056 return;
1057 switch (event->xclient.data.l[0]) {
1058 case WMTitleBarNormal:
1059 wFrameWindowChangeState(wwin->frame, WS_UNFOCUSED);
1060 break;
1061 case WMTitleBarMain:
1062 wFrameWindowChangeState(wwin->frame, WS_PFOCUSED);
1063 break;
1064 case WMTitleBarKey:
1065 wFrameWindowChangeState(wwin->frame, WS_FOCUSED);
1066 break;
1068 } else if (event->xclient.message_type == w_global.atom.wm.ignore_focus_events) {
1069 WScreen *scr = wScreenForRootWindow(event->xclient.window);
1070 if (!scr)
1071 return;
1072 scr->flags.ignore_focus_events = event->xclient.data.l[0] ? 1 : 0;
1073 } else if (wNETWMProcessClientMessage(&event->xclient)) {
1074 /* do nothing */
1075 #ifdef XDND
1076 } else if (wXDNDProcessClientMessage(&event->xclient)) {
1077 /* do nothing */
1078 #endif /* XDND */
1079 } else {
1081 * Non-standard thing, but needed by OffiX DND.
1082 * For when the icon frame gets a ClientMessage
1083 * that should have gone to the icon_window.
1085 if (XFindContext(dpy, event->xbutton.window, w_global.context.client_win, (XPointer *) & desc) != XCNOENT) {
1086 struct WIcon *icon = NULL;
1088 if (desc->parent_type == WCLASS_MINIWINDOW) {
1089 icon = (WIcon *) desc->parent;
1090 } else if (desc->parent_type == WCLASS_DOCK_ICON || desc->parent_type == WCLASS_APPICON) {
1091 icon = ((WAppIcon *) desc->parent)->icon;
1093 if (icon && (wwin = icon->owner)) {
1094 if (wwin->client_win != event->xclient.window) {
1095 event->xclient.window = wwin->client_win;
1096 XSendEvent(dpy, wwin->client_win, False, NoEventMask, event);
1103 static void raiseWindow(WScreen * scr)
1105 WWindow *wwin;
1107 scr->autoRaiseTimer = NULL;
1109 wwin = wWindowFor(scr->autoRaiseWindow);
1110 if (!wwin)
1111 return;
1113 if (!wwin->flags.destroyed && wwin->flags.focused) {
1114 wRaiseFrame(wwin->frame->core);
1115 /* this is needed or a race condition will occur */
1116 XSync(dpy, False);
1120 static void handleEnterNotify(XEvent * event)
1122 WWindow *wwin;
1123 WObjDescriptor *desc = NULL;
1124 XEvent ev;
1125 WScreen *scr = wScreenForRootWindow(event->xcrossing.root);
1127 if (XCheckTypedWindowEvent(dpy, event->xcrossing.window, LeaveNotify, &ev)) {
1128 /* already left the window... */
1129 saveTimestamp(&ev);
1130 if (ev.xcrossing.mode == event->xcrossing.mode && ev.xcrossing.detail == event->xcrossing.detail) {
1131 return;
1135 if (XFindContext(dpy, event->xcrossing.window, w_global.context.client_win, (XPointer *) & desc) != XCNOENT) {
1136 if (desc->handle_enternotify)
1137 (*desc->handle_enternotify) (desc, event);
1140 /* enter to window */
1141 wwin = wWindowFor(event->xcrossing.window);
1142 if (!wwin) {
1143 if (wPreferences.colormap_mode == WCM_POINTER) {
1144 wColormapInstallForWindow(scr, NULL);
1146 if (scr->autoRaiseTimer && event->xcrossing.root == event->xcrossing.window) {
1147 WMDeleteTimerHandler(scr->autoRaiseTimer);
1148 scr->autoRaiseTimer = NULL;
1150 } else {
1151 /* set auto raise timer even if in focus-follows-mouse mode
1152 * and the event is for the frame window, even if the window
1153 * has focus already. useful if you move the pointer from a focused
1154 * window to the root window and back pretty fast
1156 * set focus if in focus-follows-mouse mode and the event
1157 * is for the frame window and window doesn't have focus yet */
1158 if (wPreferences.focus_mode == WKF_SLOPPY
1159 && wwin->frame->core->window == event->xcrossing.window && !scr->flags.doing_alt_tab) {
1161 if (!wwin->flags.focused && !WFLAGP(wwin, no_focusable))
1162 wSetFocusTo(scr, wwin);
1164 if (scr->autoRaiseTimer)
1165 WMDeleteTimerHandler(scr->autoRaiseTimer);
1166 scr->autoRaiseTimer = NULL;
1168 if (wPreferences.raise_delay && !WFLAGP(wwin, no_focusable)) {
1169 scr->autoRaiseWindow = wwin->frame->core->window;
1170 scr->autoRaiseTimer
1171 = WMAddTimerHandler(wPreferences.raise_delay, (WMCallback *) raiseWindow, scr);
1174 /* Install colormap for window, if the colormap installation mode
1175 * is colormap_follows_mouse */
1176 if (wPreferences.colormap_mode == WCM_POINTER) {
1177 if (wwin->client_win == event->xcrossing.window)
1178 wColormapInstallForWindow(scr, wwin);
1179 else
1180 wColormapInstallForWindow(scr, NULL);
1184 if (event->xcrossing.window == event->xcrossing.root
1185 && event->xcrossing.detail == NotifyNormal
1186 && event->xcrossing.detail != NotifyInferior && wPreferences.focus_mode != WKF_CLICK) {
1188 wSetFocusTo(scr, scr->focused_window);
1190 #ifdef BALLOON_TEXT
1191 wBalloonEnteredObject(scr, desc);
1192 #endif
1195 static void handleLeaveNotify(XEvent * event)
1197 WObjDescriptor *desc = NULL;
1199 if (XFindContext(dpy, event->xcrossing.window, w_global.context.client_win, (XPointer *) & desc) != XCNOENT) {
1200 if (desc->handle_leavenotify)
1201 (*desc->handle_leavenotify) (desc, event);
1205 #ifdef USE_XSHAPE
1206 static void handleShapeNotify(XEvent * event)
1208 XShapeEvent *shev = (XShapeEvent *) event;
1209 WWindow *wwin;
1210 union {
1211 XEvent xevent;
1212 XShapeEvent xshape;
1213 } ev;
1215 while (XCheckTypedWindowEvent(dpy, shev->window, event->type, &ev.xevent)) {
1216 if (ev.xshape.kind == ShapeBounding) {
1217 if (ev.xshape.shaped == shev->shaped) {
1218 *shev = ev.xshape;
1219 } else {
1220 XPutBackEvent(dpy, &ev.xevent);
1221 break;
1226 wwin = wWindowFor(shev->window);
1227 if (!wwin || shev->kind != ShapeBounding)
1228 return;
1230 if (!shev->shaped && wwin->flags.shaped) {
1232 wwin->flags.shaped = 0;
1233 wWindowClearShape(wwin);
1235 } else if (shev->shaped) {
1237 wwin->flags.shaped = 1;
1238 wWindowSetShape(wwin);
1241 #endif /* USE_XSHAPE */
1243 #ifdef KEEP_XKB_LOCK_STATUS
1244 /* please help ]d if you know what to do */
1245 static void handleXkbIndicatorStateNotify(XkbEvent *event)
1247 WWindow *wwin;
1248 WScreen *scr;
1249 XkbStateRec staterec;
1250 int i;
1252 for (i = 0; i < w_global.screen_count; i++) {
1253 scr = wScreenWithNumber(i);
1254 wwin = scr->focused_window;
1255 if (wwin && wwin->flags.focused) {
1256 XkbGetState(dpy, XkbUseCoreKbd, &staterec);
1257 if (wwin->frame->languagemode != staterec.group) {
1258 wwin->frame->last_languagemode = wwin->frame->languagemode;
1259 wwin->frame->languagemode = staterec.group;
1261 #ifdef XKB_BUTTON_HINT
1262 if (wwin->frame->titlebar) {
1263 wFrameWindowPaint(wwin->frame);
1265 #endif
1269 #endif /*KEEP_XKB_LOCK_STATUS */
1271 static void handleColormapNotify(XEvent * event)
1273 WWindow *wwin;
1274 WScreen *scr;
1275 Bool reinstall = False;
1277 wwin = wWindowFor(event->xcolormap.window);
1278 if (!wwin)
1279 return;
1281 scr = wwin->screen_ptr;
1283 do {
1284 if (wwin) {
1285 if (event->xcolormap.new) {
1286 XWindowAttributes attr;
1288 XGetWindowAttributes(dpy, wwin->client_win, &attr);
1290 if (wwin == scr->cmap_window && wwin->cmap_window_no == 0)
1291 scr->current_colormap = attr.colormap;
1293 reinstall = True;
1294 } else if (event->xcolormap.state == ColormapUninstalled &&
1295 scr->current_colormap == event->xcolormap.colormap) {
1297 /* some bastard app (like XV) removed our colormap */
1299 * can't enforce or things like xscreensaver wont work
1300 * reinstall = True;
1302 } else if (event->xcolormap.state == ColormapInstalled &&
1303 scr->current_colormap == event->xcolormap.colormap) {
1305 /* someone has put our colormap back */
1306 reinstall = False;
1309 } while (XCheckTypedEvent(dpy, ColormapNotify, event)
1310 && ((wwin = wWindowFor(event->xcolormap.window)) || 1));
1312 if (reinstall && scr->current_colormap != None) {
1313 if (!scr->flags.colormap_stuff_blocked)
1314 XInstallColormap(dpy, scr->current_colormap);
1318 static void handleFocusIn(XEvent * event)
1320 WWindow *wwin;
1323 * For applications that like stealing the focus.
1325 while (XCheckTypedEvent(dpy, FocusIn, event)) ;
1326 saveTimestamp(event);
1327 if (event->xfocus.mode == NotifyUngrab
1328 || event->xfocus.mode == NotifyGrab || event->xfocus.detail > NotifyNonlinearVirtual) {
1329 return;
1332 wwin = wWindowFor(event->xfocus.window);
1333 if (wwin && !wwin->flags.focused) {
1334 if (wwin->flags.mapped)
1335 wSetFocusTo(wwin->screen_ptr, wwin);
1336 else
1337 wSetFocusTo(wwin->screen_ptr, NULL);
1338 } else if (!wwin) {
1339 WScreen *scr = wScreenForWindow(event->xfocus.window);
1340 if (scr)
1341 wSetFocusTo(scr, NULL);
1345 static WWindow *windowUnderPointer(WScreen * scr)
1347 unsigned int mask;
1348 int foo;
1349 Window bar, win;
1351 if (XQueryPointer(dpy, scr->root_win, &bar, &win, &foo, &foo, &foo, &foo, &mask))
1352 return wWindowFor(win);
1353 return NULL;
1356 static int CheckFullScreenWindowFocused(WScreen * scr)
1358 if (scr->focused_window && scr->focused_window->flags.fullscreen)
1359 return 1;
1360 else
1361 return 0;
1364 static void handleKeyPress(XEvent * event)
1366 WScreen *scr = wScreenForRootWindow(event->xkey.root);
1367 WWindow *wwin = scr->focused_window;
1368 short i, widx;
1369 int modifiers;
1370 int command = -1;
1371 #ifdef KEEP_XKB_LOCK_STATUS
1372 XkbStateRec staterec;
1373 #endif /*KEEP_XKB_LOCK_STATUS */
1375 /* ignore CapsLock */
1376 modifiers = event->xkey.state & w_global.shortcut.modifiers_mask;
1378 for (i = 0; i < WKBD_LAST; i++) {
1379 if (wKeyBindings[i].keycode == 0)
1380 continue;
1382 if (wKeyBindings[i].keycode == event->xkey.keycode && ( /*wKeyBindings[i].modifier==0
1383 || */ wKeyBindings[i].modifier ==
1384 modifiers)) {
1385 command = i;
1386 break;
1390 if (command < 0) {
1392 if (!wRootMenuPerformShortcut(event)) {
1393 static int dontLoop = 0;
1395 if (dontLoop > 10) {
1396 wwarning("problem with key event processing code");
1397 return;
1399 dontLoop++;
1400 /* if the focused window is an internal window, try redispatching
1401 * the event to the managed window, as it can be a WINGs window */
1402 if (wwin && wwin->flags.internal_window && wwin->client_leader != None) {
1403 /* client_leader contains the WINGs toplevel */
1404 event->xany.window = wwin->client_leader;
1405 WMHandleEvent(event);
1407 dontLoop--;
1409 return;
1411 #define ISMAPPED(w) ((w) && !(w)->flags.miniaturized && ((w)->flags.mapped || (w)->flags.shaded))
1412 #define ISFOCUSED(w) ((w) && (w)->flags.focused)
1414 switch (command) {
1416 case WKBD_ROOTMENU:
1417 /*OpenRootMenu(scr, event->xkey.x_root, event->xkey.y_root, True); */
1418 if (!CheckFullScreenWindowFocused(scr)) {
1419 WMRect rect = wGetRectForHead(scr, wGetHeadForPointerLocation(scr));
1420 OpenRootMenu(scr, rect.pos.x + rect.size.width / 2, rect.pos.y + rect.size.height / 2,
1421 True);
1423 break;
1424 case WKBD_WINDOWLIST:
1425 if (!CheckFullScreenWindowFocused(scr)) {
1426 WMRect rect = wGetRectForHead(scr, wGetHeadForPointerLocation(scr));
1427 OpenSwitchMenu(scr, rect.pos.x + rect.size.width / 2, rect.pos.y + rect.size.height / 2,
1428 True);
1430 break;
1432 case WKBD_WINDOWMENU:
1433 if (ISMAPPED(wwin) && ISFOCUSED(wwin))
1434 OpenWindowMenu(wwin, wwin->frame_x, wwin->frame_y + wwin->frame->top_width, True);
1435 break;
1436 case WKBD_MINIMIZEALL:
1437 CloseWindowMenu(scr);
1438 wHideAll(scr);
1439 break;
1440 case WKBD_MINIATURIZE:
1441 if (ISMAPPED(wwin) && ISFOCUSED(wwin)
1442 && !WFLAGP(wwin, no_miniaturizable)) {
1443 CloseWindowMenu(scr);
1445 if (wwin->protocols.MINIATURIZE_WINDOW)
1446 wClientSendProtocol(wwin, w_global.atom.gnustep.wm_miniaturize_window, event->xbutton.time);
1447 else {
1448 wIconifyWindow(wwin);
1451 break;
1452 case WKBD_HIDE:
1453 if (ISMAPPED(wwin) && ISFOCUSED(wwin)) {
1454 WApplication *wapp = wApplicationOf(wwin->main_window);
1455 CloseWindowMenu(scr);
1457 if (wapp && !WFLAGP(wapp->main_window_desc, no_appicon)) {
1458 wHideApplication(wapp);
1461 break;
1462 case WKBD_HIDE_OTHERS:
1463 if (ISMAPPED(wwin) && ISFOCUSED(wwin)) {
1464 CloseWindowMenu(scr);
1466 wHideOtherApplications(wwin);
1468 break;
1469 case WKBD_MAXIMIZE:
1470 if (ISMAPPED(wwin) && ISFOCUSED(wwin) && IS_RESIZABLE(wwin)) {
1471 CloseWindowMenu(scr);
1473 handleMaximize(wwin, MAX_VERTICAL | MAX_HORIZONTAL | MAX_KEYBOARD);
1475 break;
1476 case WKBD_VMAXIMIZE:
1477 if (ISMAPPED(wwin) && ISFOCUSED(wwin) && IS_RESIZABLE(wwin)) {
1478 CloseWindowMenu(scr);
1480 handleMaximize(wwin, MAX_VERTICAL | MAX_KEYBOARD);
1482 break;
1483 case WKBD_HMAXIMIZE:
1484 if (ISMAPPED(wwin) && ISFOCUSED(wwin) && IS_RESIZABLE(wwin)) {
1485 CloseWindowMenu(scr);
1487 handleMaximize(wwin, MAX_HORIZONTAL | MAX_KEYBOARD);
1489 break;
1490 case WKBD_LHMAXIMIZE:
1491 if (ISMAPPED(wwin) && ISFOCUSED(wwin) && IS_RESIZABLE(wwin)) {
1492 CloseWindowMenu(scr);
1494 handleMaximize(wwin, MAX_VERTICAL | MAX_LEFTHALF | MAX_KEYBOARD);
1496 break;
1497 case WKBD_RHMAXIMIZE:
1498 if (ISMAPPED(wwin) && ISFOCUSED(wwin) && IS_RESIZABLE(wwin)) {
1499 CloseWindowMenu(scr);
1501 handleMaximize(wwin, MAX_VERTICAL | MAX_RIGHTHALF | MAX_KEYBOARD);
1503 break;
1504 case WKBD_THMAXIMIZE:
1505 if (ISMAPPED(wwin) && ISFOCUSED(wwin) && IS_RESIZABLE(wwin)) {
1506 CloseWindowMenu(scr);
1508 handleMaximize(wwin, MAX_HORIZONTAL | MAX_TOPHALF | MAX_KEYBOARD);
1510 break;
1511 case WKBD_BHMAXIMIZE:
1512 if (ISMAPPED(wwin) && ISFOCUSED(wwin) && IS_RESIZABLE(wwin)) {
1513 CloseWindowMenu(scr);
1515 handleMaximize(wwin, MAX_HORIZONTAL | MAX_BOTTOMHALF | MAX_KEYBOARD);
1517 break;
1518 case WKBD_LTCMAXIMIZE:
1519 if (ISMAPPED(wwin) && ISFOCUSED(wwin) && IS_RESIZABLE(wwin)) {
1520 CloseWindowMenu(scr);
1522 handleMaximize(wwin, MAX_LEFTHALF | MAX_TOPHALF | MAX_KEYBOARD);
1524 break;
1525 case WKBD_RTCMAXIMIZE:
1526 if (ISMAPPED(wwin) && ISFOCUSED(wwin) && IS_RESIZABLE(wwin)) {
1527 CloseWindowMenu(scr);
1529 handleMaximize(wwin, MAX_RIGHTHALF | MAX_TOPHALF | MAX_KEYBOARD);
1531 break;
1532 case WKBD_LBCMAXIMIZE:
1533 if (ISMAPPED(wwin) && ISFOCUSED(wwin) && IS_RESIZABLE(wwin)) {
1534 CloseWindowMenu(scr);
1536 handleMaximize(wwin, MAX_LEFTHALF | MAX_BOTTOMHALF | MAX_KEYBOARD);
1538 break;
1539 case WKBD_RBCMAXIMIZE:
1540 if (ISMAPPED(wwin) && ISFOCUSED(wwin) && IS_RESIZABLE(wwin)) {
1541 CloseWindowMenu(scr);
1543 handleMaximize(wwin, MAX_RIGHTHALF | MAX_BOTTOMHALF | MAX_KEYBOARD);
1545 break;
1546 case WKBD_MAXIMUS:
1547 if (ISMAPPED(wwin) && ISFOCUSED(wwin) && IS_RESIZABLE(wwin)) {
1548 CloseWindowMenu(scr);
1550 handleMaximize(wwin, MAX_MAXIMUS | MAX_KEYBOARD);
1552 break;
1553 case WKBD_OMNIPRESENT:
1554 if (ISMAPPED(wwin) && ISFOCUSED(wwin)) {
1555 CloseWindowMenu(scr);
1557 wWindowSetOmnipresent(wwin, !wwin->flags.omnipresent);
1559 break;
1560 case WKBD_RAISE:
1561 if (ISMAPPED(wwin) && ISFOCUSED(wwin)) {
1562 CloseWindowMenu(scr);
1564 wRaiseFrame(wwin->frame->core);
1566 break;
1567 case WKBD_LOWER:
1568 if (ISMAPPED(wwin) && ISFOCUSED(wwin)) {
1569 CloseWindowMenu(scr);
1571 wLowerFrame(wwin->frame->core);
1573 break;
1574 case WKBD_RAISELOWER:
1575 /* raise or lower the window under the pointer, not the
1576 * focused one
1578 wwin = windowUnderPointer(scr);
1579 if (wwin)
1580 wRaiseLowerFrame(wwin->frame->core);
1581 break;
1582 case WKBD_SHADE:
1583 if (ISMAPPED(wwin) && ISFOCUSED(wwin) && !WFLAGP(wwin, no_shadeable)) {
1584 if (wwin->flags.shaded)
1585 wUnshadeWindow(wwin);
1586 else
1587 wShadeWindow(wwin);
1589 break;
1590 case WKBD_MOVERESIZE:
1591 if (ISMAPPED(wwin) && ISFOCUSED(wwin) && (IS_RESIZABLE(wwin) || IS_MOVABLE(wwin))) {
1592 CloseWindowMenu(scr);
1594 wKeyboardMoveResizeWindow(wwin);
1596 break;
1597 case WKBD_CLOSE:
1598 if (ISMAPPED(wwin) && ISFOCUSED(wwin) && !WFLAGP(wwin, no_closable)) {
1599 CloseWindowMenu(scr);
1600 if (wwin->protocols.DELETE_WINDOW)
1601 wClientSendProtocol(wwin, w_global.atom.wm.delete_window, event->xkey.time);
1603 break;
1604 case WKBD_SELECT:
1605 if (ISMAPPED(wwin) && ISFOCUSED(wwin)) {
1606 wSelectWindow(wwin, !wwin->flags.selected);
1608 break;
1609 case WKBD_FOCUSNEXT:
1610 StartWindozeCycle(wwin, event, True, False);
1611 break;
1613 case WKBD_FOCUSPREV:
1614 StartWindozeCycle(wwin, event, False, False);
1615 break;
1617 case WKBD_GROUPNEXT:
1618 StartWindozeCycle(wwin, event, True, True);
1619 break;
1621 case WKBD_GROUPPREV:
1622 StartWindozeCycle(wwin, event, False, True);
1623 break;
1625 case WKBD_WORKSPACE1 ... WKBD_WORKSPACE10:
1626 widx = command - WKBD_WORKSPACE1;
1627 i = (scr->current_workspace / 10) * 10 + widx;
1628 if (wPreferences.ws_advance || i < scr->workspace_count)
1629 wWorkspaceChange(scr, i);
1630 break;
1632 case WKBD_NEXTWORKSPACE:
1633 wWorkspaceRelativeChange(scr, 1);
1634 break;
1635 case WKBD_PREVWORKSPACE:
1636 wWorkspaceRelativeChange(scr, -1);
1637 break;
1638 case WKBD_LASTWORKSPACE:
1639 wWorkspaceChange(scr, scr->last_workspace);
1640 break;
1642 case WKBD_MOVE_WORKSPACE1 ... WKBD_MOVE_WORKSPACE10:
1643 widx = command - WKBD_MOVE_WORKSPACE1;
1644 i = (scr->current_workspace / 10) * 10 + widx;
1645 if (wwin && (wPreferences.ws_advance || i < scr->workspace_count))
1646 wWindowChangeWorkspace(wwin, i);
1647 break;
1649 case WKBD_MOVE_NEXTWORKSPACE:
1650 if (wwin)
1651 wWindowChangeWorkspaceRelative(wwin, 1);
1652 break;
1653 case WKBD_MOVE_PREVWORKSPACE:
1654 if (wwin)
1655 wWindowChangeWorkspaceRelative(wwin, -1);
1656 break;
1657 case WKBD_MOVE_LASTWORKSPACE:
1658 if (wwin)
1659 wWindowChangeWorkspace(wwin, scr->last_workspace);
1660 break;
1662 case WKBD_MOVE_NEXTWSLAYER:
1663 case WKBD_MOVE_PREVWSLAYER:
1665 if (wwin) {
1666 int row, column;
1668 row = scr->current_workspace / 10;
1669 column = scr->current_workspace % 10;
1671 if (command == WKBD_MOVE_NEXTWSLAYER) {
1672 if ((row + 1) * 10 < scr->workspace_count)
1673 wWindowChangeWorkspace(wwin, column + (row + 1) * 10);
1674 } else {
1675 if (row > 0)
1676 wWindowChangeWorkspace(wwin, column + (row - 1) * 10);
1680 break;
1682 case WKBD_WINDOW1:
1683 case WKBD_WINDOW2:
1684 case WKBD_WINDOW3:
1685 case WKBD_WINDOW4:
1686 case WKBD_WINDOW5:
1687 case WKBD_WINDOW6:
1688 case WKBD_WINDOW7:
1689 case WKBD_WINDOW8:
1690 case WKBD_WINDOW9:
1691 case WKBD_WINDOW10:
1693 widx = command - WKBD_WINDOW1;
1695 if (scr->shortcutWindows[widx]) {
1696 WMArray *list = scr->shortcutWindows[widx];
1697 int cw;
1698 int count = WMGetArrayItemCount(list);
1699 WWindow *twin;
1700 WMArrayIterator iter;
1701 WWindow *wwin;
1703 wUnselectWindows(scr);
1704 cw = scr->current_workspace;
1706 WM_ETARETI_ARRAY(list, wwin, iter) {
1707 if (count > 1)
1708 wWindowChangeWorkspace(wwin, cw);
1710 wMakeWindowVisible(wwin);
1712 if (count > 1)
1713 wSelectWindow(wwin, True);
1716 /* rotate the order of windows, to create a cycling effect */
1717 twin = WMGetFromArray(list, 0);
1718 WMDeleteFromArray(list, 0);
1719 WMAddToArray(list, twin);
1721 } else if (wwin && ISMAPPED(wwin) && ISFOCUSED(wwin)) {
1722 if (scr->shortcutWindows[widx]) {
1723 WMFreeArray(scr->shortcutWindows[widx]);
1724 scr->shortcutWindows[widx] = NULL;
1727 if (wwin->flags.selected && scr->selected_windows) {
1728 scr->shortcutWindows[widx] = WMDuplicateArray(scr->selected_windows);
1729 /*WMRemoveFromArray(scr->shortcutWindows[index], wwin);
1730 WMInsertInArray(scr->shortcutWindows[index], 0, wwin); */
1731 } else {
1732 scr->shortcutWindows[widx] = WMCreateArray(4);
1733 WMAddToArray(scr->shortcutWindows[widx], wwin);
1736 wSelectWindow(wwin, !wwin->flags.selected);
1737 XFlush(dpy);
1738 wusleep(3000);
1739 wSelectWindow(wwin, !wwin->flags.selected);
1740 XFlush(dpy);
1742 } else if (scr->selected_windows && WMGetArrayItemCount(scr->selected_windows)) {
1744 if (wwin->flags.selected && scr->selected_windows) {
1745 if (scr->shortcutWindows[widx]) {
1746 WMFreeArray(scr->shortcutWindows[widx]);
1748 scr->shortcutWindows[widx] = WMDuplicateArray(scr->selected_windows);
1752 break;
1754 case WKBD_RELAUNCH:
1755 if (ISMAPPED(wwin) && ISFOCUSED(wwin))
1756 (void) RelaunchWindow(wwin);
1758 break;
1760 case WKBD_SWITCH_SCREEN:
1761 if (w_global.screen_count > 1) {
1762 WScreen *scr2;
1763 int i;
1765 /* find index of this screen */
1766 for (i = 0; i < w_global.screen_count; i++) {
1767 if (wScreenWithNumber(i) == scr)
1768 break;
1770 i++;
1771 if (i >= w_global.screen_count) {
1772 i = 0;
1774 scr2 = wScreenWithNumber(i);
1776 if (scr2) {
1777 XWarpPointer(dpy, scr->root_win, scr2->root_win, 0, 0, 0, 0,
1778 scr2->scr_width / 2, scr2->scr_height / 2);
1781 break;
1783 case WKBD_RUN:
1785 char *cmdline;
1787 cmdline = ExpandOptions(scr, _("exec %a(Run,Type command to run:)"));
1789 if (cmdline) {
1790 XGrabPointer(dpy, scr->root_win, True, 0,
1791 GrabModeAsync, GrabModeAsync, None, wPreferences.cursor[WCUR_WAIT], CurrentTime);
1792 XSync(dpy, False);
1794 ExecuteShellCommand(scr, cmdline);
1795 wfree(cmdline);
1797 XUngrabPointer(dpy, CurrentTime);
1798 XSync(dpy, False);
1800 break;
1803 case WKBD_NEXTWSLAYER:
1804 case WKBD_PREVWSLAYER:
1806 int row, column;
1808 row = scr->current_workspace / 10;
1809 column = scr->current_workspace % 10;
1811 if (command == WKBD_NEXTWSLAYER) {
1812 if ((row + 1) * 10 < scr->workspace_count)
1813 wWorkspaceChange(scr, column + (row + 1) * 10);
1814 } else {
1815 if (row > 0)
1816 wWorkspaceChange(scr, column + (row - 1) * 10);
1819 break;
1820 case WKBD_CLIPRAISELOWER:
1821 if (!wPreferences.flags.noclip)
1822 wDockRaiseLower(scr->workspaces[scr->current_workspace]->clip);
1823 break;
1824 case WKBD_DOCKRAISELOWER:
1825 if (!wPreferences.flags.nodock)
1826 wDockRaiseLower(scr->dock);
1827 break;
1828 #ifdef KEEP_XKB_LOCK_STATUS
1829 case WKBD_TOGGLE:
1830 if (wPreferences.modelock) {
1831 /*toggle */
1832 wwin = scr->focused_window;
1834 if (wwin && wwin->flags.mapped
1835 && wwin->frame->workspace == wwin->screen_ptr->current_workspace
1836 && !wwin->flags.miniaturized && !wwin->flags.hidden) {
1837 XkbGetState(dpy, XkbUseCoreKbd, &staterec);
1839 wwin->frame->languagemode = wwin->frame->last_languagemode;
1840 wwin->frame->last_languagemode = staterec.group;
1841 XkbLockGroup(dpy, XkbUseCoreKbd, wwin->frame->languagemode);
1845 break;
1846 #endif /* KEEP_XKB_LOCK_STATUS */
1850 static void handleMotionNotify(XEvent * event)
1852 WScreen *scr = wScreenForRootWindow(event->xmotion.root);
1854 if (wPreferences.scrollable_menus) {
1855 WMPoint p = wmkpoint(event->xmotion.x_root, event->xmotion.y_root);
1856 WMRect rect = wGetRectForHead(scr, wGetHeadForPoint(scr, p));
1858 if (scr->flags.jump_back_pending ||
1859 p.x <= (rect.pos.x + 1) ||
1860 p.x >= (rect.pos.x + rect.size.width - 2) ||
1861 p.y <= (rect.pos.y + 1) || p.y >= (rect.pos.y + rect.size.height - 2)) {
1862 WMenu *menu;
1864 menu = wMenuUnderPointer(scr);
1865 if (menu != NULL)
1866 wMenuScroll(menu);
1871 static void handleVisibilityNotify(XEvent * event)
1873 WWindow *wwin;
1875 wwin = wWindowFor(event->xvisibility.window);
1876 if (!wwin)
1877 return;
1878 wwin->flags.obscured = (event->xvisibility.state == VisibilityFullyObscured);