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