4
* This file contains the main program for "wish", a windowing
5
* shell based on Tk and Tcl. It also provides a template that
6
* can be used as the basis for main programs for other Tk
9
* Copyright (c) 1990-1993 The Regents of the University of California.
10
* All rights reserved.
12
* Permission is hereby granted, without written agreement and without
13
* license or royalty fees, to use, copy, modify, and distribute this
14
* software and its documentation for any purpose, provided that the
15
* above copyright notice and the following two paragraphs appear in
16
* all copies of this software.
18
* IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
19
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
20
* OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
21
* CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23
* THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
24
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
25
* AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
26
* ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
27
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
31
/* static char rcsid[] = "$Header: /user6/ouster/wish/RCS/tkMain.c,v 1.99 93/11/11 09:35:24 ouster Exp $ SPRITE (Berkeley)"; */
42
#if (TK_MINOR_VERSION==0 && TK_MAJOR_VERSION==4)
43
#define TkCreateMainWindow Tk_CreateMainWindow
47
/*-------------------------------------------------------------------*/
49
#include <sys/types.h>
54
struct connection_state *dsfd;
55
/*-------------------------------------------------------------------*/
58
* Declarations for various library procedures and variables (don't want
59
* to include tkInt.h or tkConfig.h here, because people might copy this
60
* file out of the Tk source directory to make their own modified versions).
63
/* extern void exit _ANSI_ARGS_((int status)); */
64
extern int isatty _ANSI_ARGS_((int fd));
66
extern int read _ANSI_ARGS_((int fd, char *buf, size_t size));
68
extern char * strrchr _ANSI_ARGS_((CONST char *string, int c));
71
* Global variables used by the main program:
74
/* static Tk_Window mainWindow; The main window for the application. If
75
* NULL then the application no longer
77
static Tcl_Interp *interp; /* Interpreter for this application. */
78
char *tcl_RcFileName; /* Name of a user-specific startup script
79
* to source if the application is being run
80
* interactively (e.g. "~/.wishrc"). Set
81
* by Tcl_AppInit. NULL means don't source
83
static Tcl_DString command; /* Used to assemble lines of terminal input
84
* into Tcl commands. */
85
static int tty; /* Non-zero means standard input is a
86
* terminal-like device. Zero means it's
88
static char errorExitCmd[] = "exit 1";
91
* Command-line options:
94
static int synchronize = 0;
95
static char *fileName = NULL;
96
static char *name = NULL;
97
static char *display = NULL;
98
static char *geometry = NULL;
101
static void guiCreateCommand _ANSI_ARGS_((int idLispObject, int iSlot , char *arglist));
104
dfprintf(FILE *fp,char *s,...) {
110
fprintf(fp,"\nguis:");
117
#define CMD_SIZE 4000
118
#define SIGNAL_ERROR TCL_signal_error
124
sprintf("error %s",x);
125
Tcl_Eval(interp,buf);
131
static Tk_ArgvInfo argTable[] = {
132
{"-file", TK_ARGV_STRING, (char *) NULL, (char *) &fileName,
133
"File from which to read commands"},
134
{"-geometry", TK_ARGV_STRING, (char *) NULL, (char *) &geometry,
135
"Initial geometry for window"},
136
{"-display", TK_ARGV_STRING, (char *) NULL, (char *) &display,
138
{"-name", TK_ARGV_STRING, (char *) NULL, (char *) &name,
139
"Name to use for application"},
140
{"-sync", TK_ARGV_CONSTANT, (char *) 1, (char *) &synchronize,
141
"Use synchronous mode for display server"},
142
{(char *) NULL, TK_ARGV_END, (char *) NULL, (char *) NULL,
147
* Declaration for Tcl command procedure to create demo widget. This
148
* procedure is only invoked if SQUARE_DEMO is defined.
151
extern int SquareCmd _ANSI_ARGS_((ClientData clientData,
152
Tcl_Interp *interp, int argc, char *argv[]));
155
* Forward declarations for procedures defined later in this file:
158
static void StdinProc _ANSI_ARGS_((ClientData clientData,
162
*----------------------------------------------------------------------
166
* Main program for Wish.
169
* None. This procedure never returns (it exits the process when
173
* This procedure initializes the wish world and then starts
174
* interpreting commands; almost anything could happen, depending
175
* on the script being interpreted.
177
*----------------------------------------------------------------------
184
/* FIXME, should come in from tk header or not be called */
185
EXTERN Tk_Window TkCreateMainWindow _ANSI_ARGS_((Tcl_Interp * interp,
186
char * screenName, char * baseName));
189
TkX_Wish (argc, argv)
190
int argc; /* Number of arguments. */
191
char **argv; /* Array of argument strings. */
198
interp = Tcl_CreateInterp();
200
Tcl_InitMemory(interp);
204
* Parse command-line arguments.
207
if (Tk_ParseArgv(interp, (Tk_Window) NULL, &argc, (const char **)argv, argTable, 0)
209
fprintf(stderr, "%s\n", interp->result);
213
if (fileName != NULL) {
218
name = strrchr(p, '/');
227
* If a display was specified, put it into the DISPLAY
228
* environment variable so that it will be available for
229
* any sub-processes created by us.
232
if (display != NULL) {
233
Tcl_SetVar2(interp, "env", "DISPLAY", display, TCL_GLOBAL_ONLY);
237
* Initialize the Tk application.
240
/* mainWindow = TkCreateMainWindow(interp, display, name/\* , "Tk" *\/); */
241
/* if (mainWindow == NULL) { */
242
/* fprintf(stderr, "%s\n", interp->result); */
245
/* #ifndef __MINGW32__ */
246
/* if (synchronize) { */
247
/* XSynchronize(Tk_Display(mainWindow), True); */
250
/* Tk_GeometryRequest(mainWindow, 200, 200); */
251
/* Tk_UnmapWindow(mainWindow); */
254
* Make command-line arguments available in the Tcl variables "argc"
255
* and "argv". Also set the "geometry" variable from the geometry
256
* specified on the command line.
259
args = Tcl_Merge(argc-1, (const char **)argv+1);
260
Tcl_SetVar(interp, "argv", args, TCL_GLOBAL_ONLY);
262
sprintf(buf, "%d", argc-1);
263
Tcl_SetVar(interp, "argc", buf, TCL_GLOBAL_ONLY);
264
Tcl_SetVar(interp, "argv0", (fileName != NULL) ? fileName : argv[0],
266
if (geometry != NULL) {
267
Tcl_SetVar(interp, "geometry", geometry, TCL_GLOBAL_ONLY);
271
* Set the "tcl_interactive" variable.
274
tty = isatty(dsfd->fd);
275
Tcl_SetVar(interp, "tcl_interactive",
276
((fileName == NULL) && tty) ? "1" : "0", TCL_GLOBAL_ONLY);
279
* Add a few application-specific commands to the application's
283
/* #ifdef SQUARE_DEMO */
284
/* Tcl_CreateCommand(interp, "square", SquareCmd, (ClientData) mainWindow, */
285
/* (void (*)()) NULL); */
289
* Invoke application-specific initialization.
292
if (Tcl_AppInit(interp) != TCL_OK) {
293
fprintf(stderr, "Tcl_AppInit failed: %s\n", interp->result);
297
* Set the geometry of the main window, if requested.
300
if (geometry != NULL) {
301
code = Tcl_VarEval(interp, "wm geometry . ", geometry, (char *) NULL);
302
if (code != TCL_OK) {
303
fprintf(stderr, "%s\n", interp->result);
308
* Invoke the script specified on the command line, if any.
311
if (fileName != NULL) {
312
code = Tcl_VarEval(interp, "source ", fileName, (char *) NULL);
313
if (code != TCL_OK) {
319
* Commands will come from standard input, so set up an event
320
* handler for standard input. If the input device is aEvaluate the
321
* .rc file, if one has been specified, set up an event handler
322
* for standard input, and print a prompt if the input
323
* device is a terminal.
326
if (tcl_RcFileName != NULL) {
331
fullName = Tcl_TildeSubst(interp, tcl_RcFileName, &buffer);
332
if (fullName == NULL) {
333
fprintf(stderr, "%s\n", interp->result);
335
f = fopen(fullName, "r");
337
code = Tcl_EvalFile(interp, fullName);
338
if (code != TCL_OK) {
339
fprintf(stderr, "%s\n", interp->result);
344
Tcl_DStringFree(&buffer);
347
dfprintf(stderr, "guis : Creating file handler for %d\n", dsfd->fd);
349
Tk_CreateFileHandler(dsfd->fd, TK_READABLE, StdinProc, (ClientData) 0);
353
Tcl_DStringInit(&command);
356
* Loop infinitely, waiting for commands to execute. When there
357
* are no windows left, Tk_MainLoop returns and we exit.
363
* Don't exit directly, but rather invoke the Tcl "exit" command.
364
* This gives the application the opportunity to redefine "exit"
365
* to do additional cleanup.
368
Tcl_Eval(interp, "exit");
372
msg = Tcl_GetVar(interp, "errorInfo", TCL_GLOBAL_ONLY);
374
msg = interp->result;
376
dfprintf(stderr, "%s\n", msg);
377
Tcl_Eval(interp, errorExitCmd);
378
return; /* Needed only to prevent compiler warnings. */
381
static char *being_set_by_lisp;
384
tell_lisp_var_changed(
391
ClientData clientData;
399
if (being_set_by_lisp == 0)
400
{ const char *val = Tcl_GetVar2(interp,name1,name2, TCL_GLOBAL_ONLY);
402
STORE_3BYTES(buf,(long) clientData);
403
if(sock_write_str2(dsfd, m_set_lisp_loc, buf, 3 ,
406
{ /* what do we want to do if the write failed */}
408
if (parent > 0) kill(parent, SIGUSR1);
412
/* avoid going back to lisp if it is lisp that is doing the setting! */
413
if (strcmp(being_set_by_lisp,name1))
414
{ fprintf(stderr,"recursive setting of vars %s??",name1);}
421
*----------------------------------------------------------------------
425
* This procedure is invoked by the event dispatcher whenever
426
* standard input becomes readable. It grabs the next line of
427
* input characters, adds them to a command being assembled, and
428
* executes the command if it's complete.
434
* Could be almost arbitrary, depending on the command that's
437
*----------------------------------------------------------------------
442
StdinProc(clientData, mask)
443
ClientData clientData; /* Not used. */
444
int mask; /* Not used. */
449
struct message_header *msg;
451
msg = (struct message_header *) buf;
454
* Disable the stdin file handler while evaluating the command;
455
* otherwise if the command re-enters the event loop we might
456
* process commands from stdin before the current command is
457
* finished. Among other things, this will trash the text of the
458
* command being evaluated.
460
dfprintf(stderr, "\nguis : Disabling file handler for %d\n", dsfd->fd);
462
/* Tk_CreateFileHandler(dsfd->fd, 0, StdinProc, (ClientData) 0); */
467
msg = guiParseMsg1(dsfd,buf,sizeof(buf));
471
/*dfprintf(stderr, "Yoo !!! Empty command\n"); */
472
if (debug)perror("zero message");
474
Tk_CreateFileHandler(dsfd->fd, TK_READABLE, StdinProc, (ClientData) 0);
479
/* Need to switch to table lookup */
481
case m_create_command:
484
GET_3BYTES(msg->body,iSlot);
485
guiCreateCommand(0, iSlot, &(msg->body[3]));
489
case m_tcl_command_wait_response:
490
count = strlen(msg->body);
491
cmd = Tcl_DStringAppend(&command, msg->body, count);
493
code = Tcl_RecordAndEval(interp, cmd, 0);
495
if (msg->type == m_tcl_command_wait_response
498
unsigned char buf[4];
499
unsigned char *p = buf;
501
*p++ = (code ? '1' : '0');
502
bcopy(msg->msg_id,p,3);
504
if(sock_write_str2(dsfd, m_reply, buf, 4 ,
505
interp->result, strlen(interp->result))
507
{ /* what do we want to do if the write failed */}
509
if (msg->type == m_tcl_command_wait_response)
510
{ /* parent is waiting so dong signal */ ;}
513
if (parent> 0)kill(parent, SIGUSR1);
517
Tcl_DStringFree(&command);
519
case m_tcl_clear_connection:
520
/* we are stuck... */
522
Tcl_DStringInit(&command);
523
Tcl_DStringFree(&command);
524
fSclear_connection(dsfd->fd);
527
case m_tcl_set_text_variable:
528
{ int n = strlen(msg->body);
529
if(being_set_by_lisp) fprintf(stderr,"recursive set?");
530
/* avoid a trace on this set!! */
532
being_set_by_lisp = msg->body;
533
Tcl_SetVar2(interp,msg->body,0,msg->body+n+1,
535
being_set_by_lisp = 0;
539
case m_tcl_link_text_variable:
541
GET_3BYTES(msg->body,i);
542
Tcl_TraceVar2(interp,msg->body+3 ,0,
546
, tell_lisp_var_changed, (ClientData) i);
550
case m_tcl_unlink_text_variable:
552
GET_3BYTES(msg->body,i);
553
Tcl_UntraceVar2(interp,msg->body+3 ,0,
557
, tell_lisp_var_changed, (ClientData) i);
562
dfprintf(stderr, "Error !!! Unknown command %d\n"
565
fNotDone = fix(fScheck_dsfd_for_input(dsfd,0));
569
dfprintf(stderr, "\nguis : in StdinProc, not done, executed %s"
573
} while (fNotDone > 0);
576
/* Tk_CreateFileHandler(dsfd->fd, TK_READABLE, StdinProc, (ClientData) 0); */
577
if ((void *)msg != (void *) buf)
581
/* ----------------------------------------------------------------- */
582
typedef struct _ClientDataLispObject {
586
} ClientDataLispObject;
589
TclGenericCommandProcedure( clientData,
592
ClientData clientData;
597
char szCmd[CMD_SIZE];
598
ClientDataLispObject *pcdlo = (ClientDataLispObject *)clientData;
601
char *p = pcdlo->arglist;
603
STORE_3BYTES(q,(pcdlo->iSlot));
606
{ char *arg = (argc > 1 ? argv[1] : "");
608
if (m > CMD_SIZE -50)
609
SIGNAL_ERROR("too big command");
616
for (i=1; i< argc; i++)
617
{ if (i < n && p[i]=='s') { *q++ = '"';}
620
if (i < n && p[i]=='s') { *q++ = '"';}
626
dfprintf(stderr, "TclGenericCommandProcedure : %s\n"
630
if (sock_write_str2(dsfd,m_call, "",0, szCmd, q-szCmd) == -1)
633
"Error\t(TclGenericCommandProcedure) !!!\n\tFailed to write [%s] to socket %d (%d) cb=%d\n"
634
, szCmd, dsfd->fd, errno, cb);
638
if (parent > 0)kill(parent, SIGUSR1);
646
guiCreateCommand( idLispObject, iSlot , arglist)
647
int idLispObject; int iSlot ; char *arglist;
649
char szNameCmdProc[2000],*c;
650
ClientDataLispObject *pcdlo;
652
sprintf(szNameCmdProc, "callback_%d",iSlot);
654
pcdlo = (ClientDataLispObject *)malloc(sizeof(ClientDataLispObject));
655
pcdlo->id = idLispObject;
656
pcdlo->iSlot = iSlot;
658
{ pcdlo->arglist = 0;}
660
{c= malloc(strlen(arglist)+1);
663
Tcl_CreateCommand(interp
664
, szNameCmdProc, TclGenericCommandProcedure
665
, (ClientData *)pcdlo, free);
666
dfprintf(stderr, "TCL creating callback : %s\n", szNameCmdProc);
668
/* guiBindCallback(szNameCmdProc, szTclObject, szModifier,arglist); */
673
guiBindCallback(char *szNameCmdProc, char *szTclObject, char *szModifier,char* arglist)
678
sprintf(szCmd, "bind %s %s {%s %s}"
682
, (arglist ? arglist : "")
684
dfprintf(stderr, "TCL BIND : %s\n", szCmd);
686
code = Tcl_Eval(interp, szCmd);
689
dfprintf(stderr, "TCL Error int bind : %s\n", interp->result);
696
/* guiDeleteCallback(szCallback) */
697
/* char *szCallback; */
699
/* dfprintf(stderr, "Tcl Deleting command : %s\n", szCallback); */
701
/* Tcl_DeleteCommand(interp, szCallback); */