61130d269edcaa92d535c5f4a5494de55fd01e1d
[oweals/cde.git] / cde / lib / tt / lib / tttk / ttdesktop.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: ttdesktop.C /main/4 1999/10/14 19:06:10 mgreess $                                                   
28 /*
29  * Copyright 1993 Sun Microsystems, Inc.  All rights reserved.
30  */
31 #include "tt_options.h"
32 #include <stdio.h>
33 #include <errno.h>
34 #include <stdlib.h>
35 #include <sys/utsname.h>
36 #include <limits.h>
37 #include <sys/time.h>
38 #include <sys/resource.h>
39 #include <locale.h>
40 #include <unistd.h>
41 #include <errno.h>
42 #if defined(OPT_SYSINFO)
43 #include <sys/systeminfo.h>
44 #endif
45 #include <netdb.h>      // MAXHOSTNAMELEN
46 #include <sys/param.h>  // MAXHOSTNAMELEN on AIX
47 // Because otherwise e.g. XtNiconic cashes out as XtShellStrings[nnn]
48 #define XTSTRINGDEFINES
49 #include <X11/Intrinsic.h>
50 #include <X11/StringDefs.h>
51 #include <X11/Shell.h>
52 #include "api/c/tt_c.h"
53 #include "api/c/api_api.h"
54 #include "util/tt_Xlib.h"
55 #include "util/tt_port.h"
56 #include "tttk/tttk.h"
57 #include "tttk/tttk2free.h"
58 #include "tttk/tttkutils.h"
59 #include "tttk/tttkmessage.h"
60 #include "tttk/tttkpattern.h"
61 #include "tttk/ttdtprocid.h"
62 #include "tttk/ttdesktop.h"
63
64 extern _TtDtProcid *_ttdtme;
65
66 // These should be in a .h file...?
67
68 static const char *_Tt_categories[] = {
69         "LC_CTYPE",
70         "LC_NUMERIC",
71         "LC_TIME",
72         "LC_COLLATE",
73         "LC_MESSAGES",
74         "LC_MONETARY",
75         "LC_ALL",
76         0
77 };
78
79 //
80 // Create a message addressed to our commissioner
81 //
82 static Tt_message
83 _ttDesktopMessageCreate(
84         Tt_message              commission,
85         Tt_class                theClass,
86         const char             *handler,
87         Tttk_op                 op,
88         _TtDtMessageCB          toolkitCB,
89         void                   *clientCB,
90         void                   *clientData
91 )
92 {
93         const char *_handler = handler;
94         _TttkItem2Free fuse;
95         if ((handler == 0) && (commission != 0)) {
96                 _handler = tt_message_sender( commission );
97                 fuse = (caddr_t)_handler;
98         }
99         return _ttDtPMessageCreate( commission, theClass, TT_SESSION,
100                                     handler, op, toolkitCB,
101                                     clientCB, clientData );
102 }
103
104 //
105 // Add commission id and send, if appropriate
106 //
107 Tt_message
108 _ttDesktopMessageFinish(
109         Tt_message              msg,
110         Tt_message              commission,
111         int                     send
112 )
113 {
114         _TttkItem2Free fuse = msg;
115         Tt_status status;
116         if (commission != 0) {
117                 char *id = _tttk_message_id( commission );
118                 status = tt_message_arg_add( msg, TT_IN, Tttk_message_id, id );
119                 tt_free( id );
120                 if (status != TT_OK) {
121                         return (Tt_message)tt_error_pointer( status );
122                 }
123         }
124         if (send) {
125                 status = tt_message_send( msg );
126                 if (status != TT_OK) {
127                         return (Tt_message)tt_error_pointer( status );
128                 }
129         }
130         fuse = (caddr_t)0;
131         return msg;
132 }
133
134 //
135 // Add commission id and register in session, if appropriate
136 //
137 Tt_pattern
138 _ttDesktopPatternFinish(
139         Tt_pattern      pat,
140         Tt_message      commission,
141         int             register_it
142 )
143 {
144         _TttkItem2Free fuse = pat;
145         Tt_status status;
146         if (commission != 0) {
147                 char *id = _tttk_message_id( commission );
148                 status = tt_pattern_arg_add( pat, TT_IN, Tttk_message_id, id );
149                 tt_free( id );
150                 if (status != TT_OK) {
151                         return (Tt_pattern)tt_error_pointer( status );
152                 }
153                 tt_pattern_user_set( pat, _TttkContractKey, commission );
154         }
155         if (register_it) {
156                 status = tt_pattern_register( pat );
157                 if (status != TT_OK) {
158                         return (Tt_pattern)tt_error_pointer( status );
159                 }
160         }
161         fuse = (caddr_t)0;
162         return pat;
163 }
164
165 //
166 // Returns widget if it is a realized shell and mappedWhenManaged, else 0
167 //
168 Widget
169 _ttdt_realized_widget(
170         void *widget,
171         int   mappedWhenManaged_Shell
172 )
173 {
174         Widget _widget = (Widget)widget;
175         if (_widget == 0) return _widget;
176         if ((! _tt_load_xt()) || (! _tt_load_xlib())) {
177                 return 0;
178         }
179         if (! CALLXT(XtIsRealized)( _widget )) {
180                 return 0;
181         }
182         if (! mappedWhenManaged_Shell) {
183                 return _widget;
184         }
185         Boolean mappedWhenManaged = False;
186         CALLXT(XtVaGetValues)( _widget,
187                                XtNmappedWhenManaged, &mappedWhenManaged, 0 );
188         if (! mappedWhenManaged) {
189                 return 0;
190         }
191         return _widget;
192         /*
193         WidgetClass shellClass =
194                 (WidgetClass)XTSYM(applicationShellWidgetClass);
195         if (CALLXT(XtIsSubclass)( _widget, shellClass )) { // XXX never true!?
196                 return _widget;
197         }
198         return 0;
199         */
200 }
201
202 //
203 // Parse a request and pass it to the user's contract callback.
204 //
205 Tt_message
206 _ttdt_contract_cb(
207         Tt_message      msg,
208         Tt_pattern      pat,
209         void           *clientCB,
210         void           *clientData
211 )
212 {
213         Ttdt_contract_cb _cb = (Ttdt_contract_cb)clientCB;
214         char *opString = tt_message_op( msg );
215         Tttk_op op = tttk_string_op( opString );
216         tt_free( opString );
217         Boolean sensitive = True;
218         int silent, force;
219         static Atom wmProtocols = 0;
220         static Atom wmDeleteWindow = 0;
221         switch (op) {
222                 Widget widget;
223                 Display *dpy;
224                 Window win;
225                 XClientMessageEvent ev;
226             case TTDT_GET_STATUS:
227                 // Fill in the answers we know
228                 tt_message_arg_val_set( msg, 1, _ttdtme->vendor() );
229                 tt_message_arg_val_set( msg, 2, _ttdtme->toolname() );
230                 tt_message_arg_val_set( msg, 3, _ttdtme->version() );
231                 break;
232             case TTDT_QUIT:
233             case TTDT_PAUSE:
234             case TTDT_RESUME:
235                 if (_cb != 0) {
236                         break;
237                 }
238                 widget = _ttdt_realized_widget( clientData );
239                 if (widget == 0) {
240                         tttk_message_fail( msg, TT_DESKTOP_ENOTSUP, 0, 0 );
241                         return 0;
242                 }
243                 dpy = (Display *)CALLXT(XtDisplay)( widget );
244                 win = CALLXT(XtWindow)( widget );
245                 if (widget == 0) {
246                         tttk_message_fail( msg, TT_DESKTOP_ENOTSUP, 0, 0 );
247                         return 0;
248                 }
249                 switch (op) {
250                     case TTDT_QUIT:
251                         silent = _tttk_message_arg_ival( msg, 0, 0 );
252                         force  = _tttk_message_arg_ival( msg, 0, 0 );
253                         if (silent || force) {
254                                 //
255                                 // tttk cannot guarantee that the application
256                                 // will neither block on user input nor
257                                 // cancel the WM_DELETE_WINDOW
258                                 //
259                                 tttk_message_fail( msg, TT_DESKTOP_ENOTSUP,
260                                                    0, 0 );
261                                 return 0;
262                         }
263                         ev.type = ClientMessage;
264                         ev.window = win;
265                         if (wmProtocols == 0) {
266                                 wmProtocols = CALLX11(XInternAtom)( dpy,
267                                                 "WM_PROTOCOLS", False );
268                                 wmDeleteWindow = CALLX11(XInternAtom)( dpy,
269                                                 "WM_DELETE_WINDOW", False );
270                         }
271                         ev.message_type = wmProtocols;
272                         ev.format = 32;
273                         ev.data.l[0] = wmDeleteWindow;
274                         ev.data.l[1] = 0; //XtLastTimestampProcessed?
275                         CALLX11(XSendEvent)( dpy, win, False, 0L, (XEvent*)&ev );
276                         tt_message_reply( msg );
277                         tt_message_destroy( msg );
278                         return 0;
279                     case TTDT_PAUSE:
280                         sensitive = False;
281                     case TTDT_RESUME:
282                         if (CALLXT(XtIsSensitive)( widget ) == sensitive) {
283                                 tt_message_status_set(msg,TT_DESKTOP_EALREADY);
284                         } else {
285                                 CALLXT(XtSetSensitive)( widget, sensitive );
286                         }
287                         tt_message_reply( msg );
288                         tt_message_destroy( msg );
289                         return 0;
290                 }
291                 break;
292         }
293         if (_cb == 0) {
294                 return msg;
295         }
296         return (*_cb)( msg, clientData, _tttk_pattern_contract( pat ) );
297 }
298
299 //
300 // Create a pattern for a desktop message
301 //
302 Tt_pattern
303 _ttdt_pat(
304         Tttk_op                 op,
305         _TtDtMessageCB          internalCB,
306         Tt_category             category,
307         Tt_message              commission,
308         void                   *clientCB,
309         void                   *clientdata,
310         int                     register_it
311 )
312 {
313         Tt_pattern pat = _ttDtPatternCreate( category, TT_SESSION,
314                                              register_it, 0, op,
315                                              internalCB, clientCB,
316                                              clientdata, 0 );
317         Tt_status status = tt_ptr_error( pat );
318         if (status != TT_OK) {
319                 return pat;
320         }
321         _TttkItem2Free fuse = pat;
322         Tt_mode mode = TT_OUT;
323         switch (op) {
324             case TTDT_SET_XINFO:
325                 mode = TT_IN;
326             case TTDT_GET_XINFO:
327                 status = tt_pattern_arg_add( pat, mode, Tttk_string, 0 );
328                 if (status != TT_OK) {
329                         return (Tt_pattern)tt_error_pointer( status );
330                 }
331                 status = tt_pattern_arg_add( pat, mode, Tttk_string, 0 );
332                 if (status != TT_OK) {
333                         return (Tt_pattern)tt_error_pointer( status );
334                 }
335                 status = tt_pattern_arg_add( pat, mode, Tttk_integer, 0 );
336                 if (status != TT_OK) {
337                         return (Tt_pattern)tt_error_pointer( status );
338                 }
339                 break;
340             case TTDT_SET_GEOMETRY:
341                 mode = TT_INOUT;
342             case TTDT_GET_GEOMETRY:
343                 status = tt_pattern_arg_add( pat, mode, Tttk_width, 0 );
344                 if (status != TT_OK) {
345                         return (Tt_pattern)tt_error_pointer( status );
346                 }
347                 status = tt_pattern_arg_add( pat, mode, Tttk_height, 0 );
348                 if (status != TT_OK) {
349                         return (Tt_pattern)tt_error_pointer( status );
350                 }
351                 status = tt_pattern_arg_add( pat, mode, Tttk_xoffset, 0 );
352                 if (status != TT_OK) {
353                         return (Tt_pattern)tt_error_pointer( status );
354                 }
355                 status = tt_pattern_arg_add( pat, mode, Tttk_yoffset, 0 );
356                 if (status != TT_OK) {
357                         return (Tt_pattern)tt_error_pointer( status );
358                 }
359                 break;
360             case TTDT_SET_ICONIFIED:
361                 mode = TT_IN;
362             case TTDT_GET_ICONIFIED:
363                 status = tt_pattern_arg_add( pat, mode, Tttk_boolean, 0 );
364                 if (status != TT_OK) {
365                         return (Tt_pattern)tt_error_pointer( status );
366                 }
367                 break;
368             case TTDT_QUIT:
369                 status = tt_pattern_arg_add( pat, TT_IN, Tttk_boolean, 0 );
370                 if (status != TT_OK) {
371                         return (Tt_pattern)tt_error_pointer( status );
372                 }
373                 status = tt_pattern_arg_add( pat, TT_IN, Tttk_boolean, 0 );
374                 if (status != TT_OK) {
375                         return (Tt_pattern)tt_error_pointer( status );
376                 }
377                 break;
378             case TTDT_STATUS:
379                 mode = TT_IN;
380             case TTDT_GET_STATUS:
381                 status = tt_pattern_arg_add( pat, mode, Tttk_string, 0 );
382                 if (status != TT_OK) {
383                         return (Tt_pattern)tt_error_pointer( status );
384                 }
385                 status = tt_pattern_arg_add( pat, mode, Tttk_string, 0 );
386                 if (status != TT_OK) {
387                         return (Tt_pattern)tt_error_pointer( status );
388                 }
389                 status = tt_pattern_arg_add( pat, mode, Tttk_string, 0 );
390                 if (status != TT_OK) {
391                         return (Tt_pattern)tt_error_pointer( status );
392                 }
393                 status = tt_pattern_arg_add( pat, mode, Tttk_string, 0 );
394                 if (status != TT_OK) {
395                         return (Tt_pattern)tt_error_pointer( status );
396                 }
397                 break;
398             case TTDT_DO_COMMAND:
399                 status = tt_pattern_arg_add( pat, TT_IN, Tttk_string, 0 );
400                 if (status != TT_OK) {
401                         return (Tt_pattern)tt_error_pointer( status );
402                 }
403                 status = tt_pattern_arg_add( pat, TT_OUT, Tttk_string, 0 );
404                 if (status != TT_OK) {
405                         return (Tt_pattern)tt_error_pointer( status );
406                 }
407                 break;
408             case TTDT_PAUSE:
409             case TTDT_RESUME:
410                 // No args
411                 break;
412         }
413         fuse = (caddr_t)0;
414         return _ttDesktopPatternFinish( pat, commission, register_it );
415 }
416
417 static Tt_message
418 _ttDtStarted(
419         Tttk_op         op,
420         Tt_message      context,
421         const char     *toolName,
422         const char     *vendor,
423         const char     *version,
424         int             sendAndDestroy
425 )
426 {
427         Tt_message msg = tttk_message_create( context, TT_NOTICE, TT_SESSION, 0,
428                                               _ttDtOp( op ), 0 );
429         Tt_status status = tt_ptr_error( msg );
430         if (status != TT_OK) {
431                 return msg;
432         }
433         tt_message_arg_add( msg, TT_IN, Tttk_string, toolName );
434         tt_message_arg_add( msg, TT_IN, Tttk_string, vendor );
435         tt_message_arg_add( msg, TT_IN, Tttk_string, version );
436         if (! sendAndDestroy) {
437                 return msg;
438         }
439         status = tt_message_send( msg );
440         if (status != TT_OK) {
441                 tttk_message_destroy( msg );
442                 return (Tt_message)tt_error_pointer( status );
443         }
444         tttk_message_destroy( msg );
445         return 0;
446 }
447
448 Tt_message
449 ttdt_Started(
450         Tt_message      context,
451         const char     *toolName,
452         const char     *vendor,
453         const char     *version,
454         int             sendAndDestroy
455 )
456 {
457         return _ttDtStarted( TTDT_STARTED, context, toolName, vendor, version,
458                              sendAndDestroy );
459 }
460
461 Tt_message
462 ttdt_Stopped(
463         Tt_message      context,
464         const char     *toolName,
465         const char     *vendor,
466         const char     *version,
467         int             sendAndDestroy
468 )
469 {
470         return _ttDtStarted( TTDT_STOPPED, context, toolName, vendor, version,
471                              sendAndDestroy );
472 }
473
474 Tt_message
475 ttdt_Status(
476         Tt_message      context,
477         Tt_message      commission,
478         const char     *statusString,
479         const char     *toolName,
480         const char     *vendor,
481         const char     *version,
482         int             sendAndDestroy
483 )
484 {
485         char *handler = 0;
486         if (commission != 0) {
487                 handler = tt_message_sender( commission );
488         }
489         Tt_message msg = _ttDtPMessageCreate( context, TT_NOTICE, TT_SESSION,
490                                         handler, TTDT_STATUS, 0, 0, 0 );
491         tt_free( handler );
492         Tt_status status = tt_ptr_error( msg );
493         if (status != TT_OK) {
494                 return msg;
495         }
496         tt_message_arg_add( msg, TT_IN, Tttk_string, statusString );
497         tt_message_arg_add( msg, TT_IN, Tttk_string, toolName );
498         tt_message_arg_add( msg, TT_IN, Tttk_string, vendor );
499         tt_message_arg_add( msg, TT_IN, Tttk_string, version );
500         msg = _ttDesktopMessageFinish( msg, commission, sendAndDestroy );
501         status = tt_ptr_error( msg );
502         if (sendAndDestroy && (tt_ptr_error( msg ) == TT_OK)) {
503                 tttk_message_destroy( msg );
504                 return 0;
505         }
506         return msg;
507 }
508
509 //
510 // Returns X11 geometry of a widget
511 //
512 static int
513 _ttXGetGeometry(
514         Widget          widget,
515         int            *w,
516         int            *h,
517         int            *x,
518         int            *y
519 )
520 {
521         if ((! _tt_load_xt()) || (! _tt_load_xlib())) {
522                 return 0;
523         }
524         XWindowAttributes       attrs;
525         Window                  junkwin;
526         Display                *dpy = (Display *)CALLXT(XtDisplay)( widget );
527         Window                  win = CALLXT(XtWindow)( widget );
528         int                     rx, ry;
529         if (! CALLX11(XGetWindowAttributes)( dpy, win, &attrs )) {
530                 return 0;
531         }
532         // XXX x,y still seem to be a little off
533         CALLX11(XTranslateCoordinates)( dpy, win, attrs.root, 
534                                         -attrs.border_width,
535                                         -attrs.border_width,
536                                         &rx, &ry, &junkwin );
537         *x = rx;
538         *y = ry;
539         *w = attrs.width;
540         *h = attrs.height;
541         return 1;
542 }
543
544 //
545 // Help out on Set_Geometry requests and edicts
546 //
547 static Tt_message
548 _ttdt_do_Set_Geometry(
549         Tt_message      msg,
550         void           *clientData,
551         int            *width,
552         int            *height,
553         int            *xOffset,
554         int            *yOffset
555 )
556 {
557         _TttkItem2Free fuse = msg;
558         Widget widget = _ttdt_realized_widget( clientData );
559         if (widget == 0) {
560                 tttk_message_fail( msg, TT_DESKTOP_ENOTSUP, 0, 0 );
561                 return 0;
562         }
563         int newX, newY, newW, newH;
564         if (! _ttXGetGeometry( widget, &newW, &newH, &newX, &newY )) {
565                 tttk_message_fail( msg, TT_DESKTOP_ENOTSUP, 0, 0 );
566                 return 0;
567         }
568         if (*width > 0) {
569                 newW = *width;
570         }
571         if (*height > 0) {
572                 newH = *height;
573         }
574         if (*xOffset != INT_MAX) {
575                 newX = *xOffset;
576         }
577         if (*yOffset != INT_MAX) {
578                 newY = *yOffset;
579         }
580         CALLX11(XMoveResizeWindow)( CALLXT(XtDisplay)( widget),
581                                     CALLXT(XtWindow)( widget ),
582                                     newX, newY, newW, newH );
583         fuse = (caddr_t)0;
584         return msg;
585 }
586
587 //
588 // Pattern callback for {GS}et_Geometry requests and Set_Geometry edicts
589 //
590 Tt_message
591 _ttdt_do_GSet_Geometry(
592         Tt_message      msg,
593         void           *clientData,
594         Tt_message      ,
595         int            *width,
596         int            *height,
597         int            *xOffset,
598         int            *yOffset
599 )
600 {
601         _TttkItem2Free fuse = msg;
602         Widget widget = _ttdt_realized_widget( clientData );
603         if (widget == 0) {
604                 tttk_message_fail( msg, TT_DESKTOP_ENOTSUP, 0, 0 );
605                 return 0;
606         }
607         char *ops = tt_message_op( msg );
608         Tttk_op op = tttk_string_op( ops );
609         tt_free( ops );
610         Tt_status status;
611         if ( op == TTDT_SET_GEOMETRY) {
612                 msg = _ttdt_do_Set_Geometry( msg, clientData, width,
613                                              height, xOffset, yOffset );
614                 status = tt_ptr_error( msg );
615                 if ((status != TT_OK) || (msg == 0)) {
616                         return msg;
617                 }
618         }
619         int x, y, w, h;
620         if (! _ttXGetGeometry( widget, &w, &h, &x, &y )) {
621                 tttk_message_fail( msg, TT_DESKTOP_ENOTSUP, 0, 0 );
622                 return 0;
623         }
624         *width = w;
625         *height = h;
626         *xOffset = x;
627         *yOffset = y;
628         fuse = (caddr_t)0;
629         return msg;
630 }
631
632 //
633 // Parse Get_Geometry reply and pass it to user callback.
634 //
635 static Tt_message
636 _ttdt_Geometry_in_cb(
637         Tt_message      msg,
638         Tt_pattern      ,
639         void           *clientCB,
640         void           *clientData
641 )
642 {
643         if (! _tttk_message_in_final_state( msg )) {
644                 // Not in final state; our address space is probably handler
645                 return msg;
646         }
647         _TttkItem2Free fuse = msg;
648         int width   = _tttk_message_arg_ival( msg, 0, -1 );
649         int height  = _tttk_message_arg_ival( msg, 1, -1 );
650         int xOffset = _tttk_message_arg_ival( msg, 2, INT_MAX );
651         int yOffset = _tttk_message_arg_ival( msg, 3, INT_MAX );
652         Ttdt_Geometry_in_cb _cb = (Ttdt_Geometry_in_cb)clientCB;
653         fuse = (caddr_t)0; // prevent message destruction
654         //
655         // A Ttdt_Geometry_in_cb is never involved in a pattern,
656         // so contract is always 0.
657         //
658         msg = (*_cb)( msg, clientData, 0, width, height, xOffset, yOffset );
659         return msg;
660 }
661
662 //
663 // Create and optionally send a {GS}et_Geometry request or edict
664 //
665 static Tt_message
666 _ttdt_GSet_Geometry(
667         Tttk_op                 op,
668         const char             *handler,
669         Tt_message              commission,
670         Tt_class                theClass,
671         Ttdt_Geometry_in_cb     callback,
672         void                   *clientdata,
673         int                     w,
674         int                     h,
675         int                     x,
676         int                     y,
677         int                     send
678 )
679 {
680         Tt_message msg = _ttDesktopMessageCreate( commission, theClass,
681                                 handler, op, _ttdt_Geometry_in_cb,
682                                 callback, clientdata );
683         Tt_status status = tt_ptr_error( msg );
684         if (status != TT_OK) {
685                 return msg;
686         }
687         _TttkItem2Free fuse = msg;
688         if (op == TTDT_GET_GEOMETRY) {
689                 status = tt_message_arg_add( msg, TT_OUT, Tttk_width, 0 );
690                 if (status != TT_OK) {
691                         return (Tt_message)tt_error_pointer( status );
692                 }
693                 status = tt_message_arg_add( msg, TT_OUT, Tttk_height, 0 );
694                 if (status != TT_OK) {
695                         return (Tt_message)tt_error_pointer( status );
696                 }
697                 status = tt_message_arg_add( msg, TT_OUT, Tttk_xoffset, 0 );
698                 if (status != TT_OK) {
699                         return (Tt_message)tt_error_pointer( status );
700                 }
701                 status = tt_message_arg_add( msg, TT_OUT, Tttk_yoffset, 0 );
702                 if (status != TT_OK) {
703                         return (Tt_message)tt_error_pointer( status );
704                 }
705         } else {
706                 status = tt_message_iarg_add( msg, TT_INOUT, Tttk_width, w );
707                 if (status != TT_OK) {
708                         return (Tt_message)tt_error_pointer( status );
709                 }
710                 status = tt_message_iarg_add( msg, TT_INOUT, Tttk_height, h );
711                 if (status != TT_OK) {
712                         return (Tt_message)tt_error_pointer( status );
713                 }
714                 status = tt_message_iarg_add( msg, TT_INOUT, Tttk_xoffset, x );
715                 if (status != TT_OK) {
716                         return (Tt_message)tt_error_pointer( status );
717                 }
718                 status = tt_message_iarg_add( msg, TT_INOUT, Tttk_yoffset, y );
719                 if (status != TT_OK) {
720                         return (Tt_message)tt_error_pointer( status );
721                 }
722         }
723         fuse = (caddr_t)0;
724         return _ttDesktopMessageFinish( msg, commission, send );
725 }
726
727 Tt_message
728 ttdt_Get_Geometry(
729         const char             *handler,
730         Tt_message              commission,
731         Ttdt_Geometry_in_cb     callback,
732         void                   *clientdata,
733         int                     send
734 )
735 {
736         return _ttdt_GSet_Geometry( TTDT_GET_GEOMETRY, handler, commission,
737                                     TT_REQUEST, callback, clientdata,
738                                     0, 0, 0, 0, send );
739 }
740
741 Tt_message
742 ttdt_Set_Geometry(
743         const char             *handler,
744         Tt_message              commission,
745         Tt_class                theClass,
746         Ttdt_Geometry_in_cb     callback,
747         void                   *clientdata,
748         int                     width,
749         int                     height,
750         int                     xoffset,
751         int                     yoffset,
752         int                     send
753 )
754 {
755         return _ttdt_GSet_Geometry( TTDT_SET_GEOMETRY, handler, commission,
756                                     theClass, callback, clientdata,
757                                     width, height, xoffset, yoffset, send );
758 }
759
760 //
761 // Parse {GS}et_Geometry request, pass it to user callback,
762 // and optionally fill in and send the reply.
763 //
764 static Tt_message
765 _ttdt_Geometry_out_cb(
766         Tt_message      msg,
767         Tt_pattern      pat,
768         void           *clientCB,
769         void           *clientData
770 )
771 {
772         _TttkItem2Free fuse = msg;
773         int width   = _tttk_message_arg_ival( msg, 0, -1 );
774         int height  = _tttk_message_arg_ival( msg, 1, -1 );
775         int xOffset = _tttk_message_arg_ival( msg, 2, INT_MAX );
776         int yOffset = _tttk_message_arg_ival( msg, 3, INT_MAX );
777         Ttdt_Geometry_out_cb _cb = (Ttdt_Geometry_out_cb)clientCB;
778         Tt_message contract = _tttk_pattern_contract( pat );
779         msg = (*_cb)( msg, clientData, contract, &width, &height, &xOffset,
780                       &yOffset );
781         Tt_status status = tt_ptr_error( msg );
782         if ((status != TT_OK) || (msg == 0)) {
783                 // user already replied or failed, and destroyed
784                 fuse = (caddr_t)0;
785                 return msg;
786         }
787         if (width != -1) {
788                 tt_message_arg_ival_set( msg, 0, width );
789         }
790         if (height != -1) {
791                 tt_message_arg_ival_set( msg, 1, height );
792         }
793         if (xOffset != INT_MAX) {
794                 tt_message_arg_ival_set( msg, 2, xOffset );
795         }
796         if (yOffset != INT_MAX) {
797                 tt_message_arg_ival_set( msg, 3, yOffset );
798         }
799         status = _tttk_message_reply( msg );
800         if (status != TT_OK) {
801                 return (Tt_message)tt_error_pointer( status );
802         }
803         return 0;
804 }
805
806 Tt_pattern
807 ttdt_Set_Geometry_pat(
808         Tt_category             category,
809         Tt_message              commission,
810         Ttdt_Geometry_out_cb    callback,
811         void                   *clientdata,
812         int                     register_it
813 )
814 {
815         return _ttdt_pat( TTDT_SET_GEOMETRY, _ttdt_Geometry_out_cb,
816                           category, commission,
817                           callback, clientdata, register_it );
818 }
819
820 Tt_pattern
821 ttdt_Get_Geometry_pat(
822         Tt_category             category,
823         Tt_message              commission,
824         Ttdt_Geometry_out_cb    callback,
825         void                   *clientdata,
826         int                     register_it
827 )
828 {
829         return _ttdt_pat( TTDT_GET_GEOMETRY, _ttdt_Geometry_out_cb,
830                           category, commission,
831                           callback, clientdata, register_it );
832 }
833
834 static int
835 _ttdt_is_iconic(
836         Widget widget
837 )
838 {
839         Boolean iconic;
840         // XXX XtNiconic always returns false?!
841         CALLXT(XtVaGetValues)( widget, XtNiconic, &iconic, 0 );
842         return (iconic == TRUE);
843 }
844
845 //
846 // Pattern callback for {GS}et_Iconified, {GS}et_Mapped requests and edicts
847 //
848 Tt_message
849 _ttdt_do_wm_state(
850         Tt_message      msg,
851         void           *clientData,
852         Tt_message      ,
853         int            *iconified_or_mapped
854 )
855 {
856         Widget widget = _ttdt_realized_widget( clientData );
857         if (widget == 0) {
858                 tttk_message_fail( msg, TT_DESKTOP_ENOTSUP, 0, 0 );
859                 return 0;
860         }
861         char *ops = tt_message_op( msg );
862         Tttk_op op = tttk_string_op( ops );
863         tt_free( ops );
864         Display                *dpy = (Display *)CALLXT(XtDisplay)( widget );
865         Window                  win = CALLXT(XtWindow)( widget );
866         int                     iconic;
867         int                     mapped;
868         XWindowAttributes       attribs;
869         switch (op) {
870             case TTDT_SET_ICONIFIED:
871                 CALLX11(XGetWindowAttributes)( dpy, win, &attribs );
872                 if (*iconified_or_mapped) {
873                         int screen = CALLX11(XScreenNumberOfScreen)(
874                                         attribs.screen );
875                         CALLX11(XIconifyWindow)( dpy, win, screen );
876                 } else {
877                         CALLX11(XRaiseWindow)( dpy, win ); // or XMapRaised?
878                 }
879                 // Fall through, to see if it worked
880             case TTDT_GET_ICONIFIED:
881                 iconic = _ttdt_is_iconic( widget );
882                 if (   (op == TTDT_SET_ICONIFIED)
883                     && (*iconified_or_mapped != iconic))
884                 {
885                         tttk_message_fail( msg, TT_ERR_ACCESS, 0, 0 );
886                         return 0;
887                 }
888                 *iconified_or_mapped = iconic;
889                 break;
890             case TTDT_SET_MAPPED:
891                 CALLX11(XGetWindowAttributes)( dpy, win, &attribs );
892                 if (*iconified_or_mapped) {
893                         CALLX11(XMapWindow)( dpy, win );
894                 } else {
895                         int screen = CALLX11(XScreenNumberOfScreen)(
896                                         attribs.screen );
897                         CALLX11(XWithdrawWindow)( dpy, win, screen );
898                 }
899                 // Fall through, to see if it worked
900             case TTDT_GET_MAPPED:
901                 CALLX11(XGetWindowAttributes)( dpy, win, &attribs );
902                 mapped = attribs.map_state != IsUnmapped;
903                 if (   (op == TTDT_SET_ICONIFIED)
904                     && (*iconified_or_mapped != mapped))
905                 {
906                         tttk_message_fail( msg, TT_ERR_ACCESS, 0, 0 );
907                         return 0;
908                 }
909                 *iconified_or_mapped = mapped;
910                 break;
911         }
912         return msg;
913 }
914
915 //
916 // Parse Get_Iconified reply and pass it to user callback.
917 //
918 static Tt_message
919 _ttdt_Iconified_in_cb(
920         Tt_message      msg,
921         Tt_pattern      pat,
922         void           *clientCB,
923         void           *clientData
924 )
925 {
926         if (! _tttk_message_in_final_state( msg )) {
927                 // Not in final state; our address space is probably handler
928                 return msg;
929         }
930         _TttkItem2Free fuse = msg;
931         int iconified = _tttk_message_arg_ival( msg, 0, 0 );
932         Ttdt_Iconified_in_cb _cb = (Ttdt_Iconified_in_cb)clientCB;
933         fuse = (caddr_t)0; // prevent message destruction
934         msg = (*_cb)( msg, clientData, _tttk_pattern_contract( pat ),
935                       iconified );
936         return msg;
937 }
938
939 //
940 // Create and optionally send a {GS}et_Iconified request or edict
941 //
942 static Tt_message
943 _ttdt_GSet_Iconified(
944         Tttk_op                 op,
945         const char             *handler,
946         Tt_message              commission,
947         Tt_class                theClass,
948         Ttdt_Iconified_in_cb    callback,
949         void                   *clientdata,
950         int                     iconified,
951         int                     send
952 )
953 {
954         Tt_message msg = _ttDesktopMessageCreate( commission, theClass,
955                                 handler, op, _ttdt_Iconified_in_cb,
956                                 callback, clientdata );
957         Tt_status status = tt_ptr_error( msg );
958         if (status != TT_OK) {
959                 return msg;
960         }
961         _TttkItem2Free fuse = msg;
962         if (op == TTDT_GET_ICONIFIED) {
963                 status = tt_message_arg_add( msg, TT_OUT, Tttk_boolean, 0 );
964                 if (status != TT_OK) {
965                         return (Tt_message)tt_error_pointer( status );
966                 }
967         } else {
968                 status = tt_message_iarg_add( msg, TT_IN, Tttk_boolean,
969                                               iconified );
970                 if (status != TT_OK) {
971                         return (Tt_message)tt_error_pointer( status );
972                 }
973         }
974         fuse = (caddr_t)0;
975         return _ttDesktopMessageFinish( msg, commission, send );
976 }
977
978 Tt_message
979 ttdt_Get_Iconified(
980         const char             *handler,
981         Tt_message              commission,
982         Ttdt_Iconified_in_cb    callback,
983         void                   *clientdata,
984         int                     send
985 )
986 {
987         return _ttdt_GSet_Iconified( TTDT_GET_ICONIFIED, handler, commission,
988                                      TT_REQUEST, callback, clientdata,
989                                      0, send );
990 }
991
992 Tt_message
993 ttdt_Set_Iconified(
994         const char             *handler,
995         Tt_message              commission,
996         Tt_class                theClass,
997         Ttdt_Iconified_in_cb    callback,
998         void                   *clientdata,
999         int                     iconified,
1000         int                     send
1001 )
1002 {
1003         return _ttdt_GSet_Iconified( TTDT_SET_ICONIFIED, handler, commission,
1004                                      theClass, callback, clientdata,
1005                                      iconified, send );
1006 }
1007
1008 //
1009 // Parse {GS}et_Iconified request, pass it to user callback,
1010 // and optionally fill in and send the reply.
1011 //
1012 static Tt_message
1013 _ttdt_Iconified_out_cb(
1014         Tt_message      msg,
1015         Tt_pattern      pat,
1016         void           *clientCB,
1017         void           *clientData
1018 )
1019 {
1020         _TttkItem2Free fuse = msg;
1021         int iconified = _tttk_message_arg_ival( msg, 0, 0 );
1022         Ttdt_Iconified_out_cb _cb = (Ttdt_Iconified_out_cb)clientCB;
1023         msg = (*_cb)( msg, clientData, _tttk_pattern_contract( pat ),
1024                       &iconified );
1025         Tt_status status = tt_ptr_error( msg );
1026         if ((status != TT_OK) || (msg == 0)) {
1027                 // user already replied or failed, and destroyed
1028                 fuse = (caddr_t)0;
1029                 return msg;
1030         }
1031         tt_message_arg_ival_set( msg, 0, iconified );
1032         status = _tttk_message_reply( msg );
1033         if (status != TT_OK) {
1034                 return (Tt_message)tt_error_pointer( status );
1035         }
1036         return 0;
1037 }
1038
1039 Tt_pattern
1040 ttdt_Set_Iconified_pat(
1041         Tt_category             category,
1042         Tt_message              commission,
1043         Ttdt_Iconified_out_cb   callback,
1044         void                   *clientdata,
1045         int                     register_it
1046 )
1047 {
1048
1049         return _ttdt_pat( TTDT_SET_ICONIFIED, _ttdt_Iconified_out_cb,
1050                           category, commission,
1051                           callback, clientdata, register_it );
1052 }
1053
1054 Tt_pattern
1055 ttdt_Get_Iconified_pat(
1056         Tt_category             category,
1057         Tt_message              commission,
1058         Ttdt_Iconified_out_cb   callback,
1059         void                   *clientdata,
1060         int                     register_it
1061 )
1062 {
1063         return _ttdt_pat( TTDT_GET_ICONIFIED, _ttdt_Iconified_out_cb,
1064                           category, commission,
1065                           callback, clientdata, register_it );
1066 }
1067
1068 //
1069 // Pattern callback for Set_XInfo requests
1070 //
1071 Tt_message
1072 _ttdt_do_Set_XInfo(
1073         Tt_message      msg,
1074         void           *,
1075         Tt_message      ,
1076         char           *display,
1077         int,            //visual
1078         int             //depth
1079 )
1080 {
1081         if (display == 0) {
1082                 tttk_message_fail( msg, TT_ERR_POINTER, 0, 0 );
1083                 tttk_message_destroy( msg );
1084                 return 0;
1085         }
1086         //
1087         // Cannot change the X display of a widget,
1088         // so just set $DISPLAY
1089         //
1090         _tt_putenv( "DISPLAY", display );
1091         return msg;
1092 }
1093
1094 //
1095 // Map the name of an X11 visual to a visual #defined in X.h
1096 //
1097 static const char *_ttdt_visuals[] = {
1098         "StaticGray",
1099         "GrayScale",
1100         "StaticColor",
1101         "PseudoColor",
1102         "TrueColor",
1103         "DirectColor"
1104 };
1105
1106 static int
1107 _ttDtVisual(
1108         const char *visualName
1109 )
1110 {
1111         if (visualName == 0) {
1112                 return -1;
1113         }
1114         for (int visual = StaticGray; visual <= DirectColor; visual++) {
1115                 if (strcmp( visualName, _ttdt_visuals[visual] ) == 0) {
1116                         return visual;
1117                 }
1118         }
1119         return -1;
1120 }
1121
1122 static const char *
1123 _ttDtVisualString(
1124         int visual
1125 )
1126 {
1127         if ((visual < StaticGray) || (visual > DirectColor)) {
1128                 return 0;
1129         }
1130         return _ttdt_visuals[ visual ];
1131 }
1132
1133 //
1134 // Pattern callback for Get_XInfo requests
1135 //
1136 Tt_message
1137 _ttdt_do_Get_XInfo(
1138         Tt_message      msg,
1139         void           *_widget,
1140         Tt_message      ,
1141         char          **display,
1142         int            *visual,
1143         int            *depth
1144 )
1145 {
1146         _TttkItem2Free fuse = msg;
1147         Widget widget = _ttdt_realized_widget( _widget, 0 );
1148         if (widget == 0) {
1149                 tttk_message_fail( msg, TT_DESKTOP_ENOTSUP, 0, 0 );
1150                 return 0;
1151         }
1152         Display *dpy = (Display *)CALLXT(XtDisplay)( widget );
1153         *display = DisplayString( dpy );
1154         if (*display == 0) {
1155                 tttk_message_fail( msg, TT_ERR_INTERNAL,
1156                                    "DisplayString() == 0", 0 );
1157                 return 0;
1158         }
1159         int is_local = 0;
1160         char *display_and_screen;
1161         if ((*display)[0] == ':') {
1162                 display_and_screen = *display;
1163                 is_local = 1;
1164         } else if (strncmp(*display, "unix:", 5) == 0) {
1165                 display_and_screen = (*display)+4; // !5; we want the colon
1166                 is_local = 1;
1167         }
1168         if (is_local) {
1169                 _Tt_string portable_display = _tt_gethostname();
1170                 portable_display = portable_display.cat(display_and_screen);
1171                 *display = _tt_strdup((char *)portable_display);
1172         } else {
1173                 *display = _tt_strdup( *display );
1174         }
1175         *visual = DefaultVisualOfScreen(
1176                         DefaultScreenOfDisplay( dpy ))->c_class;
1177         *depth = DefaultDepthOfScreen( DefaultScreenOfDisplay( dpy ));
1178         fuse = (caddr_t)0;
1179         return msg;
1180 }
1181
1182 //
1183 // Parse Get_XInfo reply and pass it to user callback.
1184 //
1185 static Tt_message
1186 _ttdt_XInfo_in_cb(
1187         Tt_message      msg,
1188         Tt_pattern      pat,
1189         void           *clientCB,
1190         void           *clientData
1191 )
1192 {
1193         if (! _tttk_message_in_final_state( msg )) {
1194                 // Not in final state; our address space is probably handler
1195                 return msg;
1196         }
1197         _TttkItem2Free fuse = msg;
1198         char *display = _tttk_message_arg_val( msg, 0, 0 );
1199         char *visual = _tttk_message_arg_val( msg, 1, 0 );
1200         int _visual = _ttDtVisual( visual );
1201         tt_free( visual );
1202         int depth = _tttk_message_arg_ival( msg, 2, 0 );
1203         Ttdt_XInfo_in_cb _cb = (Ttdt_XInfo_in_cb)clientCB;
1204         fuse = (caddr_t)0; // prevent message destruction
1205         msg = (*_cb)( msg, clientData, _tttk_pattern_contract( pat ),
1206                       display, _visual, depth );
1207         return msg;
1208 }
1209
1210 //
1211 // Create and optionally send a {GS}et_XInfo request or edict
1212 //
1213 static Tt_message
1214 _ttdt_GSet_XInfo(
1215         Tttk_op                 op,
1216         const char             *handler,
1217         Tt_message              commission,
1218         Tt_class                theClass,
1219         Ttdt_XInfo_in_cb        callback,
1220         void                   *clientdata,
1221         const char             *display,
1222         int                     visual,
1223         int                     depth,
1224         int                     send
1225 )
1226 {
1227         Tt_message msg = _ttDesktopMessageCreate( commission, theClass,
1228                                 handler, op, _ttdt_XInfo_in_cb,
1229                                 callback, clientdata );
1230         Tt_status status = tt_ptr_error( msg );
1231         if (status != TT_OK) {
1232                 return msg;
1233         }
1234         _TttkItem2Free fuse = msg;
1235         if (op == TTDT_GET_XINFO) {
1236                 status = tt_message_arg_add( msg, TT_OUT, Tttk_string, 0 );
1237                 if (status != TT_OK) {
1238                         return (Tt_message)tt_error_pointer( status );
1239                 }
1240                 status = tt_message_arg_add( msg, TT_OUT, Tttk_string, 0 );
1241                 if (status != TT_OK) {
1242                         return (Tt_message)tt_error_pointer( status );
1243                 }
1244                 status = tt_message_arg_add( msg, TT_OUT, Tttk_integer, 0 );
1245                 if (status != TT_OK) {
1246                         return (Tt_message)tt_error_pointer( status );
1247                 }
1248         } else {
1249                 status = tt_message_arg_add( msg, TT_IN, Tttk_string, display);
1250                 if (status != TT_OK) {
1251                         return (Tt_message)tt_error_pointer( status );
1252                 }
1253                 status = tt_message_arg_add( msg, TT_IN, Tttk_string,
1254                                              _ttDtVisualString( visual ));
1255                 if (status != TT_OK) {
1256                         return (Tt_message)tt_error_pointer( status );
1257                 }
1258                 status = tt_message_iarg_add( msg, TT_INOUT, Tttk_integer,
1259                                               depth );
1260                 if (status != TT_OK) {
1261                         return (Tt_message)tt_error_pointer( status );
1262                 }
1263         }
1264         fuse = (caddr_t)0;
1265         return _ttDesktopMessageFinish( msg, commission, send );
1266 }
1267
1268 Tt_message
1269 ttdt_Get_XInfo(
1270         const char             *handler,
1271         Tt_message              commission,
1272         Ttdt_XInfo_in_cb        callback,
1273         void                   *clientdata,
1274         int                     send
1275 )
1276 {
1277         return _ttdt_GSet_XInfo( TTDT_GET_XINFO, handler, commission,
1278                                  TT_REQUEST, callback, clientdata,
1279                                  0, 0, 0, send );
1280 }
1281
1282 Tt_message
1283 ttdt_Set_XInfo(
1284         const char             *handler,
1285         Tt_message              commission,
1286         Tt_class                theClass,
1287         Ttdt_XInfo_in_cb        callback,
1288         void                   *clientdata,
1289         const char             *display,
1290         int                     visual,
1291         int                     depth,
1292         int                     send
1293 )
1294 {
1295         return _ttdt_GSet_XInfo( TTDT_SET_XINFO, handler, commission,
1296                                  theClass, callback, clientdata,
1297                                  display, visual, depth, send );
1298 }
1299
1300 //
1301 // Parse {GS}et_XInfo request, pass it to user callback,
1302 // and optionally fill in and send the reply.
1303 //
1304 static Tt_message
1305 _ttdt_XInfo_out_cb(
1306         Tt_message      msg,
1307         Tt_pattern      pat,
1308         void           *clientCB,
1309         void           *clientData
1310 )
1311 {
1312         _TttkItem2Free fuse = msg;
1313         char *display = _tttk_message_arg_val( msg, 0, 0 );
1314         char *_visual = _tttk_message_arg_val( msg, 1, 0 );
1315         int visual = _ttDtVisual( _visual );
1316         tt_free( _visual );
1317         int depth = _tttk_message_arg_ival( msg, 2, 1 );
1318         Ttdt_XInfo_out_cb _cb = (Ttdt_XInfo_out_cb)clientCB;
1319         msg = (*_cb)( msg, clientData, _tttk_pattern_contract( pat ),
1320                       &display, &visual, &depth );
1321         Tt_status status = tt_ptr_error( msg );
1322         if ((status != TT_OK) || (msg == 0)) {
1323                 // user already replied or failed, and destroyed
1324                 fuse = (caddr_t)0;
1325                 return msg;
1326         }
1327         tt_message_arg_val_set( msg, 0, display );
1328         tt_message_arg_val_set( msg, 1, _ttDtVisualString( visual ));
1329         tt_message_arg_ival_set( msg, 2, depth );
1330         status = _tttk_message_reply( msg );
1331         if (status != TT_OK) {
1332                 return (Tt_message)tt_error_pointer( status );
1333         }
1334         return 0;
1335 }
1336
1337 Tt_pattern
1338 ttdt_Set_XInfo_pat(
1339         Tt_category             category,
1340         Tt_message              commission,
1341         Ttdt_XInfo_in_cb        callback,
1342         void                   *clientdata,
1343         int                     register_it
1344 )
1345 {
1346         return _ttdt_pat( TTDT_SET_XINFO, _ttdt_XInfo_out_cb,
1347                           category, commission,
1348                           callback, clientdata, register_it );
1349 }
1350
1351 Tt_pattern
1352 ttdt_Get_XInfo_pat(
1353         Tt_category             category,
1354         Tt_message              commission,
1355         Ttdt_XInfo_out_cb       callback,
1356         void                   *clientdata,
1357         int                     register_it
1358 )
1359 {
1360         return _ttdt_pat( TTDT_GET_XINFO, _ttdt_XInfo_out_cb,
1361                           category, commission,
1362                           callback, clientdata, register_it );
1363 }
1364
1365 //
1366 // Parse Get_Locale reply and pass it to user callback.
1367 // Also used to parse Set_Locale request and pass it to _ttDtApplyLocale().
1368 //
1369 Tt_message
1370 _ttDtGetLocaleCB(
1371         Tt_message      msg,
1372         Tt_pattern      ,
1373         void           *clientCB,
1374         void           *clientData
1375 )
1376 {
1377         _TttkItem2Free fuse = msg;
1378         int numArgs = tt_message_args_count( msg );
1379         Tt_status status = tt_int_error( numArgs );
1380         if (status != TT_OK) {
1381                 return (Tt_message)tt_error_pointer( status );
1382         }
1383         _TttkList2Free fuses( numArgs + 2 );
1384         int n = numArgs / 2;
1385         char **categories = (char **)tt_malloc( (n + 1) * sizeof(char *) );
1386         char **locales    = (char **)tt_malloc( (n + 1) * sizeof(char *) );
1387         categories[ n ]   = 0;
1388         locales[    n ]   = 0;
1389         //
1390         // We only need these guys until after we call clientCB
1391         //
1392         fuses += (caddr_t)categories;
1393         fuses += (caddr_t)locales;
1394         for (int i = 0; i < n; i++) {
1395                 categories[i] = tt_message_arg_val( msg, 2 * i );
1396                 status = tt_ptr_error( categories[i] );
1397                 if (status != TT_OK) {
1398                         return (Tt_message)tt_error_pointer( status );
1399                 }
1400                 fuses += categories[i];
1401                 locales[i] = tt_message_arg_val( msg, 2 * i + 1 );
1402                 status = tt_ptr_error( locales[i] );
1403                 if (status != TT_OK) {
1404                         return (Tt_message)tt_error_pointer( status );
1405                 }
1406                 fuses += locales[i];
1407         }
1408         fuse = (caddr_t)0; // aborts message destruction
1409         Ttdt_Get_Locale_msg_cb _cb = (Ttdt_Get_Locale_msg_cb)clientCB;
1410         return (*_cb)( msg, clientData,
1411                        (const char **)categories,
1412                        (const char **)locales );
1413 }
1414
1415 //
1416 // Map a locale category name to a locale #defined in locale.h
1417 //
1418 static int
1419 _ttDtCategory(
1420         const char *categoryName
1421 )
1422 {
1423         if (categoryName == 0) {
1424                 return -1;
1425         }
1426         int category = -1;
1427         if (strcmp( categoryName, "LC_CTYPE" ) == 0) {
1428                 category = LC_CTYPE;
1429         } else if (strcmp( categoryName, "LC_NUMERIC" ) == 0) {
1430                 category = LC_NUMERIC;
1431         } else if (strcmp( categoryName, "LC_TIME" ) == 0) {
1432                 category = LC_TIME;
1433         } else if (strcmp( categoryName, "LC_COLLATE" ) == 0) {
1434                 category = LC_COLLATE;
1435         } else if (strcmp( categoryName, "LC_MONETARY" ) == 0) {
1436                 category = LC_MONETARY;
1437         } else if (strcmp( categoryName, "LC_MESSAGES" ) == 0) {
1438                 category = LC_MESSAGES;
1439         } else if (strcmp( categoryName, "LC_ALL" ) == 0) {
1440                 category = LC_ALL;
1441         }
1442         return category;
1443 }
1444
1445 //
1446 // A callback used internally that sets the locales of this
1447 // address space.  If clientData is non-zero, assumes it is a
1448 // DisplayInfo *.
1449 //
1450 static Tt_message
1451 _ttDtApplyLocale(
1452         Tt_message      msg,
1453         void           *clientData,
1454         const char    **categories,
1455         const char    **locales
1456 )
1457 {
1458         DisplayInfo *info = (DisplayInfo *)clientData;
1459         if (info != 0) {
1460                 info->repliesOutStanding--;
1461         }
1462         int i = 0;
1463         while (categories[ i ] != 0) {
1464                 int category = _ttDtCategory( categories[ i ] );
1465                 if (category != -1) {
1466                         setlocale( category, locales[ i ] );
1467                 }
1468                 tt_free( (caddr_t)categories[ i ] );
1469                 tt_free( (caddr_t)locales[ i ] );
1470                 i++;
1471         }
1472         tt_free( (caddr_t)categories );
1473         tt_free( (caddr_t)locales );
1474         if (_tttk_message_am_handling( msg )) {
1475                 // We are being used as a pattern callback
1476                 tt_message_reply( msg );
1477         }
1478         tttk_message_destroy( msg );
1479         return 0;
1480 }
1481
1482 //
1483 // If clientCB is 0, uses _ttDtApplyLocale, q.v. re clientData.
1484 //
1485 Tt_message
1486 ttdt_Get_Locale(
1487         const char         *handler,
1488         Tt_message          commission,
1489         Ttdt_Get_Locale_msg_cb  clientCB,
1490         void               *clientData,
1491         const char        **categories,
1492         int                 send
1493 )
1494 {
1495         const char *_handler = handler;
1496         if ((handler == 0) && (commission != 0)) {
1497                 _handler = tt_message_sender( commission );
1498         }
1499         if (clientCB == 0) {
1500                 clientCB = _ttDtApplyLocale;
1501         }
1502         Tt_message msg = _ttDtPMessageCreate( commission, TT_REQUEST,
1503                                 TT_SESSION, handler,
1504                                 TTDT_GET_LOCALE, _ttDtGetLocaleCB, clientCB,
1505                                 clientData );
1506         Tt_status status = tt_ptr_error( msg );
1507         if (status != TT_OK) {
1508                 return msg;
1509         }
1510         //
1511         // Guarantees that msg will be destroyed when this function returns
1512         //
1513         _TttkItem2Free fuse = msg;
1514         const char **_cats = categories;
1515         if (_cats == 0) {
1516                 _cats = _Tt_categories;
1517         }
1518         while (*_cats != 0) {
1519                 status = tt_message_arg_add( msg, TT_IN, Tttk_string, *_cats );
1520                 if (status != TT_OK) {
1521                         return (Tt_message)tt_error_pointer( status );
1522                 }
1523                 status = tt_message_arg_add( msg, TT_OUT, Tttk_string, 0 );
1524                 if (status != TT_OK) {
1525                         return (Tt_message)tt_error_pointer( status );
1526                 }
1527                 _cats++;
1528         }
1529         if (send) {
1530                 status = tt_message_send( msg );
1531                 if (status != TT_OK) {
1532                         return (Tt_message)tt_error_pointer( status );
1533                 }
1534         }
1535         fuse = (caddr_t)0;
1536         return msg;
1537 }
1538
1539 //
1540 // A TtDtGetSituationMsgCB used internally to chdir() to the path
1541 // returned in a reply to Get_Situation.  If clientData is nonzero,
1542 // assumes it is a DisplayInfo *.
1543 //
1544 static Tt_message
1545 _ttDtApplySituation(
1546         Tt_message      msg,
1547         void           *clientData,
1548         char           *cwd
1549 )
1550 {
1551         DisplayInfo *info = (DisplayInfo *)clientData;
1552         if (info != 0) {
1553                 info->repliesOutStanding--;
1554         }
1555         if ((cwd != 0) && (chdir( cwd ) != 0)) {
1556                 _tt_syslog( 0, LOG_ERR, "_ttDtApplySituation(): "
1557                             "chdir( %s ): %m", cwd );
1558         }
1559         tt_free( (char *)cwd );
1560         if (_tttk_message_am_handling( msg )) {
1561                 // We are being used as a pattern callback
1562                 tt_message_reply( msg );
1563         }
1564         tttk_message_destroy( msg );
1565         return 0;
1566 }
1567
1568 //
1569 // Parse Get_Situation reply and pass it to user callback
1570 //
1571 static Tt_message
1572 _ttDtGetSituationCB(
1573         Tt_message      msg,
1574         Tt_pattern      ,
1575         void           *clientCB,
1576         void           *clientData
1577 )
1578 {
1579         if (! _tttk_message_in_final_state( msg )) {
1580                 // Not in final state; our address space is probably handler
1581                 return msg;
1582         }
1583         _TttkItem2Free fuse = msg;
1584         char *cwd = tt_message_arg_val( msg, 0 );
1585         Tt_status status = tt_ptr_error( cwd );
1586         if (status != TT_OK) {
1587                 return (Tt_message)tt_error_pointer( status );
1588         }
1589         Ttdt_Get_Situation_msg_cb _cb = (Ttdt_Get_Situation_msg_cb)clientCB;
1590         msg = (*_cb)( msg, clientData, cwd );
1591         fuse = (caddr_t)0; // prevent message destruction
1592         return msg;
1593 }
1594
1595 //
1596 // If clientCB is 0, uses _ttDtApplySituation, q.v. re clientData.
1597 //
1598 Tt_message
1599 ttdt_Get_Situation(
1600         const char     *handler,
1601         Tt_message      commission,
1602         Ttdt_Get_Situation_msg_cb       clientCB,
1603         void           *clientData,
1604         int             send
1605 )
1606 {
1607         const char *_handler = handler;
1608         if ((handler == 0) && (commission != 0)) {
1609                 _handler = tt_message_sender( commission );
1610         }
1611         if (clientCB == 0) {
1612                 clientCB = _ttDtApplySituation;
1613         }
1614         Tt_message msg = _ttDtPMessageCreate( commission, TT_REQUEST,
1615                                 TT_SESSION, _handler,
1616                                 TTDT_GET_SITUATION, _ttDtGetSituationCB,
1617                                 clientCB, clientData );
1618         Tt_status status = tt_ptr_error( msg );
1619         if (status != TT_OK) {
1620                 return msg;
1621         }
1622         //
1623         // Guarantees that msg will be destroyed when this function returns
1624         //
1625         _TttkItem2Free fuse = msg;
1626         status = tt_message_arg_add( msg, TT_OUT, Tttk_string, 0 );
1627         if (status != TT_OK) {
1628                 return (Tt_message)tt_error_pointer( status );
1629         }
1630         if (send) {
1631                 status = tt_message_send( msg );
1632                 if (status != TT_OK) {
1633                         return (Tt_message)tt_error_pointer( status );
1634                 }
1635         }
1636         fuse = (caddr_t)0;
1637         return msg;
1638 }
1639
1640 //
1641 // Handle a POSIX-related Desktop request
1642 //
1643 Tt_message
1644 _ttdt_posix_cb(
1645         Tt_message      msg,
1646         Tt_pattern      pat,
1647         void           *,
1648         void           *
1649 )
1650 {       char *opString = tt_message_op( msg );
1651         Tttk_op op = tttk_string_op( opString );
1652         tt_free( opString );
1653         int numArgs = tt_message_args_count( msg );
1654         Tt_status status = tt_int_error( numArgs );
1655         if (status != TT_OK) {
1656                 return (Tt_message)tt_error_pointer( status );
1657         }
1658         switch (op) {
1659                 char           *categoryName, *variable, *value;
1660                 int             category, i;
1661                 struct utsname  names;
1662                 char            buf[ SYS_NMLN ];
1663             case TTDT_SET_LOCALE:
1664                 return _ttDtGetLocaleCB( msg, pat, _ttDtApplyLocale, 0 );
1665             case TTDT_GET_LOCALE:
1666                 for (i = 0; i < numArgs/2; i++) {
1667                         categoryName = _tttk_message_arg_val( msg, 2 * i, 0 );
1668                         if (categoryName == 0) {
1669                                 continue;
1670                         }
1671                         category = _ttDtCategory( categoryName );
1672                         if (category > 0) {
1673                                 tt_message_arg_val_set( msg, 2 * i + 1,
1674                                                 setlocale( category, 0 ));
1675                         }
1676                         tt_free( categoryName );
1677                 }
1678                 tt_message_reply( msg );
1679                 tttk_message_destroy( msg );
1680                 return 0;
1681             case TTDT_SET_ENVIRONMENT:
1682             case TTDT_GET_ENVIRONMENT:
1683                 for (i = 0; i < numArgs/2; i++) {
1684                         variable = _tttk_message_arg_val( msg, 2 * i, 0 );
1685                         if (variable == 0) {
1686                                 continue;
1687                         }
1688                         if (op == TTDT_GET_ENVIRONMENT) {
1689                                 tt_message_arg_val_set( msg, 2 * i + 1,
1690                                                 getenv( variable ));
1691                         } else if (_tttk_message_arg_is_set( msg, 2*i+1 )) {
1692                                 value = _tttk_message_arg_val( msg, 2*i+1, 0 );
1693                                 _tt_putenv( variable, value );
1694                                 tt_free( value );
1695                         } else {
1696                                 _tt_putenv( variable, 0 );
1697                         }
1698                         tt_free( variable );
1699                 }
1700                 tt_message_reply( msg );
1701                 tttk_message_destroy( msg );
1702                 return 0;
1703             case TTDT_SET_SITUATION:
1704                 value = _tttk_message_arg_val( msg, 0, 0 );
1705                 if (value == 0) {
1706                         value = tt_message_file( msg );
1707                         if (tt_ptr_error( value ) != TT_OK) {
1708                                 value = 0;
1709                         }
1710                 }
1711                 if (value == 0) {
1712                         tttk_message_fail( msg, TT_DESKTOP_EPROTO, 0, 1 );
1713                         return 0;
1714                 }
1715                 if (chdir( value ) != 0) {
1716                         tt_free( value );
1717                         tttk_message_fail( msg, _tt_errno_status( errno ),
1718                                            0, 1 );
1719                         return 0;
1720                 }
1721                 tt_free( value );
1722                 tt_message_reply( msg );
1723                 tttk_message_destroy( msg );
1724                 return 0;
1725             case TTDT_GET_SITUATION:
1726                 value = getcwd( 0, MAXPATHLEN );
1727                 if (value == 0) {
1728                         tttk_message_fail( msg, _tt_errno_status( errno ),
1729                                            0, 1 );
1730                         return 0;
1731                 }
1732                 status = tt_message_arg_val_set( msg, 0, value );
1733                 free( value );
1734                 if (status != TT_OK) {
1735                         tttk_message_fail( msg, status, 0, 1 );
1736                         return 0;
1737                 }
1738                 tt_message_reply( msg );
1739                 tttk_message_destroy( msg );
1740                 return 0;
1741             case TTDT_SIGNAL:
1742                 i = _tttk_message_arg_ival( msg, 0, 0 );
1743                 if (i <= 0) {
1744                         tttk_message_fail( msg, TT_DESKTOP_EINVAL, 0, 1 );
1745                         return 0;
1746                 }
1747                 // Reply before actually signalling, in case we die
1748                 tt_message_reply( msg );
1749                 tttk_message_destroy( msg );
1750                 kill( getpid(), i );
1751                 return 0;
1752             case TTDT_GET_SYSINFO:
1753                 if (uname( &names ) < 0) {
1754                         tttk_message_fail( msg, _tt_errno_status( errno ),
1755                                            0, 1 );
1756                         return 0;
1757                 }
1758                 // The first 5 values are from uname and seem pretty standard
1759                 tt_message_arg_val_set( msg, 0, names.sysname );
1760                 tt_message_arg_val_set( msg, 1, names.nodename );
1761                 tt_message_arg_val_set( msg, 2, names.release );
1762                 tt_message_arg_val_set( msg, 3, names.version );
1763                 tt_message_arg_val_set( msg, 4, names.machine );
1764                 // The last 3 are from sysinfo which seems to be SVR4 only.
1765                 // For platforms without the sysinfo call, we just leave
1766                 // the values unset for now, except for the serial
1767                 // number which is available from utsname onHPUX.
1768 #if defined(OPT_SYSINFO)                
1769                 if (sysinfo( SI_ARCHITECTURE, buf, SYS_NMLN ) >= 0) {
1770                         tt_message_arg_val_set( msg, 5, buf );
1771                 }
1772                 if (sysinfo( SI_HW_PROVIDER, buf, SYS_NMLN ) >= 0) {
1773                         tt_message_arg_val_set( msg, 6, buf );
1774                 }
1775                 if (sysinfo( SI_HW_SERIAL, buf, SYS_NMLN ) >= 0) {
1776                         tt_message_arg_val_set( msg, 7, buf );
1777                 }
1778 #elif defined(__hpux) || defined(hpux)
1779                 tt_message_arg_val_set( msg, 7, names.idnumber);
1780 #endif
1781                 tt_message_reply( msg );
1782                 tttk_message_destroy( msg );
1783                 return 0;
1784         }
1785         return msg;
1786 }
1787
1788 //
1789 // Pattern callback for Raise, Lower requests
1790 //
1791 Tt_message
1792 _ttdt_do_RaiseLower(
1793         Tt_message      msg,
1794         void           *_widget,
1795         Tt_message
1796 )
1797 {
1798         Widget widget = _ttdt_realized_widget( _widget, 0 );
1799         if (widget == 0) {
1800                 tttk_message_fail( msg, TT_DESKTOP_ENOTSUP, 0, 1 );
1801                 return 0;
1802         }
1803         char *ops = tt_message_op( msg );
1804         Tttk_op op = tttk_string_op( ops );
1805         tt_free( ops );
1806         Display                *dpy = (Display *)CALLXT(XtDisplay)( widget );
1807         Window                  win = CALLXT(XtWindow)( widget );
1808         switch (op) {
1809             case TTDT_RAISE:
1810                 CALLX11(XRaiseWindow)( dpy, win );
1811                 break;
1812             case TTDT_LOWER:
1813                 CALLX11(XLowerWindow)( dpy, win );
1814                 break;
1815             default:
1816                 tttk_message_fail( msg, TT_DESKTOP_ENOTSUP, 0, 1 );
1817                 return 0;
1818         }
1819         _tttk_message_reply( msg );
1820         tttk_message_destroy( msg );
1821         return 0;
1822 }