tkMacOSXSend.c
上传用户:rrhhcc
上传日期:2015-12-11
资源大小:54129k
文件大小:16k
- /*
- * tkMacOSXSend.c --
- *
- * This file provides procedures that implement the "send"
- * command, allowing commands to be passed from interpreter
- * to interpreter. This current implementation for the Mac
- * has most functionality stubed out.
- *
- * The current plan, which we have not had time to implement, is
- * for the first Wish app to create a gestalt of type 'WIsH'.
- * This gestalt will point to a table, in system memory, of
- * Tk apps. Each Tk app, when it starts up, will register their
- * name, and process ID, in this table. This will allow us to
- * implement "tk appname".
- *
- * Then the send command will look up the process id of the target
- * app in this table, and send an AppleEvent to that process. The
- * AppleEvent handler is much like the do script handler, except that
- * you have to specify the name of the tk app as well, since there may
- * be many interps in one wish app, and you need to send it to the
- * right one.
- *
- * Implementing this has been on our list of things to do, but what
- * with the demise of Tcl at Sun, and the lack of resources at
- * Scriptics it may not get done for awhile. So this sketch is
- * offered for the brave to attempt if they need the functionality...
- *
- * Copyright (c) 1989-1994 The Regents of the University of California.
- * Copyright (c) 1994-1998 Sun Microsystems, Inc.
- * Copyright 2001, Apple Computer, Inc.
- * Copyright (c) 2005-2007 Daniel A. Steffen <das@users.sourceforge.net>
- *
- * See the file "license.terms" for information on usage and redistribution
- * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
- *
- * RCS: @(#) $Id: tkMacOSXSend.c,v 1.2.2.4 2007/06/04 09:28:45 das Exp $
- */
- #include "tkMacOSXInt.h"
- /*
- * The following structure is used to keep track of the
- * interpreters registered by this process.
- */
- typedef struct RegisteredInterp {
- char *name; /* Interpreter's name (malloc-ed). */
- Tcl_Interp *interp; /* Interpreter associated with
- * name. */
- struct RegisteredInterp *nextPtr;
- /* Next in list of names associated
- * with interps in this process.
- * NULL means end of list. */
- } RegisteredInterp;
- /*
- * A registry of all interpreters for a display is kept in a
- * property "InterpRegistry" on the root window of the display.
- * It is organized as a series of zero or more concatenated strings
- * (in no particular order), each of the form
- * window space name ' '
- * where "window" is the hex id of the comm. window to use to talk
- * to an interpreter named "name".
- *
- * When the registry is being manipulated by an application (e.g. to
- * add or remove an entry), it is loaded into memory using a structure
- * of the following type:
- */
- typedef struct NameRegistry {
- TkDisplay *dispPtr; /* Display from which the registry was
- * read. */
- int locked; /* Non-zero means that the display was
- * locked when the property was read in. */
- int modified; /* Non-zero means that the property has
- * been modified, so it needs to be written
- * out when the NameRegistry is closed. */
- unsigned long propLength; /* Length of the property, in bytes. */
- char *property; /* The contents of the property, or NULL
- * if none. See format description above;
- * this is *not* terminated by the first
- * null character. Dynamically allocated. */
- int allocedByX; /* Non-zero means must free property with
- * XFree; zero means use ckfree. */
- } NameRegistry;
- static int initialized = false; /* A flag to denote if we have initialized yet. */
- static RegisteredInterp *interpListPtr = NULL;
- /* List of all interpreters
- * registered by this process. */
- /*
- * The information below is used for communication between processes
- * during "send" commands. Each process keeps a private window, never
- * even mapped, with one property, "Comm". When a command is sent to
- * an interpreter, the command is appended to the comm property of the
- * communication window associated with the interp's process. Similarly,
- * when a result is returned from a sent command, it is also appended
- * to the comm property.
- *
- * Each command and each result takes the form of ASCII text. For a
- * command, the text consists of a zero character followed by several
- * null-terminated ASCII strings. The first string consists of the
- * single letter "c". Subsequent strings have the form "option value"
- * where the following options are supported:
- *
- * -r commWindow serial
- *
- * This option means that a response should be sent to the window
- * whose X identifier is "commWindow" (in hex), and the response should
- * be identified with the serial number given by "serial" (in decimal).
- * If this option isn't specified then the send is asynchronous and
- * no response is sent.
- *
- * -n name
- * "Name" gives the name of the application for which the command is
- * intended. This option must be present.
- *
- * -s script
- *
- * "Script" is the script to be executed. This option must be present.
- *
- * The options may appear in any order. The -n and -s options must be
- * present, but -r may be omitted for asynchronous RPCs. For compatibility
- * with future releases that may add new features, there may be additional
- * options present; as long as they start with a "-" character, they will
- * be ignored.
- *
- * A result also consists of a zero character followed by several null-
- * terminated ASCII strings. The first string consists of the single
- * letter "r". Subsequent strings have the form "option value" where
- * the following options are supported:
- *
- * -s serial
- *
- * Identifies the command for which this is the result. It is the
- * same as the "serial" field from the -s option in the command. This
- * option must be present.
- *
- * -c code
- *
- * "Code" is the completion code for the script, in decimal. If the
- * code is omitted it defaults to TCL_OK.
- *
- * -r result
- *
- * "Result" is the result string for the script, which may be either
- * a result or an error message. If this field is omitted then it
- * defaults to an empty string.
- *
- * -i errorInfo
- *
- * "ErrorInfo" gives a string with which to initialize the errorInfo
- * variable. This option may be omitted; it is ignored unless the
- * completion code is TCL_ERROR.
- *
- * -e errorCode
- *
- * "ErrorCode" gives a string with with to initialize the errorCode
- * variable. This option may be omitted; it is ignored unless the
- * completion code is TCL_ERROR.
- *
- * Options may appear in any order, and only the -s option must be
- * present. As with commands, there may be additional options besides
- * these; unknown options are ignored.
- */
- /*
- * Maximum size property that can be read at one time by
- * this module:
- */
- #define MAX_PROP_WORDS 100000
- /*
- * Forward declarations for procedures defined later in this file:
- */
- static int SendInit(Tcl_Interp *interp);
- /*
- *--------------------------------------------------------------
- *
- * Tk_SetAppName --
- *
- * This procedure is called to associate an ASCII name with a Tk
- * application. If the application has already been named, the
- * name replaces the old one.
- *
- * Results:
- * The return value is the name actually given to the application.
- * This will normally be the same as name, but if name was already
- * in use for an application then a name of the form "name #2" will
- * be chosen, with a high enough number to make the name unique.
- *
- * Side effects:
- * Registration info is saved, thereby allowing the "send" command
- * to be used later to invoke commands in the application. In
- * addition, the "send" command is created in the application's
- * interpreter. The registration will be removed automatically
- * if the interpreter is deleted or the "send" command is removed.
- *
- *--------------------------------------------------------------
- */
- CONST char *
- Tk_SetAppName(
- Tk_Window tkwin, /* Token for any window in the application
- * to be named: it is just used to identify
- * the application and the display. */
- CONST char *name) /* The name that will be used to
- * refer to the interpreter in later
- * "send" commands. Must be globally
- * unique. */
- {
- TkWindow *winPtr = (TkWindow *) tkwin;
- Tcl_Interp *interp = winPtr->mainPtr->interp;
- int i, suffix, offset, result;
- RegisteredInterp *riPtr, *prevPtr;
- const char *actualName;
- Tcl_DString dString;
- Tcl_Obj *resultObjPtr, *interpNamePtr;
- char *interpName;
- if (!initialized) {
- SendInit(interp);
- }
- /*
- * See if the application is already registered; if so, remove its
- * current name from the registry. The deletion of the command
- * will take care of disposing of this entry.
- */
- for (riPtr = interpListPtr, prevPtr = NULL; riPtr != NULL;
- prevPtr = riPtr, riPtr = riPtr->nextPtr) {
- if (riPtr->interp == interp) {
- if (prevPtr == NULL) {
- interpListPtr = interpListPtr->nextPtr;
- } else {
- prevPtr->nextPtr = riPtr->nextPtr;
- }
- break;
- }
- }
- /*
- * Pick a name to use for the application. Use "name" if it's not
- * already in use. Otherwise add a suffix such as " #2", trying
- * larger and larger numbers until we eventually find one that is
- * unique.
- */
- actualName = name;
- suffix = 1;
- offset = 0;
- Tcl_DStringInit(&dString);
- TkGetInterpNames(interp, tkwin);
- resultObjPtr = Tcl_GetObjResult(interp);
- Tcl_IncrRefCount(resultObjPtr);
- for (i = 0; ; ) {
- result = Tcl_ListObjIndex(NULL, resultObjPtr, i, &interpNamePtr);
- if (interpNamePtr == NULL) {
- break;
- }
- interpName = Tcl_GetString(interpNamePtr);
- if (strcmp(actualName, interpName) == 0) {
- if (suffix == 1) {
- Tcl_DStringAppend(&dString, name, -1);
- Tcl_DStringAppend(&dString, " #", 2);
- offset = Tcl_DStringLength(&dString);
- Tcl_DStringSetLength(&dString, offset + 10);
- actualName = Tcl_DStringValue(&dString);
- }
- suffix++;
- sprintf(Tcl_DStringValue(&dString) + offset, "%d", suffix);
- i = 0;
- } else {
- i++;
- }
- }
- Tcl_DecrRefCount(resultObjPtr);
- Tcl_ResetResult(interp);
- /*
- * We have found a unique name. Now add it to the registry.
- */
- riPtr = (RegisteredInterp *) ckalloc(sizeof(RegisteredInterp));
- riPtr->interp = interp;
- riPtr->name = ckalloc(strlen(actualName) + 1);
- riPtr->nextPtr = interpListPtr;
- interpListPtr = riPtr;
- strcpy(riPtr->name, actualName);
- /*
- * TODO: DeleteProc
- */
- Tcl_CreateObjCommand(interp, "send", Tk_SendObjCmd,
- (ClientData) riPtr, NULL);
- if (Tcl_IsSafe(interp)) {
- Tcl_HideCommand(interp, "send", "send");
- }
- Tcl_DStringFree(&dString);
- return riPtr->name;
- }
- /*
- *--------------------------------------------------------------
- *
- * Tk_SendObjCmd --
- *
- * This procedure is invoked to process the "send" Tcl command.
- * See the user documentation for details on what it does.
- *
- * Results:
- * A standard Tcl result.
- *
- * Side effects:
- * See the user documentation.
- *
- *--------------------------------------------------------------
- */
- int
- Tk_SendObjCmd(
- ClientData clientData, /* Used only for deletion */
- Tcl_Interp *interp, /* The interp we are sending from */
- int objc, /* Number of arguments */
- Tcl_Obj *CONST objv[]) /* The arguments */
- {
- const char *sendOptions[] = {"-async", "-displayof", "-", NULL};
- char *stringRep, *destName;
- int async = 0;
- int i, index, firstArg;
- RegisteredInterp *riPtr;
- Tcl_Obj *resultPtr, *listObjPtr;
- int result = TCL_OK;
- for (i = 1; i < (objc - 1); ) {
- stringRep = Tcl_GetString(objv[i]);
- if (stringRep[0] == '-') {
- if (Tcl_GetIndexFromObj(interp, objv[i], sendOptions, "option", 0,
- &index) != TCL_OK) {
- return TCL_ERROR;
- }
- if (index == 0) {
- async = 1;
- i++;
- } else if (index == 1) {
- i += 2;
- } else {
- i++;
- }
- } else {
- break;
- }
- }
- if (objc < (i + 2)) {
- Tcl_WrongNumArgs(interp, 1, objv,
- "?options? interpName arg ?arg ...?");
- return TCL_ERROR;
- }
- destName = Tcl_GetString(objv[i]);
- firstArg = i + 1;
- resultPtr = Tcl_GetObjResult(interp);
- /*
- * See if the target interpreter is local. If so, execute
- * the command directly without going through the DDE server.
- * The only tricky thing is passing the result from the target
- * interpreter to the invoking interpreter. Watch out: they
- * could be the same!
- */
- for (riPtr = interpListPtr; (riPtr != NULL)
- && (strcmp(destName, riPtr->name)); riPtr = riPtr->nextPtr) {
- /*
- * Empty loop body.
- */
- }
- if (riPtr != NULL) {
- /*
- * This command is to a local interp. No need to go through
- * the server.
- */
- Tcl_Interp *localInterp;
- Tcl_Preserve((ClientData) riPtr);
- localInterp = riPtr->interp;
- Tcl_Preserve((ClientData) localInterp);
- if (firstArg == (objc - 1)) {
- /*
- * This might be one of those cases where the new
- * parser is faster.
- */
- result = Tcl_EvalObjEx(localInterp, objv[firstArg], TCL_EVAL_DIRECT);
- } else {
- listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
- for (i = firstArg; i < objc; i++) {
- Tcl_ListObjAppendList(interp, listObjPtr, objv[i]);
- }
- Tcl_IncrRefCount(listObjPtr);
- result = Tcl_EvalObjEx(localInterp, listObjPtr, TCL_EVAL_DIRECT);
- Tcl_DecrRefCount(listObjPtr);
- }
- if (interp != localInterp) {
- if (result == TCL_ERROR) {
- /* Tcl_Obj *errorObjPtr; */
- /*
- * An error occurred, so transfer error information from the
- * destination interpreter back to our interpreter. Must clear
- * interp's result before calling Tcl_AddErrorInfo, since
- * Tcl_AddErrorInfo will store the interp's result in errorInfo
- * before appending riPtr's $errorInfo; we've already got
- * everything we need in riPtr's $errorInfo.
- */
- Tcl_ResetResult(interp);
- Tcl_AddErrorInfo(interp, Tcl_GetVar2(localInterp,
- "errorInfo", NULL, TCL_GLOBAL_ONLY));
- /* errorObjPtr = Tcl_GetObjVar2(localInterp, "errorCode", NULL,
- TCL_GLOBAL_ONLY);
- Tcl_SetObjErrorCode(interp, errorObjPtr); */
- }
- Tcl_SetObjResult(interp, Tcl_GetObjResult(localInterp));
- }
- Tcl_Release((ClientData) riPtr);
- Tcl_Release((ClientData) localInterp);
- } else {
- /*
- * TODO: This is a non-local request. Send the script to the server and
- * poll it for a result.
- */
- }
- return result;
- }
- /*
- *----------------------------------------------------------------------
- *
- * TkGetInterpNames --
- *
- * This procedure is invoked to fetch a list of all the
- * interpreter names currently registered for the display
- * of a particular window.
- *
- * Results:
- * A standard Tcl return value. Interp->result will be set
- * to hold a list of all the interpreter names defined for
- * tkwin's display. If an error occurs, then TCL_ERROR
- * is returned and interp->result will hold an error message.
- *
- * Side effects:
- * None.
- *
- *----------------------------------------------------------------------
- */
- int
- TkGetInterpNames(
- Tcl_Interp *interp, /* Interpreter for returning a result. */
- Tk_Window tkwin) /* Window whose display is to be used
- * for the lookup. */
- {
- Tcl_Obj *listObjPtr;
- RegisteredInterp *riPtr;
- listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
- riPtr = interpListPtr;
- while (riPtr != NULL) {
- Tcl_ListObjAppendElement(interp, listObjPtr,
- Tcl_NewStringObj(riPtr->name, -1));
- riPtr = riPtr->nextPtr;
- }
- Tcl_SetObjResult(interp, listObjPtr);
- return TCL_OK;
- }
- /*
- *--------------------------------------------------------------
- *
- * SendInit --
- *
- * This procedure is called to initialize the
- * communication channels for sending commands and
- * receiving results.
- *
- * Results:
- * None.
- *
- * Side effects:
- * Sets up various data structures and windows.
- *
- *--------------------------------------------------------------
- */
- static int
- SendInit(
- Tcl_Interp *interp) /* Interpreter to use for error reporting
- * (no errors are ever returned, but the
- * interpreter is needed anyway). */
- {
- return TCL_OK;
- }