a550624a7c598ea79f0be9bf923e5faa2c468ff1
[oweals/cde.git] / cde / lib / tt / lib / mp / mp_desktop.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 //%%  (c) Copyright 1993, 1994 Hewlett-Packard Company                  
24 //%%  (c) Copyright 1993, 1994 International Business Machines Corp.    
25 //%%  (c) Copyright 1993, 1994 Sun Microsystems, Inc.                   
26 //%%  (c) Copyright 1993, 1994 Novell, Inc.                             
27 //%%  $TOG: mp_desktop.C /main/7 1998/04/09 17:52:01 mgreess $                                                  
28 /*
29  *
30  * @(#)mp_desktop.C     1.34 93/09/07
31  *
32  * Copyright (c) 1990,1992 by Sun Microsystems, Inc.
33  */
34
35 //
36 // This file contains method for dealing with a "desktop" connection.
37 // Typically this desktop is an X11 session but this way the interface
38 // to the desktop is relatively free of Xisms allowing for more
39 // independence from X11 in the future.
40 //
41
42
43 #include "tt_options.h"
44 // Defining BSD_COMP gets us FIONREAD on SunOS 5.x, and shouldn\'t
45 // hurt in other places.
46 #define BSD_COMP
47 #include <sys/ioctl.h>
48 #include <sys/types.h>
49 #include <sys/time.h>
50 #if defined(__STDC__) && !defined(linux)
51 extern "C" {  extern int ioctl  (int, int, ...) ; };
52 #endif     
53 #include "util/tt_global_env.h"
54 #include "util/tt_ldpath.h"
55 #include "util/tt_host.h"
56 #include "util/tt_port.h"
57 #include "util/tt_Xlib.h"
58 #include "mp/mp_desktop.h"
59 #include "mp/mp_mp.h"
60 #include <unistd.h>
61 #include <malloc.h>
62 #include "util/tt_gettext.h"
63
64 static int parse_Xdisplay_string(_Tt_string display,
65                                  _Tt_string &host,
66                                  pid_t &svnum,
67                                  _Tt_string &hostname);
68
69
70 jmp_buf _Tt_desktop::io_exception;
71
72 // The following private class data is declared and allocated here
73 // so that everybody that includes mp_desktop.h doesn't have to
74 // also include X11/Xlib.h.
75
76 struct _Tt_desktop_private {
77         Display                         *xd;
78 };                                       
79
80 _Tt_desktop::
81 _Tt_desktop()
82 {
83         priv = (_Tt_desktop_private *)malloc(sizeof(_Tt_desktop_private));
84         priv->xd = (Display *)0;
85 }
86
87
88 _Tt_desktop::
89 ~_Tt_desktop()
90 {
91         // Ungrab the server, in case the user
92         // interrupted us during a grab.
93         unlock();
94         close();
95         free((MALLOCTYPE *)priv);
96 }
97
98
99 // 
100 // Initializes a desktop object. This has the effect of connecting to the
101 // appropiate X11 server. "dt_handle" is a string identifying which
102 // desktop we want to talk to. For X11, this string will be the same as
103 // what would be specified in the DISPLAY variable. 
104 //
105 int _Tt_desktop::
106 init(_Tt_string dt_handle, _Tt_dt_type /* t */)
107 {
108         char            buf[32];
109         _Tt_string      h, hostname;
110         pid_t           s;
111         int             parse_status;
112         int             ret_val;
113
114         if (priv->xd != (Display *)0) {
115                 return(1);
116         }
117         
118         // initialize our access to Xlib
119         if (! _tt_load_xlib()) {
120                 return(0);
121         }
122
123         _Tt_string display;
124
125         // parse the dt_handle string to extract the host and server
126         // number information.
127
128         parse_status = parse_Xdisplay_string(dt_handle, h, s, hostname);
129
130         // Now we examine the parse status to determine if this is a
131         // local host or remote host and if we should use a unix
132         // connection or not. We also enforce the use of screen 0 for
133         // all connections.
134         switch (parse_status) {
135               case 1: // non-local host
136               case 2: // local host
137                 // important to always use screen 0 for the case where
138                 // multiple displays are used.
139                 sprintf(buf,":%d.0",s);
140                 if (dt_handle[0] == ':') {
141                         display = buf;
142                 } else {
143                         display = hostname.cat(buf);
144                 }
145                 break;
146               case 3: // local host and unix connection specified
147                 sprintf(buf,"unix:%d.0", s);
148                 display = buf;
149                 break;
150               case 0:
151                 _tt_syslog( 0, LOG_ERR,
152                             catgets( _ttcatd, 1, 17,
153                                      "could not parse X display name: \"%s\"" ),
154                             (char *)dt_handle );
155                 return(0);
156               default:
157                 break;
158         }
159         
160         ret_val = 1;
161         int retries = 20;
162         set_error_handler(_Tt_desktop::io_error_proc);
163         if (0 == setjmp(io_exception)) {
164
165                 // now connect to the indicated X11 server
166                 while (retries--) {
167                         if (priv->xd = (Display *)
168                                CALLX11(XOpenDisplay)((char *)display)) {
169                                 // Xlib has already emitted diagnostic
170                                 break;
171                         }
172                         sleep(1);
173                 }
174                 if (!priv->xd) ret_val = 0;
175         } else {
176                 ret_val = 0;
177         }
178         restore_user_handler();
179         return(ret_val);
180 }
181
182
183 // I/O error handler. Longjmp back to before the error occured.
184 int _Tt_desktop::
185 io_error_proc(void *)
186 {
187         longjmp(io_exception, 1);
188         return(0);
189 }
190
191
192 // Sets the error handler function which will get invoked on any I/O
193 // errors received from the X11 connection
194 void _Tt_desktop::
195 set_error_handler(_Tt_dt_errfn efn)
196 {
197         user_io_handler = (int *)
198                 CALLX11(XSetIOErrorHandler)((XIOErrorHandler)efn);
199 }
200
201
202 // Restore the users I/O error handler.
203 void _Tt_desktop::
204 restore_user_handler()
205 {
206         CALLX11(XSetIOErrorHandler)((XIOErrorHandler)user_io_handler);
207 }
208
209
210 // Closes the connection to the X11 server.
211 int _Tt_desktop::
212 close()
213 {
214         int     ret_val = 1;
215
216         set_error_handler(_Tt_desktop::io_error_proc);
217         if (0 == setjmp(io_exception)) {
218
219                 // delete all properties set and close connection to desktop
220                 if (priv->xd != (Display *)0) {
221                         CALLX11(XCloseDisplay)(priv->xd);
222                 }
223         } else {
224                 ret_val = 0;
225         }
226         restore_user_handler();
227         return(ret_val);
228 }
229
230
231 // Returns the fd to poll on when new events come from the desktop
232 // connection. 
233 int _Tt_desktop::
234 notify_fd()
235 {
236         return ConnectionNumber(priv->xd);
237 }
238
239
240 //      
241 // Method to call when an event comes in from the desktop connection.
242 // This method will return 0 if the connection is broken indicating
243 // either a network partition or the X11 server going down.
244 // 
245 int _Tt_desktop::
246 process_event()
247 {
248         XEvent          xev;
249         XMappingEvent   *xm;
250         int             pending;
251         int             iostat;
252
253         if (priv->xd == (Display *)0) {
254                 return(0);
255         }
256
257         CALLX11(XFlush)(priv->xd);
258         xev.type = 0;
259         iostat=ioctl(notify_fd(), FIONREAD, (char *)&pending);
260         if (iostat == -1 || pending == 0) {
261                 // X server went down
262                 return(0);
263         }
264         if (priv->xd != (Display *)0) {
265                 CALLX11(XNextEvent)(priv->xd, &xev);
266                 if (xev.type == MappingNotify) {
267                         xm = (XMappingEvent *)&xev;
268                         CALLX11(XRefreshKeyboardMapping)(xm);
269                 }
270         }
271
272         return(1);
273 }
274
275
276 int _Tt_desktop::
277 lock()
278 {
279         if (priv->xd != 0) {
280                 CALLX11(XGrabServer)(priv->xd);
281                 CALLX11(XFlush)(priv->xd);
282         }
283         return 1;
284 }
285
286 int _Tt_desktop::
287 unlock()
288 {
289         if (priv->xd != 0) {
290                 CALLX11(XUngrabServer)(priv->xd);
291                 CALLX11(XFlush)(priv->xd);
292         }
293         return 1;
294 }
295
296 // 
297 // Sets the value of the indicated property name to val.
298 // 
299 int _Tt_desktop::
300 set_prop(_Tt_string pname, _Tt_string &val)
301 {
302         Window                  rootw;
303         Atom                    patom;
304         char                    *v1;
305         const unsigned char     *v;
306         
307         if (priv->xd == (Display *)0) {
308                 return(0);
309         }
310
311         patom = CALLX11(XInternAtom)(priv->xd, (char *)pname, False);
312         if (patom == None) {
313                 return(0);
314         }
315         
316         rootw = DefaultRootWindow(priv->xd);
317         v1 = (char *)val;
318         v = (const unsigned char *)v1;
319         CALLX11(XChangeProperty)(priv->xd, rootw,
320                                  patom, XA_STRING, 8,
321                                  PropModeReplace,
322                                  v, val.len());
323         CALLX11(XFlush)(priv->xd);
324         return(1);
325 }
326
327
328 // 
329 // Deletes the indicated property from the desktop.
330 // 
331 int _Tt_desktop::
332 del_prop(_Tt_string pname)
333 {
334         Window          rootw;
335         Atom            patom;
336         
337         if (priv->xd == (Display *)0) {
338                 return(0);
339         }
340
341         patom = CALLX11(XInternAtom)(priv->xd, (char *)pname, False);
342         if (patom == None) {
343                 return(0);
344         }
345
346         rootw = DefaultRootWindow(priv->xd);
347         CALLX11(XDeleteProperty)(priv->xd, rootw, patom);
348         CALLX11(XFlush)(priv->xd);
349
350         return(1);
351 }
352
353
354 // 
355 // Gets the value of the indicated property from the desktop.
356 // 
357 int _Tt_desktop::
358 get_prop(_Tt_string pname, _Tt_string &pval)
359 {
360         Atom                    anatom;
361         int                     format;
362         /* X11 requires these to longs and not ints */
363         unsigned long           items;
364         unsigned long           left_to_read;
365         unsigned char           *val = (unsigned char *)0;
366         Atom                    tt_xatom;
367         
368         if (priv->xd == (Display *)0) {
369                 return(0);
370         }
371
372         tt_xatom = CALLX11(XInternAtom)(priv->xd, (char *)pname, False);
373         if (tt_xatom == None) {
374                 return(0);
375         }
376         CALLX11(XGetWindowProperty)(priv->xd,  DefaultRootWindow(priv->xd),
377                                     tt_xatom,
378                                     0, 20, False, XA_STRING,
379                                     &anatom, &format, &items, &left_to_read,
380                                     &val);      
381         if (val == (unsigned char *)0) {
382                 return(0);
383         }
384         pval = (char *)val;
385         CALLX11(XFree)((caddr_t)val);
386         return(1);
387 }
388
389
390 // 
391 // Parse a string in X11 "display" format and return the host and server
392 // number found. If parse errors occur return 0, else return 1 if the
393 // host is not the local host and 2 if the host is the local host. If the
394 // host is specified as "unix" which means use the local host with a Unix
395 // socket connection then return 3.
396 //
397 // XXX - the "hostname" paramater is an artifact of the fix for bug 1118012.
398 //       if/when session IDs are re-worked to use only the process tree
399 //       format internally, instead of the additional use of X session IDs,
400 //       the parse_Xdisplay_string() function should be re-written to
401 //       remove this.
402 // 
403 static int
404 parse_Xdisplay_string(_Tt_string display, _Tt_string &host, pid_t &svnum,_Tt_string &hostname)
405 {
406         int             status = 1;
407         int             offset;
408         _Tt_host_ptr    h;
409         _Tt_host_ptr    localh;
410         char            *dstr;
411         
412         offset = display.index(':');
413         if (offset == -1) {
414                 return(0);
415         }
416         if (offset == 0) {
417
418                 // use local host
419                 (void)_tt_global->get_local_host(h);
420                 host = h->stringaddr();
421                 hostname = h->name();
422                 status = 2;
423         } else {
424                 // Get the hostid portion of an X display of the
425                 // format "hostid:X"
426                 //
427                 host = display.mid(0, offset);
428
429                 if (host == "unix") {
430                         (void)_tt_global->get_local_host(h);
431                         host = h->stringaddr();
432                         hostname = h->name();
433                         status = 3;
434
435                 } else {
436
437                         if (! _tt_global->find_host_byname(host, h)) {
438                                 if (! _tt_global->find_host(host, h, 1)) {
439                                         return(0);
440                                 }
441                         } 
442                         host = h->stringaddr();
443                         hostname = h->name();
444                         if (_tt_global->get_local_host(localh)) {
445                                 if (localh->stringaddr() == host) {
446                                         status = 2;
447                                 }
448                         }
449                 }
450         }
451         // now get the server number
452         dstr = (char *)display + offset + 1;
453         if (*dstr == ':') {
454                 // ugh, a decnet connection
455                 dstr++;
456         }
457
458         long long_svnum;
459         if (1 != sscanf(dstr, "%ld", &long_svnum)) {
460                 svnum = (pid_t)long_svnum;
461                 return(0);
462         } else {
463                 svnum = (pid_t)long_svnum;
464         }
465
466         return(status);
467 }
468
469
470 // 
471 // Returns a suitable name for this desktop session. Changing the value
472 // returned from this method should be done with care because it could
473 // compromise older versions of tooltalk clients/servers from
474 // communicating since a canonical name for a tooltalk desktop session
475 // contains the desktop session name in it and tooltalk session names
476 // indicate to clients how to contact the session.
477 // 
478 _Tt_string _Tt_desktop::
479 session_name(_Tt_string dt_handle)
480 {
481         char            cid[BUFSIZ];
482         _Tt_string      h, hostname;
483         pid_t           s;
484         _Tt_string      name = (char *)0;
485
486         if (0==parse_Xdisplay_string(dt_handle, h, s, hostname)) {
487                 return((char *)0);
488         }
489         sprintf(cid, "X %s %d", (char *)h, s);
490         name = cid;
491         return(name);
492 }
493
494 _Tt_desktop_lock::
495 _Tt_desktop_lock()
496 {
497 }
498
499 _Tt_desktop_lock::
500 _Tt_desktop_lock( const _Tt_desktop_ptr &dt )
501 {
502         _dt = dt;
503         if (! _dt.is_null()) {
504                 _dt->lock();
505         }
506 }
507
508 _Tt_desktop_lock::
509 ~_Tt_desktop_lock()
510 {
511         if (! _dt.is_null()) {
512                 _dt->unlock();
513         }
514 }