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