]> www.wagner.pp.ru Git - oss/ck.git/blob - ckWindow.c
Ck console graphics toolkit
[oss/ck.git] / ckWindow.c
1 /* 
2  * ckWindow.c --
3  *
4  *      This file provides basic window-manipulation procedures.
5  *
6  * Copyright (c) 1995-2001 Christian Werner.
7  *
8  * See the file "license.terms" for information on usage and redistribution
9  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
10  */
11
12 #include "ckPort.h"
13 #include "ck.h"
14
15 #ifdef HAVE_GPM
16 #include "gpm.h"
17 #endif
18
19 /*
20  * Main information.
21  */
22
23 CkMainInfo *ckMainInfo = NULL;
24
25 #ifdef __WIN32__
26
27 /*
28  * Curses input event handling information.
29  */
30
31 typedef struct {
32     HANDLE stdinHandle;
33     HWND hwnd;
34     HANDLE thread;
35     CkMainInfo *mainPtr;
36 } InputInfo;
37
38 static InputInfo inputInfo = {
39     INVALID_HANDLE_VALUE,
40     NULL,
41     INVALID_HANDLE_VALUE,
42     NULL
43 };
44
45 static void             InputSetup _ANSI_ARGS_((InputInfo *inputInfo));
46 static void             InputExit _ANSI_ARGS_((ClientData clientData));
47 static void             InputThread _ANSI_ARGS_((void *arg));
48 static LRESULT CALLBACK InputHandler _ANSI_ARGS_((HWND hwnd, UINT message,
49                                                  WPARAM wParam,
50                                                  LPARAM lParam));
51 static void             InputHandler2 _ANSI_ARGS_((ClientData clientData));
52 #endif
53
54 /*
55  * The variables below hold several uid's that are used in many places
56  * in the toolkit.
57  */
58
59 Ck_Uid ckDisabledUid = NULL;
60 Ck_Uid ckActiveUid = NULL;
61 Ck_Uid ckNormalUid = NULL;
62
63 /*
64  * The following structure defines all of the commands supported by
65  * the toolkit, and the C procedures that execute them.
66  */
67
68 typedef int (CkCmdProc) _ANSI_ARGS_((ClientData clientData,
69                                      Tcl_Interp *interp,
70                                      int argc, char **argv));
71
72 typedef struct {
73     char *name;                         /* Name of command. */
74     CkCmdProc *cmdProc;                 /* Command procedure. */
75 } CkCmd;
76
77 CkCmd commands[] = {
78     /*
79      * Commands that are part of the intrinsics:
80      */
81
82 #if (TCL_MAJOR_VERSION == 7) && (TCL_MINOR_VERSION <= 4)
83     {"after",           Tk_AfterCmd},
84 #endif
85     {"bell",            Ck_BellCmd},
86     {"bind",            Ck_BindCmd},
87     {"bindtags",        Ck_BindtagsCmd},
88     {"curses",          Ck_CursesCmd},
89     {"destroy",         Ck_DestroyCmd},
90     {"exit",            Ck_ExitCmd},
91 #if (TCL_MAJOR_VERSION == 7) && (TCL_MINOR_VERSION <= 4)
92     {"fileevent",       Tk_FileeventCmd},
93 #endif
94     {"focus",           Ck_FocusCmd},
95     {"grid",            Ck_GridCmd},
96     {"lower",           Ck_LowerCmd},
97     {"option",          Ck_OptionCmd},
98     {"pack",            Ck_PackCmd},
99     {"place",           Ck_PlaceCmd},
100     {"raise",           Ck_RaiseCmd},
101     {"recorder",        Ck_RecorderCmd},
102     {"tkwait",          Ck_TkwaitCmd},
103     {"update",          Ck_UpdateCmd},
104     {"winfo",           Ck_WinfoCmd},
105
106     /*
107      * Widget-creation commands.
108      */
109
110     {"button",          Ck_ButtonCmd},
111     {"checkbutton",     Ck_ButtonCmd},
112     {"entry",           Ck_EntryCmd},
113     {"frame",           Ck_FrameCmd},
114     {"label",           Ck_ButtonCmd},
115     {"listbox",         Ck_ListboxCmd},
116     {"menu",            Ck_MenuCmd},
117     {"menubutton",      Ck_MenubuttonCmd},
118     {"message",         Ck_MessageCmd},
119     {"radiobutton",     Ck_ButtonCmd},
120     {"scrollbar",       Ck_ScrollbarCmd},
121     {"text",            Ck_TextCmd},
122     {"toplevel",        Ck_FrameCmd},
123     {"tree",            Ck_TreeCmd},
124
125     {(char *) NULL,     (CkCmdProc *) NULL}
126 };
127
128 /*
129  * Static procedures of this module.
130  */
131
132 static void     UnlinkWindow _ANSI_ARGS_((CkWindow *winPtr));
133 static void     UnlinkToplevel _ANSI_ARGS_((CkWindow *winPtr));
134 static void     ChangeToplevelFocus _ANSI_ARGS_((CkWindow *winPtr));
135 static void     DoRefresh _ANSI_ARGS_((ClientData clientData));
136 static void     RefreshToplevels _ANSI_ARGS_((CkWindow *winPtr));
137 static void     RefreshThem _ANSI_ARGS_((CkWindow *winPtr));
138 static void     UpdateHWCursor _ANSI_ARGS_((CkMainInfo *mainPtr));
139 static CkWindow *GetWindowXY _ANSI_ARGS_((CkWindow *winPtr, int *xPtr,
140                         int *yPtr));
141 static int      DeadAppCmd _ANSI_ARGS_((ClientData clientData,
142                         Tcl_Interp *interp, int argc, char **argv));
143 static int      ExecCmd _ANSI_ARGS_((ClientData clientData,
144                         Tcl_Interp *interp, int argc, char **argv));
145 static int      PutsCmd _ANSI_ARGS_((ClientData clientData,
146                         Tcl_Interp *interp, int argc, char **argv));
147 static int      CloseCmd _ANSI_ARGS_((ClientData clientData,
148                         Tcl_Interp *interp, int argc, char **argv));
149 static int      FlushCmd _ANSI_ARGS_((ClientData clientData,
150                         Tcl_Interp *interp, int argc, char **argv));
151 static int      ReadCmd _ANSI_ARGS_((ClientData clientData,
152                         Tcl_Interp *interp, int argc, char **argv));
153 static int      GetsCmd _ANSI_ARGS_((ClientData clientData,
154                         Tcl_Interp *interp, int argc, char **argv));
155
156 /*
157  * Some plain Tcl commands are handled specially.
158  */
159
160 CkCmd redirCommands[] = {
161 #ifndef __WIN32__
162     {"exec",    ExecCmd},
163 #endif
164     {"puts",    PutsCmd},
165     {"close",   CloseCmd},
166     {"flush",   FlushCmd},
167     {"read",    ReadCmd},
168     {"gets",    GetsCmd},
169     {(char *) NULL, (CkCmdProc *) NULL}
170 };
171
172 /*
173  * The following structure is used as ClientData for redirected
174  * plain Tcl commands.
175  */
176
177 typedef struct {
178     CkMainInfo *mainPtr;
179     Tcl_CmdInfo cmdInfo;
180 } RedirInfo;
181 \f
182 /*
183  *--------------------------------------------------------------
184  *
185  * NewWindow --
186  *
187  *      This procedure creates and initializes a CkWindow structure.
188  *
189  * Results:
190  *      The return value is a pointer to the new window.
191  *
192  * Side effects:
193  *      A new window structure is allocated and all its fields are
194  *      initialized.
195  *
196  *--------------------------------------------------------------
197  */
198
199 static CkWindow *
200 NewWindow(parentPtr)
201     CkWindow *parentPtr;
202 {
203     CkWindow *winPtr;
204
205     winPtr = (CkWindow *) ckalloc(sizeof (CkWindow));
206     winPtr->window = NULL;
207     winPtr->childList = NULL;
208     winPtr->lastChildPtr = NULL;
209     winPtr->parentPtr = NULL;
210     winPtr->nextPtr = NULL;
211     winPtr->topLevPtr = NULL;
212     winPtr->mainPtr = NULL;
213     winPtr->pathName = NULL;
214     winPtr->nameUid = NULL;
215     winPtr->classUid = NULL;
216     winPtr->handlerList = NULL;
217     winPtr->tagPtr = NULL;
218     winPtr->numTags = 0;
219     winPtr->focusPtr = NULL;
220     winPtr->geomMgrPtr = NULL;
221     winPtr->geomData = NULL;
222     winPtr->optionLevel = -1;
223     winPtr->reqWidth = winPtr->reqHeight = 1;
224     winPtr->x = winPtr->y = 0;
225     winPtr->width = winPtr->height = 1;
226     winPtr->fg = COLOR_WHITE;
227     winPtr->bg = COLOR_BLACK;
228     winPtr->attr = A_NORMAL;
229     winPtr->flags = 0;
230
231     return winPtr;
232 }
233 \f
234 /*
235  *----------------------------------------------------------------------
236  *
237  * NameWindow --
238  *
239  *      This procedure is invoked to give a window a name and insert
240  *      the window into the hierarchy associated with a particular
241  *      application.
242  *
243  * Results:
244  *      A standard Tcl return value.
245  *
246  * Side effects:
247  *      See above.
248  *
249  *----------------------------------------------------------------------
250  */
251
252 static int
253 NameWindow(interp, winPtr, parentPtr, name)
254     Tcl_Interp *interp;         /* Interpreter to use for error reporting. */
255     CkWindow *winPtr;           /* Window that is to be named and inserted. */
256     CkWindow *parentPtr;        /* Pointer to logical parent for winPtr
257                                  * (used for naming, options, etc.). */
258     char *name;                 /* Name for winPtr;   must be unique among
259                                  * parentPtr's children. */
260 {
261 #define FIXED_SIZE 200
262     char staticSpace[FIXED_SIZE];
263     char *pathName;
264     int new;
265     Tcl_HashEntry *hPtr;
266     int length1, length2;
267
268     /*
269      * Setup all the stuff except name right away, then do the name stuff
270      * last.  This is so that if the name stuff fails, everything else
271      * will be properly initialized (needed to destroy the window cleanly
272      * after the naming failure).
273      */
274     winPtr->parentPtr = parentPtr;
275     winPtr->nextPtr = NULL;
276     if (parentPtr->childList == NULL) {
277         parentPtr->lastChildPtr = winPtr;
278         parentPtr->childList = winPtr;
279     } else {
280         parentPtr->lastChildPtr->nextPtr = winPtr;
281         parentPtr->lastChildPtr = winPtr;
282     }
283     winPtr->mainPtr = parentPtr->mainPtr;
284     winPtr->nameUid = Ck_GetUid(name);
285
286     /*
287      * Don't permit names that start with an upper-case letter:  this
288      * will just cause confusion with class names in the option database.
289      */
290
291     if (isupper((unsigned char) name[0])) {
292         Tcl_AppendResult(interp,
293                 "window name starts with an upper-case letter: \"",
294                 name, "\"", (char *) NULL);
295         return TCL_ERROR;
296     }
297
298     /*
299      * To permit names of arbitrary length, must be prepared to malloc
300      * a buffer to hold the new path name.  To run fast in the common
301      * case where names are short, use a fixed-size buffer on the
302      * stack.
303      */
304
305     length1 = strlen(parentPtr->pathName);
306     length2 = strlen(name);
307     if ((length1+length2+2) <= FIXED_SIZE) {
308         pathName = staticSpace;
309     } else {
310         pathName = (char *) ckalloc((unsigned) (length1+length2+2));
311     }
312     if (length1 == 1) {
313         pathName[0] = '.';
314         strcpy(pathName+1, name);
315     } else {
316         strcpy(pathName, parentPtr->pathName);
317         pathName[length1] = '.';
318         strcpy(pathName+length1+1, name);
319     }
320     hPtr = Tcl_CreateHashEntry(&parentPtr->mainPtr->nameTable, pathName, &new);
321     if (pathName != staticSpace) {
322         ckfree(pathName);
323     }
324     if (!new) {
325         Tcl_AppendResult(interp, "window name \"", name,
326                 "\" already exists in parent", (char *) NULL);
327         return TCL_ERROR;
328     }
329     Tcl_SetHashValue(hPtr, winPtr);
330     winPtr->pathName = Tcl_GetHashKey(&parentPtr->mainPtr->nameTable, hPtr);
331     Tcl_CreateHashEntry(&parentPtr->mainPtr->winTable, (char *) winPtr, &new);
332     return TCL_OK;
333 }
334 \f
335 /*
336  *----------------------------------------------------------------------
337  *
338  * Ck_MainWindow --
339  *
340  *      Returns the main window for an application.
341  *
342  * Results:
343  *      If interp is associated with the main window, the main
344  *      window is returned. Otherwise NULL is returned and an
345  *      error message is left in interp->result.
346  *
347  * Side effects:
348  *      None.
349  *
350  *----------------------------------------------------------------------
351  */
352
353 CkWindow *
354 Ck_MainWindow(interp)
355     Tcl_Interp *interp;         /* Interpreter that embodies application,
356                                  * also used for error reporting. */
357 {
358     if (ckMainInfo == NULL || ckMainInfo->interp != interp) {
359         if (interp != NULL)
360             interp->result = "no main window for application.";
361         return NULL;
362     }
363     return ckMainInfo->winPtr;
364 }
365 \f
366 /*
367  *----------------------------------------------------------------------
368  *
369  * Ck_CreateMainWindow --
370  *
371  *      Make the main window.
372  *
373  * Results:
374  *      The return value is a token for the new window, or NULL if
375  *      an error prevented the new window from being created.  If
376  *      NULL is returned, an error message will be left in
377  *      interp->result.
378  *
379  * Side effects:
380  *      A new window structure is allocated locally.
381  *
382  *----------------------------------------------------------------------
383  */
384
385 CkWindow *
386 Ck_CreateMainWindow(interp, className)
387     Tcl_Interp *interp;         /* Interpreter to use for error reporting. */
388     char *className;            /* Class name of the new main window. */
389 {
390     int dummy;
391     Tcl_HashEntry *hPtr;
392     CkMainInfo *mainPtr;
393     CkWindow *winPtr;
394     CkCmd *cmdPtr;
395 #ifdef SIGTSTP
396 #ifdef HAVE_SIGACTION
397     struct sigaction oldsig, newsig;
398 #else
399     Ck_SignalProc sigproc;
400 #endif
401 #endif
402 #ifdef NCURSES_MOUSE_VERSION
403     MEVENT mEvent;
404 #endif
405     char *term;
406     int isxterm = 0;
407
408     /*
409      * For now, only one main window may exists for the application.
410      */
411     if (ckMainInfo != NULL)
412         return NULL;
413
414     /*
415      * Create the basic CkWindow structure.
416      */
417
418     winPtr = NewWindow(NULL);
419     
420     /*
421      * Create the CkMainInfo structure for this application, and set
422      * up name-related information for the new window.
423      */
424
425     mainPtr = (CkMainInfo *) ckalloc(sizeof(CkMainInfo));
426     mainPtr->winPtr = winPtr;
427     mainPtr->interp = interp;
428     Tcl_InitHashTable(&mainPtr->nameTable, TCL_STRING_KEYS);
429     Tcl_InitHashTable(&mainPtr->winTable, TCL_ONE_WORD_KEYS);
430     mainPtr->topLevPtr = NULL;
431     mainPtr->focusPtr = winPtr;
432     mainPtr->bindingTable = Ck_CreateBindingTable(interp);
433     mainPtr->optionRootPtr = NULL;
434     mainPtr->refreshCount = 0;
435     mainPtr->refreshDelay = 0;
436     mainPtr->lastRefresh = 0;
437     mainPtr->refreshTimer = NULL;
438     mainPtr->flags = 0;
439     ckMainInfo = mainPtr;
440     winPtr->mainPtr = mainPtr;
441     winPtr->nameUid = Ck_GetUid(".");
442     winPtr->classUid = Ck_GetUid("Main"); /* ??? */
443     winPtr->flags |= CK_TOPLEVEL;
444     hPtr = Tcl_CreateHashEntry(&mainPtr->nameTable, (char *) winPtr->nameUid,
445                 &dummy);
446     Tcl_SetHashValue(hPtr, winPtr);
447     winPtr->pathName = Tcl_GetHashKey(&mainPtr->nameTable, hPtr);
448     Tcl_CreateHashEntry(&mainPtr->winTable, (char *) winPtr, &dummy);
449
450     ckNormalUid = Ck_GetUid("normal");
451     ckDisabledUid = Ck_GetUid("disabled");
452     ckActiveUid = Ck_GetUid("active");
453
454 #if CK_USE_UTF
455 #ifdef __WIN32__
456     {
457         char enc[32], *envcp = getenv("CK_USE_ENCODING");
458         unsigned int cp = GetConsoleCP();
459
460         if (envcp && strncmp(envcp, "cp", 2) == 0) {
461             cp = atoi(envcp + 2);
462             SetConsoleCP(cp);
463             cp = GetConsoleCP();
464         }
465         if (GetConsoleOutputCP() != cp) {
466             SetConsoleOutputCP(cp);
467         }
468         sprintf(enc, "cp%d", cp);
469         mainPtr->isoEncoding = Tcl_GetEncoding(NULL, enc);
470     }
471 #else
472     /*
473      * Use default system encoding as suggested by
474      * Anton Kovalenko <a_kovalenko@mtu-net.ru>.
475      * May be overriden by environment variable.
476      */
477     mainPtr->isoEncoding = Tcl_GetEncoding(NULL, getenv("CK_USE_ENCODING"));
478     if (mainPtr->isoEncoding == NULL) {
479         mainPtr->isoEncoding = Tcl_GetEncoding(NULL, NULL);
480     }
481 #endif
482     if (mainPtr->isoEncoding == NULL) {
483         panic("standard encoding not found");
484     }
485     Tcl_DStringInit(&mainPtr->isoBuffer);
486 #endif
487
488     /* Curses related initialization */
489
490 #ifdef SIGTSTP
491     /* This is essential for ncurses-1.9.4 */
492 #ifdef HAVE_SIGACTION
493     newsig.sa_handler = SIG_IGN;
494     sigfillset(&newsig.sa_mask);
495     newsig.sa_flags = 0;
496     sigaction(SIGTSTP, &newsig, &oldsig);
497 #else
498     sigproc = (Ck_SignalProc) signal(SIGTSTP, SIG_IGN);
499 #endif
500 #endif
501     if (initscr() == (WINDOW *) ERR) {
502         ckfree((char *) winPtr);
503         return NULL;
504     }
505 #ifdef SIGTSTP
506     /* This is essential for ncurses-1.9.4 */
507 #ifdef HAVE_SIGACTION
508     sigaction(SIGTSTP, &oldsig, NULL);
509 #else
510     signal(SIGTSTP, sigproc);
511 #endif
512 #endif
513     raw();
514     noecho();
515     idlok(stdscr, TRUE);
516     scrollok(stdscr, FALSE);
517     keypad(stdscr, TRUE);
518     nodelay(stdscr, TRUE);
519     meta(stdscr, TRUE);
520     nonl();
521     mainPtr->maxWidth = COLS;
522     mainPtr->maxHeight = LINES;
523     winPtr->width = mainPtr->maxWidth;
524     winPtr->height = mainPtr->maxHeight;
525     winPtr->window = newwin(winPtr->height, winPtr->width, 0, 0);
526     if (has_colors()) {
527         start_color();
528         mainPtr->flags |= CK_HAS_COLOR;
529     }
530 #ifdef NCURSES_MOUSE_VERSION
531     mouseinterval(1);
532     mousemask(BUTTON1_PRESSED | BUTTON1_RELEASED |
533               BUTTON2_PRESSED | BUTTON2_RELEASED |
534               BUTTON3_PRESSED | BUTTON3_RELEASED, NULL);
535     mainPtr->flags |= (getmouse(&mEvent) != ERR) ? CK_HAS_MOUSE : 0;
536 #endif  /* NCURSES_MOUSE_VERSION */
537
538 #if defined(__WIN32__) || defined(__DJGPP__)
539     mouse_set(BUTTON1_PRESSED | BUTTON1_RELEASED |
540               BUTTON2_PRESSED | BUTTON2_RELEASED |
541               BUTTON3_PRESSED | BUTTON3_RELEASED);
542     mainPtr->flags |= CK_HAS_MOUSE;
543     term = "win32";
544 #else
545     term = getenv("TERM");
546     isxterm = strncmp(term, "xterm", 5) == 0 ||
547         strncmp(term, "rxvt", 4) == 0 ||
548         strncmp(term, "kterm", 5) == 0 ||
549         strncmp(term, "color_xterm", 11) == 0 ||
550         (term[0] != '\0' && strncmp(term + 1, "xterm", 5) == 0);
551     if (!(mainPtr->flags & CK_HAS_MOUSE) && isxterm) {
552         mainPtr->flags |= CK_HAS_MOUSE | CK_MOUSE_XTERM;
553         fflush(stdout);
554         fputs("\033[?1000h", stdout);
555         fflush(stdout);
556     }
557 #endif  /* __WIN32__ */
558
559 #ifdef HAVE_GPM
560     /*
561      * Some ncurses aren't compiled with GPM support built in,
562      * therefore by setting the following environment variable
563      * usage of GPM can be turned on.
564      */
565     if (!isxterm && (mainPtr->flags & CK_HAS_MOUSE)) {
566         char *forcegpm = getenv("CK_USE_GPM");
567
568         if (forcegpm && strchr("YyTt123456789", forcegpm[0])) {
569             mainPtr->flags &= ~CK_HAS_MOUSE;
570         }
571     }
572     if (!isxterm && !(mainPtr->flags & CK_HAS_MOUSE)) {
573         int fd;
574         Gpm_Connect conn;
575 #if (TCL_MAJOR_VERSION == 7) && (TCL_MINOR_VERSION <= 4)
576         EXTERN int CkHandleGPMInput _ANSI_ARGS_((ClientData clientData,
577             int mask, int flags));
578 #else
579         EXTERN void CkHandleGPMInput _ANSI_ARGS_((ClientData clientData,
580             int mask));
581 #endif
582
583         conn.eventMask = GPM_DOWN | GPM_UP | GPM_MOVE;
584         conn.defaultMask = 0;
585         conn.minMod = 0;
586         conn.maxMod = 0;
587         fd = Gpm_Open(&conn, 0);
588         if (fd >= 0) {
589             mainPtr->flags |= CK_HAS_MOUSE;
590 #if (TCL_MAJOR_VERSION >= 8)
591             mainPtr->mouseData = (ClientData) fd;
592             Tcl_CreateFileHandler(fd, TCL_READABLE,
593               CkHandleGPMInput, (ClientData) mainPtr);
594 #else       
595 #if (TCL_MAJOR_VERSION == 7) && (TCL_MINOR_VERSION <= 4)
596             mainPtr->mouseData = (ClientData) fd;
597             Tk_CreateFileHandler2(fd, CkHandleGPMInput, (ClientData) mainPtr);
598 #else
599             mainPtr->mouseData = (ClientData)
600                 Tcl_GetFile((ClientData) fd, TCL_UNIX_FD);
601             Tcl_CreateFileHandler((Tcl_File) mainPtr->mouseData, TCL_READABLE,
602                 CkHandleGPMInput, (ClientData) mainPtr);
603 #endif
604 #endif
605         }
606     }
607 #endif  /* HAVE_GPM */
608
609 #ifdef __WIN32__
610     /* PDCurses specific !!! */
611     inputInfo.mainPtr = mainPtr;
612     inputInfo.stdinHandle = GetStdHandle(STD_INPUT_HANDLE);
613     typeahead(-1);
614     SetConsoleMode(inputInfo.stdinHandle,
615                    ENABLE_MOUSE_INPUT | ENABLE_WINDOW_INPUT);
616     InputSetup(&inputInfo);
617 #else
618 #if (TCL_MAJOR_VERSION >= 8)
619     Tcl_CreateFileHandler(0, 
620         TCL_READABLE, CkHandleInput, (ClientData) mainPtr);
621 #else
622 #if (TCL_MAJOR_VERSION == 7) && (TCL_MINOR_VERSION <= 4)
623     Tk_CreateFileHandler2(0, CkHandleInput, (ClientData) mainPtr);
624 #else
625     Tcl_CreateFileHandler(Tcl_GetFile((ClientData) 0, TCL_UNIX_FD),
626         TCL_READABLE, CkHandleInput, (ClientData) mainPtr);
627 #endif
628 #endif
629 #endif
630
631     idlok(winPtr->window, TRUE);
632     scrollok(winPtr->window, FALSE);
633     keypad(winPtr->window, TRUE);
634     nodelay(winPtr->window, TRUE);
635     meta(winPtr->window, TRUE);
636     curs_set(0);
637     while (getch() != ERR) {
638         /* empty loop body. */
639     }
640     winPtr->flags |= CK_MAPPED;
641     Ck_SetWindowAttr(winPtr, winPtr->fg, winPtr->bg, winPtr->attr);
642     Ck_ClearToBot(winPtr, 0, 0);
643     Ck_EventuallyRefresh(winPtr);
644
645     /*
646      * Bind in Ck's commands.
647      */
648
649     for (cmdPtr = commands; cmdPtr->name != NULL; cmdPtr++) {
650         Tcl_CreateCommand(interp, cmdPtr->name, cmdPtr->cmdProc,
651                 (ClientData) winPtr, (Tcl_CmdDeleteProc *) NULL);
652     }
653
654     /*
655      * Redirect some critical Tcl commands to our own procedures
656      */
657     for (cmdPtr = redirCommands; cmdPtr->name != NULL; cmdPtr++) {
658         RedirInfo *redirInfo;
659 #if (TCL_MAJOR_VERSION >= 8)
660         Tcl_DString cmdName;
661         extern int TclRenameCommand _ANSI_ARGS_((Tcl_Interp *interp,
662                                                  char *oldName, char *newName));
663 #endif
664         redirInfo = (RedirInfo *) ckalloc(sizeof (RedirInfo));
665         redirInfo->mainPtr = mainPtr;
666         Tcl_GetCommandInfo(interp, cmdPtr->name, &redirInfo->cmdInfo);
667 #if (TCL_MAJOR_VERSION >= 8)
668         Tcl_DStringInit(&cmdName);
669         Tcl_DStringAppend(&cmdName, "____", -1);
670         Tcl_DStringAppend(&cmdName, cmdPtr->name, -1);
671         TclRenameCommand(interp, cmdPtr->name, Tcl_DStringValue(&cmdName));
672         Tcl_DStringFree(&cmdName);
673 #endif
674         Tcl_CreateCommand(interp, cmdPtr->name, cmdPtr->cmdProc,
675             (ClientData) redirInfo, (Tcl_CmdDeleteProc *) free);
676     }
677
678     /*
679      * Set variables for the intepreter.
680      */
681
682 #if (TCL_MAJOR_VERSION < 8)
683     if (Tcl_GetVar(interp, "ck_library", TCL_GLOBAL_ONLY) == NULL) {
684         /*
685          * A library directory hasn't already been set, so figure out
686          * which one to use.
687          */
688
689         char *libDir = getenv("CK_LIBRARY");
690
691         if (libDir == NULL) {
692             libDir = CK_LIBRARY;
693         }
694         Tcl_SetVar(interp, "ck_library", libDir, TCL_GLOBAL_ONLY);
695     }
696 #endif
697     Tcl_SetVar(interp, "ck_version", CK_VERSION, TCL_GLOBAL_ONLY);
698
699     /*
700      * Make main window into a frame widget.
701      */
702
703     Ck_SetClass(winPtr, className);
704     CkInitFrame(interp, winPtr, 0, NULL);
705     mainPtr->topLevPtr = winPtr;
706     winPtr->focusPtr = winPtr;
707     return winPtr;
708 }
709 \f
710 /*
711  *----------------------------------------------------------------------
712  *
713  * Ck_Init --
714  *
715  *      This procedure is invoked to add Ck to an interpreter.  It
716  *      incorporates all of Ck's commands into the interpreter and
717  *      creates the main window for a new Ck application.
718  *
719  * Results:
720  *      Returns a standard Tcl completion code and sets interp->result
721  *      if there is an error.
722  *
723  * Side effects:
724  *      Depends on what's in the ck.tcl script.
725  *
726  *----------------------------------------------------------------------
727  */
728
729 int
730 Ck_Init(interp)
731     Tcl_Interp *interp;         /* Interpreter to initialize. */
732 {
733     CkWindow *mainWindow;
734     char *p, *name, *class;
735     int code;
736     static char initCmd[] =
737 #if (TCL_MAJOR_VERSION >= 8)
738 "proc init {} {\n\
739     global ck_library ck_version\n\
740     rename init {}\n\
741     tcl_findLibrary ck $ck_version 0 ck.tcl CK_LIBRARY ck_library\n\
742 }\n\
743 init";
744 #else
745 "proc init {} {\n\
746     global ck_library ck_version env\n\
747     rename init {}\n\
748     set dirs {}\n\
749     if [info exists env(CK_LIBRARY)] {\n\
750         lappend dirs $env(CK_LIBRARY)\n\
751     }\n\
752     lappend dirs $ck_library\n\
753     lappend dirs [file dirname [info library]]/lib/ck$ck_version\n\
754     catch {lappend dirs [file dirname [file dirname \\\n\
755         [info nameofexecutable]]]/lib/ck$ck_version}\n\
756     set lib ck$ck_version\n\
757     lappend dirs [file dirname [file dirname [pwd]]]/$lib/library\n\
758     lappend dirs [file dirname [file dirname [info library]]]/$lib/library\n\
759     lappend dirs [file dirname [pwd]]/library\n\
760     foreach i $dirs {\n\
761         set ck_library $i\n\
762         if ![catch {uplevel #0 source $i/ck.tcl}] {\n\
763             return\n\
764         }\n\
765     }\n\
766     set msg \"Can't find a usable ck.tcl in the following directories: \n\"\n\
767     append msg \"    $dirs\n\"\n\
768     append msg \"This probably means that Ck wasn't installed properly.\n\"\n\
769     error $msg\n\
770 }\n\
771 init";
772 #endif
773
774     p = Tcl_GetVar(interp, "argv0", TCL_GLOBAL_ONLY);
775     if (p == NULL || *p == '\0')
776         p = "Ck";
777     name = strrchr(p, '/');
778     if (name != NULL)
779         name++;
780     else
781         name = p;
782     class = (char *) ckalloc((unsigned) (strlen(name) + 1));
783     strcpy(class, name);
784     class[0] = toupper((unsigned char) class[0]);
785     mainWindow = Ck_CreateMainWindow(interp, class);
786     ckfree(class);
787
788 #if !((TCL_MAJOR_VERSION == 7) && (TCL_MINOR_VERSION <= 4))
789     if (Tcl_PkgRequire(interp, "Tcl", TCL_VERSION, 1) == NULL)
790         return TCL_ERROR;
791     code = Tcl_PkgProvide(interp, "Ck", CK_VERSION);
792     if (code != TCL_OK)
793         return TCL_ERROR;
794 #endif
795     return Tcl_Eval(interp, initCmd);
796 }
797 \f
798 /*
799  *--------------------------------------------------------------
800  *
801  * Ck_CreateWindow --
802  *
803  *      Create a new window as a child of an existing window.
804  *
805  * Results:
806  *      The return value is the pointer to the new window.
807  *      If an error occurred in creating the window, then an
808  *      error message is left in interp->result and NULL is
809  *      returned.
810  *
811  * Side effects:
812  *      A new window structure is allocated locally.  A curses
813  *      window is not initially created, but will be created
814  *      the first time the window is mapped.
815  *
816  *--------------------------------------------------------------
817  */
818
819 CkWindow *
820 Ck_CreateWindow(interp, parentPtr, name, toplevel)
821     Tcl_Interp *interp;         /* Interpreter to use for error reporting.
822                                  * Interp->result is assumed to be
823                                  * initialized by the caller. */
824     CkWindow *parentPtr;        /* Parent of new window. */
825     char *name;                 /* Name for new window.  Must be unique
826                                  * among parent's children. */
827     int toplevel;               /* If true, create toplevel window. */
828 {
829     CkWindow *winPtr;
830
831     winPtr = NewWindow(parentPtr);
832     if (NameWindow(interp, winPtr, parentPtr, name) != TCL_OK) {
833         Ck_DestroyWindow(winPtr);
834         return NULL;
835     }
836     if (toplevel) {
837         CkWindow *wPtr;
838
839         winPtr->flags |= CK_TOPLEVEL;
840         winPtr->focusPtr = winPtr;
841         if (winPtr->mainPtr->topLevPtr == NULL) {
842             winPtr->topLevPtr = winPtr->mainPtr->topLevPtr;
843             winPtr->mainPtr->topLevPtr = winPtr;
844         } else {
845             for (wPtr = winPtr->mainPtr->topLevPtr; wPtr->topLevPtr != NULL;
846                                                     wPtr = wPtr->topLevPtr) {
847                 /* Empty loop body. */
848             }
849             winPtr->topLevPtr = wPtr->topLevPtr;
850             wPtr->topLevPtr = winPtr;
851         }
852     }
853     return winPtr;
854 }
855 \f
856 /*
857  *----------------------------------------------------------------------
858  *
859  * Ck_CreateWindowFromPath --
860  *
861  *      This procedure is similar to Ck_CreateWindow except that
862  *      it uses a path name to create the window, rather than a
863  *      parent and a child name.
864  *
865  * Results:
866  *      The return value is the pointer to the new window.
867  *      If an error occurred in creating the window, then an
868  *      error message is left in interp->result and NULL is
869  *      returned.
870  *
871  * Side effects:
872  *      A new window structure is allocated locally.  A curses
873  *      window is not initially created, but will be created
874  *      the first time the window is mapped.
875  *
876  *----------------------------------------------------------------------
877  */
878
879 CkWindow *
880 Ck_CreateWindowFromPath(interp, anywin, pathName, toplevel)
881     Tcl_Interp *interp;         /* Interpreter to use for error reporting.
882                                  * Interp->result is assumed to be
883                                  * initialized by the caller. */
884     CkWindow *anywin;           /* Pointer to any window in application
885                                  * that is to contain new window. */
886     char *pathName;             /* Path name for new window within the
887                                  * application of anywin. The parent of
888                                  * this window must already exist, but
889                                  * the window itself must not exist. */
890     int toplevel;               /* If true, create toplevel window. */
891 {
892 #define FIXED_SPACE 5
893     char fixedSpace[FIXED_SPACE+1];
894     char *p;
895     CkWindow *parentPtr, *winPtr;
896     int numChars;
897
898     /*
899      * Strip the parent's name out of pathName (it's everything up
900      * to the last dot).  There are two tricky parts: (a) must
901      * copy the parent's name somewhere else to avoid modifying
902      * the pathName string (for large names, space for the copy
903      * will have to be malloc'ed);  (b) must special-case the
904      * situation where the parent is ".".
905      */
906
907     p = strrchr(pathName, '.');
908     if (p == NULL) {
909         Tcl_AppendResult(interp, "bad window path name \"", pathName,
910                 "\"", (char *) NULL);
911         return NULL;
912     }
913     numChars = p - pathName;
914     if (numChars > FIXED_SPACE) {
915         p = (char *) ckalloc((unsigned) (numChars+1));
916     } else {
917         p = fixedSpace;
918     }
919     if (numChars == 0) {
920         *p = '.';
921         p[1] = '\0';
922     } else {
923         strncpy(p, pathName, numChars);
924         p[numChars] = '\0';
925     }
926
927     /*
928      * Find the parent window.
929      */
930
931     parentPtr = Ck_NameToWindow(interp, p, anywin);
932     if (p != fixedSpace) {
933         ckfree(p);
934     }
935     if (parentPtr == NULL)
936         return NULL;
937
938     /*
939      * Create the window.
940      */
941
942     winPtr = NewWindow(parentPtr);
943     if (NameWindow(interp, winPtr, parentPtr, pathName + numChars + 1)
944         != TCL_OK) {
945         Ck_DestroyWindow(winPtr);
946         return NULL;
947     }
948     if (toplevel) {
949         CkWindow *wPtr;
950
951         winPtr->flags |= CK_TOPLEVEL;
952         winPtr->focusPtr = winPtr;
953         if (winPtr->mainPtr->topLevPtr == NULL) {
954             winPtr->topLevPtr = winPtr->mainPtr->topLevPtr;
955             winPtr->mainPtr->topLevPtr = winPtr;
956         } else {
957             for (wPtr = winPtr->mainPtr->topLevPtr; wPtr->topLevPtr != NULL;
958                                                     wPtr = wPtr->topLevPtr) {
959                 /* Empty loop body. */
960             }
961             winPtr->topLevPtr = wPtr->topLevPtr;
962             wPtr->topLevPtr = winPtr;
963         }
964     }
965     return winPtr;
966 }
967 \f
968 /*
969  *--------------------------------------------------------------
970  *
971  * Ck_DestroyWindow --
972  *
973  *      Destroy an existing window.  After this call, the caller
974  *      should never again use the pointer.
975  *
976  * Results:
977  *      None.
978  *
979  * Side effects:
980  *      The window is deleted, along with all of its children.
981  *      Relevant callback procedures are invoked.
982  *
983  *--------------------------------------------------------------
984  */
985
986 void
987 Ck_DestroyWindow(winPtr)
988     CkWindow *winPtr;           /* Window to destroy. */
989 {
990     CkWindowEvent event;
991     Tcl_HashEntry *hPtr;
992 #ifdef NCURSES_MOUSE_VERSION
993     MEVENT mEvent;
994 #endif
995
996     if (winPtr->flags & CK_ALREADY_DEAD)
997         return;
998     winPtr->flags |= CK_ALREADY_DEAD;
999
1000     /*
1001      * Recursively destroy children.  The CK_RECURSIVE_DESTROY
1002      * flags means that the child's window needn't be explicitly
1003      * destroyed (the destroy of the parent already did it), nor
1004      * does it need to be removed from its parent's child list,
1005      * since the parent is being destroyed too.
1006      */
1007
1008     while (winPtr->childList != NULL) {
1009         winPtr->childList->flags |= CK_RECURSIVE_DESTROY;
1010         Ck_DestroyWindow(winPtr->childList);
1011     }
1012     if (winPtr->mainPtr->focusPtr == winPtr) {
1013         event.type = CK_EV_FOCUSOUT;
1014         event.winPtr = winPtr;
1015         Ck_HandleEvent(winPtr->mainPtr, (CkEvent *) &event);
1016     }
1017     if (winPtr->window != NULL) {
1018         delwin(winPtr->window);
1019         winPtr->window = NULL;
1020     }
1021     CkOptionDeadWindow(winPtr);
1022     event.type = CK_EV_DESTROY;
1023     event.winPtr = winPtr;
1024     Ck_HandleEvent(winPtr->mainPtr, (CkEvent *) &event);
1025     if (winPtr->tagPtr != NULL) {
1026         CkFreeBindingTags(winPtr);
1027     }
1028     UnlinkWindow(winPtr);
1029     CkEventDeadWindow(winPtr);
1030     hPtr = Tcl_FindHashEntry(&winPtr->mainPtr->winTable, (char *) winPtr);
1031     if (hPtr != NULL)
1032         Tcl_DeleteHashEntry(hPtr);
1033     if (winPtr->pathName != NULL) {
1034         CkMainInfo *mainPtr = winPtr->mainPtr;
1035
1036         Ck_DeleteAllBindings(mainPtr->bindingTable,
1037                 (ClientData) winPtr->pathName);
1038         Tcl_DeleteHashEntry(Tcl_FindHashEntry(&mainPtr->nameTable,
1039                 winPtr->pathName));
1040         if (mainPtr->winPtr == winPtr) {
1041             CkCmd *cmdPtr;
1042
1043             for (cmdPtr = commands; cmdPtr->name != NULL; cmdPtr++)
1044                 if (cmdPtr->cmdProc != Ck_ExitCmd)
1045                     Tcl_CreateCommand(mainPtr->interp, cmdPtr->name,
1046                         DeadAppCmd, (ClientData) NULL,
1047                         (Tcl_CmdDeleteProc *) NULL);
1048             Tcl_DeleteHashTable(&mainPtr->nameTable);
1049             Ck_DeleteBindingTable(mainPtr->bindingTable);
1050
1051 #ifdef NCURSES_MOUSE_VERSION
1052             mousemask(0, NULL);
1053             mainPtr->flags &= (getmouse(&mEvent) != ERR) ? ~CK_HAS_MOUSE : ~0;
1054 #endif  /* NCURSES_MOUSE_VERSION */
1055
1056             if (mainPtr->flags & CK_HAS_MOUSE) {
1057 #if defined(__WIN32__) || defined(__DJGPP__)
1058                 mouse_set(0);
1059 #endif
1060                 if (mainPtr->flags & CK_MOUSE_XTERM) {
1061                     fflush(stdout);
1062                     fputs("\033[?1000l", stdout);
1063                     fflush(stdout);
1064                 } else {
1065 #ifdef HAVE_GPM
1066 #if (TCL_MAJOR_VERSION >= 8)
1067                     Tcl_DeleteFileHandler((int) mainPtr->mouseData);
1068 #else
1069 #if (TCL_MAJOR_VERSION == 7) && (TCL_MINOR_VERSION <= 4)
1070                     Tk_DeleteFileHandler((int) mainPtr->mouseData);
1071 #else
1072                     Tcl_DeleteFileHandler((Tcl_File) mainPtr->mouseData);
1073 #endif
1074 #endif
1075                     Gpm_Close();
1076 #endif
1077                 }
1078             }
1079
1080             curs_set(1);
1081             if (mainPtr->flags & CK_NOCLR_ON_EXIT) {
1082                 wattrset(stdscr, A_NORMAL);
1083             } else {
1084                 wclear(stdscr);
1085                 wrefresh(stdscr);
1086             } 
1087             endwin();
1088 #if CK_USE_UTF
1089             Tcl_DStringFree(&mainPtr->isoBuffer);
1090             Tcl_FreeEncoding(mainPtr->isoEncoding);
1091 #endif
1092             ckfree((char *) mainPtr);
1093             ckMainInfo = NULL;
1094             goto done;
1095         }
1096     }
1097     if (winPtr->flags & CK_TOPLEVEL) {
1098         UnlinkToplevel(winPtr);
1099         ChangeToplevelFocus(winPtr->mainPtr->topLevPtr);
1100     } else if (winPtr->mainPtr->focusPtr == winPtr) {
1101         winPtr->mainPtr->focusPtr = winPtr->parentPtr;
1102         if (winPtr->mainPtr->focusPtr != NULL &&
1103             (winPtr->mainPtr->focusPtr->flags & CK_MAPPED)) {
1104             event.type = CK_EV_FOCUSIN;
1105             event.winPtr = winPtr->mainPtr->focusPtr;
1106             Ck_HandleEvent(winPtr->mainPtr, (CkEvent *) &event);
1107         }
1108     } else {
1109         CkWindow *topPtr;
1110
1111         for (topPtr = winPtr; topPtr != NULL && !(topPtr->flags & CK_TOPLEVEL);
1112              topPtr = topPtr->parentPtr) {
1113             /* Empty loop body. */
1114         }
1115         if (topPtr->focusPtr == winPtr)
1116             topPtr->focusPtr = winPtr->parentPtr;
1117     }
1118     Ck_EventuallyRefresh(winPtr);
1119 done:
1120     ckfree((char *) winPtr);
1121 }
1122 \f
1123 /*
1124  *--------------------------------------------------------------
1125  *
1126  * Ck_MapWindow --
1127  *
1128  *      Map a window within its parent.  This may require the
1129  *      window and/or its parents to actually be created.
1130  *
1131  * Results:
1132  *      None.
1133  *
1134  * Side effects:
1135  *      The given window will be mapped.  Windows may also
1136  *      be created.
1137  *
1138  *--------------------------------------------------------------
1139  */
1140
1141 void
1142 Ck_MapWindow(winPtr)
1143     CkWindow *winPtr;           /* Pointer to window to map. */
1144 {
1145     if (winPtr == NULL || (winPtr->flags & CK_MAPPED))
1146         return;
1147     if (!(winPtr->parentPtr->flags & CK_MAPPED))
1148         return;
1149     if (winPtr->window == NULL)
1150         Ck_MakeWindowExist(winPtr);
1151 }
1152 \f
1153 /*
1154  *--------------------------------------------------------------
1155  *
1156  * Ck_MakeWindowExist --
1157  *
1158  *      Ensure that a particular window actually exists.
1159  *
1160  * Results:
1161  *      None.
1162  *
1163  * Side effects:
1164  *      When the procedure returns, the curses window associated
1165  *      with winPtr is guaranteed to exist.  This may require the
1166  *      window's ancestors to be created also.
1167  *
1168  *--------------------------------------------------------------
1169  */
1170
1171 void
1172 Ck_MakeWindowExist(winPtr)
1173     CkWindow *winPtr;           /* Pointer to window. */
1174 {
1175     int x, y;
1176     CkMainInfo *mainPtr;
1177     CkWindow *parentPtr;
1178     CkWindowEvent event;
1179
1180     if (winPtr == NULL || winPtr->window != NULL)
1181         return;
1182
1183     mainPtr = winPtr->mainPtr;
1184     if (winPtr->parentPtr->window == NULL)
1185         Ck_MakeWindowExist(winPtr->parentPtr);
1186
1187     if (winPtr->x >= mainPtr->maxWidth)
1188         winPtr->x = mainPtr->maxWidth - 1;
1189     if (winPtr->x < 0)
1190         winPtr->x = 0;
1191     if (winPtr->y >= mainPtr->maxHeight)
1192         winPtr->y = mainPtr->maxHeight - 1;
1193     if (winPtr->y < 0)
1194         winPtr->y = 0;
1195
1196     x = winPtr->x;
1197     y = winPtr->y;
1198
1199     if (!(winPtr->flags & CK_TOPLEVEL)) {
1200         parentPtr = winPtr->parentPtr;
1201         if (x < 0)
1202             x = winPtr->x = 0;
1203         else if (x >= parentPtr->width)
1204             x = winPtr->x = parentPtr->width - 1;
1205         if (y < 0)
1206             y = winPtr->y = 0;
1207         else if (y >= parentPtr->height)
1208             y = winPtr->y = parentPtr->height - 1;
1209         if (x + winPtr->width >= parentPtr->width)
1210             winPtr->width = parentPtr->width - x;
1211         if (y + winPtr->height >= parentPtr->height)
1212             winPtr->height = parentPtr->height - y;
1213         parentPtr = winPtr;
1214         while ((parentPtr = parentPtr->parentPtr) != NULL) {
1215             x += parentPtr->x;
1216             y += parentPtr->y;
1217             if (parentPtr->flags & CK_TOPLEVEL)
1218                 break;
1219         }
1220     }
1221     if (winPtr->width <= 0)
1222         winPtr->width = 1;
1223     if (winPtr->height <= 0)
1224         winPtr->height = 1;
1225
1226     winPtr->window = newwin(winPtr->height, winPtr->width, y, x);
1227     idlok(winPtr->window, TRUE);
1228     scrollok(winPtr->window, FALSE);
1229     keypad(winPtr->window, TRUE);
1230     nodelay(winPtr->window, TRUE);
1231     meta(winPtr->window, TRUE);
1232     winPtr->flags |= CK_MAPPED;
1233     Ck_ClearToBot(winPtr, 0, 0);
1234     Ck_SetWindowAttr(winPtr, winPtr->fg, winPtr->bg, winPtr->attr);
1235     Ck_EventuallyRefresh(winPtr);
1236
1237     event.type = CK_EV_MAP;
1238     event.winPtr = winPtr;
1239     Ck_HandleEvent(mainPtr, (CkEvent *) &event);
1240     event.type = CK_EV_EXPOSE;
1241     event.winPtr = winPtr;
1242     Ck_HandleEvent(mainPtr, (CkEvent *) &event);
1243     if (winPtr == mainPtr->focusPtr) {
1244         event.type = CK_EV_FOCUSIN;
1245         event.winPtr = winPtr;
1246         Ck_HandleEvent(mainPtr, (CkEvent *) &event);
1247     }
1248 }
1249 \f
1250 /*
1251  *--------------------------------------------------------------
1252  *
1253  * Ck_MoveWindow --
1254  *
1255  *      Move given window and its children.
1256  *
1257  * Results:
1258  *      None.
1259  *
1260  * Side effects:
1261  *      None.
1262  *
1263  *--------------------------------------------------------------
1264  */
1265
1266 void
1267 Ck_MoveWindow(winPtr, x, y)
1268     CkWindow *winPtr;           /* Window to move. */
1269     int x, y;                   /* New location for window (within
1270                                  * parent). */
1271 {
1272     CkWindow *childPtr, *parentPtr;
1273     int newx, newy;
1274
1275     if (winPtr == NULL)
1276         return;
1277
1278     winPtr->x = x;
1279     winPtr->y = y;
1280     if (winPtr->window == NULL)
1281         return;
1282
1283     newx = x;
1284     newy = y;
1285     if (!(winPtr->flags & CK_TOPLEVEL)) {
1286         parentPtr = winPtr;
1287         while ((parentPtr = parentPtr->parentPtr) != NULL) {
1288             newx += parentPtr->x;
1289             newy += parentPtr->y;
1290             if (parentPtr->flags & CK_TOPLEVEL)
1291                 break;
1292         }
1293     }
1294     if (newx + winPtr->width >= winPtr->mainPtr->maxWidth) {
1295         winPtr->x -= newx - (winPtr->mainPtr->maxWidth - winPtr->width);
1296         newx = winPtr->mainPtr->maxWidth - winPtr->width;
1297     }
1298     if (newy + winPtr->height >= winPtr->mainPtr->maxHeight) {
1299         winPtr->y -= newy - (winPtr->mainPtr->maxHeight - winPtr->height);
1300         newy = winPtr->mainPtr->maxHeight - winPtr->height;
1301     }
1302     if (newx < 0) {
1303         winPtr->x -= newx;
1304         newx = 0;
1305     }
1306     if (newy < 0) {
1307         winPtr->y -= newy;
1308         newy = 0;
1309     }
1310
1311     mvwin(winPtr->window, newy, newx);
1312
1313     for (childPtr = winPtr->childList;
1314          childPtr != NULL; childPtr = childPtr->nextPtr)
1315         if (!(childPtr->flags & CK_TOPLEVEL))
1316             Ck_MoveWindow(childPtr, childPtr->x, childPtr->y);
1317     Ck_EventuallyRefresh(winPtr);
1318 }
1319 \f
1320 /*
1321  *--------------------------------------------------------------
1322  *
1323  * Ck_ResizeWindow --
1324  *
1325  *      Resize given window and eventually its children.
1326  *
1327  * Results:
1328  *      None.
1329  *
1330  * Side effects:
1331  *      None.
1332  *
1333  *--------------------------------------------------------------
1334  */
1335
1336 void
1337 Ck_ResizeWindow(winPtr, width, height)
1338     CkWindow *winPtr;           /* Window to resize. */
1339     int width, height;          /* New dimensions for window. */
1340 {
1341     CkWindow *childPtr, *parentPtr;
1342     CkWindow *mainWin = winPtr->mainPtr->winPtr;
1343     CkWindowEvent event;
1344     WINDOW *new;
1345     int x, y, evMap = 0, doResize = 0;
1346
1347     if (winPtr == NULL || winPtr == mainWin)
1348         return;
1349
1350     /*
1351      * Special case: if both width/height set to -12345, adjust
1352      * within parent window !
1353      */
1354
1355     parentPtr = winPtr->parentPtr;
1356     if (!(width == -12345 && height == -12345)) {
1357         winPtr->width = width;
1358         winPtr->height = height;
1359         doResize++;
1360     }
1361
1362     if (!(winPtr->flags & CK_TOPLEVEL)) {
1363         if (winPtr->x + winPtr->width >= parentPtr->width) {
1364             winPtr->width = parentPtr->width - winPtr->x;
1365             doResize++;
1366         }
1367         if (winPtr->y + winPtr->height >= parentPtr->height) {
1368             winPtr->height = parentPtr->height - winPtr->y;
1369             doResize++;
1370         }
1371
1372         if (!doResize)
1373             return;
1374
1375         if (winPtr->window == NULL)
1376             return;
1377
1378         parentPtr = winPtr;
1379         x = winPtr->x;
1380         y = winPtr->y;
1381         while ((parentPtr = parentPtr->parentPtr) != NULL) {
1382             x += parentPtr->x;
1383             y += parentPtr->y;
1384             if (parentPtr->flags & CK_TOPLEVEL)
1385                 break;
1386         }
1387     } else {
1388         x = winPtr->x;
1389         y = winPtr->y;
1390     }
1391
1392     if (winPtr->width <= 0)
1393         winPtr->width = 1;
1394     if (winPtr->height <= 0)
1395         winPtr->height = 1;
1396
1397     if (x + winPtr->width > winPtr->mainPtr->maxWidth)
1398         winPtr->width = winPtr->mainPtr->maxWidth - x;
1399     if (y + winPtr->height > winPtr->mainPtr->maxHeight)
1400         winPtr->height = winPtr->mainPtr->maxHeight - y;
1401
1402     new = newwin(winPtr->height, winPtr->width, y, x);
1403     if (winPtr->window == NULL) {
1404         winPtr->flags |= CK_MAPPED;
1405         evMap++;
1406     } else {
1407         delwin(winPtr->window);
1408     }
1409     winPtr->window = new;
1410     idlok(winPtr->window, TRUE);
1411     scrollok(winPtr->window, FALSE);
1412     keypad(winPtr->window, TRUE);
1413     nodelay(winPtr->window, TRUE);
1414     meta(winPtr->window, TRUE);
1415     Ck_SetWindowAttr(winPtr, winPtr->fg, winPtr->bg, winPtr->attr);
1416     Ck_ClearToBot(winPtr, 0, 0);
1417
1418     for (childPtr = winPtr->childList;
1419          childPtr != NULL; childPtr = childPtr->nextPtr) {
1420         if (childPtr->flags & CK_TOPLEVEL)
1421             continue;
1422         Ck_ResizeWindow(childPtr, -12345, -12345);
1423     }
1424     Ck_EventuallyRefresh(winPtr);
1425
1426     event.type = CK_EV_MAP;
1427     event.winPtr = winPtr;
1428     Ck_HandleEvent(mainWin->mainPtr, (CkEvent *) &event);
1429     event.type = CK_EV_EXPOSE;
1430     event.winPtr = winPtr;
1431     Ck_HandleEvent(mainWin->mainPtr, (CkEvent *) &event);
1432 }
1433 \f
1434 /*
1435  *--------------------------------------------------------------
1436  *
1437  * Ck_UnmapWindow, etc. --
1438  *
1439  *      There are several procedures under here, each of which
1440  *      mirrors an existing X procedure.  In addition to performing
1441  *      the functions of the corresponding procedure, each
1442  *      procedure also updates the local window structure and
1443  *      synthesizes an X event (if the window's structure is being
1444  *      managed internally).
1445  *
1446  * Results:
1447  *      See the manual entries.
1448  *
1449  * Side effects:
1450  *      See the manual entries.
1451  *
1452  *--------------------------------------------------------------
1453  */
1454
1455 void
1456 Ck_UnmapWindow(winPtr)
1457     CkWindow *winPtr;           /* Pointer to window to unmap. */
1458 {
1459     CkWindow *childPtr;
1460     CkMainInfo *mainPtr = winPtr->mainPtr;
1461     CkWindowEvent event;
1462
1463     for (childPtr = winPtr->childList;
1464          childPtr != NULL; childPtr = childPtr->nextPtr) {
1465         if (childPtr->flags & CK_TOPLEVEL)
1466             continue;
1467         Ck_UnmapWindow(childPtr);
1468     }
1469     if (!(winPtr->flags & CK_MAPPED))
1470         return;
1471     winPtr->flags &= ~CK_MAPPED;
1472     delwin(winPtr->window);
1473     winPtr->window = NULL;
1474     Ck_EventuallyRefresh(winPtr);
1475
1476     if (mainPtr->focusPtr == winPtr) {
1477         CkWindow *parentPtr;
1478
1479         parentPtr = winPtr->parentPtr;
1480         while (parentPtr != NULL && !(parentPtr->flags & CK_TOPLEVEL))
1481             parentPtr = parentPtr->parentPtr;
1482         mainPtr->focusPtr = parentPtr;
1483         event.type = CK_EV_FOCUSOUT;
1484         event.winPtr = winPtr;
1485         Ck_HandleEvent(mainPtr, (CkEvent *) &event);
1486     }
1487     event.type = CK_EV_UNMAP;
1488     event.winPtr = winPtr;
1489     Ck_HandleEvent(mainPtr, (CkEvent *) &event);
1490 }
1491
1492 void
1493 Ck_SetWindowAttr(winPtr, fg, bg, attr)
1494     CkWindow *winPtr;           /* Window to manipulate. */
1495     int fg, bg;                 /* Foreground/background colors. */
1496     int attr;                   /* Video attributes. */
1497 {
1498     winPtr->fg = fg;
1499     winPtr->bg = bg;
1500     winPtr->attr = attr;
1501     if (winPtr->window != NULL) {
1502         if ((winPtr->mainPtr->flags & (CK_HAS_COLOR | CK_REVERSE_KLUDGE)) ==
1503             (CK_HAS_COLOR | CK_REVERSE_KLUDGE)) {
1504             if (attr & A_REVERSE) {
1505                 int tmp;
1506
1507                 attr &= ~A_REVERSE;
1508                 tmp = bg;
1509                 bg = fg;
1510                 fg = tmp;
1511             }
1512         }
1513         wattrset(winPtr->window, attr | Ck_GetPair(winPtr, fg, bg));
1514     }
1515 }
1516
1517 void
1518 Ck_GetRootGeometry(winPtr, xPtr, yPtr, widthPtr, heightPtr)
1519     CkWindow *winPtr;
1520     int *xPtr, *yPtr, *widthPtr, *heightPtr;
1521 {
1522     int x, y;
1523
1524     if (widthPtr != NULL)
1525         *widthPtr = winPtr->width;
1526     if (heightPtr != NULL)
1527         *heightPtr = winPtr->height;
1528
1529     x = y = 0;
1530     do {
1531         x += winPtr->x;
1532         y += winPtr->y;
1533         if (winPtr->flags & CK_TOPLEVEL)
1534             break;
1535         winPtr = winPtr->parentPtr;
1536     } while (winPtr != NULL);
1537     if (xPtr != NULL)
1538         *xPtr = x;
1539     if (yPtr != NULL)
1540         *yPtr = y;
1541 }
1542 \f
1543 /*
1544  *----------------------------------------------------------------------
1545  *
1546  * Ck_NameToWindow --
1547  *
1548  *      Given a string name for a window, this procedure
1549  *      returns the pointer to the window, if there exists a
1550  *      window corresponding to the given name.
1551  *
1552  * Results:
1553  *      The return result is either the pointer to the window corresponding
1554  *      to "name", or else NULL to indicate that there is no such
1555  *      window.  In this case, an error message is left in interp->result.
1556  *
1557  * Side effects:
1558  *      None.
1559  *
1560  *----------------------------------------------------------------------
1561  */
1562
1563 CkWindow *
1564 Ck_NameToWindow(interp, pathName, winPtr)
1565     Tcl_Interp *interp;         /* Where to report errors. */
1566     char *pathName;             /* Path name of window. */
1567     CkWindow *winPtr;           /* Pointer to window:  name is assumed to
1568                                  * belong to the same main window as winPtr. */
1569 {
1570     Tcl_HashEntry *hPtr;
1571
1572     hPtr = Tcl_FindHashEntry(&winPtr->mainPtr->nameTable, pathName);
1573     if (hPtr == NULL) {
1574         Tcl_AppendResult(interp, "bad window path name \"",
1575                 pathName, "\"", (char *) NULL);
1576         return NULL;
1577     }
1578     return (CkWindow *) Tcl_GetHashValue(hPtr);
1579 }
1580 \f
1581 /*
1582  *----------------------------------------------------------------------
1583  *
1584  * Ck_SetClass --
1585  *
1586  *      This procedure is used to give a window a class.
1587  *
1588  * Results:
1589  *      None.
1590  *
1591  * Side effects:
1592  *      A new class is stored for winPtr, replacing any existing
1593  *      class for it.
1594  *
1595  *----------------------------------------------------------------------
1596  */
1597
1598 void
1599 Ck_SetClass(winPtr, className)
1600     CkWindow *winPtr;           /* Window to assign class. */
1601     char *className;            /* New class for window. */
1602 {
1603     winPtr->classUid = Ck_GetUid(className);
1604     CkOptionClassChanged(winPtr);
1605 }
1606 \f
1607 /*
1608  *----------------------------------------------------------------------
1609  *
1610  * UnlinkWindow --
1611  *
1612  *      This procedure removes a window from the childList of its
1613  *      parent.
1614  *
1615  * Results:
1616  *      None.
1617  *
1618  * Side effects:
1619  *      The window is unlinked from its childList.
1620  *
1621  *----------------------------------------------------------------------
1622  */
1623
1624 static void
1625 UnlinkWindow(winPtr)
1626     CkWindow *winPtr;                   /* Child window to be unlinked. */
1627 {
1628     CkWindow *prevPtr;
1629
1630     if (winPtr->parentPtr == NULL)
1631         return;
1632     prevPtr = winPtr->parentPtr->childList;
1633     if (prevPtr == winPtr) {
1634         winPtr->parentPtr->childList = winPtr->nextPtr;
1635         if (winPtr->nextPtr == NULL)
1636             winPtr->parentPtr->lastChildPtr = NULL;
1637     } else {
1638         while (prevPtr->nextPtr != winPtr) {
1639             prevPtr = prevPtr->nextPtr;
1640             if (prevPtr == NULL)
1641                 panic("UnlinkWindow couldn't find child in parent");
1642         }
1643         prevPtr->nextPtr = winPtr->nextPtr;
1644         if (winPtr->nextPtr == NULL)
1645             winPtr->parentPtr->lastChildPtr = prevPtr;
1646     }
1647 }
1648 \f
1649 /*
1650  *----------------------------------------------------------------------
1651  *
1652  * UnlinkToplevel --
1653  *
1654  *      This procedure removes a window from the toplevel list.
1655  *
1656  * Results:
1657  *      None.
1658  *
1659  * Side effects:
1660  *      The window is unlinked from the toplevel list.
1661  *
1662  *----------------------------------------------------------------------
1663  */
1664
1665 static void
1666 UnlinkToplevel(winPtr)
1667     CkWindow *winPtr;
1668 {
1669     CkWindow *prevPtr;
1670
1671     prevPtr = winPtr->mainPtr->topLevPtr;
1672     if (prevPtr == winPtr) {
1673         winPtr->mainPtr->topLevPtr = winPtr->topLevPtr;
1674     } else {
1675         while (prevPtr->topLevPtr != winPtr) {
1676             prevPtr = prevPtr->topLevPtr;
1677             if (prevPtr == NULL)
1678                 panic("UnlinkToplevel couldn't find toplevel");
1679         }
1680         prevPtr->topLevPtr = winPtr->topLevPtr;
1681     }
1682 }
1683 \f
1684 /*
1685  *----------------------------------------------------------------------
1686  *
1687  * Ck_RestackWindow --
1688  *
1689  *      Change a window's position in the stacking order.
1690  *
1691  * Results:
1692  *      TCL_OK is normally returned.  If other is not a descendant
1693  *      of winPtr's parent then TCL_ERROR is returned and winPtr is
1694  *      not repositioned.
1695  *
1696  * Side effects:
1697  *      WinPtr is repositioned in the stacking order.
1698  *
1699  *----------------------------------------------------------------------
1700  */
1701
1702 int
1703 Ck_RestackWindow(winPtr, aboveBelow, otherPtr)
1704     CkWindow *winPtr;           /* Pointer to window whose position in
1705                                  * the stacking order is to change. */
1706     int aboveBelow;             /* Indicates new position of winPtr relative
1707                                  * to other;  must be Above or Below. */
1708     CkWindow *otherPtr;         /* WinPtr will be moved to a position that
1709                                  * puts it just above or below this window.
1710                                  * If NULL then winPtr goes above or below
1711                                  * all windows in the same parent. */
1712 {
1713     CkWindow *prevPtr;
1714
1715     if (winPtr->flags & CK_TOPLEVEL) {
1716         if (otherPtr != NULL) {
1717             while (otherPtr != NULL && !(otherPtr->flags & CK_TOPLEVEL))
1718                 otherPtr = otherPtr->parentPtr;
1719         }
1720         if (otherPtr == winPtr)
1721             return TCL_OK;
1722
1723         UnlinkToplevel(winPtr);
1724         if (aboveBelow == CK_ABOVE) {
1725             if (otherPtr == NULL) {
1726                 winPtr->topLevPtr = winPtr->mainPtr->topLevPtr;
1727                 winPtr->mainPtr->topLevPtr = winPtr;
1728             } else {
1729                 CkWindow *thisPtr = winPtr->mainPtr->topLevPtr;
1730
1731                 prevPtr = NULL;
1732                 while (thisPtr != NULL && thisPtr != otherPtr) {
1733                     prevPtr = thisPtr;
1734                     thisPtr = thisPtr->topLevPtr;
1735                 }
1736                 if (prevPtr == NULL) {
1737                     winPtr->topLevPtr = winPtr->mainPtr->topLevPtr;
1738                     winPtr->mainPtr->topLevPtr = winPtr;
1739                 } else {
1740                     winPtr->topLevPtr = prevPtr->topLevPtr;
1741                     prevPtr->topLevPtr = winPtr;
1742                 }
1743             }
1744         } else {
1745             CkWindow *thisPtr = winPtr->mainPtr->topLevPtr;
1746
1747             prevPtr = NULL;
1748             while (thisPtr != NULL && thisPtr != otherPtr) {
1749                 prevPtr = thisPtr;
1750                 thisPtr = thisPtr->topLevPtr;
1751             }
1752             if (thisPtr == NULL) {
1753                 winPtr->topLevPtr = prevPtr->topLevPtr;
1754                 prevPtr->topLevPtr = winPtr;
1755             } else {
1756                 winPtr->topLevPtr = thisPtr->topLevPtr;
1757                 thisPtr->topLevPtr = winPtr;
1758             }
1759         }
1760         ChangeToplevelFocus(winPtr->mainPtr->topLevPtr);
1761         goto done;
1762     }
1763
1764     /*
1765      * Find an ancestor of otherPtr that is a sibling of winPtr.
1766      */
1767
1768     if (otherPtr == NULL) {
1769         if (aboveBelow == CK_BELOW)
1770             otherPtr = winPtr->parentPtr->lastChildPtr;
1771         else
1772             otherPtr = winPtr->parentPtr->childList;
1773     } else {
1774         while (winPtr->parentPtr != otherPtr->parentPtr) {
1775             otherPtr = otherPtr->parentPtr;
1776             if (otherPtr == NULL)
1777                 return TCL_ERROR;
1778         }
1779     }
1780     if (otherPtr == winPtr)
1781         return TCL_OK;
1782
1783     /*
1784      * Reposition winPtr in the stacking order.
1785      */
1786
1787     UnlinkWindow(winPtr);
1788     if (aboveBelow == CK_BELOW) {
1789         winPtr->nextPtr = otherPtr->nextPtr;
1790         if (winPtr->nextPtr == NULL)
1791             winPtr->parentPtr->lastChildPtr = winPtr;
1792         otherPtr->nextPtr = winPtr;
1793     } else {
1794         prevPtr = winPtr->parentPtr->childList;
1795         if (prevPtr == otherPtr)
1796             winPtr->parentPtr->childList = winPtr;
1797         else {
1798             while (prevPtr->nextPtr != otherPtr)
1799                 prevPtr = prevPtr->nextPtr;
1800             prevPtr->nextPtr = winPtr;
1801         }
1802         winPtr->nextPtr = otherPtr;
1803     }
1804
1805 done:
1806     Ck_EventuallyRefresh(winPtr);
1807     return TCL_OK;
1808 }
1809 \f
1810 /*
1811  *----------------------------------------------------------------------
1812  *
1813  * Ck_SetFocus --
1814  *
1815  *      This procedure is invoked to change the focus window for a
1816  *      given display in a given application.
1817  *
1818  * Results:
1819  *      None.
1820  *
1821  * Side effects:
1822  *      Event handlers may be invoked to process the change of
1823  *      focus.
1824  *
1825  *----------------------------------------------------------------------
1826  */
1827
1828 void
1829 Ck_SetFocus(winPtr)
1830     CkWindow *winPtr;           /* Window that is to be the new focus. */
1831 {
1832     CkMainInfo *mainPtr = winPtr->mainPtr;
1833     CkEvent event;
1834     CkWindow *oldTop = NULL, *newTop, *oldFocus;
1835
1836     if (winPtr == mainPtr->focusPtr)
1837         return;
1838
1839     oldFocus = mainPtr->focusPtr;
1840     if (oldFocus != NULL) {
1841         for (oldTop = oldFocus; oldTop != NULL &&
1842              !(oldTop->flags & CK_TOPLEVEL); oldTop = oldTop->parentPtr) {
1843             /* Empty loop body. */
1844         }
1845         event.win.type = CK_EV_FOCUSOUT;
1846         event.win.winPtr = oldFocus;
1847         Ck_HandleEvent(mainPtr, &event);
1848     }
1849     mainPtr->focusPtr = winPtr;
1850     for (newTop = winPtr; newTop != NULL &&
1851          !(newTop->flags & CK_TOPLEVEL); newTop = newTop->parentPtr) {
1852         /* Empty loop body. */
1853     }
1854     if (oldTop != newTop) {
1855         if (oldTop != NULL)
1856             oldTop->focusPtr = oldFocus;
1857         Ck_RestackWindow(newTop, CK_ABOVE, NULL);
1858         Ck_EventuallyRefresh(mainPtr->winPtr);
1859     }
1860     if (winPtr->flags & CK_MAPPED) {
1861         event.win.type = CK_EV_FOCUSIN;
1862         event.win.winPtr = winPtr;
1863         Ck_HandleEvent(mainPtr, &event);
1864         Ck_EventuallyRefresh(mainPtr->winPtr);
1865     }
1866 }
1867 \f
1868 /*
1869  *----------------------------------------------------------------------
1870  *
1871  * ChangeToplevelFocus --
1872  *
1873  *----------------------------------------------------------------------
1874  */
1875
1876 static void
1877 ChangeToplevelFocus(winPtr)
1878     CkWindow *winPtr;
1879 {
1880     CkWindow *winTop, *oldTop;
1881     CkMainInfo *mainPtr;
1882
1883     if (winPtr == NULL)
1884         return;
1885
1886     mainPtr = winPtr->mainPtr;
1887     for (winTop = winPtr; winTop != NULL && !(winTop->flags & CK_TOPLEVEL);
1888          winTop = winTop->parentPtr) {
1889         /* Empty loop body. */
1890     }
1891     for (oldTop = mainPtr->focusPtr; oldTop != NULL &&
1892          !(oldTop->flags & CK_TOPLEVEL); oldTop = oldTop->parentPtr) {
1893         /* Empty loop body. */
1894     }
1895     if (winTop != oldTop) {
1896         CkEvent event;
1897
1898         if (oldTop != NULL) {
1899             oldTop->focusPtr = mainPtr->focusPtr;
1900             event.win.type = CK_EV_FOCUSOUT;
1901             event.win.winPtr = mainPtr->focusPtr;
1902             Ck_HandleEvent(mainPtr, &event);
1903         }
1904         mainPtr->focusPtr = winTop->focusPtr;
1905         event.win.type = CK_EV_FOCUSIN;
1906         event.win.winPtr = mainPtr->focusPtr;
1907         Ck_HandleEvent(mainPtr, &event);
1908     }
1909 }
1910 \f
1911 /*
1912  *----------------------------------------------------------------------
1913  *
1914  * Ck_EventuallyRefresh --
1915  *
1916  *      Dispatch refresh of entire screen.
1917  *
1918  * Results:
1919  *      None.
1920  *
1921  *----------------------------------------------------------------------
1922  */
1923
1924 void
1925 Ck_EventuallyRefresh(winPtr)
1926     CkWindow *winPtr;
1927 {
1928     if (++winPtr->mainPtr->refreshCount == 1)
1929         Tcl_DoWhenIdle(DoRefresh, (ClientData) winPtr->mainPtr);
1930 }
1931 \f
1932 /*
1933  *----------------------------------------------------------------------
1934  *
1935  * DoRefresh --
1936  *
1937  *      Refresh all curses windows. If the terminal is connected via
1938  *      a network connection (ie terminal server) the curses typeahead
1939  *      mechanism is not sufficient for delaying screen updates due to
1940  *      TCP buffering.
1941  *      Therefore the refreshDelay may be used in order to limit updates
1942  *      to happen not more often than 1000/refreshDelay times per second.
1943  *
1944  * Results:
1945  *      None.
1946  *
1947  *----------------------------------------------------------------------
1948  */
1949
1950 static void
1951 DoRefresh(clientData)
1952     ClientData clientData;
1953 {
1954     CkMainInfo *mainPtr = (CkMainInfo *) clientData;
1955
1956     if (mainPtr->flags & CK_REFRESH_TIMER) {
1957         Tk_DeleteTimerHandler(mainPtr->refreshTimer);
1958         mainPtr->flags &= ~CK_REFRESH_TIMER;
1959     }
1960     if (--mainPtr->refreshCount > 0) {
1961         Tk_DoWhenIdle2(DoRefresh, clientData);
1962         return;
1963     }
1964     mainPtr->refreshCount = 0;
1965     if (mainPtr->refreshDelay > 0) {
1966 #if (TCL_MAJOR_VERSION == 7) && (TCL_MINOR_VERSION <= 4)
1967         struct timeval tv;
1968         double t0;
1969
1970         gettimeofday(&tv, (struct timezone *) NULL);
1971         t0 = (tv.tv_sec + 0.000001 * tv.tv_usec) * 1000;
1972 #else
1973         Tcl_Time tv;
1974         double t0;
1975         extern void TclpGetTime _ANSI_ARGS_((Tcl_Time *timePtr));
1976
1977         TclpGetTime(&tv);
1978         t0 = (tv.sec + 0.000001 * tv.usec) * 1000;
1979 #endif
1980         if (t0 - mainPtr->lastRefresh < mainPtr->refreshDelay) {
1981             mainPtr->refreshTimer = Tk_CreateTimerHandler(
1982                 mainPtr->refreshDelay - (int) (t0 - mainPtr->lastRefresh),
1983                 DoRefresh, clientData);
1984             mainPtr->flags |= CK_REFRESH_TIMER;
1985             return;
1986         }
1987         mainPtr->lastRefresh = t0;
1988     }
1989     curs_set(0);
1990     RefreshToplevels(mainPtr->topLevPtr);
1991     UpdateHWCursor(ckMainInfo);
1992     doupdate();
1993 }
1994 \f
1995 /*
1996  *----------------------------------------------------------------------
1997  *
1998  * RefreshToplevels --
1999  *
2000  *      Recursively refresh all toplevel windows starting at winPtr.
2001  *
2002  * Results:
2003  *      None.
2004  *
2005  *----------------------------------------------------------------------
2006  */
2007
2008 static void
2009 RefreshToplevels(winPtr)
2010     CkWindow *winPtr;
2011 {
2012     if (winPtr->topLevPtr != NULL)
2013         RefreshToplevels(winPtr->topLevPtr);
2014     if (winPtr->window != NULL) {
2015         touchwin(winPtr->window);
2016         wnoutrefresh(winPtr->window);
2017         if (winPtr->childList != NULL)
2018             RefreshThem(winPtr->childList);
2019     }
2020 }
2021 \f
2022 /*
2023  *----------------------------------------------------------------------
2024  *
2025  * RefreshThem --
2026  *
2027  *      Recursively refresh all curses windows starting at winPtr.
2028  *
2029  * Results:
2030  *      None.
2031  *
2032  *----------------------------------------------------------------------
2033  */
2034
2035 static void
2036 RefreshThem(winPtr)
2037     CkWindow *winPtr;
2038 {
2039     if (winPtr->nextPtr != NULL)
2040         RefreshThem(winPtr->nextPtr);
2041     if (winPtr->flags & CK_TOPLEVEL)
2042         return;
2043     if (winPtr->window != NULL) {
2044         touchwin(winPtr->window);
2045         wnoutrefresh(winPtr->window);
2046     }
2047     if (winPtr->childList != NULL)
2048         RefreshThem(winPtr->childList);
2049 }
2050 \f
2051 /*
2052  *----------------------------------------------------------------------
2053  *
2054  * UpdateHWCursor --
2055  *
2056  *      Make hardware cursor (in)visible for given window using curses.
2057  *
2058  * Results:
2059  *      None.
2060  *
2061  *----------------------------------------------------------------------
2062  */
2063
2064 static void
2065 UpdateHWCursor(mainPtr)
2066     CkMainInfo *mainPtr;
2067 {
2068     int x, y;
2069     CkWindow *wPtr, *stopAtWin, *winPtr = mainPtr->focusPtr;
2070
2071     if (winPtr == NULL || winPtr->window == NULL ||
2072         (winPtr->flags & (CK_SHOW_CURSOR | CK_ALREADY_DEAD)) == 0) {
2073 invisible:
2074         curs_set(0);
2075         if (mainPtr->focusPtr != NULL && mainPtr->focusPtr->window != NULL)
2076             wnoutrefresh(mainPtr->focusPtr->window);
2077         return;
2078     }
2079
2080     /*
2081      * Get position of HW cursor in winPtr coordinates.
2082      */
2083
2084     getyx(winPtr->window, y, x);
2085
2086     stopAtWin = NULL;
2087     while (winPtr != NULL) {
2088         for (wPtr = winPtr->childList;
2089              wPtr != NULL && wPtr != stopAtWin; wPtr = wPtr->nextPtr) {
2090             if ((wPtr->flags & CK_TOPLEVEL) || wPtr->window == NULL)
2091                 continue;
2092             if (x >= wPtr->x && x < wPtr->x + wPtr->width &&
2093                 y >= wPtr->y && y < wPtr->y + wPtr->height)
2094                 goto invisible;
2095         }
2096         x += winPtr->x;
2097         y += winPtr->y;
2098         stopAtWin = winPtr;
2099         if (winPtr->parentPtr == NULL)
2100             break;
2101         winPtr = winPtr->parentPtr;
2102         if (winPtr->flags & CK_TOPLEVEL)
2103             break;
2104     }
2105     for (wPtr = mainPtr->topLevPtr; wPtr != NULL && wPtr != winPtr;
2106          wPtr = wPtr->topLevPtr)
2107         if (x >= wPtr->x && x < wPtr->x + wPtr->width &&
2108             y >= wPtr->y && y < wPtr->y + wPtr->height)
2109             goto invisible;
2110     curs_set(1);
2111     wnoutrefresh(mainPtr->focusPtr->window);
2112 }
2113 \f
2114 /*
2115  *----------------------------------------------------------------------
2116  *
2117  * GetWindowXY --
2118  *
2119  *      Given X, Y coordinates, return topmost window.
2120  *
2121  * Results:
2122  *      None.
2123  *
2124  *----------------------------------------------------------------------
2125  */
2126
2127 static CkWindow *
2128 GetWindowXY(winPtr, xPtr, yPtr)
2129     CkWindow *winPtr;
2130     int *xPtr, *yPtr;
2131 {
2132     int x, y;
2133     CkWindow *wPtr;
2134
2135     x = *xPtr - winPtr->x; y = *yPtr - winPtr->y;
2136     for (wPtr = winPtr->childList; wPtr != NULL; wPtr = wPtr->nextPtr) {
2137         if (!(wPtr->flags & CK_MAPPED) || (wPtr->flags & CK_TOPLEVEL))
2138             continue;
2139         if (x >= wPtr->x && x < wPtr->x + wPtr->width &&
2140             y >= wPtr->y && y < wPtr->y + wPtr->height) {
2141             wPtr = GetWindowXY(wPtr, &x, &y);
2142             *xPtr = x;
2143             *yPtr = y;
2144             return wPtr;
2145         }
2146     }
2147     *xPtr = x;
2148     *yPtr = y;
2149     return winPtr;
2150 }
2151 \f
2152 /*
2153  *----------------------------------------------------------------------
2154  *
2155  * Ck_GetWindowXY --
2156  *
2157  *      Given X, Y coordinates, return topmost window.
2158  *      If mode is zero, consider all toplevels, otherwise consider
2159  *      only topmost toplevel in search.
2160  *
2161  * Results:
2162  *      Window pointer or NULL. *xPtr, *yPtr are adjusted to window
2163  *      coordinate system if possible.
2164  *
2165  *----------------------------------------------------------------------
2166  */
2167
2168 CkWindow *
2169 Ck_GetWindowXY(mainPtr, xPtr, yPtr, mode)
2170     CkMainInfo *mainPtr;
2171     int *xPtr, *yPtr, mode;
2172 {
2173     int x, y, x0, y0;
2174     CkWindow *wPtr;
2175
2176     x0 = *xPtr; y0 = *yPtr;
2177     wPtr = mainPtr->topLevPtr;
2178 nextToplevel:
2179     x = x0; y = y0;
2180     if (wPtr->flags & CK_MAPPED) {
2181         if (x >= wPtr->x && x < wPtr->x + wPtr->width &&
2182             y >= wPtr->y && y < wPtr->y + wPtr->height) {
2183             wPtr = GetWindowXY(wPtr, &x, &y);
2184             *xPtr = x;
2185             *yPtr = y;
2186             return wPtr;
2187         } else {
2188             *xPtr = -1;
2189             *yPtr = -1;
2190         }
2191     } else if (mode) {
2192         return NULL;
2193     }
2194     if (mode == 0) {
2195         wPtr = wPtr->topLevPtr;
2196         if (wPtr != NULL)
2197             goto nextToplevel;
2198     }
2199     return wPtr;
2200 }
2201 \f
2202 /*
2203  *----------------------------------------------------------------------
2204  *
2205  * Ck_SetHWCursor --
2206  *
2207  *      Make hardware cursor (in)visible for given window.
2208  *
2209  * Results:
2210  *      None.
2211  *
2212  *----------------------------------------------------------------------
2213  */
2214
2215 void
2216 Ck_SetHWCursor(winPtr, newState)
2217     CkWindow *winPtr;
2218     int newState;
2219 {
2220     int oldState = winPtr->flags & CK_SHOW_CURSOR;
2221
2222     if (newState == oldState)
2223         return;
2224
2225     if (newState)
2226         winPtr->flags |= CK_SHOW_CURSOR;
2227     else
2228         winPtr->flags &= ~CK_SHOW_CURSOR;
2229     if (winPtr == winPtr->mainPtr->focusPtr)
2230         UpdateHWCursor(winPtr->mainPtr);
2231 }
2232 \f
2233 /*
2234  *----------------------------------------------------------------------
2235  *
2236  * Ck_ClearToEol --
2237  *
2238  *      Clear window starting from given position, til end of line.
2239  *
2240  * Results:
2241  *      None.
2242  *
2243  *----------------------------------------------------------------------
2244  */
2245
2246 void
2247 Ck_ClearToEol(winPtr, x, y)
2248     CkWindow *winPtr;
2249     int x, y;
2250 {
2251     WINDOW *window = winPtr->window;
2252
2253     if (window == NULL)
2254         return;
2255
2256     if (x == -1 && y == -1)
2257         getyx(window, y, x);
2258     else
2259         wmove(window, y, x);
2260     for (; x < winPtr->width; x++)
2261         waddch(window, ' ');
2262 }
2263 \f
2264 /*
2265  *----------------------------------------------------------------------
2266  *
2267  * Ck_ClearToBot --
2268  *
2269  *      Clear window starting from given position, til end of screen.
2270  *
2271  * Results:
2272  *      None.
2273  *
2274  *----------------------------------------------------------------------
2275  */
2276
2277 void
2278 Ck_ClearToBot(winPtr, x, y)
2279     CkWindow *winPtr;
2280     int x, y;
2281 {
2282     WINDOW *window = winPtr->window;
2283
2284     if (window == NULL)
2285         return;
2286
2287     wmove(window, y, x);
2288     for (; x < winPtr->width; x++)
2289         waddch(window, ' ');
2290     for (++y; y < winPtr->height; y++) {
2291         wmove(window, y, 0);
2292         for (x = 0; x < winPtr->width; x++)
2293             waddch(window, ' ');
2294     }
2295 }
2296 \f
2297 /*
2298  *----------------------------------------------------------------------
2299  *
2300  * DeadAppCmd --
2301  *
2302  *      Report error since toolkit gone.
2303  *
2304  * Results:
2305  *      None.
2306  *
2307  *----------------------------------------------------------------------
2308  */
2309
2310 static int
2311 DeadAppCmd(clientData, interp, argc, argv)
2312     ClientData clientData;
2313     Tcl_Interp *interp;
2314     int argc;
2315     char **argv;
2316 {
2317     interp->result = "toolkit uninstalled";
2318     return TCL_ERROR;
2319 }
2320 \f
2321 /*
2322  *----------------------------------------------------------------------
2323  *
2324  * ExecCmd --
2325  *
2326  *      Own version of "exec" Tcl command which supports the -endwin
2327  *      option.
2328  *
2329  * Results:
2330  *      See documentation for "exec".
2331  *
2332  *----------------------------------------------------------------------
2333  */
2334
2335 static int
2336 ExecCmd(clientData, interp, argc, argv)
2337     ClientData clientData;
2338     Tcl_Interp *interp;
2339     int argc;
2340     char **argv;
2341 {
2342     RedirInfo *redirInfo = (RedirInfo *) clientData;
2343     Tcl_CmdInfo *cmdInfo = &redirInfo->cmdInfo;
2344     int result, endWin = 0;
2345     char *savedargv1;
2346 #ifdef SIGINT
2347 #ifdef HAVE_SIGACTION
2348     struct sigaction oldsig, newsig;
2349 #else
2350     Ck_SignalProc sigproc;
2351 #endif
2352 #endif
2353
2354     if (argc > 1 && strcmp(argv[1], "-endwin") == 0) {
2355         endWin = 1;
2356         savedargv1 = argv[1];
2357         argv[1] = argv[0];
2358         curs_set(1);
2359         endwin();
2360 #ifdef SIGINT
2361 #ifdef HAVE_SIGACTION
2362         newsig.sa_handler = SIG_IGN;
2363         sigfillset(&newsig.sa_mask);
2364         newsig.sa_flags = 0;
2365         sigaction(SIGINT, &newsig, &oldsig);
2366 #else
2367         sigproc = signal(SIGINT, SIG_IGN);
2368 #endif
2369 #endif
2370     }
2371     result = (*cmdInfo->proc)(cmdInfo->clientData, interp,
2372         argc - endWin, argv + endWin);
2373     if (endWin) {
2374 #ifdef SIGINT
2375 #ifdef HAVE_SIGACTION
2376         sigaction(SIGINT, &oldsig, NULL);
2377 #else
2378         signal(SIGINT, sigproc);
2379 #endif
2380 #endif
2381         argv[0] = argv[1];
2382         argv[1] = savedargv1;
2383         Ck_EventuallyRefresh(redirInfo->mainPtr->winPtr);
2384     }
2385     return result;
2386 }
2387 \f
2388 /*
2389  *----------------------------------------------------------------------
2390  *
2391  * PutsCmd --
2392  *
2393  *      Redirect "puts" Tcl command from "stdout" to "stderr" in the
2394  *      hope that it will not destroy our screen.
2395  *
2396  * Results:
2397  *      See documentation of "puts" command.
2398  *
2399  *----------------------------------------------------------------------
2400  */
2401
2402 static int
2403 PutsCmd(clientData, interp, argc, argv)
2404     ClientData clientData;
2405     Tcl_Interp *interp;
2406     int argc;
2407     char **argv;
2408 {
2409     RedirInfo *redirInfo = (RedirInfo *) clientData;
2410     Tcl_CmdInfo *cmdInfo = &redirInfo->cmdInfo;
2411     int index = 0;
2412     char *newArgv[5];
2413
2414     newArgv[0] = argv[0];
2415     if (argc > 1 && strcmp(argv[1], "-nonewline") == 0) {
2416         newArgv[1] = argv[1];
2417         index++;
2418     }
2419     if (argc == index + 2) {
2420         newArgv[index + 2] = argv[index + 1];
2421 toStderr:
2422         newArgv[index + 1] = "stderr";
2423         return (*cmdInfo->proc)(cmdInfo->clientData, interp,
2424             index + 3, newArgv);
2425     } else if (argc == index + 3 &&
2426        (strcmp(argv[index + 1], "stdout") == 0 ||
2427         strcmp(argv[index + 1], "file1") == 0)) {
2428         newArgv[index + 2] = argv[index + 2];
2429         goto toStderr;
2430     }
2431     return (*cmdInfo->proc)(cmdInfo->clientData, interp, argc, argv);
2432 }
2433 \f
2434 /*
2435  *----------------------------------------------------------------------
2436  *
2437  * CloseCmd --
2438  *
2439  *      Report error when attempt is made to close stdin or stdout.
2440  *
2441  * Results:
2442  *      See documentation of "close" command.
2443  *
2444  *----------------------------------------------------------------------
2445  */
2446
2447 static int
2448 CloseCmd(clientData, interp, argc, argv)
2449     ClientData clientData;
2450     Tcl_Interp *interp;
2451     int argc;
2452     char **argv;
2453 {
2454     RedirInfo *redirInfo = (RedirInfo *) clientData;
2455     Tcl_CmdInfo *cmdInfo = &redirInfo->cmdInfo;
2456
2457     if (argc == 2 &&
2458        (strcmp(argv[1], "stdin") == 0 ||
2459         strcmp(argv[1], "file0") == 0 ||
2460         strcmp(argv[1], "stdout") == 0 ||
2461         strcmp(argv[1], "file1") == 0)) {
2462         Tcl_AppendResult(interp, "may not close fileId \"",
2463              argv[1], "\" while in toolkit", (char *) NULL);
2464         return TCL_ERROR;
2465     }
2466     return (*cmdInfo->proc)(cmdInfo->clientData, interp, argc, argv);
2467 }
2468 \f
2469 /*
2470  *----------------------------------------------------------------------
2471  *
2472  * FlushCmd --
2473  *
2474  *      Report error when attempt is made to flush stdin or stdout.
2475  *
2476  * Results:
2477  *      See documentation of "flush" command.
2478  *
2479  *----------------------------------------------------------------------
2480  */
2481
2482 static int
2483 FlushCmd(clientData, interp, argc, argv)
2484     ClientData clientData;
2485     Tcl_Interp *interp;
2486     int argc;
2487     char **argv;
2488 {
2489     RedirInfo *redirInfo = (RedirInfo *) clientData;
2490     Tcl_CmdInfo *cmdInfo = &redirInfo->cmdInfo;
2491
2492     if (argc == 2 &&
2493        (strcmp(argv[1], "stdin") == 0 ||
2494         strcmp(argv[1], "file0") == 0 ||
2495         strcmp(argv[1], "stdout") == 0 ||
2496         strcmp(argv[1], "file1") == 0)) {
2497         Tcl_AppendResult(interp, "may not flush fileId \"",
2498              argv[1], "\" while in toolkit", (char *) NULL);
2499         return TCL_ERROR;
2500     }
2501     return (*cmdInfo->proc)(cmdInfo->clientData, interp, argc, argv);
2502 }
2503 \f
2504 /*
2505  *----------------------------------------------------------------------
2506  *
2507  * ReadCmd --
2508  *
2509  *      Report error when attempt is made to read from stdin.
2510  *
2511  * Results:
2512  *      See documentation of "read" command.
2513  *
2514  *----------------------------------------------------------------------
2515  */
2516
2517 static int
2518 ReadCmd(clientData, interp, argc, argv)
2519     ClientData clientData;
2520     Tcl_Interp *interp;
2521     int argc;
2522     char **argv;
2523 {
2524     RedirInfo *redirInfo = (RedirInfo *) clientData;
2525     Tcl_CmdInfo *cmdInfo = &redirInfo->cmdInfo;
2526
2527     if ((argc > 1 &&
2528          (strcmp(argv[1], "stdin") == 0 ||
2529           strcmp(argv[1], "file0") == 0)) ||
2530         (argc > 2 &&
2531          (strcmp(argv[2], "stdin") == 0 ||
2532           strcmp(argv[2], "file0") == 0))) {
2533         Tcl_AppendResult(interp, "may not read from fileId \"",
2534              argv[1], "\" while in toolkit", (char *) NULL);
2535         return TCL_ERROR;
2536     }
2537     return (*cmdInfo->proc)(cmdInfo->clientData, interp, argc, argv);
2538 }
2539 \f
2540 /*
2541  *----------------------------------------------------------------------
2542  *
2543  * GetsCmd --
2544  *
2545  *      Report error when attempt is made to read from stdin.
2546  *
2547  * Results:
2548  *      See documentation of "gets" command.
2549  *
2550  *----------------------------------------------------------------------
2551  */
2552
2553 static int
2554 GetsCmd(clientData, interp, argc, argv)
2555     ClientData clientData;
2556     Tcl_Interp *interp;
2557     int argc;
2558     char **argv;
2559 {
2560     RedirInfo *redirInfo = (RedirInfo *) clientData;
2561     Tcl_CmdInfo *cmdInfo = &redirInfo->cmdInfo;
2562
2563     if (argc >= 2 &&
2564        (strcmp(argv[1], "stdin") == 0 ||
2565         strcmp(argv[1], "file0") == 0)) {
2566         Tcl_AppendResult(interp, "may not gets from fileId \"",
2567              argv[1], "\" while in toolkit", (char *) NULL);
2568         return TCL_ERROR;
2569     }
2570     return (*cmdInfo->proc)(cmdInfo->clientData, interp, argc, argv);
2571 }
2572 \f
2573 #ifdef __WIN32__
2574 \f
2575 /*
2576  *----------------------------------------------------------------------
2577  *
2578  * WIN32 specific curses input event handling.
2579  *
2580  *----------------------------------------------------------------------
2581  */
2582
2583 static void
2584 InputSetup(inputInfo)
2585     InputInfo *inputInfo;
2586 {
2587     WNDCLASS class;
2588     DWORD id;
2589
2590     /*
2591      * Create the async notification window with a new class.
2592      */
2593     class.style = 0;
2594     class.cbClsExtra = 0;
2595     class.cbWndExtra = 0;
2596     class.hInstance = GetModuleHandle(NULL);
2597     class.hbrBackground = NULL;
2598     class.lpszMenuName = NULL;
2599     class.lpszClassName = "CursesInput";
2600     class.lpfnWndProc = InputHandler;
2601     class.hIcon = NULL;
2602     class.hCursor = NULL;
2603     if (RegisterClass(&class)) {
2604         inputInfo->hwnd =
2605             CreateWindow("CursesInput", "CursesInput", WS_TILED, 0, 0,
2606                          0, 0, NULL, NULL, class.hInstance, NULL);
2607     }
2608     if (inputInfo->hwnd == NULL) {
2609         panic("cannot create curses input window");
2610     }
2611     SetWindowLong(inputInfo->hwnd, GWL_USERDATA, (LONG) inputInfo);
2612     inputInfo->thread = CreateThread(NULL, 4096,
2613                                      (LPTHREAD_START_ROUTINE) InputThread,
2614                                      (void *) inputInfo, 0, &id);
2615     Tcl_CreateExitHandler(InputExit, (ClientData) inputInfo);
2616 }
2617
2618 static void
2619 InputExit(clientData)
2620     ClientData clientData;
2621 {
2622     InputInfo *inputInfo = (InputInfo *) clientData;
2623
2624     if (inputInfo->hwnd != NULL) {
2625         HWND hwnd = inputInfo->hwnd;
2626
2627         inputInfo->hwnd = NULL;
2628         DestroyWindow(hwnd);
2629     }
2630     if (inputInfo->thread != INVALID_HANDLE_VALUE) {
2631         WaitForSingleObject(inputInfo->thread, 1000);
2632     }
2633 }
2634
2635 static void
2636 InputThread(arg)
2637     void *arg;
2638 {
2639     InputInfo *inputInfo = (InputInfo *) arg;
2640     INPUT_RECORD ip;
2641     DWORD nRead;
2642
2643     while (inputInfo->hwnd != NULL) {
2644         nRead = 0;
2645         PeekConsoleInput(inputInfo->stdinHandle, &ip, 1, &nRead);
2646         if (nRead > 0) {
2647             PostMessage(inputInfo->hwnd, WM_USER + 42, 0, 0);
2648         }
2649         Sleep(10);
2650     }
2651     inputInfo->thread = INVALID_HANDLE_VALUE;
2652     ExitThread(0);
2653 }
2654
2655 static LRESULT CALLBACK
2656 InputHandler(hwnd, message, wParam, lParam)
2657     HWND hwnd;
2658     UINT message;
2659     WPARAM wParam;
2660     LPARAM lParam;
2661 {
2662     InputInfo *inputInfo = (InputInfo *) GetWindowLong(hwnd, GWL_USERDATA);
2663
2664     if (message != WM_USER + 42) {
2665         return DefWindowProc(hwnd, message, wParam, lParam);
2666     }
2667     Tk_DoWhenIdle(InputHandler2, (ClientData) inputInfo);
2668     return 0;
2669 }
2670
2671 static void
2672 InputHandler2(clientData)
2673     ClientData clientData;
2674 {
2675     InputInfo *inputInfo = (InputInfo *) clientData;
2676     INPUT_RECORD ip;
2677     DWORD nRead;
2678
2679     do {
2680         CkHandleInput((ClientData) inputInfo->mainPtr, TCL_READABLE);
2681         nRead = 0;
2682         PeekConsoleInput(inputInfo->stdinHandle, &ip, 1, &nRead);
2683     } while (nRead != 0);
2684 }
2685
2686 #endif
2687
2688