wmaker: fixed flag to avoid reloading config multiple times
[wmaker-crm.git] / src / event.c
blobac69102bb72a4e51a4c40062e750e8c7a6a16704
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 USE_XSHAPE
39 # include <X11/extensions/shape.h>
40 #endif
41 #ifdef XDND
42 #include "xdnd.h"
43 #endif
45 #ifdef USE_RANDR
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"
80 #define MOD_MASK wPreferences.modifier_mask
82 /************ Local stuff ***********/
84 static void saveTimestamp(XEvent *event);
85 static void handleColormapNotify(XEvent *event);
86 static void handleMapNotify(XEvent *event);
87 static void handleUnmapNotify(XEvent *event);
88 static void handleButtonPress(XEvent *event);
89 static void handleExpose(XEvent *event);
90 static void handleDestroyNotify(XEvent *event);
91 static void handleConfigureRequest(XEvent *event);
92 static void handleMapRequest(XEvent *event);
93 static void handlePropertyNotify(XEvent *event);
94 static void handleEnterNotify(XEvent *event);
95 static void handleLeaveNotify(XEvent *event);
96 static void handleExtensions(XEvent *event);
97 static void handleClientMessage(XEvent *event);
98 static void handleKeyPress(XEvent *event);
99 static void handleFocusIn(XEvent *event);
100 static void handleMotionNotify(XEvent *event);
101 static void handleVisibilityNotify(XEvent *event);
102 static void handle_inotify_events(void);
103 static void wdelete_death_handler(WMagicNumber id);
106 #ifdef USE_XSHAPE
107 static void handleShapeNotify(XEvent *event);
108 #endif
110 #ifdef KEEP_XKB_LOCK_STATUS
111 static void handleXkbIndicatorStateNotify(XEvent *event);
112 #endif
114 /* real dead process handler */
115 static void handleDeadProcess(void);
117 typedef struct DeadProcesses {
118 pid_t pid;
119 unsigned char exit_status;
120 } DeadProcesses;
122 /* stack of dead processes */
123 static DeadProcesses deadProcesses[MAX_DEAD_PROCESSES];
124 static int deadProcessPtr = 0;
126 typedef struct DeathHandler {
127 WDeathHandler *callback;
128 pid_t pid;
129 void *client_data;
130 } DeathHandler;
132 static WMArray *deathHandlers = NULL;
134 WMagicNumber wAddDeathHandler(pid_t pid, WDeathHandler * callback, void *cdata)
136 DeathHandler *handler;
138 handler = malloc(sizeof(DeathHandler));
139 if (!handler)
140 return 0;
142 handler->pid = pid;
143 handler->callback = callback;
144 handler->client_data = cdata;
146 if (!deathHandlers)
147 deathHandlers = WMCreateArrayWithDestructor(8, free);
149 WMAddToArray(deathHandlers, handler);
151 return handler;
154 static void wdelete_death_handler(WMagicNumber id)
156 DeathHandler *handler = (DeathHandler *) id;
158 if (!handler || !deathHandlers)
159 return;
161 /* array destructor will call free(handler) */
162 WMRemoveFromArray(deathHandlers, handler);
165 void DispatchEvent(XEvent * event)
167 if (deathHandlers)
168 handleDeadProcess();
170 if (WCHECK_STATE(WSTATE_NEED_EXIT)) {
171 WCHANGE_STATE(WSTATE_EXITING);
172 /* received SIGTERM */
174 * WMHandleEvent() can't be called from anything
175 * executed inside here, or we can get in a infinite
176 * recursive loop.
178 Shutdown(WSExitMode);
180 } else if (WCHECK_STATE(WSTATE_NEED_RESTART)) {
181 WCHANGE_STATE(WSTATE_RESTARTING);
183 Shutdown(WSRestartPreparationMode);
184 /* received SIGHUP */
185 Restart(NULL, True);
186 } else if (WCHECK_STATE(WSTATE_NEED_REREAD)) {
187 WCHANGE_STATE(WSTATE_NORMAL);
188 wDefaultsCheckDomains(NULL);
191 /* for the case that all that is wanted to be dispatched is
192 * the stuff above */
193 if (!event)
194 return;
196 saveTimestamp(event);
197 switch (event->type) {
198 case MapRequest:
199 handleMapRequest(event);
200 break;
202 case KeyPress:
203 handleKeyPress(event);
204 break;
206 case MotionNotify:
207 handleMotionNotify(event);
208 break;
210 case ConfigureRequest:
211 handleConfigureRequest(event);
212 break;
214 case DestroyNotify:
215 handleDestroyNotify(event);
216 break;
218 case MapNotify:
219 handleMapNotify(event);
220 break;
222 case UnmapNotify:
223 handleUnmapNotify(event);
224 break;
226 case ButtonPress:
227 handleButtonPress(event);
228 break;
230 case Expose:
231 handleExpose(event);
232 break;
234 case PropertyNotify:
235 handlePropertyNotify(event);
236 break;
238 case EnterNotify:
239 handleEnterNotify(event);
240 break;
242 case LeaveNotify:
243 handleLeaveNotify(event);
244 break;
246 case ClientMessage:
247 handleClientMessage(event);
248 break;
250 case ColormapNotify:
251 handleColormapNotify(event);
252 break;
254 case MappingNotify:
255 if (event->xmapping.request == MappingKeyboard || event->xmapping.request == MappingModifier)
256 XRefreshKeyboardMapping(&event->xmapping);
257 break;
259 case FocusIn:
260 handleFocusIn(event);
261 break;
263 case VisibilityNotify:
264 handleVisibilityNotify(event);
265 break;
267 case ConfigureNotify:
268 #ifdef USE_RANDR
269 if (event->xconfigure.window == DefaultRootWindow(dpy))
270 XRRUpdateConfiguration(event);
271 #endif
272 break;
274 default:
275 handleExtensions(event);
276 break;
280 #ifdef HAVE_INOTIFY
282 *----------------------------------------------------------------------
283 * handle_inotify_events-
284 * Check for inotify events
286 * Returns:
287 * After reading events for the given file descriptor (fd) and
288 * watch descriptor (wd)
290 * Side effects:
291 * Calls wDefaultsCheckDomains if config database is updated
292 *----------------------------------------------------------------------
294 static void handle_inotify_events(void)
296 ssize_t eventQLength, i = 0;
297 /* Make room for at lease 5 simultaneous events, with path + filenames */
298 char buff[ (sizeof(struct inotify_event) + NAME_MAX + 1) * 5 ];
299 /* Check config only once per read of the event queue */
300 int oneShotFlag = 0;
303 * Read off the queued events
304 * queue overflow is not checked (IN_Q_OVERFLOW). In practise this should
305 * not occur; the block is on Xevents, but a config file change will normally
306 * occur as a result of an Xevent - so the event queue should never have more than
307 * a few entries before a read().
309 eventQLength = read(w_global.inotify.fd_event_queue,
310 buff, sizeof(buff) );
312 /* check what events occured */
313 /* Should really check wd here too, but for now we only have one watch! */
314 while (i < eventQLength) {
315 struct inotify_event *pevent = (struct inotify_event *)&buff[i];
318 * see inotify.h for event types.
320 if (pevent->mask & IN_DELETE_SELF) {
321 wwarning(_("the defaults database has been deleted!"
322 " Restart Window Maker to create the database" " with the default settings"));
324 if (w_global.inotify.fd_event_queue >= 0) {
325 close(w_global.inotify.fd_event_queue);
326 w_global.inotify.fd_event_queue = -1;
329 if (pevent->mask & IN_UNMOUNT) {
330 wwarning(_("the unit containing the defaults database has"
331 " been unmounted. Setting --static mode." " Any changes will not be saved."));
333 if (w_global.inotify.fd_event_queue >= 0) {
334 close(w_global.inotify.fd_event_queue);
335 w_global.inotify.fd_event_queue = -1;
338 wPreferences.flags.noupdates = 1;
340 if ((pevent->mask & IN_MODIFY) && oneShotFlag == 0) {
341 wwarning(_("Inotify: Reading config files in defaults database."));
342 wDefaultsCheckDomains(NULL);
343 oneShotFlag = 1;
346 /* move to next event in the buffer */
347 i += sizeof(struct inotify_event) + pevent->len;
350 #endif /* HAVE_INOTIFY */
353 *----------------------------------------------------------------------
354 * EventLoop-
355 * Processes X and internal events indefinitely.
357 * Returns:
358 * Never returns
360 * Side effects:
361 * The LastTimestamp global variable is updated.
362 * Calls inotifyGetEvents if defaults database changes.
363 *----------------------------------------------------------------------
365 noreturn void EventLoop(void)
367 XEvent event;
368 #ifdef HAVE_INOTIFY
369 struct timeval time;
370 fd_set rfds;
371 int retVal = 0;
373 if (w_global.inotify.fd_event_queue < 0 || w_global.inotify.wd_defaults < 0)
374 retVal = -1;
375 #endif
377 for (;;) {
379 WMNextEvent(dpy, &event); /* Blocks here */
380 WMHandleEvent(&event);
381 #ifdef HAVE_INOTIFY
382 if (retVal != -1) {
383 time.tv_sec = 0;
384 time.tv_usec = 0;
385 FD_ZERO(&rfds);
386 FD_SET(w_global.inotify.fd_event_queue, &rfds);
388 /* check for available read data from inotify - don't block! */
389 retVal = select(w_global.inotify.fd_event_queue + 1, &rfds, NULL, NULL, &time);
391 if (retVal < 0) { /* an error has occured */
392 wwarning(_("select failed. The inotify instance will be closed."
393 " Changes to the defaults database will require"
394 " a restart to take effect."));
395 close(w_global.inotify.fd_event_queue);
396 w_global.inotify.fd_event_queue = -1;
397 continue;
399 if (FD_ISSET(w_global.inotify.fd_event_queue, &rfds))
400 handle_inotify_events();
402 #endif
407 *----------------------------------------------------------------------
408 * ProcessPendingEvents --
409 * Processes the events that are currently pending (at the time
410 * this function is called) in the display's queue.
412 * Returns:
413 * After the pending events that were present at the function call
414 * are processed.
416 * Side effects:
417 * Many -- whatever handling events may involve.
419 *----------------------------------------------------------------------
421 void ProcessPendingEvents(void)
423 XEvent event;
424 int count;
426 XSync(dpy, False);
428 /* Take a snapshot of the event count in the queue */
429 count = XPending(dpy);
431 while (count > 0 && XPending(dpy)) {
432 WMNextEvent(dpy, &event);
433 WMHandleEvent(&event);
434 count--;
438 Bool IsDoubleClick(WScreen * scr, XEvent * event)
440 if ((scr->last_click_time > 0) &&
441 (event->xbutton.time - scr->last_click_time <= wPreferences.dblclick_time)
442 && (event->xbutton.button == scr->last_click_button)
443 && (event->xbutton.window == scr->last_click_window)) {
445 scr->flags.next_click_is_not_double = 1;
446 scr->last_click_time = 0;
447 scr->last_click_window = event->xbutton.window;
449 return True;
451 return False;
454 void NotifyDeadProcess(pid_t pid, unsigned char status)
456 if (deadProcessPtr >= MAX_DEAD_PROCESSES - 1) {
457 wwarning("stack overflow: too many dead processes");
458 return;
460 /* stack the process to be handled later,
461 * as this is called from the signal handler */
462 deadProcesses[deadProcessPtr].pid = pid;
463 deadProcesses[deadProcessPtr].exit_status = status;
464 deadProcessPtr++;
467 static void handleDeadProcess(void)
469 DeathHandler *tmp;
470 int i;
472 for (i = 0; i < deadProcessPtr; i++) {
473 wWindowDeleteSavedStatesForPID(deadProcesses[i].pid);
476 if (!deathHandlers) {
477 deadProcessPtr = 0;
478 return;
481 /* get the pids on the queue and call handlers */
482 while (deadProcessPtr > 0) {
483 deadProcessPtr--;
485 for (i = WMGetArrayItemCount(deathHandlers) - 1; i >= 0; i--) {
486 tmp = WMGetFromArray(deathHandlers, i);
487 if (!tmp)
488 continue;
490 if (tmp->pid == deadProcesses[deadProcessPtr].pid) {
491 (*tmp->callback) (tmp->pid,
492 deadProcesses[deadProcessPtr].exit_status, tmp->client_data);
493 wdelete_death_handler(tmp);
499 static void saveTimestamp(XEvent * event)
502 * Never save CurrentTime as LastTimestamp because CurrentTime
503 * it's not a real timestamp (it's the 0L constant)
506 switch (event->type) {
507 case ButtonRelease:
508 case ButtonPress:
509 w_global.timestamp.last_event = event->xbutton.time;
510 break;
511 case KeyPress:
512 case KeyRelease:
513 w_global.timestamp.last_event = event->xkey.time;
514 break;
515 case MotionNotify:
516 w_global.timestamp.last_event = event->xmotion.time;
517 break;
518 case PropertyNotify:
519 w_global.timestamp.last_event = event->xproperty.time;
520 break;
521 case EnterNotify:
522 case LeaveNotify:
523 w_global.timestamp.last_event = event->xcrossing.time;
524 break;
525 case SelectionClear:
526 w_global.timestamp.last_event = event->xselectionclear.time;
527 break;
528 case SelectionRequest:
529 w_global.timestamp.last_event = event->xselectionrequest.time;
530 break;
531 case SelectionNotify:
532 w_global.timestamp.last_event = event->xselection.time;
533 #ifdef XDND
534 wXDNDProcessSelection(event);
535 #endif
536 break;
540 static int matchWindow(const void *item, const void *cdata)
542 return (((WFakeGroupLeader *) item)->origLeader == (Window) cdata);
545 static void handleExtensions(XEvent * event)
547 #ifdef KEEP_XKB_LOCK_STATUS
548 XkbEvent *xkbevent;
549 xkbevent = (XkbEvent *) event;
550 #endif /*KEEP_XKB_LOCK_STATUS */
551 #ifdef USE_XSHAPE
552 if (w_global.xext.shape.supported && event->type == (w_global.xext.shape.event_base + ShapeNotify)) {
553 handleShapeNotify(event);
555 #endif
556 #ifdef KEEP_XKB_LOCK_STATUS
557 if (wPreferences.modelock && (xkbevent->type == w_global.xext.xkb.event_base)) {
558 handleXkbIndicatorStateNotify(event);
560 #endif /*KEEP_XKB_LOCK_STATUS */
561 #ifdef USE_RANDR
562 if (w_global.xext.randr.supported && event->type == (w_global.xext.randr.event_base + RRScreenChangeNotify)) {
563 /* From xrandr man page: "Clients must call back into Xlib using
564 * XRRUpdateConfiguration when screen configuration change notify
565 * events are generated */
566 XRRUpdateConfiguration(event);
567 WCHANGE_STATE(WSTATE_RESTARTING);
568 Shutdown(WSRestartPreparationMode);
569 Restart(NULL,True);
571 #endif
574 static void handleMapRequest(XEvent * ev)
576 WWindow *wwin;
577 WScreen *scr = NULL;
578 Window window = ev->xmaprequest.window;
580 if ((wwin = wWindowFor(window))) {
581 if (wwin->flags.shaded) {
582 wUnshadeWindow(wwin);
584 /* deiconify window */
585 if (wwin->flags.miniaturized) {
586 wDeiconifyWindow(wwin);
587 } else if (wwin->flags.hidden) {
588 WApplication *wapp = wApplicationOf(wwin->main_window);
589 /* go to the last workspace that the user worked on the app */
590 if (wapp) {
591 wWorkspaceChange(wwin->screen_ptr, wapp->last_workspace);
593 wUnhideApplication(wapp, False, False);
595 return;
598 scr = wScreenForRootWindow(ev->xmaprequest.parent);
600 wwin = wManageWindow(scr, window);
603 * This is to let the Dock know that the application it launched
604 * has already been mapped (eg: it has finished launching).
605 * It is not necessary for normally docked apps, but is needed for
606 * apps that were forcedly docked (like with dockit).
608 if (scr->last_dock) {
609 if (wwin && wwin->main_window != None && wwin->main_window != window)
610 wDockTrackWindowLaunch(scr->last_dock, wwin->main_window);
611 else
612 wDockTrackWindowLaunch(scr->last_dock, window);
615 if (wwin) {
616 wClientSetState(wwin, NormalState, None);
617 if (wwin->flags.maximized) {
618 wMaximizeWindow(wwin, wwin->flags.maximized);
620 if (wwin->flags.shaded) {
621 wwin->flags.shaded = 0;
622 wwin->flags.skip_next_animation = 1;
623 wShadeWindow(wwin);
625 if (wwin->flags.miniaturized) {
626 wwin->flags.miniaturized = 0;
627 wwin->flags.skip_next_animation = 1;
628 wIconifyWindow(wwin);
630 if (wwin->flags.fullscreen) {
631 wwin->flags.fullscreen = 0;
632 wFullscreenWindow(wwin);
634 if (wwin->flags.hidden) {
635 WApplication *wapp = wApplicationOf(wwin->main_window);
637 wwin->flags.hidden = 0;
638 wwin->flags.skip_next_animation = 1;
639 if (wapp) {
640 wHideApplication(wapp);
646 static void handleDestroyNotify(XEvent * event)
648 WWindow *wwin;
649 WApplication *app;
650 Window window = event->xdestroywindow.window;
651 WScreen *scr = wScreenForRootWindow(event->xdestroywindow.event);
652 int widx;
654 wwin = wWindowFor(window);
655 if (wwin) {
656 wUnmanageWindow(wwin, False, True);
659 if (scr != NULL) {
660 while ((widx = WMFindInArray(scr->fakeGroupLeaders, matchWindow, (void *)window)) != WANotFound) {
661 WFakeGroupLeader *fPtr;
663 fPtr = WMGetFromArray(scr->fakeGroupLeaders, widx);
664 if (fPtr->retainCount > 0) {
665 fPtr->retainCount--;
666 if (fPtr->retainCount == 0 && fPtr->leader != None) {
667 XDestroyWindow(dpy, fPtr->leader);
668 fPtr->leader = None;
669 XFlush(dpy);
672 fPtr->origLeader = None;
676 app = wApplicationOf(window);
677 if (app) {
678 if (window == app->main_window) {
679 app->refcount = 0;
680 wwin = app->main_window_desc->screen_ptr->focused_window;
681 while (wwin) {
682 if (wwin->main_window == window) {
683 wwin->main_window = None;
685 wwin = wwin->prev;
688 wApplicationDestroy(app);
692 static void handleExpose(XEvent * event)
694 WObjDescriptor *desc;
695 XEvent ev;
697 while (XCheckTypedWindowEvent(dpy, event->xexpose.window, Expose, &ev)) ;
699 if (XFindContext(dpy, event->xexpose.window, w_global.context.client_win, (XPointer *) & desc) == XCNOENT) {
700 return;
703 if (desc->handle_expose) {
704 (*desc->handle_expose) (desc, event);
708 static void executeButtonAction(WScreen * scr, XEvent * event, int action)
710 switch (action) {
711 case WA_SELECT_WINDOWS:
712 wUnselectWindows(scr);
713 wSelectWindows(scr, event);
714 break;
715 case WA_OPEN_APPMENU:
716 OpenRootMenu(scr, event->xbutton.x_root, event->xbutton.y_root, False);
717 /* ugly hack */
718 if (scr->root_menu) {
719 if (scr->root_menu->brother->flags.mapped)
720 event->xbutton.window = scr->root_menu->brother->frame->core->window;
721 else
722 event->xbutton.window = scr->root_menu->frame->core->window;
724 break;
725 case WA_OPEN_WINLISTMENU:
726 OpenSwitchMenu(scr, event->xbutton.x_root, event->xbutton.y_root, False);
727 if (scr->switch_menu) {
728 if (scr->switch_menu->brother->flags.mapped)
729 event->xbutton.window = scr->switch_menu->brother->frame->core->window;
730 else
731 event->xbutton.window = scr->switch_menu->frame->core->window;
733 break;
734 default:
735 break;
739 /* bindable */
740 static void handleButtonPress(XEvent * event)
742 WObjDescriptor *desc;
743 WScreen *scr;
745 scr = wScreenForRootWindow(event->xbutton.root);
747 #ifdef BALLOON_TEXT
748 wBalloonHide(scr);
749 #endif
751 if (event->xbutton.window == scr->root_win) {
752 if (event->xbutton.button == Button1 && wPreferences.mouse_button1 != WA_NONE) {
753 executeButtonAction(scr, event, wPreferences.mouse_button1);
754 } else if (event->xbutton.button == Button2 && wPreferences.mouse_button2 != WA_NONE) {
755 executeButtonAction(scr, event, wPreferences.mouse_button2);
756 } else if (event->xbutton.button == Button3 && wPreferences.mouse_button3 != WA_NONE) {
757 executeButtonAction(scr, event, wPreferences.mouse_button3);
758 } else if (event->xbutton.button == Button4 && wPreferences.mouse_wheel != WA_NONE) {
759 wWorkspaceRelativeChange(scr, 1);
760 } else if (event->xbutton.button == Button5 && wPreferences.mouse_wheel != WA_NONE) {
761 wWorkspaceRelativeChange(scr, -1);
765 desc = NULL;
766 if (XFindContext(dpy, event->xbutton.subwindow, w_global.context.client_win, (XPointer *) & desc) == XCNOENT) {
767 if (XFindContext(dpy, event->xbutton.window, w_global.context.client_win, (XPointer *) & desc) == XCNOENT) {
768 return;
772 if (desc->parent_type == WCLASS_WINDOW) {
773 XSync(dpy, 0);
775 if (event->xbutton.state & ( MOD_MASK | ControlMask )) {
776 XAllowEvents(dpy, AsyncPointer, CurrentTime);
777 } else {
778 /* if (wPreferences.focus_mode == WKF_CLICK) { */
779 if (wPreferences.ignore_focus_click) {
780 XAllowEvents(dpy, AsyncPointer, CurrentTime);
782 XAllowEvents(dpy, ReplayPointer, CurrentTime);
783 /* } */
785 XSync(dpy, 0);
786 } else if (desc->parent_type == WCLASS_APPICON
787 || desc->parent_type == WCLASS_MINIWINDOW || desc->parent_type == WCLASS_DOCK_ICON) {
788 if (event->xbutton.state & MOD_MASK) {
789 XSync(dpy, 0);
790 XAllowEvents(dpy, AsyncPointer, CurrentTime);
791 XSync(dpy, 0);
795 if (desc->handle_mousedown != NULL) {
796 (*desc->handle_mousedown) (desc, event);
799 /* save double-click information */
800 if (scr->flags.next_click_is_not_double) {
801 scr->flags.next_click_is_not_double = 0;
802 } else {
803 scr->last_click_time = event->xbutton.time;
804 scr->last_click_button = event->xbutton.button;
805 scr->last_click_window = event->xbutton.window;
809 static void handleMapNotify(XEvent * event)
811 WWindow *wwin;
813 wwin = wWindowFor(event->xmap.event);
814 if (wwin && wwin->client_win == event->xmap.event) {
815 if (wwin->flags.miniaturized) {
816 wDeiconifyWindow(wwin);
817 } else {
818 XGrabServer(dpy);
819 wWindowMap(wwin);
820 wClientSetState(wwin, NormalState, None);
821 XUngrabServer(dpy);
826 static void handleUnmapNotify(XEvent * event)
828 WWindow *wwin;
829 XEvent ev;
830 Bool withdraw = False;
832 /* only process windows with StructureNotify selected
833 * (ignore SubstructureNotify) */
834 wwin = wWindowFor(event->xunmap.window);
835 if (!wwin)
836 return;
838 /* whether the event is a Withdrawal request */
839 if (event->xunmap.event == wwin->screen_ptr->root_win && event->xunmap.send_event)
840 withdraw = True;
842 if (wwin->client_win != event->xunmap.event && !withdraw)
843 return;
845 if (!wwin->flags.mapped && !withdraw
846 && wwin->frame->workspace == w_global.workspace.current
847 && !wwin->flags.miniaturized && !wwin->flags.hidden)
848 return;
850 XGrabServer(dpy);
851 XUnmapWindow(dpy, wwin->frame->core->window);
852 wwin->flags.mapped = 0;
853 XSync(dpy, 0);
854 /* check if the window was destroyed */
855 if (XCheckTypedWindowEvent(dpy, wwin->client_win, DestroyNotify, &ev)) {
856 DispatchEvent(&ev);
857 } else {
858 Bool reparented = False;
860 if (XCheckTypedWindowEvent(dpy, wwin->client_win, ReparentNotify, &ev))
861 reparented = True;
863 /* withdraw window */
864 wwin->flags.mapped = 0;
865 if (!reparented)
866 wClientSetState(wwin, WithdrawnState, None);
868 /* if the window was reparented, do not reparent it back to the
869 * root window */
870 wUnmanageWindow(wwin, !reparented, False);
872 XUngrabServer(dpy);
875 static void handleConfigureRequest(XEvent * event)
877 WWindow *wwin;
879 if (!(wwin = wWindowFor(event->xconfigurerequest.window))) {
881 * Configure request for unmapped window
883 wClientConfigure(NULL, &(event->xconfigurerequest));
884 } else {
885 wClientConfigure(wwin, &(event->xconfigurerequest));
889 static void handlePropertyNotify(XEvent * event)
891 WWindow *wwin;
892 WApplication *wapp;
893 Window jr;
894 int ji;
895 unsigned int ju;
897 wwin = wWindowFor(event->xproperty.window);
898 if (wwin) {
899 if (!XGetGeometry(dpy, wwin->client_win, &jr, &ji, &ji, &ju, &ju, &ju, &ju)) {
900 return;
902 wClientCheckProperty(wwin, &event->xproperty);
904 wapp = wApplicationOf(event->xproperty.window);
905 if (wapp) {
906 wClientCheckProperty(wapp->main_window_desc, &event->xproperty);
910 static void handleClientMessage(XEvent * event)
912 WWindow *wwin;
913 WObjDescriptor *desc;
915 /* handle transition from Normal to Iconic state */
916 if (event->xclient.message_type == w_global.atom.wm.change_state
917 && event->xclient.format == 32 && event->xclient.data.l[0] == IconicState) {
919 wwin = wWindowFor(event->xclient.window);
920 if (!wwin)
921 return;
922 if (!wwin->flags.miniaturized)
923 wIconifyWindow(wwin);
924 } else if (event->xclient.message_type == w_global.atom.wm.colormap_notify && event->xclient.format == 32) {
925 WScreen *scr = wScreenForRootWindow(event->xclient.window);
927 if (!scr)
928 return;
930 if (event->xclient.data.l[1] == 1) { /* starting */
931 wColormapAllowClientInstallation(scr, True);
932 } else { /* stopping */
933 wColormapAllowClientInstallation(scr, False);
935 } else if (event->xclient.message_type == w_global.atom.wmaker.command) {
937 char *command;
938 size_t len;
940 len = sizeof(event->xclient.data.b) + 1;
941 command = wmalloc(len);
942 strncpy(command, event->xclient.data.b, sizeof(event->xclient.data.b));
944 if (strncmp(command, "Reconfigure", sizeof("Reconfigure")) == 0) {
945 wwarning(_("Got Reconfigure command"));
946 wDefaultsCheckDomains(NULL);
947 } else {
948 wwarning(_("Got unknown command %s"), command);
951 wfree(command);
953 } else if (event->xclient.message_type == w_global.atom.wmaker.wm_function) {
954 WApplication *wapp;
955 int done = 0;
956 wapp = wApplicationOf(event->xclient.window);
957 if (wapp) {
958 switch (event->xclient.data.l[0]) {
959 case WMFHideOtherApplications:
960 wHideOtherApplications(wapp->main_window_desc);
961 done = 1;
962 break;
964 case WMFHideApplication:
965 wHideApplication(wapp);
966 done = 1;
967 break;
970 if (!done) {
971 wwin = wWindowFor(event->xclient.window);
972 if (wwin) {
973 switch (event->xclient.data.l[0]) {
974 case WMFHideOtherApplications:
975 wHideOtherApplications(wwin);
976 break;
978 case WMFHideApplication:
979 wHideApplication(wApplicationOf(wwin->main_window));
980 break;
984 } else if (event->xclient.message_type == w_global.atom.gnustep.wm_attr) {
985 wwin = wWindowFor(event->xclient.window);
986 if (!wwin)
987 return;
988 switch (event->xclient.data.l[0]) {
989 case GSWindowLevelAttr:
991 int level = (int)event->xclient.data.l[1];
993 if (WINDOW_LEVEL(wwin) != level) {
994 ChangeStackingLevel(wwin->frame->core, level);
997 break;
999 } else if (event->xclient.message_type == w_global.atom.gnustep.titlebar_state) {
1000 wwin = wWindowFor(event->xclient.window);
1001 if (!wwin)
1002 return;
1003 switch (event->xclient.data.l[0]) {
1004 case WMTitleBarNormal:
1005 wFrameWindowChangeState(wwin->frame, WS_UNFOCUSED);
1006 break;
1007 case WMTitleBarMain:
1008 wFrameWindowChangeState(wwin->frame, WS_PFOCUSED);
1009 break;
1010 case WMTitleBarKey:
1011 wFrameWindowChangeState(wwin->frame, WS_FOCUSED);
1012 break;
1014 } else if (event->xclient.message_type == w_global.atom.wm.ignore_focus_events) {
1015 WScreen *scr = wScreenForRootWindow(event->xclient.window);
1016 if (!scr)
1017 return;
1018 scr->flags.ignore_focus_events = event->xclient.data.l[0] ? 1 : 0;
1019 } else if (wNETWMProcessClientMessage(&event->xclient)) {
1020 /* do nothing */
1021 #ifdef XDND
1022 } else if (wXDNDProcessClientMessage(&event->xclient)) {
1023 /* do nothing */
1024 #endif /* XDND */
1025 } else {
1027 * Non-standard thing, but needed by OffiX DND.
1028 * For when the icon frame gets a ClientMessage
1029 * that should have gone to the icon_window.
1031 if (XFindContext(dpy, event->xbutton.window, w_global.context.client_win, (XPointer *) & desc) != XCNOENT) {
1032 struct WIcon *icon = NULL;
1034 if (desc->parent_type == WCLASS_MINIWINDOW) {
1035 icon = (WIcon *) desc->parent;
1036 } else if (desc->parent_type == WCLASS_DOCK_ICON || desc->parent_type == WCLASS_APPICON) {
1037 icon = ((WAppIcon *) desc->parent)->icon;
1039 if (icon && (wwin = icon->owner)) {
1040 if (wwin->client_win != event->xclient.window) {
1041 event->xclient.window = wwin->client_win;
1042 XSendEvent(dpy, wwin->client_win, False, NoEventMask, event);
1049 static void raiseWindow(WScreen * scr)
1051 WWindow *wwin;
1053 scr->autoRaiseTimer = NULL;
1055 wwin = wWindowFor(scr->autoRaiseWindow);
1056 if (!wwin)
1057 return;
1059 if (!wwin->flags.destroyed && wwin->flags.focused) {
1060 wRaiseFrame(wwin->frame->core);
1061 /* this is needed or a race condition will occur */
1062 XSync(dpy, False);
1066 static void handleEnterNotify(XEvent * event)
1068 WWindow *wwin;
1069 WObjDescriptor *desc = NULL;
1070 XEvent ev;
1071 WScreen *scr = wScreenForRootWindow(event->xcrossing.root);
1073 if (XCheckTypedWindowEvent(dpy, event->xcrossing.window, LeaveNotify, &ev)) {
1074 /* already left the window... */
1075 saveTimestamp(&ev);
1076 if (ev.xcrossing.mode == event->xcrossing.mode && ev.xcrossing.detail == event->xcrossing.detail) {
1077 return;
1081 if (XFindContext(dpy, event->xcrossing.window, w_global.context.client_win, (XPointer *) & desc) != XCNOENT) {
1082 if (desc->handle_enternotify)
1083 (*desc->handle_enternotify) (desc, event);
1086 /* enter to window */
1087 wwin = wWindowFor(event->xcrossing.window);
1088 if (!wwin) {
1089 if (wPreferences.colormap_mode == WCM_POINTER) {
1090 wColormapInstallForWindow(scr, NULL);
1092 if (scr->autoRaiseTimer && event->xcrossing.root == event->xcrossing.window) {
1093 WMDeleteTimerHandler(scr->autoRaiseTimer);
1094 scr->autoRaiseTimer = NULL;
1096 } else {
1097 /* set auto raise timer even if in focus-follows-mouse mode
1098 * and the event is for the frame window, even if the window
1099 * has focus already. useful if you move the pointer from a focused
1100 * window to the root window and back pretty fast
1102 * set focus if in focus-follows-mouse mode and the event
1103 * is for the frame window and window doesn't have focus yet */
1104 if (wPreferences.focus_mode == WKF_SLOPPY
1105 && wwin->frame->core->window == event->xcrossing.window && !scr->flags.doing_alt_tab) {
1107 if (!wwin->flags.focused && !WFLAGP(wwin, no_focusable))
1108 wSetFocusTo(scr, wwin);
1110 if (scr->autoRaiseTimer)
1111 WMDeleteTimerHandler(scr->autoRaiseTimer);
1112 scr->autoRaiseTimer = NULL;
1114 if (wPreferences.raise_delay && !WFLAGP(wwin, no_focusable)) {
1115 scr->autoRaiseWindow = wwin->frame->core->window;
1116 scr->autoRaiseTimer
1117 = WMAddTimerHandler(wPreferences.raise_delay, (WMCallback *) raiseWindow, scr);
1120 /* Install colormap for window, if the colormap installation mode
1121 * is colormap_follows_mouse */
1122 if (wPreferences.colormap_mode == WCM_POINTER) {
1123 if (wwin->client_win == event->xcrossing.window)
1124 wColormapInstallForWindow(scr, wwin);
1125 else
1126 wColormapInstallForWindow(scr, NULL);
1130 if (event->xcrossing.window == event->xcrossing.root
1131 && event->xcrossing.detail == NotifyNormal
1132 && event->xcrossing.detail != NotifyInferior && wPreferences.focus_mode != WKF_CLICK) {
1134 wSetFocusTo(scr, scr->focused_window);
1136 #ifdef BALLOON_TEXT
1137 wBalloonEnteredObject(scr, desc);
1138 #endif
1141 static void handleLeaveNotify(XEvent * event)
1143 WObjDescriptor *desc = NULL;
1145 if (XFindContext(dpy, event->xcrossing.window, w_global.context.client_win, (XPointer *) & desc) != XCNOENT) {
1146 if (desc->handle_leavenotify)
1147 (*desc->handle_leavenotify) (desc, event);
1151 #ifdef USE_XSHAPE
1152 static void handleShapeNotify(XEvent * event)
1154 XShapeEvent *shev = (XShapeEvent *) event;
1155 WWindow *wwin;
1156 union {
1157 XEvent xevent;
1158 XShapeEvent xshape;
1159 } ev;
1161 while (XCheckTypedWindowEvent(dpy, shev->window, event->type, &ev.xevent)) {
1162 if (ev.xshape.kind == ShapeBounding) {
1163 if (ev.xshape.shaped == shev->shaped) {
1164 *shev = ev.xshape;
1165 } else {
1166 XPutBackEvent(dpy, &ev.xevent);
1167 break;
1172 wwin = wWindowFor(shev->window);
1173 if (!wwin || shev->kind != ShapeBounding)
1174 return;
1176 if (!shev->shaped && wwin->flags.shaped) {
1178 wwin->flags.shaped = 0;
1179 wWindowClearShape(wwin);
1181 } else if (shev->shaped) {
1183 wwin->flags.shaped = 1;
1184 wWindowSetShape(wwin);
1187 #endif /* USE_XSHAPE */
1189 #ifdef KEEP_XKB_LOCK_STATUS
1190 /* please help ]d if you know what to do */
1191 static void handleXkbIndicatorStateNotify(XEvent *event)
1193 WWindow *wwin;
1194 WScreen *scr;
1195 XkbStateRec staterec;
1196 int i;
1198 for (i = 0; i < w_global.screen_count; i++) {
1199 scr = wScreenWithNumber(i);
1200 wwin = scr->focused_window;
1201 if (wwin && wwin->flags.focused) {
1202 XkbGetState(dpy, XkbUseCoreKbd, &staterec);
1203 if (wwin->frame->languagemode != staterec.group) {
1204 wwin->frame->last_languagemode = wwin->frame->languagemode;
1205 wwin->frame->languagemode = staterec.group;
1207 #ifdef XKB_BUTTON_HINT
1208 if (wwin->frame->titlebar) {
1209 wFrameWindowPaint(wwin->frame);
1211 #endif
1215 #endif /*KEEP_XKB_LOCK_STATUS */
1217 static void handleColormapNotify(XEvent * event)
1219 WWindow *wwin;
1220 WScreen *scr;
1221 Bool reinstall = False;
1223 wwin = wWindowFor(event->xcolormap.window);
1224 if (!wwin)
1225 return;
1227 scr = wwin->screen_ptr;
1229 do {
1230 if (wwin) {
1231 if (event->xcolormap.new) {
1232 XWindowAttributes attr;
1234 XGetWindowAttributes(dpy, wwin->client_win, &attr);
1236 if (wwin == scr->cmap_window && wwin->cmap_window_no == 0)
1237 scr->current_colormap = attr.colormap;
1239 reinstall = True;
1240 } else if (event->xcolormap.state == ColormapUninstalled &&
1241 scr->current_colormap == event->xcolormap.colormap) {
1243 /* some bastard app (like XV) removed our colormap */
1245 * can't enforce or things like xscreensaver wont work
1246 * reinstall = True;
1248 } else if (event->xcolormap.state == ColormapInstalled &&
1249 scr->current_colormap == event->xcolormap.colormap) {
1251 /* someone has put our colormap back */
1252 reinstall = False;
1255 } while (XCheckTypedEvent(dpy, ColormapNotify, event)
1256 && ((wwin = wWindowFor(event->xcolormap.window)) || 1));
1258 if (reinstall && scr->current_colormap != None) {
1259 if (!scr->flags.colormap_stuff_blocked)
1260 XInstallColormap(dpy, scr->current_colormap);
1264 static void handleFocusIn(XEvent * event)
1266 WWindow *wwin;
1269 * For applications that like stealing the focus.
1271 while (XCheckTypedEvent(dpy, FocusIn, event)) ;
1272 saveTimestamp(event);
1273 if (event->xfocus.mode == NotifyUngrab
1274 || event->xfocus.mode == NotifyGrab || event->xfocus.detail > NotifyNonlinearVirtual) {
1275 return;
1278 wwin = wWindowFor(event->xfocus.window);
1279 if (wwin && !wwin->flags.focused) {
1280 if (wwin->flags.mapped)
1281 wSetFocusTo(wwin->screen_ptr, wwin);
1282 else
1283 wSetFocusTo(wwin->screen_ptr, NULL);
1284 } else if (!wwin) {
1285 WScreen *scr = wScreenForWindow(event->xfocus.window);
1286 if (scr)
1287 wSetFocusTo(scr, NULL);
1291 static WWindow *windowUnderPointer(WScreen * scr)
1293 unsigned int mask;
1294 int foo;
1295 Window bar, win;
1297 if (XQueryPointer(dpy, scr->root_win, &bar, &win, &foo, &foo, &foo, &foo, &mask))
1298 return wWindowFor(win);
1299 return NULL;
1302 static int CheckFullScreenWindowFocused(WScreen * scr)
1304 if (scr->focused_window && scr->focused_window->flags.fullscreen)
1305 return 1;
1306 else
1307 return 0;
1310 static void handleKeyPress(XEvent * event)
1312 WScreen *scr = wScreenForRootWindow(event->xkey.root);
1313 WWindow *wwin = scr->focused_window;
1314 short i, widx;
1315 int modifiers;
1316 int command = -1;
1317 #ifdef KEEP_XKB_LOCK_STATUS
1318 XkbStateRec staterec;
1319 #endif /*KEEP_XKB_LOCK_STATUS */
1321 /* ignore CapsLock */
1322 modifiers = event->xkey.state & w_global.shortcut.modifiers_mask;
1324 for (i = 0; i < WKBD_LAST; i++) {
1325 if (wKeyBindings[i].keycode == 0)
1326 continue;
1328 if (wKeyBindings[i].keycode == event->xkey.keycode && ( /*wKeyBindings[i].modifier==0
1329 || */ wKeyBindings[i].modifier ==
1330 modifiers)) {
1331 command = i;
1332 break;
1336 if (command < 0) {
1338 if (!wRootMenuPerformShortcut(event)) {
1339 static int dontLoop = 0;
1341 if (dontLoop > 10) {
1342 wwarning("problem with key event processing code");
1343 return;
1345 dontLoop++;
1346 /* if the focused window is an internal window, try redispatching
1347 * the event to the managed window, as it can be a WINGs window */
1348 if (wwin && wwin->flags.internal_window && wwin->client_leader != None) {
1349 /* client_leader contains the WINGs toplevel */
1350 event->xany.window = wwin->client_leader;
1351 WMHandleEvent(event);
1353 dontLoop--;
1355 return;
1357 #define ISMAPPED(w) ((w) && !(w)->flags.miniaturized && ((w)->flags.mapped || (w)->flags.shaded))
1358 #define ISFOCUSED(w) ((w) && (w)->flags.focused)
1360 switch (command) {
1362 case WKBD_ROOTMENU:
1363 /*OpenRootMenu(scr, event->xkey.x_root, event->xkey.y_root, True); */
1364 if (!CheckFullScreenWindowFocused(scr)) {
1365 WMRect rect = wGetRectForHead(scr, wGetHeadForPointerLocation(scr));
1366 OpenRootMenu(scr, rect.pos.x + rect.size.width / 2, rect.pos.y + rect.size.height / 2,
1367 True);
1369 break;
1370 case WKBD_WINDOWLIST:
1371 if (!CheckFullScreenWindowFocused(scr)) {
1372 WMRect rect = wGetRectForHead(scr, wGetHeadForPointerLocation(scr));
1373 OpenSwitchMenu(scr, rect.pos.x + rect.size.width / 2, rect.pos.y + rect.size.height / 2,
1374 True);
1376 break;
1378 case WKBD_WINDOWMENU:
1379 if (ISMAPPED(wwin) && ISFOCUSED(wwin))
1380 OpenWindowMenu(wwin, wwin->frame_x, wwin->frame_y + wwin->frame->top_width, True);
1381 break;
1382 case WKBD_MINIMIZEALL:
1383 CloseWindowMenu(scr);
1384 wHideAll(scr);
1385 break;
1386 case WKBD_MINIATURIZE:
1387 if (ISMAPPED(wwin) && ISFOCUSED(wwin)
1388 && !WFLAGP(wwin, no_miniaturizable)) {
1389 CloseWindowMenu(scr);
1391 if (wwin->protocols.MINIATURIZE_WINDOW)
1392 wClientSendProtocol(wwin, w_global.atom.gnustep.wm_miniaturize_window, event->xbutton.time);
1393 else {
1394 wIconifyWindow(wwin);
1397 break;
1398 case WKBD_HIDE:
1399 if (ISMAPPED(wwin) && ISFOCUSED(wwin)) {
1400 WApplication *wapp = wApplicationOf(wwin->main_window);
1401 CloseWindowMenu(scr);
1403 if (wapp && !WFLAGP(wapp->main_window_desc, no_appicon)) {
1404 wHideApplication(wapp);
1407 break;
1408 case WKBD_HIDE_OTHERS:
1409 if (ISMAPPED(wwin) && ISFOCUSED(wwin)) {
1410 CloseWindowMenu(scr);
1412 wHideOtherApplications(wwin);
1414 break;
1415 case WKBD_MAXIMIZE:
1416 if (ISMAPPED(wwin) && ISFOCUSED(wwin) && IS_RESIZABLE(wwin)) {
1417 CloseWindowMenu(scr);
1419 handleMaximize(wwin, MAX_VERTICAL | MAX_HORIZONTAL | MAX_KEYBOARD);
1421 break;
1422 case WKBD_VMAXIMIZE:
1423 if (ISMAPPED(wwin) && ISFOCUSED(wwin) && IS_RESIZABLE(wwin)) {
1424 CloseWindowMenu(scr);
1426 handleMaximize(wwin, MAX_VERTICAL | MAX_KEYBOARD);
1428 break;
1429 case WKBD_HMAXIMIZE:
1430 if (ISMAPPED(wwin) && ISFOCUSED(wwin) && IS_RESIZABLE(wwin)) {
1431 CloseWindowMenu(scr);
1433 handleMaximize(wwin, MAX_HORIZONTAL | MAX_KEYBOARD);
1435 break;
1436 case WKBD_LHMAXIMIZE:
1437 if (ISMAPPED(wwin) && ISFOCUSED(wwin) && IS_RESIZABLE(wwin)) {
1438 CloseWindowMenu(scr);
1440 handleMaximize(wwin, MAX_VERTICAL | MAX_LEFTHALF | MAX_KEYBOARD);
1442 break;
1443 case WKBD_RHMAXIMIZE:
1444 if (ISMAPPED(wwin) && ISFOCUSED(wwin) && IS_RESIZABLE(wwin)) {
1445 CloseWindowMenu(scr);
1447 handleMaximize(wwin, MAX_VERTICAL | MAX_RIGHTHALF | MAX_KEYBOARD);
1449 break;
1450 case WKBD_THMAXIMIZE:
1451 if (ISMAPPED(wwin) && ISFOCUSED(wwin) && IS_RESIZABLE(wwin)) {
1452 CloseWindowMenu(scr);
1454 handleMaximize(wwin, MAX_HORIZONTAL | MAX_TOPHALF | MAX_KEYBOARD);
1456 break;
1457 case WKBD_BHMAXIMIZE:
1458 if (ISMAPPED(wwin) && ISFOCUSED(wwin) && IS_RESIZABLE(wwin)) {
1459 CloseWindowMenu(scr);
1461 handleMaximize(wwin, MAX_HORIZONTAL | MAX_BOTTOMHALF | MAX_KEYBOARD);
1463 break;
1464 case WKBD_LTCMAXIMIZE:
1465 if (ISMAPPED(wwin) && ISFOCUSED(wwin) && IS_RESIZABLE(wwin)) {
1466 CloseWindowMenu(scr);
1468 handleMaximize(wwin, MAX_LEFTHALF | MAX_TOPHALF | MAX_KEYBOARD);
1470 break;
1471 case WKBD_RTCMAXIMIZE:
1472 if (ISMAPPED(wwin) && ISFOCUSED(wwin) && IS_RESIZABLE(wwin)) {
1473 CloseWindowMenu(scr);
1475 handleMaximize(wwin, MAX_RIGHTHALF | MAX_TOPHALF | MAX_KEYBOARD);
1477 break;
1478 case WKBD_LBCMAXIMIZE:
1479 if (ISMAPPED(wwin) && ISFOCUSED(wwin) && IS_RESIZABLE(wwin)) {
1480 CloseWindowMenu(scr);
1482 handleMaximize(wwin, MAX_LEFTHALF | MAX_BOTTOMHALF | MAX_KEYBOARD);
1484 break;
1485 case WKBD_RBCMAXIMIZE:
1486 if (ISMAPPED(wwin) && ISFOCUSED(wwin) && IS_RESIZABLE(wwin)) {
1487 CloseWindowMenu(scr);
1489 handleMaximize(wwin, MAX_RIGHTHALF | MAX_BOTTOMHALF | MAX_KEYBOARD);
1491 break;
1492 case WKBD_MAXIMUS:
1493 if (ISMAPPED(wwin) && ISFOCUSED(wwin) && IS_RESIZABLE(wwin)) {
1494 CloseWindowMenu(scr);
1496 handleMaximize(wwin, MAX_MAXIMUS | MAX_KEYBOARD);
1498 break;
1499 case WKBD_RAISE:
1500 if (ISMAPPED(wwin) && ISFOCUSED(wwin)) {
1501 CloseWindowMenu(scr);
1503 wRaiseFrame(wwin->frame->core);
1505 break;
1506 case WKBD_LOWER:
1507 if (ISMAPPED(wwin) && ISFOCUSED(wwin)) {
1508 CloseWindowMenu(scr);
1510 wLowerFrame(wwin->frame->core);
1512 break;
1513 case WKBD_RAISELOWER:
1514 /* raise or lower the window under the pointer, not the
1515 * focused one
1517 wwin = windowUnderPointer(scr);
1518 if (wwin)
1519 wRaiseLowerFrame(wwin->frame->core);
1520 break;
1521 case WKBD_SHADE:
1522 if (ISMAPPED(wwin) && ISFOCUSED(wwin) && !WFLAGP(wwin, no_shadeable)) {
1523 if (wwin->flags.shaded)
1524 wUnshadeWindow(wwin);
1525 else
1526 wShadeWindow(wwin);
1528 break;
1529 case WKBD_MOVERESIZE:
1530 if (ISMAPPED(wwin) && ISFOCUSED(wwin) && (IS_RESIZABLE(wwin) || IS_MOVABLE(wwin))) {
1531 CloseWindowMenu(scr);
1533 wKeyboardMoveResizeWindow(wwin);
1535 break;
1536 case WKBD_CLOSE:
1537 if (ISMAPPED(wwin) && ISFOCUSED(wwin) && !WFLAGP(wwin, no_closable)) {
1538 CloseWindowMenu(scr);
1539 if (wwin->protocols.DELETE_WINDOW)
1540 wClientSendProtocol(wwin, w_global.atom.wm.delete_window, event->xkey.time);
1542 break;
1543 case WKBD_SELECT:
1544 if (ISMAPPED(wwin) && ISFOCUSED(wwin)) {
1545 wSelectWindow(wwin, !wwin->flags.selected);
1547 break;
1548 case WKBD_FOCUSNEXT:
1549 StartWindozeCycle(wwin, event, True, False);
1550 break;
1552 case WKBD_FOCUSPREV:
1553 StartWindozeCycle(wwin, event, False, False);
1554 break;
1556 case WKBD_GROUPNEXT:
1557 StartWindozeCycle(wwin, event, True, True);
1558 break;
1560 case WKBD_GROUPPREV:
1561 StartWindozeCycle(wwin, event, False, True);
1562 break;
1564 case WKBD_WORKSPACE1 ... WKBD_WORKSPACE10:
1565 widx = command - WKBD_WORKSPACE1;
1566 i = (w_global.workspace.current / 10) * 10 + widx;
1567 if (wPreferences.ws_advance || i < w_global.workspace.count)
1568 wWorkspaceChange(scr, i);
1569 break;
1571 case WKBD_NEXTWORKSPACE:
1572 wWorkspaceRelativeChange(scr, 1);
1573 break;
1574 case WKBD_PREVWORKSPACE:
1575 wWorkspaceRelativeChange(scr, -1);
1576 break;
1577 case WKBD_LASTWORKSPACE:
1578 wWorkspaceChange(scr, w_global.workspace.last_used);
1579 break;
1581 case WKBD_MOVE_WORKSPACE1 ... WKBD_MOVE_WORKSPACE10:
1582 widx = command - WKBD_MOVE_WORKSPACE1;
1583 i = (w_global.workspace.current / 10) * 10 + widx;
1584 if (wwin && (wPreferences.ws_advance || i < w_global.workspace.count))
1585 wWindowChangeWorkspace(wwin, i);
1586 break;
1588 case WKBD_MOVE_NEXTWORKSPACE:
1589 if (wwin)
1590 wWindowChangeWorkspaceRelative(wwin, 1);
1591 break;
1592 case WKBD_MOVE_PREVWORKSPACE:
1593 if (wwin)
1594 wWindowChangeWorkspaceRelative(wwin, -1);
1595 break;
1596 case WKBD_MOVE_LASTWORKSPACE:
1597 if (wwin)
1598 wWindowChangeWorkspace(wwin, w_global.workspace.last_used);
1599 break;
1601 case WKBD_MOVE_NEXTWSLAYER:
1602 case WKBD_MOVE_PREVWSLAYER:
1604 if (wwin) {
1605 int row, column;
1607 row = w_global.workspace.current / 10;
1608 column = w_global.workspace.current % 10;
1610 if (command == WKBD_MOVE_NEXTWSLAYER) {
1611 if ((row + 1) * 10 < w_global.workspace.count)
1612 wWindowChangeWorkspace(wwin, column + (row + 1) * 10);
1613 } else {
1614 if (row > 0)
1615 wWindowChangeWorkspace(wwin, column + (row - 1) * 10);
1619 break;
1621 case WKBD_WINDOW1:
1622 case WKBD_WINDOW2:
1623 case WKBD_WINDOW3:
1624 case WKBD_WINDOW4:
1625 case WKBD_WINDOW5:
1626 case WKBD_WINDOW6:
1627 case WKBD_WINDOW7:
1628 case WKBD_WINDOW8:
1629 case WKBD_WINDOW9:
1630 case WKBD_WINDOW10:
1632 widx = command - WKBD_WINDOW1;
1634 if (w_global.shortcut.windows[widx]) {
1635 WMArray *list = w_global.shortcut.windows[widx];
1636 int cw;
1637 int count = WMGetArrayItemCount(list);
1638 WWindow *twin;
1639 WMArrayIterator iter;
1640 WWindow *wwin;
1642 wUnselectWindows(scr);
1643 cw = w_global.workspace.current;
1645 WM_ETARETI_ARRAY(list, wwin, iter) {
1646 if (count > 1)
1647 wWindowChangeWorkspace(wwin, cw);
1649 wMakeWindowVisible(wwin);
1651 if (count > 1)
1652 wSelectWindow(wwin, True);
1655 /* rotate the order of windows, to create a cycling effect */
1656 twin = WMGetFromArray(list, 0);
1657 WMDeleteFromArray(list, 0);
1658 WMAddToArray(list, twin);
1660 } else if (wwin && ISMAPPED(wwin) && ISFOCUSED(wwin)) {
1661 if (w_global.shortcut.windows[widx]) {
1662 WMFreeArray(w_global.shortcut.windows[widx]);
1663 w_global.shortcut.windows[widx] = NULL;
1666 if (wwin->flags.selected && scr->selected_windows) {
1667 w_global.shortcut.windows[widx] = WMDuplicateArray(scr->selected_windows);
1668 } else {
1669 w_global.shortcut.windows[widx] = WMCreateArray(4);
1670 WMAddToArray(w_global.shortcut.windows[widx], wwin);
1673 wSelectWindow(wwin, !wwin->flags.selected);
1674 XFlush(dpy);
1675 wusleep(3000);
1676 wSelectWindow(wwin, !wwin->flags.selected);
1677 XFlush(dpy);
1679 } else if (scr->selected_windows && WMGetArrayItemCount(scr->selected_windows)) {
1680 if (wwin->flags.selected && scr->selected_windows) {
1681 if (w_global.shortcut.windows[widx])
1682 WMFreeArray(w_global.shortcut.windows[widx]);
1684 w_global.shortcut.windows[widx] = WMDuplicateArray(scr->selected_windows);
1688 break;
1690 case WKBD_RELAUNCH:
1691 if (ISMAPPED(wwin) && ISFOCUSED(wwin))
1692 (void) RelaunchWindow(wwin);
1694 break;
1696 case WKBD_SWITCH_SCREEN:
1697 if (w_global.screen_count > 1) {
1698 WScreen *scr2;
1699 int i;
1701 /* find index of this screen */
1702 for (i = 0; i < w_global.screen_count; i++) {
1703 if (wScreenWithNumber(i) == scr)
1704 break;
1706 i++;
1707 if (i >= w_global.screen_count) {
1708 i = 0;
1710 scr2 = wScreenWithNumber(i);
1712 if (scr2) {
1713 XWarpPointer(dpy, scr->root_win, scr2->root_win, 0, 0, 0, 0,
1714 scr2->scr_width / 2, scr2->scr_height / 2);
1717 break;
1719 case WKBD_RUN:
1721 char *cmdline;
1723 cmdline = ExpandOptions(scr, _("exec %a(Run,Type command to run:)"));
1725 if (cmdline) {
1726 XGrabPointer(dpy, scr->root_win, True, 0,
1727 GrabModeAsync, GrabModeAsync, None, wPreferences.cursor[WCUR_WAIT], CurrentTime);
1728 XSync(dpy, False);
1730 ExecuteShellCommand(scr, cmdline);
1731 wfree(cmdline);
1733 XUngrabPointer(dpy, CurrentTime);
1734 XSync(dpy, False);
1736 break;
1739 case WKBD_NEXTWSLAYER:
1740 case WKBD_PREVWSLAYER:
1742 int row, column;
1744 row = w_global.workspace.current / 10;
1745 column = w_global.workspace.current % 10;
1747 if (command == WKBD_NEXTWSLAYER) {
1748 if ((row + 1) * 10 < w_global.workspace.count)
1749 wWorkspaceChange(scr, column + (row + 1) * 10);
1750 } else {
1751 if (row > 0)
1752 wWorkspaceChange(scr, column + (row - 1) * 10);
1755 break;
1756 case WKBD_CLIPRAISELOWER:
1757 if (!wPreferences.flags.noclip)
1758 wDockRaiseLower(w_global.workspace.array[w_global.workspace.current]->clip);
1759 break;
1760 case WKBD_DOCKRAISELOWER:
1761 if (!wPreferences.flags.nodock)
1762 wDockRaiseLower(scr->dock);
1763 break;
1764 #ifdef KEEP_XKB_LOCK_STATUS
1765 case WKBD_TOGGLE:
1766 if (wPreferences.modelock) {
1767 /*toggle */
1768 wwin = scr->focused_window;
1770 if (wwin && wwin->flags.mapped
1771 && wwin->frame->workspace == w_global.workspace.current
1772 && !wwin->flags.miniaturized && !wwin->flags.hidden) {
1773 XkbGetState(dpy, XkbUseCoreKbd, &staterec);
1775 wwin->frame->languagemode = wwin->frame->last_languagemode;
1776 wwin->frame->last_languagemode = staterec.group;
1777 XkbLockGroup(dpy, XkbUseCoreKbd, wwin->frame->languagemode);
1781 break;
1782 #endif /* KEEP_XKB_LOCK_STATUS */
1786 static void handleMotionNotify(XEvent * event)
1788 WScreen *scr = wScreenForRootWindow(event->xmotion.root);
1790 if (wPreferences.scrollable_menus) {
1791 WMPoint p = wmkpoint(event->xmotion.x_root, event->xmotion.y_root);
1792 WMRect rect = wGetRectForHead(scr, wGetHeadForPoint(scr, p));
1794 if (scr->flags.jump_back_pending ||
1795 p.x <= (rect.pos.x + 1) ||
1796 p.x >= (rect.pos.x + rect.size.width - 2) ||
1797 p.y <= (rect.pos.y + 1) || p.y >= (rect.pos.y + rect.size.height - 2)) {
1798 WMenu *menu;
1800 menu = wMenuUnderPointer(scr);
1801 if (menu != NULL)
1802 wMenuScroll(menu);
1807 static void handleVisibilityNotify(XEvent * event)
1809 WWindow *wwin;
1811 wwin = wWindowFor(event->xvisibility.window);
1812 if (!wwin)
1813 return;
1814 wwin->flags.obscured = (event->xvisibility.state == VisibilityFullyObscured);