tkMacOSXSend.c
上传用户:rrhhcc
上传日期:2015-12-11
资源大小:54129k
文件大小:16k
源码类别:

通讯编程

开发平台:

Visual C++

  1. /*
  2.  * tkMacOSXSend.c --
  3.  *
  4.  * This file provides procedures that implement the "send"
  5.  * command, allowing commands to be passed from interpreter
  6.  * to interpreter. This current implementation for the Mac
  7.  * has most functionality stubed out.
  8.  *
  9.  * The current plan, which we have not had time to implement, is
  10.  * for the first Wish app to create a gestalt of type 'WIsH'.
  11.  * This gestalt will point to a table, in system memory, of
  12.  * Tk apps. Each Tk app, when it starts up, will register their
  13.  * name, and process ID, in this table. This will allow us to
  14.  * implement "tk appname".
  15.  *
  16.  * Then the send command will look up the process id of the target
  17.  * app in this table, and send an AppleEvent to that process. The
  18.  * AppleEvent handler is much like the do script handler, except that
  19.  * you have to specify the name of the tk app as well, since there may
  20.  * be many interps in one wish app, and you need to send it to the
  21.  * right one.
  22.  *
  23.  * Implementing this has been on our list of things to do, but what
  24.  * with the demise of Tcl at Sun, and the lack of resources at
  25.  * Scriptics it may not get done for awhile. So this sketch is
  26.  * offered for the brave to attempt if they need the functionality...
  27.  *
  28.  * Copyright (c) 1989-1994 The Regents of the University of California.
  29.  * Copyright (c) 1994-1998 Sun Microsystems, Inc.
  30.  * Copyright 2001, Apple Computer, Inc.
  31.  * Copyright (c) 2005-2007 Daniel A. Steffen <das@users.sourceforge.net>
  32.  *
  33.  * See the file "license.terms" for information on usage and redistribution
  34.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  35.  *
  36.  * RCS: @(#) $Id: tkMacOSXSend.c,v 1.2.2.4 2007/06/04 09:28:45 das Exp $
  37.  */
  38. #include "tkMacOSXInt.h"
  39. /*
  40.  * The following structure is used to keep track of the
  41.  * interpreters registered by this process.
  42.  */
  43. typedef struct RegisteredInterp {
  44.     char *name; /* Interpreter's name (malloc-ed). */
  45.     Tcl_Interp *interp; /* Interpreter associated with
  46.  * name. */
  47.     struct RegisteredInterp *nextPtr;
  48.     /* Next in list of names associated
  49.      * with interps in this process.
  50.      * NULL means end of list. */
  51. } RegisteredInterp;
  52. /*
  53.  * A registry of all interpreters for a display is kept in a
  54.  * property "InterpRegistry" on the root window of the display.
  55.  * It is organized as a series of zero or more concatenated strings
  56.  * (in no particular order), each of the form
  57.  * window space name ''
  58.  * where "window" is the hex id of the comm. window to use to talk
  59.  * to an interpreter named "name".
  60.  *
  61.  * When the registry is being manipulated by an application (e.g. to
  62.  * add or remove an entry), it is loaded into memory using a structure
  63.  * of the following type:
  64.  */
  65. typedef struct NameRegistry {
  66.     TkDisplay *dispPtr; /* Display from which the registry was
  67.  * read. */
  68.     int locked; /* Non-zero means that the display was
  69.  * locked when the property was read in. */
  70.     int modified; /* Non-zero means that the property has
  71.  * been modified, so it needs to be written
  72.  * out when the NameRegistry is closed. */
  73.     unsigned long propLength; /* Length of the property, in bytes. */
  74.     char *property; /* The contents of the property, or NULL
  75.  * if none. See format description above;
  76.  * this is *not* terminated by the first
  77.  * null character. Dynamically allocated. */
  78.     int allocedByX; /* Non-zero means must free property with
  79.  * XFree; zero means use ckfree. */
  80. } NameRegistry;
  81. static int initialized = false; /* A flag to denote if we have initialized yet. */
  82. static RegisteredInterp *interpListPtr = NULL;
  83. /* List of all interpreters
  84.  * registered by this process. */
  85.      /*
  86.       * The information below is used for communication between processes
  87.       * during "send" commands. Each process keeps a private window, never
  88.       * even mapped, with one property, "Comm". When a command is sent to
  89.       * an interpreter, the command is appended to the comm property of the
  90.       * communication window associated with the interp's process. Similarly,
  91.       * when a result is returned from a sent command, it is also appended
  92.       * to the comm property.
  93.       *
  94.       * Each command and each result takes the form of ASCII text. For a
  95.       * command, the text consists of a zero character followed by several
  96.       * null-terminated ASCII strings. The first string consists of the
  97.       * single letter "c". Subsequent strings have the form "option value"
  98.       * where the following options are supported:
  99.       *
  100.       * -r commWindow serial
  101.       *
  102.       * This option means that a response should be sent to the window
  103.       * whose X identifier is "commWindow" (in hex), and the response should
  104.       * be identified with the serial number given by "serial" (in decimal).
  105.       * If this option isn't specified then the send is asynchronous and
  106.       * no response is sent.
  107.       *
  108.       * -n name
  109.       * "Name" gives the name of the application for which the command is
  110.       * intended. This option must be present.
  111.       *
  112.       * -s script
  113.       *
  114.       * "Script" is the script to be executed. This option must be present.
  115.       *
  116.       * The options may appear in any order. The -n and -s options must be
  117.       * present, but -r may be omitted for asynchronous RPCs. For compatibility
  118.       * with future releases that may add new features, there may be additional
  119.       * options present; as long as they start with a "-" character, they will
  120.       * be ignored.
  121.       *
  122.       * A result also consists of a zero character followed by several null-
  123.       * terminated ASCII strings. The first string consists of the single
  124.       * letter "r". Subsequent strings have the form "option value" where
  125.       * the following options are supported:
  126.       *
  127.       * -s serial
  128.       *
  129.       * Identifies the command for which this is the result. It is the
  130.       * same as the "serial" field from the -s option in the command. This
  131.       * option must be present.
  132.       *
  133.       * -c code
  134.       *
  135.       * "Code" is the completion code for the script, in decimal. If the
  136.       * code is omitted it defaults to TCL_OK.
  137.       *
  138.       * -r result
  139.       *
  140.       * "Result" is the result string for the script, which may be either
  141.       * a result or an error message. If this field is omitted then it
  142.       * defaults to an empty string.
  143.       *
  144.       * -i errorInfo
  145.       *
  146.       * "ErrorInfo" gives a string with which to initialize the errorInfo
  147.       * variable. This option may be omitted; it is ignored unless the
  148.       * completion code is TCL_ERROR.
  149.       *
  150.       * -e errorCode
  151.       *
  152.       * "ErrorCode" gives a string with with to initialize the errorCode
  153.       * variable. This option may be omitted; it is ignored  unless the
  154.       * completion code is TCL_ERROR.
  155.       *
  156.       * Options may appear in any order, and only the -s option must be
  157.       * present. As with commands, there may be additional options besides
  158.       * these; unknown options are ignored.
  159.       */
  160.      /*
  161.       * Maximum size property that can be read at one time by
  162.       * this module:
  163.       */
  164. #define MAX_PROP_WORDS 100000
  165. /*
  166.  * Forward declarations for procedures defined later in this file:
  167.  */
  168. static int SendInit(Tcl_Interp *interp);
  169. /*
  170.  *--------------------------------------------------------------
  171.  *
  172.  * Tk_SetAppName --
  173.  *
  174.  * This procedure is called to associate an ASCII name with a Tk
  175.  * application. If the application has already been named, the
  176.  * name replaces the old one.
  177.  *
  178.  * Results:
  179.  * The return value is the name actually given to the application.
  180.  * This will normally be the same as name, but if name was already
  181.  * in use for an application then a name of the form "name #2" will
  182.  * be chosen, with a high enough number to make the name unique.
  183.  *
  184.  * Side effects:
  185.  * Registration info is saved, thereby allowing the "send" command
  186.  * to be used later to invoke commands in the application. In
  187.  * addition, the "send" command is created in the application's
  188.  * interpreter. The registration will be removed automatically
  189.  * if the interpreter is deleted or the "send" command is removed.
  190.  *
  191.  *--------------------------------------------------------------
  192.  */
  193. CONST char *
  194. Tk_SetAppName(
  195.     Tk_Window tkwin, /* Token for any window in the application
  196.  * to be named:  it is just used to identify
  197.  * the application and the display. */
  198.     CONST char *name) /* The name that will be used to
  199.  * refer to the interpreter in later
  200.  * "send" commands. Must be globally
  201.  * unique. */
  202. {
  203.     TkWindow *winPtr = (TkWindow *) tkwin;
  204.     Tcl_Interp *interp = winPtr->mainPtr->interp;
  205.     int i, suffix, offset, result;
  206.     RegisteredInterp *riPtr, *prevPtr;
  207.     const char *actualName;
  208.     Tcl_DString dString;
  209.     Tcl_Obj *resultObjPtr, *interpNamePtr;
  210.     char *interpName;
  211.     if (!initialized) {
  212. SendInit(interp);
  213.     }
  214.     /*
  215.      * See if the application is already registered; if so, remove its
  216.      * current name from the registry. The deletion of the command
  217.      * will take care of disposing of this entry.
  218.      */
  219.     for (riPtr = interpListPtr, prevPtr = NULL; riPtr != NULL;
  220.     prevPtr = riPtr, riPtr = riPtr->nextPtr) {
  221. if (riPtr->interp == interp) {
  222.     if (prevPtr == NULL) {
  223. interpListPtr = interpListPtr->nextPtr;
  224.     } else {
  225. prevPtr->nextPtr = riPtr->nextPtr;
  226.     }
  227.     break;
  228. }
  229.     }
  230.     /*
  231.      * Pick a name to use for the application. Use "name" if it's not
  232.      * already in use. Otherwise add a suffix such as " #2", trying
  233.      * larger and larger numbers until we eventually find one that is
  234.      * unique.
  235.      */
  236.     actualName = name;
  237.     suffix = 1;
  238.     offset = 0;
  239.     Tcl_DStringInit(&dString);
  240.     TkGetInterpNames(interp, tkwin);
  241.     resultObjPtr = Tcl_GetObjResult(interp);
  242.     Tcl_IncrRefCount(resultObjPtr);
  243.     for (i = 0; ; ) {
  244. result = Tcl_ListObjIndex(NULL, resultObjPtr, i, &interpNamePtr);
  245. if (interpNamePtr == NULL) {
  246.     break;
  247. }
  248. interpName = Tcl_GetString(interpNamePtr);
  249. if (strcmp(actualName, interpName) == 0) {
  250.     if (suffix == 1) {
  251. Tcl_DStringAppend(&dString, name, -1);
  252. Tcl_DStringAppend(&dString, " #", 2);
  253. offset = Tcl_DStringLength(&dString);
  254. Tcl_DStringSetLength(&dString, offset + 10);
  255. actualName = Tcl_DStringValue(&dString);
  256.     }
  257.     suffix++;
  258.     sprintf(Tcl_DStringValue(&dString) + offset, "%d", suffix);
  259.     i = 0;
  260. } else {
  261.     i++;
  262. }
  263.     }
  264.     Tcl_DecrRefCount(resultObjPtr);
  265.     Tcl_ResetResult(interp);
  266.     /*
  267.      * We have found a unique name. Now add it to the registry.
  268.      */
  269.     riPtr = (RegisteredInterp *) ckalloc(sizeof(RegisteredInterp));
  270.     riPtr->interp = interp;
  271.     riPtr->name = ckalloc(strlen(actualName) + 1);
  272.     riPtr->nextPtr = interpListPtr;
  273.     interpListPtr = riPtr;
  274.     strcpy(riPtr->name, actualName);
  275.     /*
  276.      * TODO: DeleteProc
  277.      */
  278.     Tcl_CreateObjCommand(interp, "send", Tk_SendObjCmd,
  279.     (ClientData) riPtr, NULL);
  280.     if (Tcl_IsSafe(interp)) {
  281. Tcl_HideCommand(interp, "send", "send");
  282.     }
  283.     Tcl_DStringFree(&dString);
  284.     return riPtr->name;
  285. }
  286. /*
  287.  *--------------------------------------------------------------
  288.  *
  289.  * Tk_SendObjCmd --
  290.  *
  291.  * This procedure is invoked to process the "send" Tcl command.
  292.  * See the user documentation for details on what it does.
  293.  *
  294.  * Results:
  295.  * A standard Tcl result.
  296.  *
  297.  * Side effects:
  298.  * See the user documentation.
  299.  *
  300.  *--------------------------------------------------------------
  301.  */
  302. int
  303. Tk_SendObjCmd(
  304.     ClientData clientData, /* Used only for deletion */
  305.     Tcl_Interp *interp, /* The interp we are sending from */
  306.     int objc, /* Number of arguments */
  307.     Tcl_Obj *CONST objv[]) /* The arguments */
  308. {
  309.     const char *sendOptions[] = {"-async", "-displayof", "-", NULL};
  310.     char *stringRep, *destName;
  311.     int async = 0;
  312.     int i, index, firstArg;
  313.     RegisteredInterp *riPtr;
  314.     Tcl_Obj *resultPtr, *listObjPtr;
  315.     int result = TCL_OK;
  316.     for (i = 1; i < (objc - 1); ) {
  317. stringRep = Tcl_GetString(objv[i]);
  318. if (stringRep[0] == '-') {
  319.     if (Tcl_GetIndexFromObj(interp, objv[i], sendOptions, "option", 0,
  320.     &index) != TCL_OK) {
  321. return TCL_ERROR;
  322.     }
  323.     if (index == 0) {
  324. async = 1;
  325. i++;
  326.     } else if (index == 1) {
  327. i += 2;
  328.     } else {
  329. i++;
  330.     }
  331. } else {
  332.     break;
  333. }
  334.     }
  335.     if (objc < (i + 2)) {
  336. Tcl_WrongNumArgs(interp, 1, objv,
  337. "?options? interpName arg ?arg ...?");
  338. return TCL_ERROR;
  339.     }
  340.     destName = Tcl_GetString(objv[i]);
  341.     firstArg = i + 1;
  342.     resultPtr = Tcl_GetObjResult(interp);
  343.     /*
  344.      * See if the target interpreter is local. If so, execute
  345.      * the command directly without going through the DDE server.
  346.      * The only tricky thing is passing the result from the target
  347.      * interpreter to the invoking interpreter. Watch out:  they
  348.      * could be the same!
  349.      */
  350.     for (riPtr = interpListPtr; (riPtr != NULL)
  351.     && (strcmp(destName, riPtr->name)); riPtr = riPtr->nextPtr) {
  352. /*
  353.  * Empty loop body.
  354.  */
  355.     }
  356.     if (riPtr != NULL) {
  357. /*
  358.  * This command is to a local interp. No need to go through
  359.  * the server.
  360.  */
  361. Tcl_Interp *localInterp;
  362. Tcl_Preserve((ClientData) riPtr);
  363. localInterp = riPtr->interp;
  364. Tcl_Preserve((ClientData) localInterp);
  365. if (firstArg == (objc - 1)) {
  366.     /*
  367.      * This might be one of those cases where the new
  368.      * parser is faster.
  369.      */
  370.     result = Tcl_EvalObjEx(localInterp, objv[firstArg], TCL_EVAL_DIRECT);
  371. } else {
  372.     listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
  373.     for (i = firstArg; i < objc; i++) {
  374. Tcl_ListObjAppendList(interp, listObjPtr, objv[i]);
  375.     }
  376.     Tcl_IncrRefCount(listObjPtr);
  377.     result = Tcl_EvalObjEx(localInterp, listObjPtr, TCL_EVAL_DIRECT);
  378.     Tcl_DecrRefCount(listObjPtr);
  379. }
  380. if (interp != localInterp) {
  381.     if (result == TCL_ERROR) {
  382. /* Tcl_Obj *errorObjPtr; */
  383. /*
  384.  * An error occurred, so transfer error information from the
  385.  * destination interpreter back to our interpreter. Must clear
  386.  * interp's result before calling Tcl_AddErrorInfo, since
  387.  * Tcl_AddErrorInfo will store the interp's result in errorInfo
  388.  * before appending riPtr's $errorInfo; we've already got
  389.  * everything we need in riPtr's $errorInfo.
  390.  */
  391. Tcl_ResetResult(interp);
  392. Tcl_AddErrorInfo(interp, Tcl_GetVar2(localInterp,
  393. "errorInfo", NULL, TCL_GLOBAL_ONLY));
  394. /* errorObjPtr = Tcl_GetObjVar2(localInterp, "errorCode", NULL,
  395. TCL_GLOBAL_ONLY);
  396. Tcl_SetObjErrorCode(interp, errorObjPtr); */
  397.     }
  398.     Tcl_SetObjResult(interp, Tcl_GetObjResult(localInterp));
  399. }
  400. Tcl_Release((ClientData) riPtr);
  401. Tcl_Release((ClientData) localInterp);
  402.     } else {
  403. /*
  404.  * TODO: This is a non-local request. Send the script to the server and
  405.  * poll it for a result.
  406.  */
  407.     }
  408.     return result;
  409. }
  410. /*
  411.  *----------------------------------------------------------------------
  412.  *
  413.  * TkGetInterpNames --
  414.  *
  415.  * This procedure is invoked to fetch a list of all the
  416.  * interpreter names currently registered for the display
  417.  * of a particular window.
  418.  *
  419.  * Results:
  420.  * A standard Tcl return value. Interp->result will be set
  421.  * to hold a list of all the interpreter names defined for
  422.  * tkwin's display. If an error occurs, then TCL_ERROR
  423.  * is returned and interp->result will hold an error message.
  424.  *
  425.  * Side effects:
  426.  * None.
  427.  *
  428.  *----------------------------------------------------------------------
  429.  */
  430. int
  431. TkGetInterpNames(
  432.     Tcl_Interp *interp, /* Interpreter for returning a result. */
  433.     Tk_Window tkwin) /* Window whose display is to be used
  434.  * for the lookup. */
  435. {
  436.     Tcl_Obj *listObjPtr;
  437.     RegisteredInterp *riPtr;
  438.     listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
  439.     riPtr = interpListPtr;
  440.     while (riPtr != NULL) {
  441. Tcl_ListObjAppendElement(interp, listObjPtr,
  442. Tcl_NewStringObj(riPtr->name, -1));
  443. riPtr = riPtr->nextPtr;
  444.     }
  445.     Tcl_SetObjResult(interp, listObjPtr);
  446.     return TCL_OK;
  447. }
  448. /*
  449.  *--------------------------------------------------------------
  450.  *
  451.  * SendInit --
  452.  *
  453.  * This procedure is called to initialize the
  454.  * communication channels for sending commands and
  455.  * receiving results.
  456.  *
  457.  * Results:
  458.  * None.
  459.  *
  460.  * Side effects:
  461.  * Sets up various data structures and windows.
  462.  *
  463.  *--------------------------------------------------------------
  464.  */
  465. static int
  466. SendInit(
  467.     Tcl_Interp *interp) /* Interpreter to use for error reporting
  468.  * (no errors are ever returned, but the
  469.  * interpreter is needed anyway). */
  470. {
  471.     return TCL_OK;
  472. }