Use C++ linker
[oweals/cde.git] / cde / programs / dtwm / WmKeyFocus.c
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
6  * These libraries and programs are free software; you can
7  * redistribute them and/or modify them under the terms of the GNU
8  * Lesser General Public License as published by the Free Software
9  * Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * These libraries and programs are distributed in the hope that
13  * they will be useful, but WITHOUT ANY WARRANTY; without even the
14  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15  * PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with these librararies and programs; if not, write
20  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21  * Floor, Boston, MA 02110-1301 USA
22  */
23 /* 
24  * (c) Copyright 1989, 1990, 1991, 1992, 1993 OPEN SOFTWARE FOUNDATION, INC. 
25  * ALL RIGHTS RESERVED 
26 */ 
27 /* 
28  * Motif Release 1.2.3
29 */ 
30 #ifdef REV_INFO
31 #ifndef lint
32 static char rcsid[] = "$XConsortium: WmKeyFocus.c /main/5 1996/05/17 12:53:16 rswiston $"
33 #endif
34 #endif
35 /*
36  * (c) Copyright 1987, 1988, 1989, 1990 HEWLETT-PACKARD COMPANY */
37
38 /*
39  * Included Files:
40  */
41
42 #include "WmGlobal.h"
43 /*
44  * include extern functions
45  */
46 #include "WmKeyFocus.h"
47 #include "WmCDecor.h"
48 #include "WmColormap.h"
49 #include "WmEvent.h"
50 #include "WmCEvent.h"
51 #include "WmFunction.h"
52 #include "WmIDecor.h"
53 #include "WmProtocol.h"
54 #include "WmWinInfo.h"
55 #include "WmWinList.h"
56
57
58
59 /*
60  * Global Variables:
61  */
62
63 static Boolean removeSelectGrab = True;
64
65
66 \f
67 /*************************************<->*************************************
68  *
69  *  InitKeyboardFocus ()
70  *
71  *
72  *  Description:
73  *  -----------
74  *  This function sets the keyboard input focus to a client window or icon
75  *  when the window manager starts up.
76  *
77  *
78  *  Inputs:
79  *  ------
80  *  wmGD = (keyboardFocusPolicy, colormapFocusPolicy)
81  *
82  *************************************<->***********************************/
83
84 void InitKeyboardFocus (void)
85 {
86     ClientData *pCD;
87     Boolean sameScreen;
88     Boolean focusSet = False;
89     int scr;
90     int junk;
91     Window junk_win, root_returned;
92     int  currentX, currentY;
93
94
95     /*
96      * Set the keyboard focus based on the keyboard focus policy.
97      */
98
99     wmGD.keyboardFocus = NULL;
100     wmGD.nextKeyboardFocus = NULL;
101
102     for (scr = 0; scr < wmGD.numScreens; scr++)
103     {
104         if (wmGD.Screens[scr].managed)
105         {
106             wmGD.Screens[scr].focusPriority = 0;
107
108             if (wmGD.keyboardFocusPolicy == KEYBOARD_FOCUS_POINTER)
109             {
110                 /*
111                  * Set the keyboard focus to the window that 
112                  * currently contains the pointer.
113                  */
114
115                 pCD = GetClientUnderPointer (&sameScreen);
116
117                 if (wmGD.colormapFocusPolicy == CMAP_FOCUS_POINTER)
118                 {
119                     /*
120                      * Do some colormap installation that has been 
121                      * deferred from the InitColormapFocus routine.
122                      */
123
124                     SetColormapFocus (ACTIVE_PSD, pCD);
125                 }
126
127                 if (pCD)
128                 {
129                     Do_Focus_Key (pCD, GetTimestamp (), ALWAYS_SET_FOCUS);
130                     focusSet = True;
131                 }
132             }
133             else
134             {
135                 ButtonSpec *buttonSpec;
136                 
137                 /*
138                  * Prepare to do explicit selection button grabs.
139                  */
140
141                 buttonSpec = wmGD.Screens[scr].buttonSpecs;
142                 while (buttonSpec)
143                 {
144                     if ((buttonSpec->button == FOCUS_SELECT_BUTTON) &&
145                         (buttonSpec->context & F_CONTEXT_WINDOW) &&
146                         (buttonSpec->subContext & F_SUBCONTEXT_W_CLIENT))
147                     {
148                         if (buttonSpec->state == 0)
149                         {
150                             removeSelectGrab = False;
151                         }
152                     }
153                     buttonSpec = buttonSpec->nextButtonSpec;
154                 }
155             }
156         }
157     }
158
159
160     if (!focusSet)
161     {
162         /*
163          * This is keyboard focus policy is either "explicit" or it it 
164          * "pointer"
165          * and there is no window under the pointer.  No window currently has
166          * the keyboard input focus.  Set the keyboard focus to the window
167          * manager default (non-client) OR to the last client with focus.
168          *
169          * In Mwm 1.1.4 and later, calling Do_Focus_Key with NULL will try
170          * to find a 'reasonable' window to put focus.  This means that on
171          * startup and restarts, a Mwm window will have focus!  Yeah!
172          */
173
174         /* 
175          * Set Active Screen First 
176          */
177         if (XQueryPointer(DISPLAY, DefaultRootWindow(DISPLAY), 
178                           &root_returned, &junk_win,
179                           &currentX, &currentY, 
180                           &junk, &junk, (unsigned int *)&junk))
181         {
182             for (scr = 0; scr < wmGD.numScreens; scr++)
183             {
184                 if (wmGD.Screens[scr].managed && 
185                     wmGD.Screens[scr].rootWindow == root_returned)
186                 {
187                     SetActiveScreen(&(wmGD.Screens[scr]));
188                     break;
189                 }
190             }
191         }
192
193         Do_Focus_Key ((ClientData *)NULL, CurrentTime, ALWAYS_SET_FOCUS);
194     }
195
196 } /* END OF FUNCTION InitKeyboardFocus */
197
198
199 \f
200 /*************************************<->*************************************
201  *
202  *  SetKeyboardFocus (pCD, focusFlags)
203  *
204  *
205  *  Description:
206  *  -----------
207  *  This function is used to handle a client window getting the input
208  *  focus (as the RESULT of an XSetInput call - probably done by 
209  *  Do_Focus_Key).
210  *
211  *
212  *  Inputs:
213  *  ------
214  *  pCD = pointer to client data for window that is to get the focus
215  *
216  *  focusFlags = flags that indicate focus change details
217  *      {REFRESH_LAST_FOCUS}
218  *
219  * 
220  *  Outputs:
221  *  -------
222  *  wmGD = (keyboardFocus)
223  * 
224  *************************************<->***********************************/
225
226 void SetKeyboardFocus (ClientData *pCD, long focusFlags)
227 {
228     ClientData *currentFocus;
229
230     
231     /*
232      * Don't set the keyboard input focus if it is already set to
233      * the client window.
234      */
235
236     if (wmGD.keyboardFocus == pCD)
237     {
238         return;
239     }
240     currentFocus = wmGD.keyboardFocus;
241     ACTIVE_PSD->focusPriority++;
242
243
244     /*
245      * If the keyboard input focus policy is "explicit" then reset the
246      * selection button event handling.
247      */
248
249     if (wmGD.keyboardFocusPolicy == KEYBOARD_FOCUS_EXPLICIT)
250     {
251         /*
252          * Reset explicit focus selection event tracking on the last focus
253          * window (reset the passive grab on the focus button).
254          */
255
256         if (currentFocus)
257         {
258             ResetExplicitSelectHandling (currentFocus);
259             wmGD.keyboardFocus = NULL;
260         }
261         
262         if (pCD && ((pCD->clientState == NORMAL_STATE) ||
263                     (pCD->clientState == MAXIMIZED_STATE)))
264         {
265             /*
266              * The focus is to be set to a client window (not the root).
267              * Stop explicit focus selection event tracking on the new focus
268              * window.
269              */
270
271             if (removeSelectGrab)
272             {
273                 WmUngrabButton (DISPLAY, FOCUS_SELECT_BUTTON, 0,
274                     pCD->clientBaseWin);
275             }
276         }
277     }
278     
279     wmGD.keyboardFocus = pCD;
280
281
282     /*
283      * Do focus auto raise if specified.
284      */
285
286     if (pCD && pCD->focusAutoRaise)
287     {
288         if (wmGD.autoRaiseDelay &&
289             (wmGD.keyboardFocusPolicy == KEYBOARD_FOCUS_POINTER))
290         {
291             AddWmTimer (TIMER_RAISE, (unsigned long)wmGD.autoRaiseDelay,
292                 pCD);
293         }
294         else
295         {
296             Boolean sameScreen;
297
298             if (((wmGD.keyboardFocusPolicy == KEYBOARD_FOCUS_EXPLICIT) &&
299                  (!pCD->focusAutoRaiseDisabled)) ||
300                 ((wmGD.keyboardFocusPolicy == KEYBOARD_FOCUS_POINTER) &&
301                  (pCD == GetClientUnderPointer (&sameScreen))))
302             {
303                 Do_Raise (pCD, (ClientListEntry *)NULL, STACK_NORMAL);
304             }
305         }
306     }
307
308
309     /*
310      * Clear the focus indication if it is set for a client window or icon.
311      */
312
313     if (currentFocus)
314     {
315         ClearFocusIndication (currentFocus,
316             ((focusFlags & REFRESH_LAST_FOCUS) ? True : False));
317     }
318
319
320     /*
321      * Install the client window colormap if the colormap focus policy is
322      * "keyboard".
323      */
324
325     if ((wmGD.colormapFocusPolicy == CMAP_FOCUS_KEYBOARD) &&
326         (!(focusFlags & SCREEN_SWITCH_FOCUS)))
327     {
328         SetColormapFocus (ACTIVE_PSD, pCD);
329     }
330
331
332     /*
333      * Set the focus window or icon visual indication.
334      */
335
336     if (pCD)
337     {
338         pCD->focusPriority = ACTIVE_PSD->focusPriority;
339         SetFocusIndication (pCD);
340     }
341
342 } /* END OF FUNCTION SetKeyboardFocus */
343
344
345 \f
346 /*************************************<->*************************************
347  *
348  *  ResetExplicitSelectHandling (pCD)
349  *
350  *
351  *  Description:
352  *  -----------
353  *  This function resets the selection button event handling for a client
354  *  window or icon.  This applies only if the keyboard focus policy is
355  *  "explicit".
356  *
357  *
358  *  Inputs:
359  *  ------
360  *  pCD = pointer to client data for window that has focus handling reset
361  *
362  *************************************<->***********************************/
363
364 void ResetExplicitSelectHandling (ClientData *pCD)
365 {
366 #ifdef WSM
367     Boolean bUnseen;
368
369     bUnseen = (pCD->clientState & UNSEEN_STATE) ? True : False;
370     if (bUnseen)
371         pCD->clientState &= ~UNSEEN_STATE;
372
373 #endif /* WSM */
374     if ((pCD->clientState == NORMAL_STATE) ||
375         (pCD->clientState == MAXIMIZED_STATE))
376     {
377         /*
378          * A client window was selected.
379          */
380
381         DoExplicitSelectGrab (pCD->clientBaseWin);
382     }
383     else if (pCD->clientState == MINIMIZED_STATE)
384     {
385         /*
386          * An icon was selected.
387          */
388
389         /* !!! grab reset if client icon window? !!! */
390     }
391 #ifdef WSM
392
393     if (bUnseen)
394         pCD->clientState |= UNSEEN_STATE;
395 #endif /* WSM */
396     
397
398 } /* END OF FUNCTION ResetExplicitSelectHandling */    
399
400
401 \f
402 /*************************************<->*************************************
403  *
404  *  DoExplicitSelectGrab (window)
405  *
406  *
407  *  Description:
408  *  -----------
409  *  This function is used to do a grab button on the specified window.  The
410  *  grab is intended to catch the keyboard focus select button.
411  *
412  *
413  *  Inputs:
414  *  ------
415  *  widow = grab widow for the select button
416  * 
417  *************************************<->***********************************/
418
419 void DoExplicitSelectGrab (Window window)
420 {
421
422     WmGrabButton (DISPLAY, FOCUS_SELECT_BUTTON, 0, window,
423         False, ButtonReleaseMask, GrabModeSync, GrabModeSync, None,
424         None);
425
426 } /* END OF FUNCTION DoExplicitSelectGrab */
427
428
429 \f
430 /*************************************<->*************************************
431  *
432  *  SetFocusIndication (pCD)
433  *
434  *
435  *  Description:
436  *  -----------
437  *  This function changes the client window or icon decoration to have it
438  *  indicate that the window or icon has the keyboard input focus.
439  *
440  *
441  *  Inputs:
442  *  ------
443  *  pCD = pointer to client data for window/icon that is getting the focus
444  *
445  * 
446  *************************************<->***********************************/
447
448 void SetFocusIndication (ClientData *pCD)
449 {
450     ClientData *saveCD;
451
452     /* 
453      * Set the "focus" to pCD to insure correct display of the frame 
454      * This is necessary because the called routines get GCs based
455      * on the current keyboard focus.
456      */
457     saveCD = wmGD.keyboardFocus;
458     wmGD.keyboardFocus = pCD;
459
460     if ((pCD->clientState == NORMAL_STATE) ||
461         (pCD->clientState == MAXIMIZED_STATE))
462     {
463         /*
464          * A client window has the input focus.
465          */
466
467         ShowActiveClientFrame (pCD);
468     }
469     else if (pCD->clientState == MINIMIZED_STATE)
470     {
471         /*
472          * An icon has the input focus.
473          */
474
475         ShowActiveIcon (pCD);
476     }
477
478     /* restore old keyboard focus */
479     wmGD.keyboardFocus = saveCD;
480
481 } /* END OF FUNCTION SetFocusIndication */
482
483
484 \f
485 /*************************************<->*************************************
486  *
487  *  ClearFocusIndication (pCD, refresh)
488  *
489  *
490  *  Description:
491  *  -----------
492  *  This function changes the client window or icon decoration to have it
493  *  indicate that the window or icon does not have the keyboard input focus
494  *  (i.e. it is inactive).
495  *
496  *
497  *  Inputs:
498  *  ------
499  *  pCD = pointer to client data for window/icon that is losing the focus
500  *
501  *  refresh = True if window/icon frame is to redrawn
502  *
503  *************************************<->***********************************/
504
505 void ClearFocusIndication (ClientData *pCD, Boolean refresh)
506 {
507     ClientData *saveCD;
508 #ifdef WSM
509     Boolean bUnseen;
510 #endif /* WSM */
511
512     /* 
513      * Set the "focus" to NULL to insure correct display of the frame 
514      * This is necessary because the called routines get GCs based
515      * on the current keyboard focus.
516      */
517
518     saveCD = wmGD.keyboardFocus;
519     wmGD.keyboardFocus = NULL;
520 #ifdef WSM
521     bUnseen = (pCD->clientState & UNSEEN_STATE) ? True : False;
522     if (bUnseen)
523         pCD->clientState &= ~UNSEEN_STATE;
524 #endif /* WSM */
525
526     if ((pCD->clientState == NORMAL_STATE) ||
527         (pCD->clientState == MAXIMIZED_STATE))
528     {
529         /*
530          * A client window no longer has the input focus.
531          */
532
533         ShowInactiveClientFrame (pCD);
534     }
535     else if (pCD->clientState == MINIMIZED_STATE)
536     {
537         /*
538          * An icon no longer has the input focus.
539          */
540
541         ShowInactiveIcon (pCD, refresh);
542     }
543
544 #ifdef WSM
545     if (bUnseen) 
546         pCD->clientState |= UNSEEN_STATE;
547 #endif /* WSM */
548
549     /* restore old keyboard focus */
550     wmGD.keyboardFocus = saveCD;
551
552 } /* END OF FUNCTION ClearFocusIndication */
553
554
555 \f
556 /*************************************<->*************************************
557  *
558  *  GetClientUnderPointer (pSameScreen)
559  *
560  *
561  *  Description:
562  *  -----------
563  *  This function identifies the managed client window or icon that is under
564  *  the pointer.
565  *
566  *
567  *  Outputs:
568  *  -------
569  *  pSameScreen = pointer to flag that indicates if pointer is on the wm screen
570  *
571  *  Return = client data pointer for the client window / icon under the
572  *           mouse cursor
573  *        
574  *************************************<->***********************************/
575
576 ClientData *GetClientUnderPointer (pSameScreen)
577     Boolean *pSameScreen;
578
579 {
580     Window root;
581     Window child;
582     int rootX;
583     int rootY;
584     int winX;
585     int winY;
586     unsigned int mask;
587     ClientData *pCD;
588
589
590     if ((*pSameScreen = XQueryPointer (DISPLAY, ACTIVE_ROOT, &root, &child,
591                            &rootX, &rootY, &winX, &winY, &mask)) != False)
592     {
593         if (child && 
594             !XFindContext (DISPLAY, child, wmGD.windowContextType,
595                  (caddr_t *)&pCD))
596         {
597             /*
598              * There is a client window or icon under the pointer.
599              */
600
601             return (pCD);
602         }
603     }
604
605     return (NULL);
606
607 } /* END OF FUNCTION GetClientUnderPointer */
608
609
610 \f
611 /*************************************<->*************************************
612  *
613  *  FocusNextWindow (type, focusTime)
614  *
615  *
616  *  Description:
617  *  -----------
618  *  This function is used to change the focus to the next window in the 
619  *  window stacking order.  The next focus window must be of the specified
620  *  type(s).  If the focus traversal cannot be done because there is not
621  *  an object of the specified type (accepting focus) then don't change the
622  *  focus (!!!should the focus be unset in this case!!!).
623  *
624  *
625  *  Inputs:
626  *  ------
627  *  type = type of objects to change the focus to
628  *
629  *  focusTime = timestamp to be used for setting the input focus
630  *
631  *************************************<->***********************************/
632
633 Boolean FocusNextWindow (unsigned long type, Time focusTime)
634 {
635     ClientListEntry *pCurrentEntry;
636     ClientListEntry *pNextEntry;
637     Boolean focused = False;
638     ClientData *pCD;
639
640
641     /*
642      * Identify the window or icon that currently has the focus and start
643      * traversing to the next object.
644      */
645
646     if (type & F_GROUP_TRANSIENT)
647     {
648         /*
649          * Move the keyboard input focus around in a transient tree.
650          */
651
652         focused = FocusNextTransient (wmGD.keyboardFocus, type, False,
653                                       focusTime);
654     }
655
656     if (!focused)
657     {
658         if (wmGD.systemModalActive)
659         {
660             focused = True;
661         }
662         else if (wmGD.keyboardFocus)
663         {
664             if (wmGD.keyboardFocus->transientLeader)
665             {
666                 pCD = FindTransientTreeLeader (wmGD.keyboardFocus);
667             }
668             else
669             {
670                 pCD = wmGD.keyboardFocus;
671             }
672
673             if (pCD->clientState == MINIMIZED_STATE)
674             {
675                 pCurrentEntry = &pCD->iconEntry;
676             }
677             else
678             {
679                 pCurrentEntry = &pCD->clientEntry;
680             }
681
682             pNextEntry = pCurrentEntry->nextSibling;
683             if (!pNextEntry)
684             {
685                 pNextEntry = ACTIVE_PSD->clientList;
686             }
687         }
688         else
689         {
690                 pCurrentEntry = ACTIVE_PSD->clientList;
691                 pNextEntry = pCurrentEntry;
692         }
693     }
694
695
696     while (!focused && pNextEntry)
697     {
698         focused = CheckForKeyFocus (pNextEntry, type, True /*next*/, focusTime);
699         if (!focused)
700         {
701             pNextEntry = pNextEntry->nextSibling;
702         }
703     }
704
705     if (!focused)
706     {
707         pNextEntry = ACTIVE_PSD->clientList;
708         while ((pNextEntry != pCurrentEntry) && !focused)
709         {
710             focused = CheckForKeyFocus (pNextEntry, type, True/*next*/,
711                                         focusTime);
712             if (!focused)
713             {
714                 pNextEntry = pNextEntry->nextSibling;
715             }
716         }
717     }
718
719     return (focused);
720
721 } /* END OF FUNCTION FocusNextWindow */
722
723
724 \f
725 /*************************************<->*************************************
726  *
727  *  FocusNextTransient (pCD, type, initiate, focusTime)
728  *
729  *
730  *  Description:
731  *  -----------
732  *  This function is used to determine if another window in a transient
733  *  tree should get the input focus.
734  *
735  *  Inputs:
736  *  ------
737  *  pCD = pointer to the client data for the client window that has the focus
738  *
739  *  type = type of objects to change the focus to
740  *
741  *  initiate = set True if transient focus traversal is to be initiated;
742  *      set to False if transient focus traversal is to be continued
743  *
744  *  focusTime = timestamp to be used to set the input focus
745  *
746  *
747  *  Outputs:
748  *  -------
749  *  RETURN = True if the focus window has been identified and the focus
750  *      has been set (or is already set)
751  *
752  *************************************<->***********************************/
753
754 Boolean FocusNextTransient (ClientData *pCD, unsigned long type, Boolean initiate, Time focusTime)
755 {
756     Boolean focused = False;
757     unsigned long startAt;
758     ClientData *pcdLeader;
759     ClientData *pcdLowerLeader;
760     ClientData *pcdFocus;
761
762
763     if (initiate && !(type & F_GROUP_TRANSIENT))
764     {
765         /*
766          * If in a transient tree focus on the last transient window that
767          * had the focus.
768          */
769
770         if (pCD->transientChildren)
771         {
772             pcdFocus = FindLastTransientTreeFocus (pCD, (ClientData *)NULL);
773             if (pcdFocus != wmGD.keyboardFocus)
774             {
775                 pcdLeader = FindTransientTreeLeader (pcdFocus);
776                 if (wmGD.keyboardFocus && wmGD.keyboardFocus->focusAutoRaise &&
777                     (wmGD.keyboardFocus != pcdLeader))
778                 {
779                     pcdLowerLeader =
780                                 FindTransientTreeLeader (wmGD.keyboardFocus);
781                     if (pcdLowerLeader == pcdLeader)
782                     {
783                         if (PutTransientBelowSiblings (wmGD.keyboardFocus))
784                         {
785                             RestackTransients (pcdLeader);
786                         }
787                     }
788                     else
789                     {
790                         F_Lower (NULL, wmGD.keyboardFocus, (XEvent *) NULL);
791                     }
792                 }
793                 Do_Focus_Key (pcdFocus, focusTime, ALWAYS_SET_FOCUS);
794             }
795             focused = True;
796         }
797         else
798         {
799             focused = False;
800         }
801     }
802     else if (pCD && (pCD->clientState != MINIMIZED_STATE) &&
803              (pCD->transientLeader || pCD->transientChildren))
804     {
805         startAt = (initiate) ? (ACTIVE_PSD->clientCounter + 1) : 
806             pCD->clientID;
807         pcdLeader = FindTransientTreeLeader (pCD);
808         pcdFocus = FindNextTFocusInSeq (pcdLeader, startAt);
809         if ((pcdFocus == NULL) && (type == F_GROUP_TRANSIENT))
810         {
811             /*
812              * Wrap around and find a focus window.
813              */
814
815             pcdFocus = FindNextTFocusInSeq (pcdLeader,
816                            (unsigned long) (ACTIVE_PSD->clientCounter + 1));
817         }
818         if (pcdFocus)
819         {
820             if (pcdFocus != wmGD.keyboardFocus)
821             {
822                 if (wmGD.keyboardFocus && wmGD.keyboardFocus->focusAutoRaise &&
823                     (wmGD.keyboardFocus != pcdLeader))
824                 {
825                     pcdLowerLeader =
826                                 FindTransientTreeLeader (wmGD.keyboardFocus);
827                     if (pcdLowerLeader == pcdLeader)
828                     {
829                         if (PutTransientBelowSiblings (wmGD.keyboardFocus))
830                         {
831                             RestackTransients (pcdLeader);
832                         }
833                     }
834                     else
835                     {
836                         F_Lower (NULL, wmGD.keyboardFocus, (XEvent *)NULL);
837                     }
838                 }
839                 Do_Focus_Key (pcdFocus, focusTime, ALWAYS_SET_FOCUS);
840             }
841             focused = True;
842         }
843     }
844     else
845     {
846         if (type == F_GROUP_TRANSIENT)
847         {
848             /*
849              * Focus only within a transient tree.  In this case the current
850              * or prospective focus is not within a transient tree so leave
851              * the focus where it is.
852              */
853
854             focused = True;
855         }
856     }
857
858     return (focused);
859
860 } /* END OF FUNCTION FocusNextTransient */
861
862
863 \f
864 /*************************************<->*************************************
865  *
866  *  FindLastTransientTreeFocus (pCD, pcdNoFocus)
867  *
868  *
869  *  Description:
870  *  -----------
871  *  This function is used to scan a transient tree for the last window in
872  *  the tree that had the focus.
873  *
874  *  Inputs:
875  *  ------
876  *  pCD = pointer to the client data for the transient tree (or subtree)
877  *      leader.
878  *
879  *  pcdNoFocus = pointer to the client data for a client window that is not
880  *      to get the input focus (NULL if no client window restriction).
881  *
882  *  Outputs:
883  *  -------
884  *  RETURN = pointer to the client data of the window that last had the
885  *      focus.
886  *
887  *************************************<->***********************************/
888
889 ClientData *FindLastTransientTreeFocus (pCD, pcdNoFocus)
890     ClientData *pCD;
891     ClientData *pcdNoFocus;
892
893 {
894     ClientData *pcdNext;
895     ClientData *pcdFocus;
896     ClientData *pcdLastFocus = NULL;
897
898
899     pcdNext = pCD->transientChildren;
900     while (pcdNext)
901     {
902         pcdFocus = FindLastTransientTreeFocus (pcdNext, pcdNoFocus);
903         if (pcdFocus &&
904             (!IS_APP_MODALIZED(pcdFocus)) &&
905             ((pcdLastFocus == NULL) ||
906              (pcdFocus->focusPriority > pcdLastFocus->focusPriority)))
907         {
908             pcdLastFocus = pcdFocus;
909         }
910         pcdNext = pcdNext->transientSiblings;
911     }
912
913     if ((!IS_APP_MODALIZED(pCD)) &&
914         ((pcdLastFocus == NULL) ||
915          (pCD->focusPriority > pcdLastFocus->focusPriority)))
916     {
917         pcdLastFocus = pCD;
918     }
919
920     return (pcdLastFocus);
921
922
923 } /* END OF FUNCTION FindLastTransientTreeFocus */
924
925
926 \f
927 /*************************************<->*************************************
928  *
929  *  FindNextTFocusInSeq (pCD, startAt)
930  *
931  *
932  *  Description:
933  *  -----------
934  *  This function is used to scan a transient tree for the next window that
935  *  can accept the focus.
936  *
937  *  Inputs:
938  *  ------
939  *  pCD = pointer to the client data for the transient tree (or subtree)
940  *      leader.
941  *
942  *  startAt = focus window should have a lower id then this client id
943  *
944  *
945  *  Outputs:
946  *  -------
947  *  RETURN = pointer to the client data of the window that should get the
948  *      focus.
949  *
950  *************************************<->***********************************/
951
952 ClientData *FindNextTFocusInSeq (pCD, startAt)
953     ClientData *pCD;
954     unsigned long startAt;
955
956 {
957     ClientData *pcdNextFocus = NULL;
958     ClientData *pcdNext;
959     ClientData *pcdFocus;
960
961
962     pcdNext = pCD->transientChildren;
963     while (pcdNext)
964     {
965         pcdFocus = FindNextTFocusInSeq (pcdNext, startAt);
966         if (pcdFocus)
967         {
968             if ((pcdNextFocus == NULL) ||
969                 (pcdFocus->clientID > pcdNextFocus->clientID))
970             {
971                 pcdNextFocus = pcdFocus;
972             }
973         }
974         pcdNext = pcdNext->transientSiblings;
975     }
976
977     if ((pcdNextFocus == NULL) ||
978         (pCD->clientID > pcdNextFocus->clientID))
979     {
980         if ((!IS_APP_MODALIZED(pCD)) && (pCD->clientID < startAt))
981         {
982             pcdNextFocus = pCD;
983         }
984     }
985
986     return (pcdNextFocus);
987
988
989 } /* END OF FUNCTION FindNextTFocusInSeq */
990
991
992 \f
993 /*************************************<->*************************************
994  *
995  *  FocusPrevWindow (type, focusTime)
996  *
997  *
998  *  Description:
999  *  -----------
1000  *  This function is used to change the focus to the previous window in the 
1001  *  window stacking order.  The next focus window must be of the specified
1002  *  type(s).  If the focus traversal cannot be done because there is not
1003  *  an object of the specified type (accepting focus) then don't change the
1004  *  focus (!!!should the focus be unset in this case!!!).
1005  *
1006  *
1007  *  Inputs:
1008  *  ------
1009  *  type = type of objects to change the focus to
1010  *
1011  *  focusTime = timestamp to be used to set the input focus
1012  *
1013  *************************************<->***********************************/
1014
1015 Boolean FocusPrevWindow (unsigned long type, Time focusTime)
1016 {
1017     ClientListEntry *pCurrentEntry;
1018     ClientListEntry *pNextEntry;
1019     Boolean focused = False;
1020     ClientData *pCD;
1021
1022
1023     /*
1024      * Identify the window or icon that currently has the focus and start
1025      * traversing to the previous object.
1026      */
1027
1028     if (type & F_GROUP_TRANSIENT)
1029     {
1030         /*
1031          * Move the keyboard input focus around in a transient tree.
1032          */
1033
1034         focused = FocusPrevTransient (wmGD.keyboardFocus, type, False,
1035                                       focusTime);
1036     }
1037     
1038     if (!focused)
1039     {
1040         if (wmGD.systemModalActive)
1041         {
1042             focused = True;
1043         }
1044         else if (wmGD.keyboardFocus)
1045         {
1046             if (wmGD.keyboardFocus->transientLeader)
1047             {
1048                 pCD = FindTransientTreeLeader (wmGD.keyboardFocus);
1049             }
1050             else
1051             {
1052                 pCD = wmGD.keyboardFocus;
1053             }
1054
1055             if (pCD->clientState == MINIMIZED_STATE)
1056             {
1057                 pCurrentEntry = &pCD->iconEntry;
1058             }
1059             else
1060             {
1061                 pCurrentEntry = &pCD->clientEntry;
1062             }
1063
1064             pNextEntry = pCurrentEntry->prevSibling;
1065             if (!pNextEntry)
1066             {
1067                 pNextEntry = ACTIVE_PSD->lastClient;
1068             }
1069         }
1070         else
1071         {
1072             pCurrentEntry = ACTIVE_PSD->lastClient;
1073             pNextEntry = pCurrentEntry;
1074         }
1075     }
1076
1077
1078     while (!focused && pNextEntry)
1079     {
1080         focused = CheckForKeyFocus (pNextEntry, type, False /*previous*/,
1081                                     focusTime);
1082         if (!focused)
1083         {
1084             pNextEntry = pNextEntry->prevSibling;
1085         }
1086     }
1087
1088     if (!focused)
1089     {
1090         pNextEntry = ACTIVE_PSD->lastClient;
1091         while ((pNextEntry != pCurrentEntry) && !focused)
1092         {
1093             focused = CheckForKeyFocus (pNextEntry, type, False/*previous*/,
1094                                         focusTime);
1095             if (!focused)
1096             {
1097                 pNextEntry = pNextEntry->prevSibling;
1098             }
1099         }
1100     }
1101
1102     return (focused);
1103
1104 } /* END OF FUNCTION FocusPrevWindow */
1105
1106
1107 \f
1108 /*************************************<->*************************************
1109  *
1110  *  FocusPrevTransient (pCD, type, initiate, focusTime)
1111  *
1112  *
1113  *  Description:
1114  *  -----------
1115  *  This function is used to determine if another (previous) window in a
1116  *  transient tree should get the input focus.
1117  *
1118  *  Inputs:
1119  *  ------
1120  *  pCD = pointer to the client data for the client window that has the focus
1121  *
1122  *  type = type of objects to change the focus to
1123  *
1124  *  initiate = set True if transient focus traversal is to be initiated;
1125  *      set to False if transient focus traversal is to be continued
1126  *
1127  *  focusTime = timestamp to be used to set the input focus
1128  *
1129  *
1130  *  Outputs:
1131  *  -------
1132  *  RETURN = True if the focus window has been identified and the focus
1133  *      has been set (or is already set)
1134  *
1135  *************************************<->***********************************/
1136
1137 Boolean FocusPrevTransient (ClientData *pCD, unsigned long type, Boolean initiate, Time focusTime)
1138 {
1139     Boolean focused = False;
1140     unsigned long startAt;
1141     ClientData *pcdLeader;
1142     ClientData *pcdFocus;
1143
1144
1145     if (initiate && !(type & F_GROUP_TRANSIENT))
1146     {
1147         /*
1148          * If in a transient tree focus on the last transient window that
1149          * had the focus.
1150          */
1151
1152         if (pCD->transientChildren)
1153         {
1154             pcdFocus = FindLastTransientTreeFocus (pCD, (ClientData *)NULL);
1155             if (pcdFocus != wmGD.keyboardFocus)
1156             {
1157                 Do_Focus_Key (pcdFocus, focusTime, ALWAYS_SET_FOCUS);
1158             }
1159             focused = True;
1160         }
1161         else
1162         {
1163             focused = False;
1164         }
1165     }
1166     else if (pCD && (pCD->clientState != MINIMIZED_STATE) &&
1167              (pCD->transientLeader || pCD->transientChildren))
1168     {
1169         startAt = (initiate) ? 0 : pCD->clientID;
1170         pcdLeader = FindTransientTreeLeader (pCD);
1171         pcdFocus = FindPrevTFocusInSeq (pcdLeader, startAt);
1172         if ((pcdFocus == NULL) && (type == F_GROUP_TRANSIENT))
1173         {
1174             /*
1175              * Wrap around and find a focus window.
1176              */
1177
1178             pcdFocus = FindPrevTFocusInSeq (pcdLeader, 0);
1179         }
1180         if (pcdFocus)
1181         {
1182             if (pcdFocus != wmGD.keyboardFocus)
1183             {
1184                 Do_Focus_Key (pcdFocus, focusTime, ALWAYS_SET_FOCUS);
1185             }
1186             focused = True;
1187         }
1188     }
1189     else
1190     {
1191         if (type == F_GROUP_TRANSIENT)
1192         {
1193             /*
1194              * Focus only within a transient tree.  In this case the current
1195              * or prospective focus is not within a transient tree so leave
1196              * the focus where it is.
1197              */
1198
1199             focused = True;
1200         }
1201     }
1202
1203     return (focused);
1204
1205 } /* END OF FUNCTION FocusPrevTransient */
1206
1207
1208 \f
1209 /*************************************<->*************************************
1210  *
1211  *  FindPrevTFocusInSeq (pCD, startAt)
1212  *
1213  *
1214  *  Description:
1215  *  -----------
1216  *  This function is used to scan a transient tree for the previous window that
1217  *  can accept the focus.
1218  *
1219  *  Inputs:
1220  *  ------
1221  *  pCD = pointer to the client data for the transient tree (or subtree)
1222  *      leader.
1223  *
1224  *  startAt = focus window should have a higher id then this client id
1225  *
1226  *
1227  *  Outputs:
1228  *  -------
1229  *  RETURN = pointer to the client data of the window that should get the
1230  *      focus.
1231  *
1232  *************************************<->***********************************/
1233
1234 ClientData *FindPrevTFocusInSeq (pCD, startAt)
1235     ClientData *pCD;
1236     unsigned long startAt;
1237
1238 {
1239     ClientData *pcdNextFocus = NULL;
1240     ClientData *pcdNext;
1241     ClientData *pcdFocus;
1242
1243
1244     pcdNext = pCD->transientChildren;
1245     while (pcdNext)
1246     {
1247         pcdFocus = FindPrevTFocusInSeq (pcdNext, startAt);
1248         if (pcdFocus)
1249         {
1250             if ((pcdNextFocus == NULL) ||
1251                 (pcdFocus->clientID < pcdNextFocus->clientID))
1252             {
1253                 pcdNextFocus = pcdFocus;
1254             }
1255         }
1256         pcdNext = pcdNext->transientSiblings;
1257     }
1258
1259     if ((pcdNextFocus == NULL) ||
1260         (pCD->clientID < pcdNextFocus->clientID))
1261     {
1262         if (!(IS_APP_MODALIZED(pCD)) && (pCD->clientID > startAt))
1263         {
1264             pcdNextFocus = pCD;
1265         }
1266     }
1267
1268     return (pcdNextFocus);
1269
1270
1271 } /* END OF FUNCTION FindPrevTFocusInSeq */
1272
1273
1274 \f
1275 /*************************************<->*************************************
1276  *
1277  *  CheckForKeyFocus (pNextEntry, type, focusNext, focusTime)
1278  *
1279  *
1280  *  Description:
1281  *  -----------
1282  *  This function is used to determine if a window is a worthy candidate for
1283  *  getting the input focus (it is on-screen and is of the desired type).
1284  *  If it is, the window gets the keyboard input focus.
1285  *
1286  *
1287  *  Inputs:
1288  *  ------
1289  *  pNextEntry = the client list entry to be checked
1290  *
1291  *  type = the desired type of the focus window
1292  *
1293  *  focusNext = if true then focus the next window in the window stack
1294  *
1295  *  focusTime = timestamp to be used to set the input focus
1296  *
1297  * 
1298  *  Outputs:
1299  *  -------
1300  *  Return = True if the window gets the keyboard input focus otherwise False
1301  *
1302  *************************************<->***********************************/
1303
1304 Boolean CheckForKeyFocus (ClientListEntry *pNextEntry, unsigned long type, Boolean focusNext, Time focusTime)
1305 {
1306     ClientData *pCD = pNextEntry->pCD;
1307     unsigned long windowType;
1308     Boolean focused = False;
1309
1310
1311     /*
1312      * First check for focusing within a transient tree.
1313      */
1314
1315
1316     /*
1317      * Make sure the window is being displayed and is of the specified type.
1318      */
1319
1320     if (((pNextEntry->type == NORMAL_STATE) &&
1321 #ifdef WSM
1322          (!(pCD->clientState & UNSEEN_STATE)) &&
1323 #endif /* WSM */
1324          (pCD->clientState != MINIMIZED_STATE)) ||
1325         ((pNextEntry->type == MINIMIZED_STATE) &&
1326          (pCD->clientState == MINIMIZED_STATE)))
1327     {
1328         if (pCD->clientState == MINIMIZED_STATE)
1329         {
1330             windowType = F_GROUP_ICON;
1331         }
1332         else
1333         {
1334             if (focusNext)
1335             {
1336                 focused = FocusNextTransient (pCD, type, True, focusTime);
1337             }
1338             else
1339             {
1340                 focused = FocusPrevTransient (pCD, type, True, focusTime);
1341             }
1342             windowType = F_GROUP_WINDOW;
1343             if (pCD->transientLeader || pCD->transientChildren)
1344             {
1345                 windowType |= F_GROUP_TRANSIENT;
1346             }
1347         }
1348
1349         if (!focused && (type & windowType))
1350         {
1351             focused = True;
1352             if (focusNext && wmGD.keyboardFocus &&
1353                 wmGD.keyboardFocus->focusAutoRaise)
1354             {
1355                 F_Lower (NULL, wmGD.keyboardFocus, (XEvent *)NULL);
1356             }
1357             Do_Focus_Key (pCD, focusTime, ALWAYS_SET_FOCUS);
1358         }
1359     }
1360
1361     return (focused);
1362
1363 } /* END OF FUNCTION CheckForKeyFocus */
1364
1365
1366 \f
1367 /*************************************<->*************************************
1368  *
1369  *  RepairFocus ()
1370  *
1371  *
1372  *  Description:
1373  *  -----------
1374  *  This function sets the keyboard and colormap focus to a client 
1375  *  window or icon when the window manager is recovering from a 
1376  *  configuration action.
1377  *
1378  *
1379  *  Inputs:
1380  *  ------
1381  *
1382  * 
1383  *  Comments:
1384  *  --------
1385  *  o we only need to repair keyboard focus policy is "pointer"
1386  * 
1387  *************************************<->***********************************/
1388
1389 void RepairFocus (void)
1390 {
1391     ClientData *pCD;
1392     Boolean sameScreen;
1393     XEvent event;
1394
1395
1396     /*
1397      * Repair the keyboard and colormap focus based on the policies
1398      */
1399
1400     if ((wmGD.keyboardFocusPolicy == KEYBOARD_FOCUS_POINTER) ||
1401         (wmGD.colormapFocusPolicy == CMAP_FOCUS_POINTER))
1402     {
1403         /*
1404          * Move old enter and leave events and then get the window that
1405          * the pointer is currently in.
1406          */
1407
1408         XSync (DISPLAY, False);
1409         while (XCheckMaskEvent (DISPLAY, EnterWindowMask | LeaveWindowMask,
1410                    &event))
1411         {
1412         }
1413
1414         pCD = GetClientUnderPointer (&sameScreen);
1415
1416         /*
1417          * Set the keyboard focus to the window that currently contains the
1418          * pointer.
1419          */
1420
1421         if (wmGD.keyboardFocusPolicy == KEYBOARD_FOCUS_POINTER)
1422         {
1423             /*
1424              * This will also set colormap focus if it is CMAP_FOCUS_KEYBOARD.
1425              */
1426
1427             Do_Focus_Key (pCD, CurrentTime, ALWAYS_SET_FOCUS);
1428         }
1429         else if (wmGD.colormapFocusPolicy == CMAP_FOCUS_POINTER)
1430         {
1431             SetColormapFocus (ACTIVE_PSD, pCD);
1432         }
1433     }
1434
1435 } /* END OF FUNCTION RepairFocus */
1436
1437
1438 \f
1439 /*************************************<->*************************************
1440  *
1441  *  AutoResetKeyFocus (pcdFocus, focusTime)
1442  *
1443  *
1444  *  Description:
1445  *  -----------
1446  *  This function resets the keyboard input focus when a window with the
1447  *  focus is withdrawn or iconified.  The focus is set to the last window
1448  *  that had the focus.  The focus is not set to an icon.
1449  *
1450  *
1451  *  Inputs:
1452  *  ------
1453  *  pcdFocus = pointer to the client data of the window with the focus or
1454  *      the leader of the transient tree that contains the focus window;
1455  *      the focus should not be set to the pcdFocus window or subordinates.
1456  *
1457  *  focusTime = timestamp to be used in setting the keyboard input focus.
1458  * 
1459  *************************************<->***********************************/
1460
1461 void AutoResetKeyFocus (ClientData *pcdNoFocus, Time focusTime)
1462 {
1463     ClientListEntry *pNextEntry;
1464     ClientData *pCD;
1465     ClientData *pcdLastFocus = NULL;
1466     ClientData *pcdFocus;
1467
1468
1469     /*
1470      * Scan through the list of clients to find a window to get the focus.
1471      */
1472
1473     pNextEntry = ACTIVE_PSD->clientList;
1474
1475     while (pNextEntry)
1476     {
1477         pCD = pNextEntry->pCD;
1478         if (!wmGD.systemModalActive ||
1479             (wmGD.systemModalClient == pCD))
1480         {
1481             if ((pNextEntry->type != MINIMIZED_STATE) &&
1482                 (pCD->clientState != MINIMIZED_STATE) &&
1483 #ifdef WSM
1484                 (!(pCD->clientState & UNSEEN_STATE)) &&
1485 #endif /* WSM */
1486                 (pCD != pcdNoFocus))
1487             {
1488                 if (pCD->transientChildren)
1489                 {
1490                     pcdFocus = FindLastTransientTreeFocus (pCD, pcdNoFocus);
1491                 }
1492                 else
1493                 {
1494                     pcdFocus = pCD;
1495                 }
1496                 if (pcdFocus &&
1497                     ((pcdLastFocus == NULL) ||
1498                      (pcdFocus->focusPriority > pcdLastFocus->focusPriority)))
1499                 {
1500                     pcdLastFocus = pcdFocus;
1501                 }
1502             }
1503         }
1504         pNextEntry = pNextEntry->nextSibling;
1505     }
1506
1507
1508     /*
1509      * Set the focus if there is a window that is a good candidate for
1510      * getting the focus.
1511      */
1512
1513     if (pcdLastFocus)
1514     {
1515         Do_Focus_Key (pcdLastFocus, focusTime, ALWAYS_SET_FOCUS);
1516     }
1517     else
1518     {
1519         /*
1520          * !!! Immediately set the focus indication !!!
1521          */
1522
1523         Do_Focus_Key ((ClientData *)NULL, focusTime, ALWAYS_SET_FOCUS);
1524     }
1525
1526 } /* END OF FUNCTION AutoResetKeyFocus */