1 /*
2  *  Copyright (C) 1999 AT&T Laboratories Cambridge.  All Rights Reserved.
3  *
4  *  This is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation; either version 2 of the License, or
7  *  (at your option) any later version.
8  *
9  *  This software is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License
15  *  along with this software; if not, write to the Free Software
16  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
17  *  USA.
18  */
19 
20 /*
21  * misc.c - miscellaneous functions.
22  */
23 
24 #include <vncviewer.h>
25 #include <signal.h>
26 #include <fcntl.h>
27 
28 static void CleanupSignalHandler(int sig);
29 static int CleanupXErrorHandler(Display *dpy, XErrorEvent *error);
30 static int CleanupXIOErrorHandler(Display *dpy);
31 static void CleanupXtErrorHandler(String message);
32 static Bool IconifyNamedWindow(Window w, char *name, Bool undo);
33 
34 Dimension dpyWidth, dpyHeight;
35 Atom wmDeleteWindow, wmState;
36 
37 static Bool xloginIconified = False;
38 static XErrorHandler defaultXErrorHandler;
39 static XIOErrorHandler defaultXIOErrorHandler;
40 static XtErrorHandler defaultXtErrorHandler;
41 
42 
43 /*
44  * ToplevelInitBeforeRealization sets the title, geometry and other resources
45  * on the toplevel window.
46  */
47 
48 void
ToplevelInitBeforeRealization()49 ToplevelInitBeforeRealization()
50 {
51   char *titleFormat;
52   char *title;
53   char *geometry;
54 
55   XtVaGetValues(toplevel, XtNtitle, &titleFormat, NULL);
56   title = XtMalloc(strlen(titleFormat) + strlen(desktopName) + 1);
57   sprintf(title, titleFormat, desktopName);
58   XtVaSetValues(toplevel, XtNtitle, title, XtNiconName, title, NULL);
59 
60   XtVaSetValues(toplevel, XtNmaxWidth, si.framebufferWidth,
61 		XtNmaxHeight, si.framebufferHeight, NULL);
62 
63   dpyWidth = WidthOfScreen(DefaultScreenOfDisplay(dpy));
64   dpyHeight = HeightOfScreen(DefaultScreenOfDisplay(dpy));
65 
66   if (appData.fullScreen) {
67 
68     /* full screen - set position to 0,0, but defer size calculation until
69        widgets are realized */
70 
71     XtVaSetValues(toplevel, XtNoverrideRedirect, True,
72 		  XtNgeometry, "+0+0", NULL);
73 
74   } else {
75 
76     /* not full screen - work out geometry for middle of screen unless
77        specified by user */
78 
79     XtVaGetValues(toplevel, XtNgeometry, &geometry, NULL);
80 
81     if (geometry == NULL) {
82       Dimension toplevelX, toplevelY;
83       Dimension toplevelWidth = si.framebufferWidth;
84       Dimension toplevelHeight = si.framebufferHeight;
85 
86       if ((toplevelWidth + appData.wmDecorationWidth) >= dpyWidth)
87 	toplevelWidth = dpyWidth - appData.wmDecorationWidth;
88 
89       if ((toplevelHeight + appData.wmDecorationHeight) >= dpyHeight)
90 	toplevelHeight = dpyHeight - appData.wmDecorationHeight;
91 
92       toplevelX = (dpyWidth - toplevelWidth - appData.wmDecorationWidth) / 2;
93 
94       toplevelY = (dpyHeight - toplevelHeight - appData.wmDecorationHeight) /2;
95 
96       /* set position via "geometry" so that window manager thinks it's a
97 	 user-specified position and therefore honours it */
98 
99       geometry = XtMalloc(256);
100 
101       sprintf(geometry, "%dx%d+%d+%d",
102 	      toplevelWidth, toplevelHeight, toplevelX, toplevelY);
103       XtVaSetValues(toplevel, XtNgeometry, geometry, NULL);
104     }
105   }
106 
107   /* Test if the keyboard is grabbed.  If so, it's probably because the
108      XDM login window is up, so try iconifying it to release the grab */
109 
110   if (XGrabKeyboard(dpy, DefaultRootWindow(dpy), False, GrabModeSync,
111 		    GrabModeSync, CurrentTime) == GrabSuccess) {
112     XUngrabKeyboard(dpy, CurrentTime);
113   } else {
114     wmState = XInternAtom(dpy, "WM_STATE", False);
115 
116     if (IconifyNamedWindow(DefaultRootWindow(dpy), "xlogin", False)) {
117       xloginIconified = True;
118       XSync(dpy, False);
119       sleep(1);
120     }
121   }
122 
123   /* Set handlers for signals and X errors to perform cleanup */
124 
125   signal(SIGHUP, CleanupSignalHandler);
126   signal(SIGINT, CleanupSignalHandler);
127   signal(SIGTERM, CleanupSignalHandler);
128   defaultXErrorHandler = XSetErrorHandler(CleanupXErrorHandler);
129   defaultXIOErrorHandler = XSetIOErrorHandler(CleanupXIOErrorHandler);
130   defaultXtErrorHandler = XtAppSetErrorHandler(appContext,
131 					       CleanupXtErrorHandler);
132 }
133 
134 
135 /*
136  * ToplevelInitAfterRealization initialises things which require the X windows
137  * to exist.  It goes into full-screen mode if appropriate, and tells the
138  * window manager we accept the "delete window" message.
139  */
140 
141 void
ToplevelInitAfterRealization()142 ToplevelInitAfterRealization()
143 {
144   if (appData.fullScreen) {
145     FullScreenOn();
146   }
147 
148   wmDeleteWindow = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
149   XSetWMProtocols(dpy, XtWindow(toplevel), &wmDeleteWindow, 1);
150   XtOverrideTranslations
151       (toplevel, XtParseTranslationTable ("<Message>WM_PROTOCOLS: Quit()"));
152 }
153 
154 
155 /*
156  * TimeFromEvent() gets the time field out of the given event.  It returns
157  * CurrentTime if the event has no time field.
158  */
159 
160 Time
TimeFromEvent(XEvent * ev)161 TimeFromEvent(XEvent *ev)
162 {
163   switch (ev->type) {
164   case KeyPress:
165   case KeyRelease:
166     return ev->xkey.time;
167   case ButtonPress:
168   case ButtonRelease:
169     return ev->xbutton.time;
170   case MotionNotify:
171     return ev->xmotion.time;
172   case EnterNotify:
173   case LeaveNotify:
174     return ev->xcrossing.time;
175   case PropertyNotify:
176     return ev->xproperty.time;
177   case SelectionClear:
178     return ev->xselectionclear.time;
179   case SelectionRequest:
180     return ev->xselectionrequest.time;
181   case SelectionNotify:
182     return ev->xselection.time;
183   default:
184     return CurrentTime;
185   }
186 }
187 
188 
189 /*
190  * Pause is an action which pauses for a number of milliseconds (100 by
191  * default).  It is sometimes useful to space out "fake" pointer events
192  * generated by SendRFBEvent.
193  */
194 
195 void
Pause(Widget w,XEvent * event,String * params,Cardinal * num_params)196 Pause(Widget w, XEvent *event, String *params, Cardinal *num_params)
197 {
198   int msec;
199 
200   if (*num_params == 0) {
201     msec = 100;
202   } else {
203     msec = atoi(params[0]);
204   }
205 
206   usleep(msec * 1000);
207 }
208 
209 
210 /*
211  * Run an arbitrary command via execvp()
212  */
213 void
RunCommand(Widget w,XEvent * event,String * params,Cardinal * num_params)214 RunCommand(Widget w, XEvent *event, String *params, Cardinal *num_params)
215 {
216   int childstatus;
217 
218   if (*num_params == 0)
219     return;
220 
221   if (fcntl (ConnectionNumber (dpy), F_SETFD, 1L) == -1)
222       fprintf(stderr, "warning: file descriptor %d unusable for spawned program", ConnectionNumber(dpy));
223 
224   if (fcntl (rfbsock, F_SETFD, 1L) == -1)
225       fprintf(stderr, "warning: file descriptor %d unusable for spawned program", rfbsock);
226 
227   switch (fork()) {
228   case -1:
229     perror("fork");
230     break;
231   case 0:
232       /* Child 1. Fork again. */
233       switch (fork()) {
234       case -1:
235 	  perror("fork");
236 	  break;
237 
238       case 0:
239 	  /* Child 2. Do some work. */
240 	  execvp(params[0], params);
241 	  perror("exec");
242 	  exit(1);
243 	  break;
244 
245       default:
246 	  break;
247       }
248 
249       /* Child 1. Exit, and let init adopt our child */
250       exit(0);
251 
252   default:
253     break;
254   }
255 
256   /* Wait for Child 1 to die */
257   wait(&childstatus);
258 
259   return;
260 }
261 
262 
263 /*
264  * Quit action - called when we get a "delete window" message.
265  */
266 
267 void
Quit(Widget w,XEvent * event,String * params,Cardinal * num_params)268 Quit(Widget w, XEvent *event, String *params, Cardinal *num_params)
269 {
270   Cleanup();
271   exit(0);
272 }
273 
274 
275 /*
276  * Cleanup - perform any cleanup operations prior to exiting.
277  */
278 
279 void
Cleanup()280 Cleanup()
281 {
282   if (xloginIconified) {
283     IconifyNamedWindow(DefaultRootWindow(dpy), "xlogin", True);
284     XFlush(dpy);
285   }
286 #ifdef MITSHM
287   if (appData.useShm)
288     ShmCleanup();
289 #endif
290 }
291 
292 static int
CleanupXErrorHandler(Display * dpy,XErrorEvent * error)293 CleanupXErrorHandler(Display *dpy, XErrorEvent *error)
294 {
295   fprintf(stderr,"CleanupXErrorHandler called\n");
296   Cleanup();
297   return (*defaultXErrorHandler)(dpy, error);
298 }
299 
300 static int
CleanupXIOErrorHandler(Display * dpy)301 CleanupXIOErrorHandler(Display *dpy)
302 {
303   fprintf(stderr,"CleanupXIOErrorHandler called\n");
304   Cleanup();
305   return (*defaultXIOErrorHandler)(dpy);
306 }
307 
308 static void
CleanupXtErrorHandler(String message)309 CleanupXtErrorHandler(String message)
310 {
311   fprintf(stderr,"CleanupXtErrorHandler called\n");
312   Cleanup();
313   (*defaultXtErrorHandler)(message);
314 }
315 
316 static void
CleanupSignalHandler(int sig)317 CleanupSignalHandler(int sig)
318 {
319   fprintf(stderr,"CleanupSignalHandler called\n");
320   Cleanup();
321   exit(1);
322 }
323 
324 
325 /*
326  * IconifyNamedWindow iconifies another client's window with the given name.
327  */
328 
329 static Bool
IconifyNamedWindow(Window w,char * name,Bool undo)330 IconifyNamedWindow(Window w, char *name, Bool undo)
331 {
332   Window *children, dummy;
333   unsigned int nchildren;
334   int i;
335   char *window_name;
336   Atom type = None;
337   int format;
338   unsigned long nitems, after;
339   unsigned char *data;
340 
341   if (XFetchName(dpy, w, &window_name)) {
342     if (strcmp(window_name, name) == 0) {
343       if (undo) {
344 	XMapWindow(dpy, w);
345       } else {
346 	XIconifyWindow(dpy, w, DefaultScreen(dpy));
347       }
348       XFree(window_name);
349       return True;
350     }
351     XFree(window_name);
352   }
353 
354   XGetWindowProperty(dpy, w, wmState, 0, 0, False,
355 		     AnyPropertyType, &type, &format, &nitems,
356 		     &after, &data);
357   if (type != None) {
358     XFree(data);
359     return False;
360   }
361 
362   if (!XQueryTree(dpy, w, &dummy, &dummy, &children, &nchildren))
363     return False;
364 
365   for (i = 0; i < nchildren; i++) {
366     if (IconifyNamedWindow(children[i], name, undo)) {
367       XFree ((char *)children);
368       return True;
369     }
370   }
371   if (children) XFree ((char *)children);
372   return False;
373 }
374