Desktop Integration Guide
  Cerca solo questo libro
Scarica il manuale in formato PDF

The ToolTalk Service and DeskSet Integration

7

The ToolTalk services allow your application to exchange messages with DeskSet applications. These messages can be commands to start an application, to load a specified file, or to execute a process on a file or object.
This chapter describes:
  • DeskSet's ToolTalk message protocol
  • Instructions for integrating DeskSet services into your application using ToolTalk
  • An example scenario of ToolTalk services used to communicate between two desktop applications
For information about ToolTalk beyond the scope of this manual, see the ToolTalk User's Guide and the ToolTalk Reference Guide.

7.1 The ToolTalk Messaging Protocol

In the ToolTalk messaging protocol, one process (the requestor) requests Desktop services from another process, (the handler). For example, the requestor may request that another running tool (the handler) prepare to receive some data from the requestor.
When implemented correctly in tools that run on the Solaris Desktop, the ToolTalk protocol guarantees that any two autonomous processes cooperate effectively in observing and responding to such requests. (Refer to "The ToolTalk Message Sets" section below.)

7.1.1 How the Tooltalk Protocol Works

Under the Tooltalk/Message Alliance protocol, a Tooltalk request is sent by a tool or process that needs a service provided by another tool. If a process is not available to handle the request, an appropriate tool is started and the message is delivered to that tool. The receiving tool (handler) then decides either to service it, or to reject or fail it.
The message is failed if it seems to be improperly formed, or is otherwise not legitimate. The recipient or intended handler sets the error status code and fails the message. The message is rejected if it is legitimate and properly formed, but the recipient or intended handler cannot handle it right now. Tooltalk will then look for another handler and will fail the message if one can not be found.
If the message is properly formed and otherwise legitimate, its processing is normally started immediately by the handler (the process that seems to be the intended recipient of the message). While processing, the handler keeps track of two ID codes: the process ID of the requestor, and the message ID. The responder keeps track of these IDs until it is no longer responsible for the message; that is, until it has done something with it, either rejected or performed the requested action. Immediately upon receiving the message and deciding not to reject it, the handler sends a status message, letting the requestor know that the original message is being handled.
Then the handler immediately begins to process the request, which generally requires that it begin to handle data.

7.1.2 New Duties of the Handler

The ToolTalk/Message Alliance protocol gives handlers the responsibility of acquainting themselves with their surroundings when servicing a request. Simply put, the handler must issue requests for any information the need for which is specific to its situation, and thus not included in the initial message. These inquiries are performed with the Get_* messages.
This permits tools with different implementations to interchangeably implement the same message interface. For a given operation, one brand of handler might need to know what host the requestor is on, while another brand might want to know what the value of $PATH is in the requestor's
environment. Instead of enclosing in the initial request the answers to all possible questions a handler might have, handlers are made responsible for making just the inquiries they care about.

7.2 The ToolTalk Message Sets

SunSoft has specified a set of ToolTalk messages commonly used by most Solaris DeskSet applications. (Refer to Appendix F, "The ToolTalk Desktop Services Message Set"). Each application running on the Solaris Desktop, whether a Solaris DeskSet application or a third party application, should be capable of handling these messages.
The following Solaris DeskSet applications support this same core set of ToolTalk messages:
  • Mail Tool
  • Audiotool
  • Text Editor
  • Binder
  • Color Changer
  • Icon Editor
Applications may require more specialized messaging operations for their interaction, and developers can add to the current list of messages. For example, the Solaris Calendar Tool and Mail Tool require three special message types for their interaction. (These messages are discussed below in "Editing with the Open Request.")
There are other standard ToolTalk message sets defined for this release. Developers of multimedia applications will find the Media Exchange message set of interest. (Refer to Appendix G, "The ToolTalk Document and Media Exchange Message Set.")

7.3 Example ToolTalk Messaging Scenarios

Following are two typical scenarios of tools cooperating on the Solaris Desktop, with ToolTalk messages.

7.3.1 Display Request

One scenario arises when a tool (for example, Mailtool) requests that another tool (a text editor) display some of its data. The scenario begins when the Mailtool process (the requestor) sends a message requesting a display of data. The text editor recognizes the message is intended for it, and becomes the handler. It notes the process ID of the requestor, and the message ID of the display request, and immediately sends a message to the requestor indicating that it will service the original message. At this time, it also can send a message to the original requestor asking for additional data regarding the intended display.
When data transmission is complete and the handler has completed the display, it replies to the Display message so the requestor can then clean up any scratch data or other outstanding activities related to the display request.

7.3.2 Edit Request

A different scenario arises when one tool (the requestor) requests that an editor tool (the handler) perform edits on a data file. The sequence might be as follows:
  1. The requestor sends an Edit message to the handler.

  2. The handler takes note of the process ID of the requestor, and the message ID of the Edit message.

    The handler then sends back a Status message, taking responsibility for servicing this request.

  3. The handler may send a series of incremental updates with the Deposit messages to the requestor.

  4. At any time while this edit session is being handled, the requestor can send additional service requests to the same message ID.

    Such as Iconify, Lower or Raise messages to change the visual appearance of the target on the Desktop.

  5. When all desired data has been transferred, requestor replies to the Edit message with the final, modified, data.

    The handler saves the file with all changes.

7.3.3 Editing with the Open Request

A different scenario arises when one tool requests that another tool perform edits on some data controlled by the second tool. For example, a Calendar tool might wish to send data to a Mail Tool; namely, a request to mail a reminder to the other party of a planned meeting. Scenarios such as this caused SunSoft to develop three additional messages: Open, Paste, and Close. The sequence might be as follows:
  1. The requestor sends an Open message to the handler.

    This requests that a data file be made available for edits by the requestor.

  2. The handler takes note of the process ID of the requestor, and replies to the Open request with a bufferID.

  3. The requestor sends a series of Paste messages to the handler.

    These correspond to small editing transactions on the target data file.

  4. The requestor sends a Close message to the handler.

    This indicates that all intended edits are complete.

  5. The handler (Mailtool) can then proceed with its job by displaying the compose window and allowing the user to actually send the email.

7.4 Example Tooltalk Program with Deskset

The source files for this program can be found online in the Solaris 2.2 distribution at $OPENWINHOME/share/src/dig_samples/Tooltalk. This sample code creates a simple yet working editor that can be launched from Mailtool when a user double clicks on a text attachment. To test it out, perform the following steps:
  1. Save your original setup with the command

tt_type_comp -p > save_file

  1. Run make

  2. Run make tooltalk

  3. Exit mailtool and ttsession

  4. In one shelltool:

a. Set the environment variable XENVIRONMENT to be the full path to the Resources file: setenv XENVIRONMENT /usr/openwin/demo/tooltalk/Resources
b. Set your path environment variable to be this directory.
c. Restart ttsession so it has both these environment variables.
  1. Restart mailtool and select a text attachment.

When your testing is done and you want to go back to your original setup, you can give the command:
tt_type_comp save_file

or if you had no local changes to begin with, you can give the command:

rm -rf ~/.tt

7.4.1 Files for this Example

Here is a summary of the files that comprise the example source.
Table 7-1
olit_tt.cThese are the routines that support the GUI.
tt_code.cThese are the routines that initialize tooltalk and set up the other callbacks.
tt_callbacks.cThese are the routines that get called in response to tooltalk and/or GUI events.
types.fileContains tooltalk static ptypes used to identify to Tooltalk that this application should be started for text applications.
ResourcesContains X resources for the GUI.
MakefileThis builds the application and installs the ptypes.

7.4.2 olit_tt.c


  /* olit_tt.c  (continued)  */  
  #include <X11/Intrinsic.h>  
  #include <X11/StringDefs.h>  
  #include <Xol/OpenLook.h>  
  #include <Xol/TextEdit.h>  
  #include <Xol/ScrolledWi.h>  
  #include <Xol/RubberTile.h>  
  #include <Xol/MenuButton.h>  
  #include <Xol/OblongButt.h>  
  #include <Xol/StaticText.h>  
  #include <Xol/DropTarget.h>  
  #include <Xol/Exclusives.h>  
  #include <Xol/RectButton.h>  
  
  #defineTEXT_UNMODIFIEDFALSE  
  #define TEXT_MODIFIEDTRUE  
  
  staticWidgettoplevel, base, textedit;  
  staticchar*saved_text = NULL;  
  
  /* callback to hide the window */  
  hide_frame()  
  {  
       XtPopdown(base);  
  }  
  
  /* callback to expose the window */  
  show_frame()  
  {  
       XtPopup(base, XtGrabNone);  
  }  
  
  /* callback to place the data in the text window */  
  display_data(char *text)  
  {  
       XtVaSetValues(textedit,  
       XtNsourceType,(XtArgVal)OL_STRING_SOURCE,  
       XtNsource,(XtArgVal)text,  
       XtNuserData,(XtArgVal)TEXT_UNMODIFIED,  
       XtNdisplayPosition,(XtArgVal)0,  
       XtNcursorPosition,(XtArgVal)0,  


  /* olit_tt.c  (continued)  */  
       XtNselectStart,(XtArgVal)0,  
       XtNselectEnd,(XtArgVal)0,  
       NULL);  
  
       if (saved_text != NULL)  
       {  
       XtFree(saved_text);  
       }  
  
       OlTextEditCopyBuffer((TextEditWidget)textedit, &saved_text);  
  }  
  
  /* callback to get the data from the text window */  
  Boolean  
  get_data(char **text, int *len)  
  {  
       if (!OlTextEditCopyBuffer((TextEditWidget)textedit, text))  
       {  
       OlWarning("getData: error trying to copy textedit buffer\n");  
       return(FALSE);  
       }  
  
       *len = strlen(*text);  
       return(TRUE);  
  
  }  
  
  /* callback to check if the data in the text window is modified */  
  Boolean  
  data_is_modified(void)  
  {  
       inttext_state;  
  
       XtVaGetValues(textedit, XtNuserData, &text_state, NULL);  
  
       return(text_state);  
  }  
  
  /* callback to restore the data of the text window to its last  
  unmodified  
   * state  
   */  
  restore_data()  
  {  


  /* olit_tt.c  (continued)  */  
       if(data_is_modified())  
       {  
       display_data(saved_text);  
       }  
  }  
  
  /* callback to clear the data in the text window */  
  clear_data()  
  {  
       OlTextEditClearBuffer((TextEditWidget)textedit);  
  }  
  
  /* callback to quit this application */  
  void  
  quit(void)  
  {  
       quit_tt();  
       exit(0);  
  }  
  
  /* Save button callback to save back this data  */  
  static void  
  saveTextCB(Widget w, XtPointer client_data, XtPointer callData)  
  {  
       save_tt();  
  }  
  
  /* Quit button callback to quit  */  
  static void  
  quitTextCB(Widget w, XtPointer client_data, XtPointer callData)  
  {  
       quit_tt();  
       exit(0);  
  }  
  
  /* callback to be called when we get a tooltalk message  */  
  static void  
  handleMessageCB(Widget w, XtPointer client_data, XtPointer  
  callData)  
  {  
       handle_tt_message();  
  }  
  
  /* main initialization routine */  


  /* olit_tt.c  (continued)  */  
  main(int argc, char **argv)  
  {  
       XtAppContext    appContext;  
       Widgetcontrol, scrolledwin;  
       Widgetsave_btn, quit_btn;  
       Widgetblank;  
       intfd;  
  
       /* check if we were started by tooltalk */  
       check_tt_startup(&argc, &argv);  
  
       /* initialize and build the widgets for the application */  
       OlToolkitInitialize((XtPointer) NULL);  
       toplevel = XtAppInitialize(&appContext, "AsciiEdit",  
                (XrmOptionDescList) NULL,  
                0, &argc, argv, (String *) NULL,  
                (ArgList) NULL, 0);  
  
       base = XtVaCreateManagedWidget("base",  
           rubberTileWidgetClass,  
           toplevel,  
           NULL);  
  
       control = XtVaCreateManagedWidget("control",  
           rubberTileWidgetClass,  
           base,  
           NULL);  
  
       save_btn = XtVaCreateManagedWidget("save",  
           oblongButtonWidgetClass,  
           control,  
           NULL);  
  
       blank = XtVaCreateManagedWidget("blank",  
           staticTextWidgetClass,  
           control,  
           NULL);  
  
       quit_btn = XtVaCreateManagedWidget("quit",  
           oblongButtonWidgetClass,  
           control,  
           NULL);  
  
       scrolledwin = XtVaCreateManagedWidget("scrolledwin",  


  /* olit_tt.c  (continued)  */  
           scrolledWindowWidgetClass,  
           base,  
           NULL);  
  
       textedit = XtVaCreateManagedWidget("textedit",  
           textEditWidgetClass,  
           scrolledwin,  
           NULL);  
  
       /* add the callbacks for the save and quit buttons */  
       XtAddCallback(save_btn, XtNselect, saveTextCB, NULL);  
       XtAddCallback(quit_btn, XtNselect, quitTextCB, NULL);  
  
       /* start to handle the tooltalk messages and set the callback  
          when we get messages in  
        */  
       fd = start_handling_messages();  
       XtAppAddInput(appContext, fd, XtInputReadMask,  
  handleMessageCB, NULL);  
  
       /* realize the widgets and start the notification process */  
       XtRealizeWidget(toplevel);  
       XtAppMainLoop(appContext);  
  
  }  

7.4.3 tt_code.c


  /* tt_code.c  (continued)  */  
  #include <desktop/tt_c.h>  
  #include <poll.h>  
  
  externTt_callback_actionxinfo_cb(Tt_message m, Tt_pattern p);  
  externTt_callback_actionlocale_cb(Tt_message m, Tt_pattern p);  
  externTt_callback_actionhandle_desktop(Tt_message m, Tt_pattern  
  p);  
  externTt_callback_actionhandle_display(Tt_message m, Tt_pattern  
  p);  
  externTt_callback_actionhandle_edit(Tt_message m, Tt_pattern p);  


  /* tt_code.c  (continued)  */  
  
  #ifndefDEBUG  
  #defineDPif(0)  
  #else  
  #defineDPif(1)  
  #endif  
  
  Tt_messagesave_msg;/* startup message */  
  
  structstartup  
  {  
       char*new[40];/* pointers for new argv */  
       intargcount;/* count for new argv pointers */  
       intddvl;/* or'ed flag to test for messages */  
  }newargs;  
  
  Tt_status  
  check_tt_startup(int *argc, char ***argv)  
  {  
       structpollfdmyfds;/* structure for poll command */  
       Tt_messagem;/* temporary message */  
       Tt_statusstatus;/* message status holder */  
       char*toolid;/* procid of tool sending msg */  
       int i=0, n, j = 0;/* counters */  
       char**tmp;/* tmp location for argv array */  
       /* open Tooltalk session and check for errors */  
       status = tt_ptr_error(tt_open());  
       if(status != TT_OK)  
       {  
       return(status);  
       }  
  
       /* get and save the incoming message */  
       save_msg = tt_message_receive();  
  
       /* if Tooltalk started us up */  
       if(TT_WRN_START_MESSAGE == tt_message_status(save_msg))  
       {  
       /* argv initalization */  
       newargs.argcount = 0;  
       newargs.ddvl = 0;  
  
       /* continue to deliver messages, I am just working on  
        * this one for a while  


  /* tt_code.c  (continued)  */  
              * If you are using a TT library prior to Solaris 2.2  
  this line  
              * is not available and you will not be able to handle  
  multiple  
              * messages until you reply to the message that started  
  you up.  
        */  
       /* tt_message_accept(save_msg); */  
  
       /* get the procid of the requesting application */  
       toolid = tt_message_sender(save_msg),  
  
       /* request the display, visual & depth */  
       m = tt_message_create();  
       tt_message_address_set(m, TT_HANDLER);  
       tt_message_class_set(m, TT_REQUEST);  
       tt_message_scope_set(m, TT_SESSION);  
       tt_message_session_set(m,  tt_default_session());  
       tt_message_op_set(m, "Get_XInfo");  
       tt_message_handler_set(m, toolid);  
       tt_message_disposition_set(m, TT_DISCARD);  
       tt_message_arg_add(m, TT_OUT, "string", NULL);  
       tt_message_arg_add(m, TT_OUT, "string", NULL);  
       tt_message_arg_add(m, TT_OUT, "integer", "");  
       tt_message_scope_set(m, TT_SESSION);  
       tt_message_callback_add(m, xinfo_cb);  
       tt_message_user_set(m, 0, &newargs);  
       tt_message_send(m);  
  
       /* request the locale info */  
       m = tt_message_create();  
       tt_message_address_set(m, TT_HANDLER);  
       tt_message_class_set(m, TT_REQUEST);  
       tt_message_scope_set(m, TT_SESSION);  
       tt_message_session_set(m,  tt_default_session());  
       tt_message_op_set(m, "Get_Locale");  
       tt_message_handler_set(m, toolid);  
       tt_message_disposition_set(m, TT_DISCARD);  
       tt_message_arg_add(m, TT_OUT, "string", "LC_CTYPE");  
       tt_message_arg_add(m, TT_OUT, "string", NULL);  
       tt_message_arg_add(m, TT_OUT, "string", "LC_TIME");  
       tt_message_arg_add(m, TT_OUT, "string", NULL);  
       tt_message_arg_add(m, TT_OUT, "string", "LC_NUMERIC");  
       tt_message_arg_add(m, TT_OUT, "string", NULL);  


  /* tt_code.c  (continued)  */  
       tt_message_arg_add(m, TT_OUT, "string", "LC_MESSAGES");  
       tt_message_arg_add(m, TT_OUT, "string", NULL);  
       tt_message_scope_set(m, TT_SESSION);  
       tt_message_callback_add(m, locale_cb);  
       tt_message_user_set(m, 0, &newargs);  
       tt_message_send(m);  
  
       /* poll for messages */  
       myfds.fd = tt_fd();  
       myfds.events = POLLIN;  
       while(newargs.ddvl != 3)  
       {  
           poll(&myfds, 1, -1);  
           /* got one */  
           m = tt_message_receive();  
           if(m)  
           {  
                /* it's not one we are looking for */  
                tt_message_reject(m);  
           }  
       }  
  
       /* take all the new args that we got and create a  
        * new argv/argc  
        */  
       n = *argc+newargs.argcount;  
       tmp = (char **)malloc((n+1)*sizeof(char *));  
       for(i = 0 ; i < *argc; i++)  
       {  
           tmp[i] = *argv[i];  
       }  
       for(j = 0; i < n; i++,j++)  
       {  
           tmp[i] = newargs.new[j];  
       }  
       tmp[i] = 0;  
       *argc = n;  
       *argv = tmp;  
       }  
       return(TT_OK);  
  }  
  
  int  
  start_handling_messages()  


  /* tt_code.c  (continued)  */  
  {  
       /* patterns for desktop messages and display/edit messages */  
       Tt_patterndesktop_pat;  
       Tt_patterndisplay_pat;  
       Tt_patternedit_pat;  
       char*op;/* op of saved message */  
  
       /* prepare to handle Desktop messages */  
       desktop_pat = tt_pattern_create();  
       tt_pattern_op_add(desktop_pat, "Set_Mapped");  
       tt_pattern_op_add(desktop_pat, "Quit");  
       tt_pattern_scope_add(desktop_pat, TT_SESSION);  
       tt_pattern_session_add(desktop_pat, tt_default_session());  
       tt_pattern_category_set(desktop_pat, TT_HANDLE);  
       tt_pattern_callback_add(desktop_pat, handle_desktop);  
       tt_pattern_register(desktop_pat);  
  
       /* prepare to handle Display messages */  
       display_pat = tt_pattern_create();  
       tt_pattern_op_add(display_pat, "Display");  
       tt_pattern_arg_add(display_pat, TT_IN, "ISO_Latin_1", NULL);  
       tt_pattern_scope_add(display_pat, TT_SESSION);  
       tt_pattern_session_add(display_pat, tt_default_session());  
       tt_pattern_category_set(display_pat, TT_HANDLE);  
       tt_pattern_class_add(display_pat, TT_REQUEST);  
       tt_pattern_callback_add(display_pat, handle_display);  
       tt_pattern_register(display_pat);  
  
       /* prepare to handle Edit messages */  
       edit_pat = tt_pattern_create();  
       tt_pattern_op_add(edit_pat, "Edit");  
       tt_pattern_arg_add(edit_pat, TT_OUT, "ISO_Latin_1", NULL);  
       tt_pattern_arg_add(edit_pat, TT_INOUT, "ISO_LATIN_1", NULL);  
       tt_pattern_scope_add(edit_pat, TT_SESSION);  
       tt_pattern_session_add(edit_pat, tt_default_session());  
       tt_pattern_category_set(edit_pat, TT_HANDLE);  
       tt_pattern_class_add(edit_pat, TT_REQUEST);  
       tt_pattern_callback_add(edit_pat, handle_edit);  
       tt_pattern_register(edit_pat);  
  
       /* if we have one we haven't handled because we were doing  
        * the window/application setup, handle it now  
        */  
       if(save_msg != NULL)  


  /* tt_code.c  (continued)  */  
       {  
       /* find out its type */  
       op = tt_message_op(save_msg);  
  
       if(strcmp(op, "Display") == 0)/* if Display message */  
       {  
           handle_display(save_msg, display_pat);  
       }  
       else if(strcmp(op, "Edit") == 0)/* if Edit message */  
       {  
           handle_edit(save_msg, edit_pat);;  
       }  
       else /* all others we don't want yet */  
       {  
           tt_message_reject(save_msg);  
           tt_message_destroy(save_msg);  
       }  
       }  
  
       /* return the file descriptor to watch for tooltalk activity  
  on */  
       return(tt_fd());  
  }  

7.4.4 tt_callbacks.c


  /* tt_callbacks.c  (continued)  */  
  #include <desktop/tt_c.h>  
  #include <locale.h>  
  
  #ifndefDEBUG  
  #defineDPif(0)  
  #else  
  #defineDPif(1)  
  #endif  
  
  #defineTT_DESKTOP_ENOENT1538  
  #defineTT_DESKTOP_EINVAL1558  
  #defineTT_DESKTOP_EXITING1697  
  #defineTT_DESKTOP_CANCELED1698  
  #defineTT_DESKTOP_UNMODIFIED1699  


  /* tt_callbacks.c  (continued)  */  
  
  #defineTT_MEDIA_ERR_SIZE1700  
  #defineTT_MEDIA_ERR_FORMAT1701  
  #defineTT_MEDIA_NO_CONTENTS1702  
  
  structstartup  
  {  
       char*new[40];  
       intargcount;  
       intddvl;  
  };  
  
  staticchar*argname[] = {"-display", "-visual", "-depth", "-  
  lc_basiclocale"};  
  
  externvoidshow_frame();  
  externvoidhide_frame();  
  externvoidquit();  
  
  #defineMSG_DISPLAY1  
  #defineMSG_EDIT2  
  
  structcur_msg_state  
  {  
       Tt_messagett_msg;  
       int type;  
       char*msgid;  
  } cur_msg_state = { NULL, NULL};  
  
  void close_out_old_msg(struct cur_msg_state *);  
  
  /* Tooltalk call back to handle the Get_XInfo request */  
  Tt_callback_action  
  xinfo_cb(Tt_message m, Tt_pattern p)  
  {  
       char*display;/* Xdisplay */  
       char*visual;/* Screen visual */  
       intdepth;/* Screen depth */  
       intcount;/* Number of args in this msg */  
       charbuff[10];/* tmp format buffer */  
       structstartup*newargs;/* new argv data structure */  
  
       /* check the state of the returned message */  
       switch(tt_message_state(m))  


  /* tt_callbacks.c  (continued)  */  
       {  
       caseTT_HANDLED:/* everything came back ok */  
  
       /* get the structure to save the argv data to */  
       newargs = tt_message_user(m, 0);  
  
       /* create the display argument */  
       display = tt_message_arg_val(m, 0);  
       if(display)  
       {  
           newargs->new[newargs->argcount++] = "-display";  
           newargs->new[newargs->argcount++] = (char  
  *)strdup(display);  
       }  
  
       /* create the visual argument */  
       visual = tt_message_arg_val(m, 1);  
       if(visual)  
       {  
           newargs->new[newargs->argcount++] = "-visual";  
           newargs->new[newargs->argcount++] = (char  
  *)strdup(visual);  
       }  
  
       /* create the depth argument */  
       tt_message_arg_ival(m, 2, &depth);  
       if(depth > 0)  
       {  
           newargs->new[newargs->argcount++] = "-depth";  
           sprintf(buff, "%d", depth);  
           newargs->new[newargs->argcount++] = (char *)strdup(buff);  
       }  
       break;  
       default:  
       /* just in case something goes wrong */  
       DP printf("xinfo_cb: tt_message_state = %d\n",  
  tt_message_state(m));  
       }  
  
       /* set the lage so we know we got this one */  
       newargs->ddvl |= 1;  
  
       /* clean up the message and return */  
       tt_message_destroy(m);  


  /* tt_callbacks.c  (continued)  */  
       return(TT_CALLBACK_PROCESSED);  
  }  
  
  /* Tooltalk call back to handle the Get_Locale request */  
  Tt_callback_action  
  locale_cb(Tt_message m, Tt_pattern p)  
  {  
       inti;   /* counter */  
       intcount = 0;/* number of locale values */  
       char*cat;/* category */  
       char*locale;/* locale */  
       structstartup*newargs;/* new argv data structure */  
  
       /* check the state of the returned message */  
       switch(tt_message_state(m))  
       {  
       caseTT_HANDLED:/* everything came back ok */  
  
       /* get the structure to save the argv data to */  
       newargs = tt_message_user(m, 0);  
  
       /* get the number of values */  
       count = tt_message_args_count(m);  
  
       /* for each set of category/locale */  
       for(i = 0; i < count/2; i++)  
       {  
           /* get the category and locale info */  
           cat = tt_message_arg_val(m, i);  
           locale = tt_message_arg_val(m, i+1);  
  
           /* if the locale has been set */  
           if(locale)  
           {  
                /* and if the category is one we are  
                 * interested in add the arguments  
                 */  
                if(strcmp(cat, "LC_CTYPE") == 0)  
                {  
                newargs->new[newargs->argcount++] = "-  
  lc_basiclocale";  
                newargs->new[newargs->argcount++] = (char  
  *)strdup(locale);  


  /* tt_callbacks.c  (continued)  */  
                newargs->new[newargs->argcount++] = "-lc_inputlang";  
                newargs->new[newargs->argcount++] = (char  
  *)strdup(locale);  
                }  
                else if(strcmp(cat, "LC_TIME") == 0)  
                {  
                newargs->new[newargs->argcount++] = "-lc_timeformat";  
                newargs->new[newargs->argcount++] = (char  
  *)strdup(locale);  
                }  
                else if(strcmp(cat, "LC_NUMERIC") == 0)  
                {  
                newargs->new[newargs->argcount++] = "-lc_numeric";  
                newargs->new[newargs->argcount++] = (char  
  *)strdup(locale);  
                }  
                else if(strcmp(cat, "LC_MESSAGES") == 0)  
                {  
                newargs->new[newargs->argcount++] = "-  
  lc_displaylang";  
                newargs->new[newargs->argcount++] = (char  
  *)strdup(locale);  
                }  
           }  
       }  
       /* set the flag so we know we got this one */  
       newargs->ddvl |= 2;  
       break;  
       default:  
       DP printf("locale_cb: tt_message_state = %d\n",  
  tt_message_state(m));  
       }  
       /* clean up the message and return */  
       tt_message_destroy(m);  
       return(TT_CALLBACK_PROCESSED);  
  }  
  
  /* when the tooltalk file descriptor becomes active */  
  handle_tt_message()  
  {  
       Tt_messagem;  
  
       /* receive the message */  
       m = tt_message_receive();  


  /* tt_callbacks.c  (continued)  */  
       if(m)  
       {  
       /* this means none of our callbacks got called so it  
        * is not one of ours so throw it back  
        */  
       tt_message_reject(m);  
       }  
  }  
  
  /* when the message is one from the desktop pattern */  
  Tt_callback_action  
  handle_desktop(Tt_message m, Tt_pattern p)  
  {  
       intmapped;/* the map operator */  
       char*op;/* the type of message */  
  
       /* get the message name */  
       op = tt_message_op(m);  
  
       if(strcmp(op, "Set_Mapped") == 0)/* if it is a mapped message  
  */  
       {  
       /* get the map operator */  
       tt_message_arg_ival(m, 0, &mapped);  
  
       /* set it to mapped or not based on the operator */  
       if(mapped == 0)  
       {  
           hide_frame();  
       }  
       else  
       {  
           show_frame();  
       }  
       }  
       else if(strcmp(op, "Quit") == 0)/* if it is the quit message  
  */  
       {  
       /* since this is a simple demo just quit */  
       quit();  
       }  
       return(TT_CALLBACK_PROCESSED);  
  }  


  /* tt_callbacks.c  (continued)  */  
  /* if we get a Display message */  
  Tt_callback_action  
  handle_display(Tt_message m, Tt_pattern p)  
  {  
       char*file;/* not used for this simple app */  
       char*media;/* media (for this it SHOULD be ISO_Latin_1 */  
       char*type;/* vtype of message arg */  
       char*data;/* contents of message */  
       int size;/* size of the data */  
       char*msgid;/* request's ID */  
       char*title;/* request's title */  
       int count;/* argument count */  
       int i;  /* tmp counter */  
       Tt_messagett_msg;/* out going messages */  
  
       /* get the media type (paranoids should check it */  
       media = tt_message_arg_type(m, 0);  
  
       /* get the count to see if we have the optional msgID/title */  
       count = tt_message_args_count(m);  
       DP printf("count = %d\n", count);  
       for(i = 1; i < count; i++)  
       {  
       /* get the msg type */  
       type = tt_message_arg_type(m, i);  
       DP printf("type = '%s'\n", type);  
       if(strcmp(type, "messageID") == 0) /* its optional msgID */  
       {  
           /* save it */  
           msgid = tt_message_arg_val(m, i);  
           DP printf("msgid = '%s'\n", msgid);  
       }  
       else if(strcmp(type, "title") == 0) /* its optional title */  
       {  
           /* save it */  
           title = tt_message_arg_val(m, i);  
       }  
       }  
       if(file = tt_message_file(m)) /* its a file type */  
       {  
       DP printf("Displaying a file\n");  
       /* this type of message is not handled for simplicity sake */  
       tt_message_reject(m);  
       tt_message_destroy(m);  


  /* tt_callbacks.c  (continued)  */  
       return(TT_CALLBACK_PROCESSED);  
       }  
       else/* its a contents type */  
       {  
       /* get the data */  
       tt_message_arg_bval(m, 0, (unsigned char **)&data, &size);  
       if(data == NULL || *data == '\0')  
       {  
           DP printf("data is NULL so fail this message\n");  
           /* its not good so fail it */  
           tt_message_status_set(m, TT_MEDIA_NO_CONTENTS);  
           tt_message_fail(m);  
           return(TT_CALLBACK_PROCESSED);  
       }  
       else  
       {  
           /* if we have an outstanding msg handle it */  
           close_out_old_msg(&cur_msg_state);  
           cur_msg_state.tt_msg = m;  
           DP printf("Displaying data\n");  
  
           /* display the new data */  
           display_data(data);  
       }  
       }  
  
       /* save the current message state */  
       cur_msg_state.type = MSG_DISPLAY;  
       cur_msg_state.msgid = msgid;  
  
       /* send back pt-pt a status msg to say all is well */  
       tt_msg = tt_message_create();  
       tt_message_address_set(tt_msg, TT_HANDLER);  
       tt_message_handler_set(tt_msg, tt_message_sender(m));  
       tt_message_op_set(tt_msg, "Status");  
       tt_message_class_set(tt_msg, TT_NOTICE);  
       tt_message_scope_set(tt_msg, TT_SESSION);  
       tt_message_session_set(tt_msg,  tt_default_session());  
       tt_message_disposition_set(tt_msg, TT_DISCARD);  
       tt_message_arg_add(tt_msg, TT_IN, "string", "Request  
  Received");  
       tt_message_arg_add(tt_msg, TT_IN, "string", "ACME Vendor");  
       tt_message_arg_add(tt_msg, TT_IN, "string", "Sample Editor");  
       tt_message_arg_add(tt_msg, TT_IN, "string", "0.1");  


  /* tt_callbacks.c  (continued)  */  
       tt_message_arg_add(tt_msg, TT_IN, "messageID",  
  cur_msg_state.msgid);  
       tt_message_arg_add(tt_msg, TT_IN, "domain",  
  setlocale(LC_CTYPE, NULL));  
       tt_message_scope_set(tt_msg, TT_SESSION);  
       tt_message_send(tt_msg);  
  
       return(TT_CALLBACK_PROCESSED);  
  }  
  
  /* if we get a Edit message */  
  Tt_callback_action  
  handle_edit(Tt_message m, Tt_pattern p)  
  {  
  
       char*file;/* not used for this simple app */  
       char*media;/* media (for this it SHOULD be ISO_Latin_1 */  
       char*type;/* vtype of message arg */  
       char*data;/* contents of message */  
       int size;/* size of the data */  
       char*msgid;/* request's ID */  
       char*title;/* request's title */  
       int count;/* argument count */  
       int i;  /* tmp counter */  
       Tt_messagett_msg;/* out going messages */  
  
  /*  
   *  REJECT MESSAGES  
   */  
       /* get the media type (paranoids should check it */  
       media = tt_message_arg_type(m, 0);  
  
       /* get the count to see if we have the optional msgID/title */  
       count = tt_message_args_count(m);  
       DP printf("count = %d\n", count);  
       for(i = 1; i < count; i++)  
       {  
       /* get the msg type */  
       type = tt_message_arg_type(m, i);  
       DP printf("type = '%s'\n", type);  
       if(strcmp(type, "messageID") == 0)  
       {  
           /* save it */  
           msgid = tt_message_arg_val(m, i);  


  /* tt_callbacks.c  (continued)  */  
           DP printf("msgid = '%s'\n", msgid);  
       }  
       else if(strcmp(type, "title") == 0)  
       {  
           /* save it */  
           title = tt_message_arg_val(m, i);  
       }  
       }  
       if(file = tt_message_file(m)) /* its a file type */  
       {  
       /* this type of message is not handled for simplicity sake */  
       tt_message_reject(m);  
       tt_message_destroy(m);  
       return(TT_CALLBACK_PROCESSED);  
       }  
       else/* its a contents type */  
       {  
       /* get the data */  
       tt_message_arg_bval(m, 0, (unsigned char **)&data, &size);  
       close_out_old_msg(&cur_msg_state);  
       if(data == NULL || *data == '\0')  
       {  
           /* its compose time */  
           clear_data();  
       }  
       else  
       {  
           cur_msg_state.tt_msg = m;  
           DP printf("Displaying data\n");  
           /* display the new data */  
           display_data(data);  
       }  
       }  
       /* save the current message state */  
       cur_msg_state.type = MSG_EDIT;  
       cur_msg_state.msgid = msgid;  
  
       /* send back pt-pt a status msg to say all is well */  
       tt_msg = tt_message_create();  
       tt_message_address_set(tt_msg, TT_HANDLER);  
       tt_message_handler_set(tt_msg, tt_message_sender(m));  
       tt_message_op_set(tt_msg, "Status");  
       tt_message_class_set(tt_msg, TT_NOTICE);  
       tt_message_scope_set(tt_msg, TT_SESSION);  


  /* tt_callbacks.c  (continued)  */  
       tt_message_session_set(tt_msg,  tt_default_session());  
       tt_message_disposition_set(tt_msg, TT_DISCARD);  
       tt_message_arg_add(tt_msg, TT_IN, "string", "Request  
  Received");  
       tt_message_arg_add(tt_msg, TT_IN, "string", "ACME Vendor");  
       tt_message_arg_add(tt_msg, TT_IN, "string", "Sample Editor");  
       tt_message_arg_add(tt_msg, TT_IN, "string", "0.1");  
       tt_message_arg_add(tt_msg, TT_IN, "messageID",  
  cur_msg_state.msgid);  
       tt_message_arg_add(tt_msg, TT_IN, "domain",  
  setlocale(LC_CTYPE, NULL));  
       tt_message_scope_set(tt_msg, TT_SESSION);  
       tt_message_send(tt_msg);  
  
       return(TT_CALLBACK_PROCESSED);  
  }  
  
  void  
  close_out_old_msg(struct cur_msg_state *old_msg)  
  {  
       if(old_msg->tt_msg == NULL)  
       {  
       return;  
       }  
       if(strcmp(tt_message_op(old_msg->tt_msg), "Display") ==  
  NULL)  
       {  
       tt_message_reply(old_msg->tt_msg);  
       tt_message_destroy(old_msg->tt_msg);  
       }  
       else  
       {  
       /* more work here for save/old data etc. */  
       tt_message_reply(old_msg->tt_msg);  
       tt_message_destroy(old_msg->tt_msg);  
       }  
       old_msg->tt_msg = NULL;  
  }  
  
  /* when we want to quit we need to make sure the  
   * current message gets handled  
   */  
  void  
  quit_tt()  


  /* tt_callbacks.c  (continued)  */  
  {  
       char*data;/* current data */  
       intsize;/* current size */  
  
       /* if no current message we are done */  
       if(cur_msg_state.tt_msg == 0)  
       {  
       return;  
       }  
       if(cur_msg_state.type == MSG_DISPLAY) /* we're handling a  
  Display msg */  
       {  
       /* just reply to it */  
       tt_message_reply(cur_msg_state.tt_msg);  
       tt_message_destroy(cur_msg_state.tt_msg);  
       }  
       else if(cur_msg_state.type == MSG_EDIT)/* handling an Edit  
  msg */  
       {  
       /* get the correct data */  
       if(data_is_modified())  
       {  
           restore_data();  
       }  
       get_data(&data, &size);  
  
       /* use that data to reply to the message */  
       tt_message_arg_val_set(cur_msg_state.tt_msg, 0, data);  
       tt_message_reply(cur_msg_state.tt_msg);  
       tt_message_destroy(cur_msg_state.tt_msg);  
       }  
       cur_msg_state.tt_msg = 0;  
  }  
  
  /* called when a deposit has completed */  
  Tt_callback_action  
  save_cb(Tt_message m, Tt_pattern p)  
  {  
       switch(tt_message_state(m))  
       {  
       caseTT_HANDLED:  
       /* show a successfull save was done */  
       break;  
       caseTT_FAILED:  


  /* tt_callbacks.c  (continued)  */  
       /* error state */  
       break;  
       default:  
       DP printf("some thing else\n");  
       }  
  }  
  
  /* called when you need to save the data back to the  
   * calling process. (This only needs to be done if you're  
   * handling a Display msg or you want to save an intermediate  
   * step in Edit)  
   */  
  save_tt()  
  {  
       Tt_message      tt_msg = 0;  
       int             null = 0;  
       Tt_status       rc;  
       char*toolid;  
       char*data;  
       int size;  
  
       /* if we really have a tooltalk msg to save */  
       if(cur_msg_state.tt_msg != NULL)  
       {  
       /* create and send a Deposit message */  
       tt_msg = tt_message_create();  
       tt_message_address_set(tt_msg, TT_PROCEDURE);  
       tt_message_class_set(tt_msg, TT_REQUEST);  
       tt_message_scope_set(tt_msg, TT_SESSION);  
       tt_message_session_set(tt_msg,  tt_default_session());  
       tt_message_disposition_set(tt_msg, TT_DISCARD);  
  
       tt_message_address_set(tt_msg, TT_HANDLER);  
  
       toolid = tt_message_sender(cur_msg_state.tt_msg);  
       tt_message_handler_set(tt_msg, toolid);  
       tt_message_op_set(tt_msg, "Deposit");  
       get_data(&data, &size);  
       tt_message_arg_add(tt_msg, TT_IN, "ISO_Latin_1", data);  
  
       tt_message_arg_add(tt_msg, TT_IN,  
                "messageID", cur_msg_state.msgid);  
       tt_message_callback_add(tt_msg, save_cb);  


  /* tt_callbacks.c  (continued)  */  
       tt_message_send(tt_msg);  
       }  
  }  

7.4.5 types.file


  ptype Sun_MA_textedit  
  {  
  start "olit_tt";  
  per_session 5;  
  handle:  
  /*  
   *  
   * Optional extra arguments for these requests:  
   *            in    string      title  
   *            in    messageID   text  
   */  
  /* content display */  
  session Display (in ISO_Latin_1 text) => start;  
  session Display (in ISO_Latin_1 text, in title text) => start;  
  session Display (in ISO_Latin_1 text, in messageID text) =>  
  start;  
  session Display (in ISO_Latin_1 text, in messageID text, in title  
  text) => start;  
  
  /* content compose */  
  session Edit (out ISO_Latin_1 text) => start;  
  session Edit (out ISO_Latin_1 text, in title text) => start;  
  session Edit (out ISO_Latin_1 text, in messageID text) => start;  
  session Edit (out ISO_Latin_1 text, in messageID text, in title  
  text) => start;  
  
  /* content edits */  
  session Edit (inout ISO_Latin_1 text) => start;  
  session Edit (inout ISO_Latin_1 text, in title text) => start;  
  session Edit (inout ISO_Latin_1 text, in messageID text) =>  
  start;  
  session Edit (inout ISO_Latin_1 text, in messageID text, in title  
  text) => start;  
  
  /*  


   * Optional extra arguments for these requests:  
   *            in    string    title  
   *            in    messageID   text  
   */  
  
  /* file display */  
  file Display (in ISO_Latin_1 text) => start;  
  file Display (in ISO_Latin_1 text, in title text) => start;  
  file Display (in ISO_Latin_1 text, in messageID text) => start;  
  file Display (in ISO_Latin_1 text, in messageID text, in title  
  text) => start;  
  
  /* file compose */  
  file Edit (out ISO_Latin_1 text) => start;  
  file Edit (out ISO_Latin_1 text, in title text) => start;  
  file Edit (out ISO_Latin_1 text, in messageID text) => start;  
  file Edit (out ISO_Latin_1 text, in messageID text, in title text)  
  => start;  
  
  /* file edits */  
  file Edit (inout ISO_Latin_1 text) => start;  
  file Edit (inout ISO_Latin_1 text, in title text) => start;  
  file Edit (inout ISO_Latin_1 text, in messageID text) => start;  
  file Edit (inout ISO_Latin_1 text, in messageID text, in title  
  text) => start;  
  };  

7.4.6 Resources


  AsciiEdit.base.orientation: vertical  
  AsciiEdit.base.control.orientation: horizontal  
  AsciiEdit.base.control.weight: 0  
  
  AsciiEdit.base.control.save.weight: 0  
  AsciiEdit.base.control.blank.weight: 1  
  AsciiEdit.base.control.quit.weight: 0  
  
  AsciiEdit.base.scrolledwin.weight: 1  
  
  AsciiEdit.base.scrolledwin.forceHorizontalSB: False  
  AsciiEdit.base.scrolledwin.forceVerticalSB: True  
  AsciiEdit.base.scrolledwin.textedit.charsVisible: 80  


  AsciiEdit.base.orientation: vertical  
  AsciiEdit.base.scrolledwin.textedit.linesVisible: 60  
  AsciiEdit.title: Simple Text Editor  
  AsciiEdit*font: lucidasans  
  !-----------------------------------------------  
  AsciiEdit.base.control.save.label: Save  
  AsciiEdit.base.control.quit.label: Quit  

7.4.7 Makefile


  #  
  ###############################################################  
  #  
  #  
  SRC += olit_tt.c tt_code.c tt_callbacks.c  
  HDR += Resources types.file  
  OBJ += $(SRC:%.c=%.o)  
  INCLUDE+= -I${OPENWINHOME}/include  
  #CFLAGS+= -g -DDEBUG  
  CFLAGS+= ${INCLUDE}  
  LDFLAGS+= -L${OPENWINHOME}/lib -R${OPENWINHOME}/lib  
  LIBS+= -lXol -lXt -lX11 -ltt  
  PROGRAM+= tt_demo  
  .KEEP_STATE:  
  $(PROGRAM):$(OBJ)  
       $(CC) -o $(PROGRAM) $(OBJ) $(CFLAGS) $(LDFLAGS) $(LIBS)  
  tooltalk:  
       tt_type_comp types.file  
  clean:  
       rm -f core $(PROGRAM) $(OBJ) types.file.deps  
  .INIT: $(SRC) $(HDR)  


  #  
  
  # End makefile  
  ###############################################################  
  ########