Initial import of the CDE 2.1.30 sources from the Open Group.
[oweals/cde.git] / cde / programs / dtsession / SmWindow.c
1 /* $TOG: SmWindow.c /main/6 1997/03/07 10:25:30 barstow $ */
2 /*                                                                      *
3  * (c) Copyright 1993, 1994 Hewlett-Packard Company                     *
4  * (c) Copyright 1993, 1994 International Business Machines Corp.       *
5  * (c) Copyright 1993, 1994 Sun Microsystems, Inc.                      *
6  * (c) Copyright 1993, 1994 Novell, Inc.                                *
7  */
8 /*************************************<+>*************************************
9  *****************************************************************************
10  **
11  **  File:        SmWindow.c
12  **
13  **  Project:     HP DT Session Manager (dtsession)
14  **
15  **  Description:
16  **  -----------
17  **  This file contains all routines needed to query the window tree.
18  **  The window tree needs to be queried to find all top level windows.
19  **
20  **
21  *******************************************************************
22  **  (c) Copyright Hewlett-Packard Company, 1990.  All rights are  
23  **  reserved.  Copying or other reproduction of this program      
24  **  except for archival purposes is prohibited without prior      
25  **  written consent of Hewlett-Packard Company.                     
26  ********************************************************************
27  **
28  **
29  **
30  *****************************************************************************
31  *************************************<+>*************************************/
32
33 #include <stdio.h>
34 #include <X11/Intrinsic.h>
35 #include <X11/Xutil.h>
36 #include <X11/Xatom.h>
37 #include <Dt/UserMsg.h>
38 #include "Sm.h"
39 #include "SmError.h"
40 #include "SmWindow.h"
41 #include "SmProtocol.h"
42 #include "SmGlobals.h"
43
44
45 /*
46  * Variables global to this module only
47  */
48 static Boolean commandTimeout;
49
50 /*
51  * Local Function declarations
52  */
53
54 static WindowInfo GetTopLevelWindowInfo( 
55                         Window window) ;
56 static void WaitForCommand( 
57                         Window window) ;
58 static void WaitTimeout( XtPointer , XtIntervalId *) ;
59
60
61
62 \f
63 /*************************************<->*************************************
64  *
65  *  GetTopLevelWindowInfo (window)
66  *
67  *
68  *  Description:
69  *  -----------
70  *  Given a child of the root - find the top level window for that child.
71  *
72  *
73  *  Inputs:
74  *  ------
75  *  window = the current window that is being queried about
76  *
77  * 
78  *  Outputs:
79  *  -------
80  *  retInfo = a WindowInfo structure (a window id + state of the window) that
81  *            gives the top level window information.
82  *
83  *  Comments:
84  *  --------
85  * 
86  *************************************<->***********************************/
87 static WindowInfo 
88 GetTopLevelWindowInfo(
89         Window window )
90 {
91     register int i;
92     Window parent,root,*children;
93     WindowInfo retInfo;
94     Atom actualType;
95     int actualFormat;
96     unsigned long nitems;
97     unsigned long leftover;
98     unsigned int nchildren;
99     WM_STATE *wmState = NULL;
100     XWindowAttributes windowAttr;
101
102     if ((XGetWindowAttributes(smGD.display, window,&windowAttr)) == 0)
103     {
104         retInfo.wid = 0;
105         retInfo.termState = 0;
106         return(retInfo);
107     }
108
109     /*
110      * If WM_STATE could not be interned at the beginning - the window manager
111      * may have been slow in coming up.  Try it again now.
112      */
113     if(XaWmState == None)
114     {
115         XaWmState = XInternAtom(smGD.display, _XA_WM_STATE, True);
116     }
117
118     XGetWindowProperty(smGD.display,window,XaWmState,
119                        0L,(long)BUFSIZ,False,
120                        XaWmState,&actualType,&actualFormat,
121                        &nitems,
122                        &leftover,(unsigned char **) &wmState);
123     if (actualType==XaWmState)
124     {
125         retInfo.wid = window;
126         retInfo.termState = wmState->state;
127
128         /*
129          * This data needs to be freed up!
130          */
131         SM_FREE((char *) wmState);
132         return(retInfo);
133     }
134     else 
135     {
136         /*
137          * Be sure to free the window property each time we get it
138          * if the property exists
139          */
140         if(actualType != None)
141         {
142             SM_FREE((char *) wmState);
143         }
144         
145         if(XQueryTree(smGD.display,window,&root,
146                       &parent,&children,&nchildren) != 0)
147         {
148             if(nchildren > 0)
149             {
150                 i = 0;
151                 while (nchildren--) 
152                 {
153                     retInfo = GetTopLevelWindowInfo(children[i++]);
154                     if(retInfo.wid != 0)
155                     {
156                         SM_FREE((char *) children);
157                         return(retInfo);
158                     }
159                 }
160                 SM_FREE((char *) children);
161             }
162         }
163         retInfo.wid = 0;
164         retInfo.termState = 0;
165         return(retInfo);
166     }
167 }
168
169
170 \f
171 /*************************************<->*************************************
172  *
173  *  WaitForCommand (window)
174  *
175  *
176  *  Description:
177  *  -----------
178  *  This routine waits for an update on the WM_COMMAND property of a top
179  *  level window after a WM_SAVE_YOURSELF has been placed on that window.
180  *
181  *
182  *  Inputs:
183  *  ------
184  *  window = window id for the
185  *
186  * 
187  *  Outputs:
188  *  -------
189  *
190  *  Comments:
191  *  --------
192  * 
193  *************************************<->***********************************/
194 static void 
195 WaitForCommand(
196         Window window )
197 {
198     XtInputMask isThere;
199     XEvent event;
200     XPropertyEvent *pEvent=(XPropertyEvent *)&event;
201     XtIntervalId        comTimerId;
202     Boolean commandUpdated;
203
204     /*
205      * Set a configurable timer which stops the block
206      */
207     commandUpdated = False;
208     commandTimeout = False;
209     comTimerId = XtAppAddTimeOut(smGD.appCon, smRes.saveYourselfTimeout,
210                                  WaitTimeout, (XtPointer) window);
211
212     while((commandUpdated == False) && (commandTimeout == False))
213     {
214         if((isThere = XtAppPending(smGD.appCon)) != 0)
215         {
216             if(isThere & XtIMXEvent)
217             {
218                 XtAppPeekEvent(smGD.appCon, &event);
219                 if (event.type==PropertyNotify&&pEvent->window==window&&
220                     pEvent->atom==XA_WM_COMMAND)
221                 {
222                     commandUpdated = True;
223                 }
224             }
225             if(commandTimeout == False)
226             {
227                 XtAppProcessEvent(smGD.appCon, XtIMXEvent | XtIMTimer);
228             }
229         }
230     }
231
232     XtRemoveTimeOut(comTimerId);
233     XSelectInput(smGD.display, window,NoEventMask);
234
235     return;
236 }
237
238
239 \f
240 /*************************************<->*************************************
241  *
242  *  SaveYourself (windowInfo)
243  *
244  *
245  *  Description:
246  *  -----------
247  *  Places the WM_SAVE_YOURSELF property on each top level window.  It then
248  *  waits for the window to update its WM_COMMAND property.
249  *
250  *
251  *  Inputs:
252  *  ------
253  *  windowInfo = window id for the top level wincow and the state of that
254  *               window.
255  *
256  * 
257  *  Outputs:
258  *  -------
259  *
260  *  Comments:
261  *  --------
262  * 
263  *************************************<->***********************************/
264 int 
265 SaveYourself(
266         WindowInfo windowInfo )
267 {
268
269     int i;
270     Atom *protoRet;
271     int nitems;
272     XClientMessageEvent saveYourselfMessage;
273     
274     /*
275      * Get the WM_PROTOCOLS property on the clients top-level window.
276      */
277     if(XGetWMProtocols(smGD.display, windowInfo.wid, &protoRet, &nitems) == 0)
278     {
279         /*
280          * If the client doesn't have a WM_PROTOCOLS property,
281          * it doesn't support any protocols.
282          */
283         return (-1);    
284     }
285         
286     /* Look for WM_SAVE_YOURSELF atom.  */
287     for (i=0;i<nitems;++i) 
288     {
289         if (protoRet[i]==XaWmSaveYourself) 
290             break;
291     }
292
293     if (i==nitems)
294     {
295         SM_FREE((char *) protoRet);
296         return(-1);     /* doesn't participate in WM_SAVE_YOURSELF */
297     }
298
299     /* Construct the ClientMessage. */
300     saveYourselfMessage.type=ClientMessage;
301     saveYourselfMessage.window=windowInfo.wid;
302     saveYourselfMessage.message_type=XaWmProtocols;
303     saveYourselfMessage.format=32;
304     saveYourselfMessage.data.l[0]=XaWmSaveYourself;
305     saveYourselfMessage.data.l[1]=CurrentTime;
306     
307     /*
308      * look for changes in WM_COMMAND property
309      */
310     XSelectInput(smGD.display,windowInfo.wid,PropertyChangeMask);
311     XFlush(smGD.display);
312
313     /*
314      * Send the ClientMessage to the client.  XSendEvent returns a 1 if it
315      * is successful in converting the event to a wire event.
316      */
317     if (XSendEvent(smGD.display,windowInfo.wid,False,NoEventMask,
318                    (XEvent *) &saveYourselfMessage) != 1)
319     {
320         PrintError(DtError, GETMESSAGE(20, 1, "Client message failed.  Client information will not be saved."));
321         return(-1);
322     }
323
324     /* Wait for client to update WM_COMMAND. */
325     WaitForCommand(windowInfo.wid);
326
327     SM_FREE((char *) protoRet);
328     
329     return (0);
330 }
331
332
333 \f
334 /*************************************<->*************************************
335  *
336  *  GetTopLevelWindows (screen, toplist, toplistlength, containedListLength)
337  *
338  *
339  *  Description:
340  *  -----------
341  *  Querys the window tree and constructs a list of all top level windows
342  *
343  *
344  *  Inputs:
345  *  ------
346  *  screen = pointer to the screen we're currently querying on
347  *
348  * 
349  *  Outputs:
350  *  -------
351  *  toplist = a pointer to the list of top level windows
352  *  toplistlength = the length of the list of top level windows
353  *  containedListLength = the length of the list of contained windows
354  *
355  *  Comments:
356  *  --------
357  * 
358  *************************************<->***********************************/
359 int 
360 GetTopLevelWindows(
361         int screen,
362         WindowInfo **topList,
363         unsigned int *topListLength,
364         unsigned int *containedListLength )
365 {
366     Window rootWindow, parentWindow, *tmpChild;
367     Window *childList, *embeddedList, *tmpList;
368     WindowInfo topLevelWindowInfo;
369     int i;
370     unsigned long numEmbedded;
371
372     /*
373      * Get a list of children of the root window
374      */
375     if (XQueryTree(smGD.display, RootWindow(smGD.display, screen),&rootWindow,
376             &parentWindow, &childList, topListLength) == 0)
377     {
378         PrintError(DtError, GETMESSAGE(20, 2, "Invalid root window.  Can not save client information."));
379         SM_EXIT(-1);
380     }
381
382     /*
383      * add in the list of top level windows embedded in the front panel
384      */
385     if(_DtGetEmbeddedClients(smGD.display, RootWindow(smGD.display, screen),
386                              &embeddedList, &numEmbedded) != Success)
387     {
388         numEmbedded = 0;
389     }
390     
391     if (*topListLength) 
392         *topList=(WindowInfo *) SM_MALLOC(sizeof(WindowInfo)*
393                                        (*topListLength + numEmbedded));
394
395     tmpChild = childList;
396     /* scan list */
397     for (i=0 ; i<*topListLength; ++i, tmpChild++) 
398     {
399         topLevelWindowInfo = GetTopLevelWindowInfo(*tmpChild);
400         if (!topLevelWindowInfo.wid)
401         {
402             topLevelWindowInfo.wid = (*tmpChild);
403             /*
404              * Assume if you can't find a state that it is "don't care"
405              * this could be a faulty assumption CHECK IT OUT
406              */
407             topLevelWindowInfo.termState = 0;
408         }
409         (*topList)[i] = topLevelWindowInfo;
410     }
411
412     /*
413      * Now add in the extra window id's to check
414      */
415     tmpList = embeddedList;
416     for(i = *topListLength;i < (*topListLength + numEmbedded);i++)
417     {
418         (*topList)[i].wid = *tmpList;tmpList++;
419         (*topList)[i].termState = NormalState;
420     }
421
422     if(numEmbedded > 0)
423     {
424         SM_FREE((char *) embeddedList);
425     }
426
427     if(*topListLength)
428     {
429         SM_FREE((char *) childList);
430     }
431    
432    *containedListLength = numEmbedded;
433     
434     return(0);
435 }
436
437
438 \f
439 /*************************************<->*************************************
440  *
441  *  WaitTimeout
442  *
443  *
444  *  Description:
445  *  -----------
446  *  Timeout procedure the WaitForCommand routine.  It stops a loop waiting
447  *  for an update of the WM_COMMAND property from a client.
448  *
449  *
450  *  Inputs:
451  *  ------
452  *
453  * 
454  *  Outputs:
455  *  -------
456  *  commandTimeout = (global) flag that stops the loop
457  *
458  *  Comments:
459  *  --------
460  * 
461  *************************************<->***********************************/
462 static void 
463 WaitTimeout(
464         XtPointer client_data,
465         XtIntervalId *id )
466 {
467     String tmpString, tmpError;
468     char   *winName;
469     Status success;
470
471     success = XFetchName(smGD.display, (Window) client_data, &winName);
472     if (success && winName)
473     {
474         tmpString = GETMESSAGE(20, 4, "Session restoration information not updated for client %s.  Invalid information may be saved.");
475         tmpError = SM_MALLOC(strlen(winName) + strlen(tmpString) + 5);
476         if (tmpError)
477         {
478             sprintf(tmpError, tmpString, winName);
479             PrintError(DtError, tmpError);
480             SM_FREE(tmpError);
481         }
482         SM_FREE(winName);
483     }
484     commandTimeout = True;
485     return;
486 } /* END OF FUNCTION WaitTimeout */
487