Add GNU LGPL headers to all .c .C and .h files
[oweals/cde.git] / cde / lib / DtSvc / DtUtil1 / Saver.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 /* $XConsortium: Saver.c /main/8 1996/11/21 19:56:41 drk $ */
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:        Saver.c
34  **
35  **  Description:
36  **  -----------
37  **  This file contains public and private screen saver utilities.
38  **
39  **  Public:
40  **    DtSaverGetWindows() - return array of windows on which saver can draw
41  **
42  **  Private:
43  **    _DtSaverStart() - launch specified screen saver
44  **    _DtSaverStop() - kill specified screen saver
45  **
46  *****************************************************************************
47  *************************************<+>*************************************/
48
49 #include <stdio.h>
50 #include <stdlib.h>
51 #define X_INCLUDE_STRING_H
52 #define XOS_USE_XT_LOCKING
53 #include <X11/Xos_r.h>
54 #include <X11/Intrinsic.h>
55 #include <Saver.h>
56 #include <SaverP.h>
57 #include "DtSvcLock.h"
58
59 /*
60  * Constants global to this file.
61  */
62 #define DT_SAVER_MAX_SCREENS 10
63
64 struct saver_state {
65   unsigned short serial;
66   Window xid;
67   struct saver_state *next; 
68 };
69
70 static Atom xa_saver_register;
71
72 static struct saver_state saver_list = {0, (Window)0, NULL};
73
74 /*
75  * Local functions.
76  */
77 static void RegisterSaverCB(
78   Widget w, 
79   XtPointer client_data, 
80   XEvent *event,
81   Boolean *continue_to_dispatch
82 );
83
84 \f
85 /*************************************<->*************************************
86  *
87  *  _DtSaverStart() - start a screen saver
88  *
89  *  Description:
90  *  -----------
91  *  _DtSaverStart() is one of a suite of screen saver API's used in the
92  *  desktop. These APIs are:
93  *
94  *  _DtSaverStart() starts a screen saver (private)
95  *  _DtSaverStop() stops a screen saver (private)
96  *  DtSaverGetWindows() return array of windows on which saver can draw
97  *
98  *  The _DtSaverStart() API allocates a state variable for the screen saver
99  *  which contains a serial number and NIL window ID. A list of these state
100  *  variables is maintained, one for each call to _DtSaverStart(). 
101  *  DtSaverStart() then sets up the DTSAVERINFO environment variable containing
102  *  the serial number, window count and window list provided. Finally, it
103  *  launches the provided action and returns an opaque pointer to the state 
104  *  variable. The action is expected to be a screen saver which makes use 
105  *  of the DtSaverGetWindows() API.
106  *    
107  *  When the screen saver starts, it calls the DtSaverGetWindows() API. From
108  *  the screen saver perspective, the API returns an array of windows on which
109  *  the screen saver can draw. To to this, the API obtains the DTSAVERINFO
110  *  environment variable, and parses out the window array. The API also 
111  *  creates a window and sends a ClientMessage to the first DTSAVERINFO window
112  *  containing the serial number and newly created window id.
113  *
114  *  RegisterSaverCB() is a callback called when the ClientMessage arrives from
115  *  a screen saver. This callback first searches the screen saver state list
116  *  by serial number. If a state variable is not found, RegisterSaverCB() 
117  *  assumes the screen saver must have been stopped by a call to 
118  * _DtSaverStop(), so kills the client via XKillClient() using the window id
119  *  provided in the message. If the state variable is located, 
120  *  RegisterSaverCB() stores the window id in the state variable.
121  *  
122  *  _DtSaverStop() searches the screen saver list using the serial number
123  *  provided in the input state variable. It should always be found. When
124  *  found, if the state variable window id is set, _DtSaverStop() kills the
125  *  screen saver client via XKillClient(), deletes the state variable from
126  *  the list and deallocates the state variable.
127  *
128  *  Inputs:
129  *  ------
130  *  display - display structure
131  *  drawArea - array of widgets to be stored drawn upon by saver
132  *  count - number of elements in drawArea array
133  *  saverAction - screen saver action name to invoke
134  *  wAction - widget on which possible DtActionInvoke() errors should display
135  * 
136  *  Outputs:
137  *  -------
138  * 
139  *  Return:
140  *  -------
141  *  state - pointer to opaque state structure
142  *
143  *  Comments:
144  *  --------
145  *  This function uses DtActionInvoke() to launch an action. As a result,
146  *  the caller is responsible for loading and maintaining the action database
147  *  using the DtDbLoad() function and procedures. The caller
148  *  must call _DtSaverStop() to terminate screen saver
149  * 
150  *************************************<->***********************************/
151 void *
152 _DtSaverStart(
153   Display *display,
154   Widget *drawArea,
155   int count,
156   char *saverAction,
157   Widget wAction)
158 {
159   static char envdata[(DT_SAVER_MAX_SCREENS * 12) + 20];
160   struct saver_state *state;
161   struct saver_state *p;
162   int i;
163
164   _DtSvcProcessLock();
165  /*
166   * If first time in, insert envdata in process environment.
167   */
168   if (saver_list.serial == 0)
169   {
170     putenv(envdata);
171     envdata[0] = '\0';
172     xa_saver_register = XInternAtom(display, "_DT_SAVER_REGISTER", False);
173   }
174
175  /* 
176   * Add event handler (it might already be there - that's ok).
177   */
178   XtAddEventHandler(drawArea[0], 0, True, RegisterSaverCB, NULL);
179
180  /*
181   * Allocate state structure for this saver.
182   */
183   if (!(state = (struct saver_state *)malloc(sizeof(struct saver_state))))
184   {
185     _DtSvcProcessUnlock();
186     return(NULL);
187   }
188
189  /*
190   * Initialize state structure and append to saver_list.
191   */
192   state->serial = saver_list.serial++;
193   state->xid = (Window)0;
194   state->next = NULL;
195   p = &saver_list;
196   while (p->next != NULL)
197   {
198     p = p->next;
199   }
200   p->next = state;
201
202  /*
203   * Set up environment. It will look like:
204   *   DTSAVERINFO="<serial> <count> <win0> <win1> ... <winN>"
205   */
206   sprintf(envdata, "DTSAVERINFO=%u %i %lx",
207           state->serial, count, XtWindow(drawArea[0]));
208   for (i = 1; i < count; i++)
209   {
210     char *pe = envdata + strlen(envdata);
211     sprintf(pe, " %lx", XtWindow(drawArea[i]));
212   }
213   _DtSvcProcessUnlock();
214
215   /*
216   * Launch saver.
217   */
218   DtActionInvoke(wAction, saverAction, NULL, NULL,
219                  NULL, NULL, NULL, NULL, NULL, NULL);
220
221  /*
222   * Return array as state information.
223   */
224   return((void *)state);
225 }
226
227 \f
228 /*************************************<->*************************************
229  *
230  *  _DtSaverStop() - stop a screen saver
231  *
232  *  Description:
233  *  -----------
234  *  Stop an external screen saver started with DtStartSaver(). 
235  *
236  *  _DtSaverStop() searches the screen saver list using the serial number
237  *  provided in the input state variable. It should always be found. When
238  *  found, if the state variable window id is set, _DtSaverStop() kills the
239  *  screen saver client via XKillClient(), deletes the state variable from
240  *  the list and deallocates the state variable.
241  *
242  *  Inputs:
243  *  ------
244  *  display - display structure
245  *  state - state returned from _DtSaverStart()
246  *
247  *  Outputs:
248  *  -------
249  * 
250  *  Return:
251  *  -------
252  *
253  *  Comments:
254  *  --------
255  * 
256  *************************************<->***********************************/
257
258 void
259 _DtSaverStop(
260   Display *display,
261   void *pstate)
262 {
263   struct saver_state *state = (struct saver_state *)pstate;
264   struct saver_state *p;
265
266   _DtSvcProcessLock();
267  /*
268   * Unlink from saver_list.
269   */
270   p = &saver_list;
271   while (p->next != state)
272   {
273     p = p->next;
274   }
275   p->next = state->next; 
276   _DtSvcProcessUnlock();
277
278  /*
279   * Kill client using window id provided by RegisterSaverCB().
280   */
281   if (state->xid != (Window)0)
282   {
283     XKillClient(display, state->xid);
284   }
285   
286  /*
287   * Free state allocated by _DtSaverStart();
288   */
289   free(pstate);
290 }
291
292 \f
293 /*************************************<->*************************************
294  *
295  *  DtSaverGetWindows() - return array of windows on which saver can draw
296  *
297  *  Description:
298  *  -----------
299  *
300  *  This is a PUBLIC API.
301  *
302  *  When the screen saver starts, it calls the DtSaverGetWindows() API. From
303  *  the screen saver perspective, the API returns an array of windows on which
304  *  the screen saver can draw. To to this, the API obtains the DTSAVERINFO
305  *  environment variable, and parses out the window array. The API also
306  *  creates a window and sends a ClientMessage to the first DTSAVERINFO window
307  *  containing the serial number and newly created window id.
308  *
309  *  Inputs:
310  *  ------
311  *  display - display structure
312  *  window - pointer to memory in which to place pointer to array
313  *  count - pointer to memory in which to place count
314  *
315  *  Outputs:
316  *  -------
317  *  *window - pointer to array
318  *  *count - count
319  * 
320  *  Return:
321  *  -------
322  *  True - window list returned 
323  *  False - window list not returned
324  *  
325  *  Comments:
326  *  --------
327  *  The array memory should be freed by the caller via free().
328  *
329  *************************************<->***********************************/
330
331 Boolean
332 DtSaverGetWindows(
333   Display *display,
334   Window **window,
335   int *count)
336 {
337   char               *envdata, *p, *q;
338   unsigned short      serial;
339   int                 envcount;
340   XClientMessageEvent event;
341   Window              xid_window;
342   _Xstrtokparams      strtok_buf;
343   _DtSvcDisplayToAppContext(display); 
344
345   _DtSvcAppLock(app);
346   *window = NULL;
347   *count = 0;
348
349   _DtSvcProcessLock();
350   xa_saver_register = XInternAtom(display, "_DT_SAVER_REGISTER", False);
351
352  /*
353   * Get invocation information from environment.
354   */
355   envdata = getenv("DTSAVERINFO");
356   if (!envdata)
357   {
358     _DtSvcProcessUnlock();
359     _DtSvcAppUnlock(app);
360     return(False);
361   }
362
363  /*
364   * Copy string for later strtok() use.
365   */
366   p = strdup(envdata); 
367   if (!p)
368   {
369     _DtSvcProcessUnlock();
370     _DtSvcAppUnlock(app);
371     return(False);
372   }
373
374  /*
375   * Extract serial.
376   */
377   q = _XStrtok(p, " ", strtok_buf);
378   serial = (unsigned short)strtoul(q, NULL, 10);
379
380  /*
381   * Extract envcount.
382   */
383   q = _XStrtok(NULL, " ", strtok_buf);
384   envcount = (int)strtoul(q, NULL, 10);
385
386  /*
387   * Allocate memory for window array.
388   */
389   *window = (Window *)malloc((envcount)*sizeof(Window *));
390   if (!*window)
391   {
392     free(p);
393     _DtSvcProcessUnlock();
394     _DtSvcAppUnlock(app);
395     return(False);
396   }
397
398  /*
399   * Populate result array and envcount.
400   */
401   for (*count = 0; *count < envcount; (*count)++)
402   {
403     q = _XStrtok(NULL, " ", strtok_buf);
404     (*window)[*count] = (Window)strtoul(q, NULL, 16);
405   }
406
407  /*
408   * Free temp copy of envdata.
409   */
410   free(p);
411
412  /*
413   * Create dummy window to obtain XID.
414   */
415   xid_window = XCreateWindow(display, DefaultRootWindow(display),
416                              0, 0, 1, 1, 0,
417                              CopyFromParent, InputOutput, CopyFromParent,
418                              0, NULL);
419
420   if (xid_window == (Window)0)
421   {
422    /*
423     * Could not create dummy window.
424     */
425     free((char *)*window);
426     *window = NULL;
427     *count = 0;
428     _DtSvcProcessUnlock();
429     _DtSvcAppUnlock(app);
430     return(False);
431   }
432
433  /*
434   * Send client message to win0 to register.
435   */
436   event.type = ClientMessage;
437   event.window = (*window)[0];
438   event.message_type = xa_saver_register;
439   event.format = 32;
440   event.data.l[0] = (Atom)0;
441   event.data.l[1] = (long)serial;
442   event.data.l[2] = (long)xid_window;
443   event.data.l[3] = CurrentTime;
444   XSendEvent(display, (*window)[0], False, NoEventMask,
445                         (XEvent *) &event);
446
447  _DtSvcProcessUnlock();
448  /*
449   * Ensure window creation and client message have been processed by
450   * the server before continuing.
451   */
452   XSync(display, False);
453
454   _DtSvcAppUnlock(app);
455   return(True);
456 }
457
458 \f
459 /*************************************<->*************************************
460  *
461  *  RegisterSaverCB() - register a screen saver
462  *
463  *  Description:
464  *  -----------
465  *  RegisterSaverCB() is a callback called when the ClientMessage arrives from
466  *  a screen saver. This callback first searches the screen saver state list
467  *  by serial number. If a state variable is not found, RegisterSaverCB()
468  *  assumes the screen saver must have been stopped by a call to
469  * _DtSaverStop(), so kills the client via XKillClient() using the window id
470  *  provided in the message. If the state variable is located,
471  *  RegisterSaverCB() stores the window id in the state variable.
472  *
473  *  Inputs:
474  *  ------
475  *  w - widget from which we derive the display
476  *  client_data - pointer to client data (unused)
477  *  event - ClientMessage event structure
478  *  continue_to_dispatch - dispatch to remaining event handlers (unused)
479  * 
480  *  Outputs:
481  *  -------
482  * 
483  *  Return:
484  *  -------
485  *
486  *  Comments:
487  *  --------
488  * 
489  *************************************<->***********************************/
490
491 static void
492 RegisterSaverCB(
493   Widget w,
494   XtPointer client_data,
495   XEvent *event,
496   Boolean *continue_to_dispatch)
497 {
498   if (event->type == ClientMessage)
499   {
500     XClientMessageEvent *cEvent = (XClientMessageEvent *) event;
501
502     _DtSvcProcessLock();
503     if (cEvent->message_type == xa_saver_register)
504     {
505       unsigned short serial = (unsigned short)cEvent->data.l[1];
506       Window win = (Window)cEvent->data.l[2];
507       struct saver_state *state;
508   
509      /*
510       * Find event in saver list.
511       */
512       state = saver_list.next;
513       while (state != NULL && state->serial != serial)
514       {
515         state = state->next;
516       }
517    
518       if (state != NULL) 
519       {
520        /*
521         * _DtSaverStop() not yet called for this saver. Store xid in 
522         * saver's state for _DtSaverStop()'s use.
523         */
524         state->xid = win;
525       }
526       else
527       {
528        /*
529         * _DtSaverStop() has already been called for this saver, but at the
530         * time, the saver had not yet registered. Kill the saver client.
531         */
532         XKillClient(XtDisplay(w), win);
533       }
534     }
535     _DtSvcProcessUnlock();
536   }
537 }