Initial revision
[wmaker-crm.git] / src / event.c
blob2d7a0d4ce379ffb22d6e4542140ef29cf691f029
1 /* event.c- event loop and handling
2 *
3 * WindowMaker window manager
4 *
5 * Copyright (c) 1997, 1998 Alfredo K. Kojima
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
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
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
20 * USA.
24 #include "wconfig.h"
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <unistd.h>
30 #include <string.h>
32 #include <X11/Xlib.h>
33 #include <X11/Xutil.h>
34 #ifdef SHAPE
35 #include <X11/extensions/shape.h>
36 #endif
38 #include "WindowMaker.h"
39 #include "window.h"
40 #include "actions.h"
41 #include "client.h"
42 #include "funcs.h"
43 #include "keybind.h"
44 #include "application.h"
45 #include "stacking.h"
46 #include "defaults.h"
47 #include "workspace.h"
48 #include "dock.h"
49 #include "framewin.h"
50 #include "properties.h"
51 #include "balloon.h"
54 /******** Global Variables **********/
55 extern XContext wWinContext;
57 extern Cursor wCursor[WCUR_LAST];
59 extern WShortKey wKeyBindings[WKBD_LAST];
60 extern int wScreenCount;
61 extern Time LastTimestamp;
62 extern Time LastFocusChange;
64 extern WPreferences wPreferences;
66 #define MOD_MASK wPreferences.modifier_mask
68 extern Atom _XA_WM_CHANGE_STATE;
69 extern Atom _XA_WM_DELETE_WINDOW;
70 extern Atom _XA_WINDOWMAKER_WM_MINIATURIZE_WINDOW;
71 extern Atom _XA_WINDOWMAKER_WM_FUNCTION;
73 #ifdef OFFIX_DND
74 extern Atom _XA_DND_PROTOCOL;
75 #endif
77 #ifdef SHAPE
78 extern int ShapeEventBase;
79 #endif
81 /* special flags */
82 extern char WRestartASAP;
83 extern char WExitASAP;
84 extern char WDelayedActionSet;
87 /************ Local stuff ***********/
90 static void saveTimestamp(XEvent *event);
91 static void handleColormapNotify();
92 static void handleMapNotify(), handleUnmapNotify();
93 static void handleButtonPress(), handleExpose();
94 static void handleDestroyNotify();
95 static void handleConfigureRequest();
96 static void handleMapRequest();
97 static void handlePropertyNotify();
98 static void handleEnterNotify();
99 static void handleLeaveNotify();
100 static void handleExtensions();
101 static void handleClientMessage();
102 static void handleKeyPress();
103 static void handleFocusIn();
104 static void handleMotionNotify();
105 #ifdef SHAPE
106 static void handleShapeNotify();
107 #endif
109 /* called from the signal handler */
110 void NotifyDeadProcess(pid_t pid, unsigned char status);
112 /* real dead process handler */
113 static void handleDeadProcess(void *foo);
116 typedef struct DeadProcesses {
117 pid_t pid;
118 unsigned char exit_status;
119 } DeadProcesses;
121 /* stack of dead processes */
122 static DeadProcesses deadProcesses[MAX_DEAD_PROCESSES];
123 static int deadProcessPtr=0;
126 typedef struct DeathHandler {
127 WDeathHandler *callback;
128 pid_t pid;
129 struct DeathHandler *next;
130 void *client_data;
131 } DeathHandler;
133 static DeathHandler *deathHandler=NULL;
135 static WWindowState *windowState=NULL;
140 WMagicNumber
141 wAddWindowSavedState(char *instance, char *class, char *command,
142 pid_t pid, WSavedState *state)
144 WWindowState *wstate;
146 wstate = malloc(sizeof(WWindowState));
147 if (!wstate)
148 return 0;
150 memset(wstate, 0, sizeof(WWindowState));
151 wstate->pid = pid;
152 if (instance)
153 wstate->instance = wstrdup(instance);
154 if (class)
155 wstate->class = wstrdup(class);
156 if (command)
157 wstate->command = wstrdup(command);
158 wstate->state = state;
160 wstate->next = windowState;
161 windowState = wstate;
163 #ifdef DEBUG
164 printf("Added WindowState with ID %p, for %s.%s : \"%s\"\n", wstate, instance,
165 class, command);
166 #endif
168 return wstate;
172 #define SAME(x, y) (((x) && (y) && !strcmp((x), (y))) || (!(x) && !(y)))
175 WMagicNumber
176 wGetWindowSavedState(Window win)
178 char *instance, *class, *command=NULL;
179 WWindowState *wstate = windowState;
180 char **argv;
181 int argc;
183 if (!wstate)
184 return NULL;
186 if (XGetCommand(dpy, win, &argv, &argc) && argc>0) {
187 command = FlattenStringList(argv, argc);
188 XFreeStringList(argv);
190 if (!command)
191 return NULL;
193 if (PropGetWMClass(win, &class, &instance)) {
194 while (wstate) {
195 if (SAME(instance, wstate->instance) &&
196 SAME(class, wstate->class) &&
197 SAME(command, wstate->command)) {
198 break;
200 wstate = wstate->next;
202 } else {
203 wstate = NULL;
206 #ifdef DEBUG
207 printf("Read WindowState with ID %p, for %s.%s : \"%s\"\n", wstate, instance,
208 class, command);
209 #endif
211 if (command) free(command);
212 if (instance) XFree(instance);
213 if (class) XFree(class);
215 return wstate;
219 void
220 wDeleteWindowSavedState(WMagicNumber id)
222 WWindowState *tmp, *wstate=(WWindowState*)id;
224 if (!wstate || !windowState)
225 return;
227 tmp = windowState;
228 if (tmp==wstate) {
229 windowState = wstate->next;
230 #ifdef DEBUG
231 printf("Deleted WindowState with ID %p, for %s.%s : \"%s\"\n",
232 wstate, wstate->instance, wstate->class, wstate->command);
233 #endif
234 if (wstate->instance) free(wstate->instance);
235 if (wstate->class) free(wstate->class);
236 if (wstate->command) free(wstate->command);
237 free(wstate->state);
238 free(wstate);
239 } else {
240 while (tmp->next) {
241 if (tmp->next==wstate) {
242 tmp->next=wstate->next;
243 #ifdef DEBUG
244 printf("Deleted WindowState with ID %p, for %s.%s : \"%s\"\n",
245 wstate, wstate->instance, wstate->class, wstate->command);
246 #endif
247 if (wstate->instance) free(wstate->instance);
248 if (wstate->class) free(wstate->class);
249 if (wstate->command) free(wstate->command);
250 free(wstate->state);
251 free(wstate);
252 break;
254 tmp = tmp->next;
260 WMagicNumber
261 wAddDeathHandler(pid_t pid, WDeathHandler *callback, void *cdata)
263 DeathHandler *handler;
265 handler = malloc(sizeof(DeathHandler));
266 if (!handler)
267 return 0;
269 handler->pid = pid;
270 handler->callback = callback;
271 handler->client_data = cdata;
273 handler->next = deathHandler;
275 deathHandler = handler;
277 return handler;
282 void
283 wDeleteDeathHandler(WMagicNumber id)
285 DeathHandler *tmp, *handler=(DeathHandler*)id;
287 if (!handler || !deathHandler)
288 return;
290 tmp = deathHandler;
291 if (tmp==handler) {
292 deathHandler = handler->next;
293 free(handler);
294 } else {
295 while (tmp->next) {
296 if (tmp->next==handler) {
297 tmp->next=handler->next;
298 free(handler);
299 break;
301 tmp = tmp->next;
307 void
308 DispatchEvent(XEvent *event)
310 int i;
312 if (deathHandler)
313 handleDeadProcess(NULL);
315 if (WExitASAP) {
316 WExitASAP = 0;
319 * WMHandleEvent() can't be called from anything
320 * executed inside here, or we can get in a infinite
321 * recursive loop.
323 for (i=0; i<wScreenCount; i++) {
324 WScreen *scr;
325 scr = wScreenWithNumber(i);
326 if (scr) {
327 wScreenSaveState(scr);
328 RestoreDesktop(scr);
331 ExecExitScript();
332 /* received SIGTERM */
333 exit(0);
334 } else if (WRestartASAP) {
335 WRestartASAP = 0;
337 for (i=0; i<wScreenCount; i++) {
338 WScreen *scr;
339 scr = wScreenWithNumber(i);
340 if (scr) {
341 wScreenSaveState(scr);
342 RestoreDesktop(scr);
345 /* received SIGHUP */
346 Restart(NULL);
349 /* for the case that all that is wanted to be dispatched is
350 * the stuff above */
351 if (!event)
352 return;
354 saveTimestamp(event);
355 switch (event->type) {
356 case MapRequest:
357 handleMapRequest(event);
358 break;
360 case KeyPress:
361 handleKeyPress(event);
362 break;
364 case MotionNotify:
365 handleMotionNotify(event);
366 break;
368 case ConfigureRequest:
369 handleConfigureRequest(event);
370 break;
372 case DestroyNotify:
373 handleDestroyNotify(event->xdestroywindow.window);
374 break;
376 case MapNotify:
377 handleMapNotify(event->xmap.window);
378 break;
380 case UnmapNotify:
381 handleUnmapNotify(event);
382 break;
384 case ButtonPress:
385 handleButtonPress(event);
386 break;
388 case Expose:
389 handleExpose(event);
390 break;
392 case PropertyNotify:
393 handlePropertyNotify(event);
394 break;
396 case EnterNotify:
397 handleEnterNotify(event);
398 break;
400 case LeaveNotify:
401 handleLeaveNotify(event);
402 break;
404 case ClientMessage:
405 handleClientMessage(event);
406 break;
408 case ColormapNotify:
409 handleColormapNotify(event);
410 break;
412 case MappingNotify:
413 if (event->xmapping.request == MappingKeyboard
414 || event->xmapping.request == MappingModifier)
415 XRefreshKeyboardMapping(&event->xmapping);
416 break;
418 case FocusIn:
419 handleFocusIn(event);
420 break;
422 default:
423 handleExtensions(event);
429 *----------------------------------------------------------------------
430 * EventLoop-
431 * Processes X and internal events indefinitely.
433 * Returns:
434 * Never returns
436 * Side effects:
437 * The LastTimestamp global variable is updated.
438 *----------------------------------------------------------------------
440 void
441 EventLoop()
443 XEvent event;
445 for(;;) {
446 WMNextEvent(dpy, &event);
447 WMHandleEvent(&event);
453 Bool
454 IsDoubleClick(WScreen *scr, XEvent *event)
456 if ((scr->last_click_time>0) &&
457 (event->xbutton.time-scr->last_click_time<=wPreferences.dblclick_time)
458 && (event->xbutton.button == scr->last_click_button)
459 && (event->xbutton.window == scr->last_click_window)) {
461 scr->flags.next_click_is_not_double = 1;
462 scr->last_click_time = 0;
463 scr->last_click_window = None;
465 return True;
467 return False;
471 void
472 NotifyDeadProcess(pid_t pid, unsigned char status)
474 if (deadProcessPtr>=MAX_DEAD_PROCESSES-1) {
475 wwarning(_("stack overflow: too many dead processes"));
476 return;
478 /* stack the process to be handled later,
479 * as this is called from the signal handler */
480 deadProcesses[deadProcessPtr].pid = pid;
481 deadProcesses[deadProcessPtr].exit_status = status;
482 deadProcessPtr++;
486 static void
487 handleDeadProcess(void *foo)
489 DeathHandler *tmp;
490 WWindowState *wins;
492 int tmpPtr = deadProcessPtr;
494 if (windowState) {
495 while (tmpPtr>0) {
496 tmpPtr--;
498 wins = windowState;
499 while (wins) {
500 WWindowState *t;
502 t = wins->next;
504 if (wins->pid == deadProcesses[tmpPtr].pid) {
505 wDeleteWindowSavedState(wins);
507 wins = t;
512 if (!deathHandler) {
513 deadProcessPtr=0;
514 return;
517 /* get the pids on the queue and call handlers */
518 while (deadProcessPtr>0) {
519 deadProcessPtr--;
521 tmp = deathHandler;
522 while (tmp) {
523 DeathHandler *t;
525 t = tmp->next;
527 if (tmp->pid == deadProcesses[deadProcessPtr].pid) {
528 (*tmp->callback)(tmp->pid,
529 deadProcesses[deadProcessPtr].exit_status,
530 tmp->client_data);
531 wDeleteDeathHandler(tmp);
533 tmp = t;
539 static void
540 saveTimestamp(XEvent *event)
542 LastTimestamp = CurrentTime;
544 switch (event->type) {
545 case ButtonRelease:
546 case ButtonPress:
547 LastTimestamp = event->xbutton.time;
548 break;
549 case KeyPress:
550 case KeyRelease:
551 LastTimestamp = event->xkey.time;
552 break;
553 case MotionNotify:
554 LastTimestamp = event->xmotion.time;
555 break;
556 case PropertyNotify:
557 LastTimestamp = event->xproperty.time;
558 break;
559 case EnterNotify:
560 case LeaveNotify:
561 LastTimestamp = event->xcrossing.time;
562 break;
563 case SelectionClear:
564 LastTimestamp = event->xselectionclear.time;
565 break;
566 case SelectionRequest:
567 LastTimestamp = event->xselectionrequest.time;
568 break;
569 case SelectionNotify:
570 LastTimestamp = event->xselection.time;
571 break;
576 static void
577 handleExtensions(XEvent *event)
579 #ifdef SHAPE
580 if (event->type == (ShapeEventBase+ShapeNotify)) {
581 handleShapeNotify(event);
583 #endif
586 static void
587 handleMapRequest(XEvent *ev)
589 WWindow *wwin;
590 WScreen *scr = NULL;
591 Window window = ev->xmaprequest.window;
593 #ifdef DEBUG
594 printf("got map request for %x\n", (unsigned)window);
595 #endif
597 if ((wwin=wWindowFor(window))) {
598 /* deiconify window */
599 if (wwin->flags.shaded)
600 wUnshadeWindow(wwin);
601 if (wwin->flags.miniaturized) {
602 wDeiconifyWindow(wwin);
603 } else if (wwin->flags.hidden) {
604 WApplication *wapp = wApplicationOf(wwin->main_window);
605 /* go to the last workspace that the user worked on the app */
606 #ifndef REDUCE_APPICONS
607 /* This severely breaks REDUCE_APPICONS. last_workspace is a neat
608 * concept but it needs to be reworked to handle REDUCE_APPICONS -cls
610 if (wapp) {
611 wWorkspaceChange(wwin->screen_ptr, wapp->last_workspace);
613 #endif
614 wUnhideApplication(wapp, False, False);
616 return;
619 scr = wScreenForRootWindow(ev->xmaprequest.parent);
621 wwin = wManageWindow(scr, window);
624 * This is to let the Dock know that the application it launched
625 * has already been mapped (eg: it has finished launching).
626 * It is not necessary for normally docked apps, but is needed for
627 * apps that were forcedly docked (like with dockit).
629 if (scr->last_dock) {
630 if (wwin && wwin->main_window!=None && wwin->main_window!=window)
631 wDockTrackWindowLaunch(scr->last_dock, wwin->main_window);
632 else
633 wDockTrackWindowLaunch(scr->last_dock, window);
636 if (wwin) {
637 int state;
639 if (wwin->wm_hints && (wwin->wm_hints->flags & StateHint))
640 state = wwin->wm_hints->initial_state;
641 else
642 state = NormalState;
644 if (state==IconicState)
645 wwin->flags.miniaturized = 1;
647 if (state==WithdrawnState) {
648 wwin->flags.mapped = 0;
649 wClientSetState(wwin, WithdrawnState, None);
650 XSelectInput(dpy, wwin->client_win, NoEventMask);
651 XRemoveFromSaveSet(dpy, wwin->client_win);
652 wUnmanageWindow(wwin, True);
653 } else {
654 wClientSetState(wwin, NormalState, None);
655 if (wwin->flags.shaded) {
656 wwin->flags.shaded = 0;
657 wwin->flags.skip_next_animation = 1;
658 wwin->flags.ignore_next_unmap = 1; /* ??? */
659 wShadeWindow(wwin);
661 if (wwin->flags.miniaturized) {
662 wwin->flags.miniaturized = 0;
663 wwin->flags.hidden = 0;
664 wwin->flags.skip_next_animation = 1;
665 wwin->flags.ignore_next_unmap = 1;
666 wIconifyWindow(wwin);
667 } else if (wwin->flags.hidden) {
668 WApplication *wapp = wApplicationOf(wwin->main_window);
669 wwin->flags.hidden = 0;
670 wwin->flags.skip_next_animation = 1;
671 if (wapp) {
672 wHideApplication(wapp);
674 wwin->flags.ignore_next_unmap = 1;
677 #if 0
678 switch (state) {
679 case WithdrawnState:
680 wwin->flags.mapped = 0;
681 wClientSetState(wwin, WithdrawnState, None);
682 XSelectInput(dpy, wwin->client_win, NoEventMask);
683 XRemoveFromSaveSet(dpy, wwin->client_win);
684 wUnmanageWindow(wwin, True);
685 break;
687 case IconicState:
688 if (!wwin->flags.miniaturized) {
689 wwin->flags.ignore_next_unmap=1;
690 wwin->flags.skip_next_animation=1;
691 wIconifyWindow(wwin);
693 break;
695 case DontCareState:
696 case NormalState:
698 default:
699 wClientSetState(wwin, NormalState, None);
700 break;
702 #endif
707 static void
708 handleDestroyNotify(Window window)
710 WWindow *wwin;
711 WApplication *app;
712 #ifdef DEBUG
713 puts("got destroy notify");
714 #endif
716 wwin = wWindowFor(window);
717 if (wwin) {
718 wUnmanageWindow(wwin, False);
721 app = wApplicationOf(window);
722 if (app) {
723 if (window == app->main_window) {
724 app->refcount = 0;
725 wwin = app->main_window_desc->screen_ptr->focused_window;
726 while (wwin) {
727 if (wwin->main_window == window) {
728 wwin->main_window = None;
730 wwin = wwin->prev;
733 wApplicationDestroy(app);
739 static void
740 handleExpose(XEvent *event)
742 WObjDescriptor *desc;
744 #ifdef DEBUG
745 puts("got expose");
746 #endif
747 if (event->xexpose.count!=0) {
748 return;
751 if (XFindContext(dpy, event->xexpose.window, wWinContext,
752 (XPointer *)&desc)==XCNOENT) {
753 return;
756 if (desc->handle_expose) {
757 (*desc->handle_expose)(desc, event);
762 /* bindable */
763 static void
764 handleButtonPress(XEvent *event)
766 WObjDescriptor *desc;
767 WScreen *scr;
769 #ifdef DEBUG
770 puts("got button press");
771 #endif
773 scr = wScreenForRootWindow(event->xbutton.root);
775 #ifdef BALLOON_TEXT
776 wBalloonHide(scr);
777 #endif
779 if (event->xbutton.window==scr->root_win) {
780 if (event->xbutton.button==wPreferences.menu_button) {
781 OpenRootMenu(scr, event->xbutton.x_root,
782 event->xbutton.y_root, False);
783 /* ugly hack */
784 if (scr->root_menu) {
785 if (scr->root_menu->brother->flags.mapped)
786 event->xbutton.window = scr->root_menu->brother->frame->core->window;
787 else
788 event->xbutton.window = scr->root_menu->frame->core->window;
790 } else if (event->xbutton.button==wPreferences.windowl_button) {
792 OpenSwitchMenu(scr, event->xbutton.x_root,
793 event->xbutton.y_root, False);
794 if (scr->switch_menu) {
795 if (scr->switch_menu->brother->flags.mapped)
796 event->xbutton.window = scr->switch_menu->brother->frame->core->window;
797 else
798 event->xbutton.window = scr->switch_menu->frame->core->window;
800 } else if (event->xbutton.button==wPreferences.select_button) {
802 wUnselectWindows();
803 wSelectWindows(scr, event);
808 if (XFindContext(dpy, event->xbutton.window, wWinContext,
809 (XPointer *)&desc)==XCNOENT) {
810 return;
813 if (desc->parent_type == WCLASS_WINDOW) {
814 XSync(dpy, 0);
816 if (event->xbutton.state & MOD_MASK) {
817 XAllowEvents(dpy, AsyncPointer, CurrentTime);
820 if (wPreferences.focus_mode == WKF_CLICK) {
821 if (wPreferences.ignore_focus_click) {
822 XAllowEvents(dpy, AsyncPointer, CurrentTime);
824 XAllowEvents(dpy, ReplayPointer, CurrentTime);
826 XSync(dpy, 0);
827 } else if (desc->parent_type == WCLASS_APPICON
828 || desc->parent_type == WCLASS_MINIWINDOW
829 || desc->parent_type == WCLASS_DOCK_ICON) {
830 if (event->xbutton.state & MOD_MASK) {
831 XSync(dpy, 0);
832 XAllowEvents(dpy, AsyncPointer, CurrentTime);
833 XSync(dpy, 0);
837 if (desc->handle_mousedown!=NULL) {
838 (*desc->handle_mousedown)(desc, event);
841 /* save double-click information */
842 if (scr->flags.next_click_is_not_double) {
843 scr->flags.next_click_is_not_double = 0;
844 } else {
845 scr->last_click_time = event->xbutton.time;
846 scr->last_click_button = event->xbutton.button;
847 scr->last_click_window = event->xbutton.window;
852 static void
853 handleMapNotify(Window window)
855 WWindow *wwin;
857 #ifdef DEBUG
858 puts("got map");
859 #endif
860 wwin= wWindowFor(window);
861 if (wwin && wwin->client_win==window) {
862 if (wwin->flags.ignore_next_unmap) {
863 wwin->flags.ignore_next_unmap=0;
864 return;
866 if (wwin->flags.miniaturized) {
867 wDeiconifyWindow(wwin);
868 } else {
869 XGrabServer(dpy);
870 XSync(dpy,0);
871 XMapWindow(dpy, wwin->client_win);
872 XMapWindow(dpy, wwin->frame->core->window);
873 wwin->flags.mapped=1;
874 wClientSetState(wwin, NormalState, None);
875 XUngrabServer(dpy);
881 static void
882 handleUnmapNotify(XEvent *event)
884 WWindow *wwin;
885 XEvent ev;
887 #ifdef DEBUG
888 puts("got unmap");
889 #endif
890 wwin = wWindowFor(event->xunmap.window);
891 if (!wwin || wwin->client_win!=event->xunmap.window)
892 return;
894 if (!wwin->flags.mapped
895 && wwin->frame->workspace==wwin->screen_ptr->current_workspace
896 && !wwin->flags.miniaturized && !wwin->flags.hidden)
897 return;
899 if (wwin->flags.ignore_next_unmap) {
900 return;
902 XGrabServer(dpy);
903 XUnmapWindow(dpy, wwin->frame->core->window);
904 wwin->flags.mapped = 0;
905 XSync(dpy, 0);
906 /* check if the window was destroyed */
907 if (XCheckTypedWindowEvent(dpy, wwin->client_win, DestroyNotify,&ev)) {
908 DispatchEvent(&ev);
909 } else {
910 /* withdraw window */
911 wwin->flags.mapped = 0;
912 XSelectInput(dpy, wwin->client_win, NoEventMask);
913 XRemoveFromSaveSet(dpy, wwin->client_win);
914 wClientSetState(wwin, WithdrawnState, None);
915 wUnmanageWindow(wwin, True);
917 XUngrabServer(dpy);
921 static void
922 handleConfigureRequest(XEvent *event)
924 WWindow *wwin;
926 #ifdef DEBUG
927 puts("got configure request");
928 #endif
929 if (!(wwin=wWindowFor(event->xconfigurerequest.window))) {
931 * Configure request for unmapped window
933 wClientConfigure(NULL, &(event->xconfigurerequest));
934 } else {
935 wClientConfigure(wwin, &(event->xconfigurerequest));
940 static void
941 handlePropertyNotify(XEvent *event)
943 WWindow *wwin;
944 WApplication *wapp;
945 Window jr;
946 int ji;
947 unsigned int ju;
949 #ifdef DEBUG
950 puts("got property notify");
951 #endif
952 if ((wwin=wWindowFor(event->xproperty.window))) {
953 if (!XGetGeometry(dpy, wwin->client_win, &jr, &ji, &ji,
954 &ju, &ju, &ju, &ju)) {
955 return;
957 wClientCheckProperty(wwin, &event->xproperty);
959 wapp = wApplicationOf(event->xproperty.window);
960 if (wapp) {
961 wClientCheckProperty(wapp->main_window_desc, &event->xproperty);
966 static void
967 handleClientMessage(XEvent *event)
969 WWindow *wwin;
970 WObjDescriptor *desc;
972 #ifdef DEBUG
973 puts("got client message");
974 #endif
975 /* handle transition from Normal to Iconic state */
976 if (event->xclient.message_type == _XA_WM_CHANGE_STATE
977 && event->xclient.format == 32
978 && event->xclient.data.l[0] == IconicState) {
980 wwin = wWindowFor(event->xclient.window);
981 if (!wwin) return;
982 if (!wwin->flags.miniaturized)
983 wIconifyWindow(wwin);
984 } else if (event->xclient.message_type == _XA_WINDOWMAKER_WM_FUNCTION) {
985 WApplication *wapp;
986 int done=0;
987 wapp = wApplicationOf(event->xclient.window);
988 if (wapp) {
989 switch (event->xclient.data.l[0]) {
990 case WMFHideOtherApplications:
991 wHideOtherApplications(wapp->main_window_desc);
992 done = 1;
993 break;
995 case WMFHideApplication:
996 wHideApplication(wapp);
997 done = 1;
998 break;
1001 if (!done) {
1002 wwin = wWindowFor(event->xclient.window);
1003 if (wwin) {
1004 switch (event->xclient.data.l[0]) {
1005 case WMFHideOtherApplications:
1006 wHideOtherApplications(wwin);
1007 break;
1009 case WMFHideApplication:
1010 wHideApplication(wApplicationOf(wwin->main_window));
1011 break;
1015 #ifdef OFFIX_DND
1016 } else if (event->xclient.message_type==_XA_DND_PROTOCOL) {
1017 if (wDockReceiveDNDDrop(wScreenForWindow(event->xclient.window),event))
1018 goto redirect_message;
1019 #endif /* OFFIX_DND */
1020 } else {
1021 #ifdef OFFIX_DND
1022 redirect_message:
1023 #endif
1025 * Non-standard thing, but needed by OffiX DND.
1026 * For when the icon frame gets a ClientMessage
1027 * that should have gone to the icon_window.
1029 if (XFindContext(dpy, event->xbutton.window, wWinContext,
1030 (XPointer *)&desc)!=XCNOENT) {
1031 struct WIcon *icon=NULL;
1033 if (desc->parent_type == WCLASS_MINIWINDOW) {
1034 icon = (WIcon*)desc->parent;
1035 } else if (desc->parent_type == WCLASS_DOCK_ICON
1036 || 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,
1043 event);
1051 static void
1052 raiseWindow(WScreen *scr)
1054 WWindow *wwin;
1056 scr->autoRaiseTimer = NULL;
1058 wwin = wWindowFor(scr->autoRaiseWindow);
1059 if (!wwin)
1060 return;
1062 if (!wwin->flags.destroyed) {
1063 wRaiseFrame(wwin->frame->core);
1064 /* this is needed or a race condition will occur */
1065 XSync(dpy, False);
1070 static void
1071 handleEnterNotify(XEvent *event)
1073 WWindow *wwin;
1074 WObjDescriptor *desc = NULL;
1075 XEvent ev;
1076 WScreen *scr = wScreenForRootWindow(event->xcrossing.root);
1079 #ifdef DEBUG
1080 puts("got enter notify");
1081 #endif
1083 if (XCheckTypedWindowEvent(dpy, event->xcrossing.window, LeaveNotify,
1084 &ev)) {
1085 /* already left the window... */
1086 saveTimestamp(&ev);
1087 if (ev.xcrossing.mode==event->xcrossing.mode
1088 && ev.xcrossing.detail==event->xcrossing.detail) {
1089 return;
1093 if (XFindContext(dpy, event->xcrossing.window, wWinContext,
1094 (XPointer *)&desc)!=XCNOENT) {
1095 if(desc->handle_enternotify)
1096 (*desc->handle_enternotify)(desc, event);
1099 /* enter to window */
1100 wwin = wWindowFor(event->xcrossing.window);
1101 if (!wwin) {
1102 if (wPreferences.focus_mode==WKF_POINTER
1103 && event->xcrossing.window==event->xcrossing.root) {
1104 wSetFocusTo(scr, NULL);
1106 if (wPreferences.colormap_mode==WKF_POINTER) {
1107 wColormapInstallForWindow(scr, NULL);
1109 if (scr->autoRaiseTimer
1110 && event->xcrossing.root==event->xcrossing.window) {
1111 WMDeleteTimerHandler(scr->autoRaiseTimer);
1112 scr->autoRaiseTimer = NULL;
1114 } else {
1115 /* set focus if in focus-follows-mouse mode and the event
1116 * is for the frame window and window doesn't have focus yet */
1117 if ((wPreferences.focus_mode==WKF_POINTER
1118 || wPreferences.focus_mode==WKF_SLOPPY)
1119 && wwin->frame->core->window==event->xcrossing.window
1120 && !wwin->flags.focused) {
1121 wSetFocusTo(scr, wwin);
1123 if (scr->autoRaiseTimer)
1124 WMDeleteTimerHandler(scr->autoRaiseTimer);
1125 scr->autoRaiseTimer = NULL;
1127 if (wPreferences.raise_delay && !wwin->window_flags.no_focusable) {
1128 scr->autoRaiseWindow = wwin->frame->core->window;
1129 scr->autoRaiseTimer
1130 = WMAddTimerHandler(wPreferences.raise_delay,
1131 (WMCallback*)raiseWindow, scr);
1134 /* Install colormap for window, if the colormap installation mode
1135 * is colormap_follows_mouse */
1136 if (wPreferences.colormap_mode==WKF_POINTER) {
1137 if (wwin->client_win==event->xcrossing.window)
1138 wColormapInstallForWindow(scr, wwin);
1139 else
1140 wColormapInstallForWindow(scr, NULL);
1144 /* a little kluge to hide the clip balloon */
1145 if (!wPreferences.flags.noclip && scr->flags.clip_balloon_mapped) {
1146 if (!desc) {
1147 XUnmapWindow(dpy, scr->clip_balloon);
1148 scr->flags.clip_balloon_mapped = 0;
1149 } else {
1150 if (desc->parent_type!=WCLASS_DOCK_ICON
1151 || scr->clip_icon != desc->parent) {
1152 XUnmapWindow(dpy, scr->clip_balloon);
1153 scr->flags.clip_balloon_mapped = 0;
1158 if (event->xcrossing.window == event->xcrossing.root
1159 && event->xcrossing.detail == NotifyNormal
1160 && event->xcrossing.detail != NotifyInferior
1161 && wPreferences.focus_mode != WKF_CLICK) {
1163 wSetFocusTo(scr, scr->focused_window);
1166 #ifdef BALLOON_TEXT
1167 wBalloonEnteredObject(scr, desc);
1168 #endif
1172 static void
1173 handleLeaveNotify(XEvent *event)
1175 WObjDescriptor *desc = NULL;
1177 if (XFindContext(dpy, event->xcrossing.window, wWinContext,
1178 (XPointer *)&desc)!=XCNOENT) {
1179 if(desc->handle_leavenotify)
1180 (*desc->handle_leavenotify)(desc, event);
1182 if (event->xcrossing.window == event->xcrossing.root
1183 && event->xcrossing.mode == NotifyNormal
1184 && event->xcrossing.detail != NotifyInferior
1185 && wPreferences.focus_mode != WKF_CLICK) {
1187 WScreen *scr = wScreenForRootWindow(event->xcrossing.root);
1189 wSetFocusTo(scr, NULL);
1194 #ifdef SHAPE
1195 static void
1196 handleShapeNotify(XEvent *event)
1198 XShapeEvent *shev = (XShapeEvent*)event;
1199 WWindow *wwin;
1200 puts("got shape notify");
1201 #ifdef DEBUG
1202 puts("got shape notify");
1203 #endif
1204 wwin = wWindowFor(event->xany.window);
1205 if (!wwin || shev->kind != ShapeBounding)
1206 return;
1208 wwin->flags.shaped = shev->shaped;
1209 wWindowSetShape(wwin);
1211 #endif /* SHAPE */
1214 static void
1215 handleColormapNotify(XEvent *event)
1217 WWindow *wwin;
1218 WScreen *scr;
1219 Bool reinstall = False;
1221 wwin = wWindowFor(event->xcolormap.window);
1222 if (!wwin)
1223 return;
1225 scr = wwin->screen_ptr;
1227 do {
1228 if (wwin) {
1229 if (event->xcolormap.new) {
1230 XWindowAttributes attr;
1232 XGetWindowAttributes(dpy, wwin->client_win, &attr);
1234 if (wwin == scr->cmap_window && wwin->cmap_window_no == 0)
1235 scr->current_colormap = attr.colormap;
1237 reinstall = True;
1238 } else if (event->xcolormap.state == ColormapUninstalled &&
1239 scr->current_colormap == event->xcolormap.colormap) {
1241 /* some bastard app (like XV) removed our colormap */
1242 reinstall = True;
1243 } else if (event->xcolormap.state == ColormapInstalled &&
1244 scr->current_colormap == event->xcolormap.colormap) {
1246 /* someone has put our colormap back */
1247 reinstall = False;
1250 } while (XCheckTypedEvent(dpy, ColormapNotify, event)
1251 && ((wwin = wWindowFor(event->xcolormap.window)) || 1));
1253 if (reinstall && scr->current_colormap!=None) {
1255 XInstallColormap(dpy, scr->current_colormap);
1261 static void
1262 handleFocusIn(XEvent *event)
1264 WWindow *wwin;
1266 if (event->xfocus.mode == NotifyUngrab) {
1267 return;
1270 * For applications that like stealing the focus.
1272 while (XCheckTypedEvent(dpy, FocusIn, event));
1273 saveTimestamp(event);
1274 if (event->xfocus.mode == NotifyUngrab) {
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 wSetFocusTo(wScreenForWindow(event->xfocus.window), NULL);
1290 static WWindow*
1291 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,
1298 &mask))
1299 return wWindowFor(win);
1300 return NULL;
1303 static void
1304 handleKeyPress(XEvent *event)
1306 WScreen *scr = wScreenForRootWindow(event->xkey.root);
1307 WWindow *wwin = scr->focused_window;
1308 int i;
1309 int modifiers;
1310 int command=-1;
1312 /* ignore CapsLock */
1313 modifiers = event->xkey.state & ValidModMask;
1315 for (i=0; i<WKBD_LAST; i++) {
1316 if (wKeyBindings[i].keycode==0)
1317 continue;
1319 if (wKeyBindings[i].keycode==event->xkey.keycode
1320 && (/*wKeyBindings[i].modifier==0
1321 ||*/ wKeyBindings[i].modifier==modifiers)) {
1322 command = i;
1323 break;
1327 if (command < 0) {
1328 if (!wRootMenuPerformShortcut(event)) {
1329 static int dontLoop = 0;
1331 if (dontLoop > 10) {
1332 wwarning("problem with key event processing code");
1333 return;
1335 dontLoop++;
1336 /* if the focused window is an internal window, try redispatching
1337 * the event to the managed window, as it can be a WINGs window */
1338 if (wwin && wwin->flags.internal_window
1339 && wwin->client_leader!=None) {
1340 /* client_leader contains the WINGs toplevel */
1341 event->xany.window = wwin->client_leader;
1342 WMHandleEvent(event);
1344 dontLoop--;
1346 return;
1349 #define ISMAPPED(w) ((w) && !(w)->flags.miniaturized && ((w)->flags.mapped || (w)->flags.shaded))
1350 #define ISFOCUSED(w) ((w) && (w)->flags.focused)
1352 switch (command) {
1353 case WKBD_ROOTMENU:
1354 OpenRootMenu(scr, event->xkey.x_root, event->xkey.y_root, True);
1355 break;
1356 case WKBD_WINDOWMENU:
1357 if (ISMAPPED(wwin) && ISFOCUSED(wwin))
1358 OpenWindowMenu(wwin, wwin->frame_x,
1359 wwin->frame_y+wwin->frame->top_width, True);
1360 break;
1361 case WKBD_WINDOWLIST:
1362 OpenSwitchMenu(scr, event->xkey.x_root, event->xkey.y_root, True);
1363 break;
1364 case WKBD_MINIATURIZE:
1365 if (ISMAPPED(wwin) && ISFOCUSED(wwin)) {
1366 CloseWindowMenu(scr);
1368 if (wwin->protocols.MINIATURIZE_WINDOW)
1369 wClientSendProtocol(wwin, _XA_WINDOWMAKER_WM_MINIATURIZE_WINDOW,
1370 event->xbutton.time);
1371 else {
1372 if (!wwin->window_flags.no_miniaturizable)
1373 wIconifyWindow(wwin);
1376 break;
1377 case WKBD_HIDE:
1378 if (ISMAPPED(wwin) && ISFOCUSED(wwin)) {
1379 WApplication *wapp = wApplicationOf(wwin->main_window);
1380 CloseWindowMenu(scr);
1382 if (wapp && !wapp->main_window_desc->window_flags.no_appicon) {
1383 wHideApplication(wapp);
1386 break;
1387 case WKBD_MAXIMIZE:
1388 if (ISMAPPED(wwin) && ISFOCUSED(wwin)
1389 && !wwin->window_flags.no_resizable) {
1390 CloseWindowMenu(scr);
1392 if (wwin->flags.maximized) {
1393 wUnmaximizeWindow(wwin);
1394 } else {
1395 wMaximizeWindow(wwin, MAX_VERTICAL|MAX_HORIZONTAL);
1398 break;
1399 case WKBD_VMAXIMIZE:
1400 if (ISMAPPED(wwin) && ISFOCUSED(wwin)
1401 && !wwin->window_flags.no_resizable) {
1402 CloseWindowMenu(scr);
1404 if (wwin->flags.maximized) {
1405 wUnmaximizeWindow(wwin);
1406 } else {
1407 wMaximizeWindow(wwin, MAX_VERTICAL);
1410 break;
1411 case WKBD_RAISE:
1412 if (ISMAPPED(wwin) && ISFOCUSED(wwin)) {
1413 CloseWindowMenu(scr);
1415 wRaiseFrame(wwin->frame->core);
1417 break;
1418 case WKBD_LOWER:
1419 if (ISMAPPED(wwin) && ISFOCUSED(wwin)) {
1420 CloseWindowMenu(scr);
1422 wLowerFrame(wwin->frame->core);
1424 break;
1425 case WKBD_RAISELOWER:
1426 /* raise or lower the window under the pointer, not the
1427 * focused one
1429 wwin = windowUnderPointer(scr);
1430 if (wwin)
1431 wRaiseLowerFrame(wwin->frame->core);
1432 break;
1433 case WKBD_SHADE:
1434 if (ISMAPPED(wwin) && ISFOCUSED(wwin)
1435 && !wwin->window_flags.no_shadeable) {
1436 if (wwin->flags.shaded)
1437 wUnshadeWindow(wwin);
1438 else
1439 wShadeWindow(wwin);
1441 break;
1442 case WKBD_CLOSE:
1443 if (ISMAPPED(wwin) && ISFOCUSED(wwin)
1444 && !wwin->window_flags.no_closable) {
1445 CloseWindowMenu(scr);
1446 if (wwin->protocols.DELETE_WINDOW)
1447 wClientSendProtocol(wwin, _XA_WM_DELETE_WINDOW,
1448 event->xkey.time);
1450 break;
1451 case WKBD_SELECT:
1452 if (ISMAPPED(wwin) && ISFOCUSED(wwin)) {
1453 wSelectWindow(wwin);
1455 break;
1456 case WKBD_FOCUSNEXT:
1457 wwin = NextFocusWindow(scr);
1458 if (wwin != NULL) {
1459 wSetFocusTo(scr, wwin);
1460 if (wPreferences.circ_raise)
1461 wRaiseFrame(wwin->frame->core);
1463 break;
1464 case WKBD_FOCUSPREV:
1465 wwin = PrevFocusWindow(scr);
1466 if (wwin != NULL) {
1467 wSetFocusTo(scr, wwin);
1468 if (wPreferences.circ_raise)
1469 wRaiseFrame(wwin->frame->core);
1471 break;
1472 case WKBD_WORKSPACE1:
1473 wWorkspaceChange(scr, 0);
1474 break;
1475 case WKBD_WORKSPACE2:
1476 wWorkspaceChange(scr, 1);
1477 break;
1478 case WKBD_WORKSPACE3:
1479 wWorkspaceChange(scr, 2);
1480 break;
1481 case WKBD_WORKSPACE4:
1482 wWorkspaceChange(scr, 3);
1483 break;
1484 case WKBD_WORKSPACE5:
1485 wWorkspaceChange(scr, 4);
1486 break;
1487 case WKBD_WORKSPACE6:
1488 wWorkspaceChange(scr, 5);
1489 break;
1490 case WKBD_WORKSPACE7:
1491 wWorkspaceChange(scr, 6);
1492 break;
1493 case WKBD_WORKSPACE8:
1494 wWorkspaceChange(scr, 7);
1495 break;
1496 case WKBD_WORKSPACE9:
1497 wWorkspaceChange(scr, 8);
1498 break;
1499 case WKBD_WORKSPACE10:
1500 wWorkspaceChange(scr, 9);
1501 break;
1502 case WKBD_NEXTWORKSPACE:
1503 if (scr->current_workspace < scr->workspace_count-1)
1504 wWorkspaceChange(scr, scr->current_workspace+1);
1505 else if (scr->current_workspace == scr->workspace_count-1) {
1506 if (wPreferences.ws_advance &&
1507 scr->current_workspace < MAX_WORKSPACES-1)
1508 wWorkspaceChange(scr, scr->current_workspace+1);
1509 else if (wPreferences.ws_cycle)
1510 wWorkspaceChange(scr, 0);
1512 break;
1513 case WKBD_PREVWORKSPACE:
1514 if (scr->current_workspace > 0)
1515 wWorkspaceChange(scr, scr->current_workspace-1);
1516 else if (scr->current_workspace==0 && wPreferences.ws_cycle)
1517 wWorkspaceChange(scr, scr->workspace_count-1);
1518 break;
1520 case WKBD_NEXTWSLAYER:
1521 case WKBD_PREVWSLAYER:
1523 int row, column;
1525 row = scr->current_workspace/10;
1526 column = scr->current_workspace%10;
1528 if (command==WKBD_NEXTWSLAYER) {
1529 if ((row+1)*10 < scr->workspace_count)
1530 wWorkspaceChange(scr, column+(row+1)*10);
1531 } else {
1532 if (row > 0)
1533 wWorkspaceChange(scr, column+(row-1)*10);
1536 break;
1537 case WKBD_CLIPLOWER:
1538 if (!wPreferences.flags.noclip)
1539 wDockLower(scr->workspaces[scr->current_workspace]->clip);
1540 break;
1541 case WKBD_CLIPRAISE:
1542 if (!wPreferences.flags.noclip)
1543 wDockRaise(scr->workspaces[scr->current_workspace]->clip);
1544 break;
1545 case WKBD_CLIPRAISELOWER:
1546 if (!wPreferences.flags.noclip)
1547 wDockRaiseLower(scr->workspaces[scr->current_workspace]->clip);
1548 break;
1553 static void
1554 handleMotionNotify(XEvent *event)
1556 WMenu *menu;
1557 WScreen *scr = wScreenForRootWindow(event->xmotion.root);
1559 if (wPreferences.scrollable_menus) {
1560 if (event->xmotion.x_root <= 1 ||
1561 event->xmotion.x_root >= (scr->scr_width - 2) ||
1562 event->xmotion.y_root <= 1 ||
1563 event->xmotion.y_root >= (scr->scr_height - 2)) {
1565 #ifdef DEBUG
1566 puts("pointer at screen edge");
1567 #endif
1569 menu = wMenuUnderPointer(scr);
1570 if (menu!=NULL)
1571 wMenuScroll(menu, event);