/* grTk1.c * * Copyright 2003 Open Circuit Design, Inc., for MultiGiG Ltd. * * This file contains primitive functions to manipulate an X window system * Included here are initialization and closing * functions, and several utility routines used by the other X * modules. */ #include #include #include #include #include #include #include #include #include #include #include #include "tcltk/tclmagic.h" #include "utils/main.h" #include "utils/magic.h" #include "utils/malloc.h" #include "utils/magsgtty.h" #include "utils/geometry.h" #include "windows/windows.h" #include "graphics/graphics.h" #include "graphics/graphicsInt.h" #include "textio/textio.h" #include "textio/txcommands.h" #include "utils/signals.h" #include "tiles/tile.h" #include "utils/utils.h" #include "utils/hash.h" #include "database/database.h" #include "drc/drc.h" #include "utils/macros.h" #include "grTkInt.h" #include "utils/paths.h" #include "graphics/grTkCommon.h" /* C99 compat */ #include "dbwind/dbwind.h" GR_CURRENT grCurrent = { (Tk_Font)0, 0, 0, 0, 0, 0, (MagWindow *)NULL }; GR_DISPLAY grDisplay; GC grGCFill, grGCText, grGCDraw, grGCCopy, grGCGlyph, grGCStipple; Visual *grVisual; int grClass; Colormap grXcmap; extern char *DBWStyleType; unsigned long grPixels[256]; unsigned long grPlanes[256]; XColor colors[256*3]; /* Unique colors used by Magic */ Pixmap *grTkStipples; HashTable grTkWindowTable; /* locals */ typedef struct { char dashlist[8]; int dlen; } LineStyle; static LineStyle LineStyleTab[256]; #define grMagicToXs(n) (DisplayHeight(grXdpy,grXscrn)-(n)) #define grXsToMagic(n) (DisplayHeight(grXdpy,grXscrn)-(n)) extern bool GrTkInstalledCMap; /* machine-dependent constants - see below */ #ifdef __APPLE__ #define X_COLORMAP_BASE 128 #define X_COLORMAP_RESERVED 4 #else #if defined(CYGWIN) #define X_COLORMAP_BASE 128 #define X_COLORMAP_RESERVED 0 #else #define X_COLORMAP_BASE 0 #define X_COLORMAP_RESERVED 2 #endif #endif /* This is kind of a long story, and very kludgy, but the following * things need to be defined as externals because of the way lint * libraries are made by taking this module and changing all procedures * names "Xxxx" to "Grxxx". The change is only done at the declaration * of the procedure, so we need these declarations to handle uses * of those names, which don't get modified. Check out the Makefile * for details on this. */ extern void GrTkClose(), GrTkFlush(); extern void GrTkDelete(),GrTkConfigure(),GrTkRaise(),GrTkLower(); extern void GrTkLock(),GrTkUnlock(),GrTkIconUpdate(); extern bool GrTkInit(); extern bool GrTkEventPending(), GrTkCreate(), grtkGetCursorPos(); extern int GrTkWindowId(); extern char *GrTkWindowName(); /*--------------------------------------------------------- * grtkSetWMandC: * This is a local routine that resets the value of the current * write mask and color, if necessary. * * Results: None. * * Side Effects: None. * * Errors: None. *--------------------------------------------------------- */ void grtkSetWMandC (mask, c) long mask; /* New value for write mask */ int c; /* New value for current color */ { static int oldC = -1; static int oldM = -1; c = grPixels[c]; if(grDisplay.depth <= 8) { mask = grPlanes[mask]; if (mask == -65) mask = AllPlanes; } else { mask = AllPlanes; } if (oldC == c && oldM == mask) return; GR_TK_FLUSH_BATCH(); XSetPlaneMask(grXdpy,grGCFill,mask); XSetPlaneMask(grXdpy,grGCDraw,mask); XSetPlaneMask(grXdpy,grGCText,mask); XSetForeground(grXdpy,grGCFill,c); XSetForeground(grXdpy,grGCDraw,c); XSetForeground(grXdpy,grGCText,c); oldC = c; oldM = mask; } /*--------------------------------------------------------- * grtkSetLineStyle: * This local routine sets the current line style. * * Results: None. * * Side Effects: * A new line style is output to the display. * *--------------------------------------------------------- */ void grtkSetLineStyle (style) int style; /* New stipple pattern for lines. */ { static int oldStyle = -1; LineStyle *linestyle; int xstyle; style &= 0xFF; if (style == oldStyle) return; oldStyle = style; GR_TK_FLUSH_BATCH(); switch (style) { case 0xFF: case 0x00: xstyle = LineSolid; break; default: xstyle = LineOnOffDash; linestyle = &LineStyleTab[style]; if (linestyle->dlen == 0) { /* translate style to an X11 dashlist */ char *e; int cnt,offset,cur,new,curnew,i,match; e = linestyle->dashlist; cnt = 0; offset = 1; cur = 0; for (i = 7; i >= 0; i--) { new = (style >> i) & 1; curnew = (cur << 1) | new; switch (curnew) { case 0: case 3: cnt++; break; case 1: if (cnt > 0) *e++ = cnt; else offset = 0; cnt = 1; break; case 2: *e++ = cnt; cnt = 1; break; } cur = new; } *e++ = cnt; cnt = e - linestyle->dashlist; if (offset) { cur = e[0]; for (i = 0; i < cnt-1; i++) e[i] = e[i+1]; e[cnt-1] = cur; } match = 1; do { if (cnt % 2) break; for (i = 0; i < cnt/2; i++) { if (e[i] != e[cnt/2 + i]) match = 0; } if (match == 0) break; cnt = cnt/2; } while (match); linestyle->dlen = cnt; } XSetDashes(grXdpy, grGCDraw, 0, linestyle->dashlist, linestyle->dlen); } #ifdef OLD_XFREE /* Bypass bug in XFree-2.x server */ XSetLineAttributes(grXdpy, grGCDraw, 1, xstyle, CapNotLast, JoinMiter); #else XSetLineAttributes(grXdpy, grGCDraw, 0, xstyle, CapNotLast, JoinMiter); #endif } /*--------------------------------------------------------- * grtkSetSPattern: * xSetSPattern associates a stipple pattern with a given * stipple number. This is a local routine called from * grStyle.c . * * Results: None. * * Side Effects: None. *--------------------------------------------------------- */ void grtkSetSPattern (sttable, numstipples) int **sttable; /* The table of patterns */ int numstipples; /* Number of stipples */ { Tk_Window tkwind; Window xwid; Pixmap p; int i, x, y, pat; tkwind = Tk_MainWindow(magicinterp); /* This routine may be executed before Tk tells X11 to create the */ /* main window. So if no X11 window ID is registered, force the */ /* window creation and update. */ if (tkwind == 0 || Tk_WindowId(tkwind) == 0) Tk_MakeWindowExist(tkwind); xwid = Tk_WindowId(tkwind); grTkStipples = (Pixmap *)mallocMagic(numstipples * sizeof(Pixmap)); for (i = 0; i < numstipples; i++) { p = Tk_GetPixmap(grXdpy, xwid, 8, 8, 1); if (grGCStipple == NULL) { /* grGCStipple = Tk_GetGC(tkwind, 0, 0); */ grGCStipple = XCreateGC(grXdpy, p, 0, 0); } for (y = 0; y < 8; y++) { pat = sttable[i][y]; for (x = 0; x < 8; x++) { XSetForeground(grXdpy, grGCStipple, pat & 1); XDrawPoint(grXdpy, p, grGCStipple, x, y); pat >>= 1; } } grTkStipples[i] = p; } } /*--------------------------------------------------------- * grtkSetStipple: * This routine sets the Xs current stipple number. * * Results: None. * * Side Effects: * The current clipmask in the X is set to stipple, * if it wasn't that already. *--------------------------------------------------------- */ void grtkSetStipple (stipple) int stipple; /* The stipple number to be used. */ { static int oldStip = -1; if (stipple == oldStip) return; oldStip = stipple; GR_TK_FLUSH_BATCH(); if (stipple == 0 || stipple > grNumStipples) { XSetFillStyle(grXdpy, grGCFill, FillSolid); } else { if (grTkStipples[stipple] == 0) MainExit(1); XSetStipple(grXdpy, grGCFill, grTkStipples[stipple]); XSetFillStyle(grXdpy, grGCFill, FillStippled); } } /*--------------------------------------------------------- * GrTkInit: * * GrTkInit initializes the graphics display. The depth * of the display is queried from the server, and the * "best" visual selected. The environment variable * "MAGIC_COLOR" can override this choice. A colormap * is selected based on the visual type, but will be * filled in later. * * Results: TRUE if successful. *--------------------------------------------------------- */ #define visual_table_len 7 bool GrTkInit(dispType) char *dispType; { int i,j; XVisualInfo grvisual_info, *grvisual_get, grtemplate; VisualID defpsvid; int defpsindex = -1; int gritems, gritems_list, grcolorCount; bool rstatus; Window xwind; const char *visual_type[] = { "StaticGrey", "GreyScale", "StaticColor", "PseudoColor", "TrueColor", "DirectColor", "UNKNOWN" }; int visual_table[visual_table_len]; char *log_color, *env_str; int color_base, color_reserved; int status; if (Tk_InitStubs(magicinterp, Tclmagic_InitStubsVersion, 0) == NULL) return FALSE; grCurrent.window = Tk_MainWindow(magicinterp); if (grCurrent.window == NULL) { TxError("No Top-Level Tk window available. . . is Tk running?\n"); return FALSE; } grXdpy = Tk_Display(grCurrent.window); grDisplay.depth = Tk_Depth(grCurrent.window); grCurrent.windowid = Tk_WindowId(grCurrent.window); grXscrn = Tk_ScreenNumber(grCurrent.window); /* The idea here is to first try allocating the required * planes out of the default colormap. This is the kindest, * gentlest thing to do because it doesn't cause all the other * windows to go technicolor when the cursor is in a magic window. * If this fails, we go ahead and allocate a colormap specifically * for magic. The problem now is using this colormap in such * a way that the other windows' colors get mangled the least. * Unfortunately, doing this is X-server dependent. This is where * the constants above come in. X_COLORMAP_BASE indicates * which part of the colormap (assuming the number of planes * required is less than the number of planes in the display) * to fill in the colors magic requires. X_COLORMAP_RESERVED * tells how many high-end colors the server won't let us touch; * if we even try to query these colors, we get an X error. * If, starting at X_COLORMAP_BASE, the number of colors required * would push us into the top X_COLORMAP_RESERVED colors, then * we won't be able to set all the colors the user wanted us * to set. The top colors will remain identical to those * in the default colormap. */ grXcmap = XDefaultColormap(grXdpy,grXscrn); /* Discover properties of Server. */ grVisual = XDefaultVisual(grXdpy, grXscrn); defpsvid = XVisualIDFromVisual(grVisual); grtemplate.screen = grXscrn; grtemplate.depth = 0; grvisual_get = XGetVisualInfo(grXdpy, VisualScreenMask, &grtemplate, &gritems); if (grvisual_get == NULL) { TxPrintf("Could not obtain Visual Info from Server %s. Will attempt default.\n", getenv("DISPLAY")); grDisplay.depth = 8; grDisplay.colorCount = 1 << grDisplay.depth; } else { gritems_list = gritems; for (gritems = 0; gritems < gritems_list; gritems++) { j = grvisual_get[gritems].class; if ( j < 0 || j > 5) { TxPrintf("Unknown visual class index: %d\n", j); j = 6; } if ((grvisual_get[gritems].class == 3) && (grvisual_get[gritems].visualid == defpsvid)) defpsindex = gritems; } /* Unfortunately, the list returned by Xservers has classes in * random order. Therefore, a search is needed to find a good * choice. The only currently supported classes are PseudoColor * at depth 8 and TrueColor at depth 15, 16, and 24. It is likely * that TrueColor depths 8 through 32 will work, but these have * not been tested. In addition, it has been discovered that some * SUN systems "offer" more than one Pseudocolor at depth 8, but * with differing colormap sizes. There is nothing about how to * handle this in the X11 documentation, so the search below chooses * the "first" class. The class with 256 colors seems preferable and * works at present. The second Pseudocolor in the list gives a * BatMatch reject from the server, so it is useless. Basing the * selection on 256 colors might be effective, but might conflict in * other cases... As usual X11 is just guesswork. At present the * preferred order is: PseudoColor at 8, then TrueColor at 24, * then TrueColor at 16, ... Unless this is overridden by the * MAGIC_COLOR environment variable, which can be: bw, 8, 16, 24 */ for (j = 0; j < visual_table_len; j++) visual_table[j] = -1; for (j = 0; j < gritems_list; j++) { if ((grvisual_get[j].class == 0) && (grvisual_get[j].depth == 8) && (visual_table[1] == -1)) visual_table[1] = j; /* StaticGrey */ if ((grvisual_get[j].class == 1) && (grvisual_get[j].depth == 8) && (visual_table[2] == -1)) visual_table[2] = j; /* GreyScale */ if ((grvisual_get[j].class == 3) && (grvisual_get[j].depth == 8) && (visual_table[3] == -1)) visual_table[3] = j; /* Pseudocolor */ if ((grvisual_get[j].class == 4) && (grvisual_get[j].depth == 15) && (visual_table[4] == -1)) visual_table[4] = j; /* TrueColor */ if ((grvisual_get[j].class == 4) && (grvisual_get[j].depth == 16) && (visual_table[5] == -1)) visual_table[5] = j; /* TrueColor */ if ((grvisual_get[j].class == 4) && (grvisual_get[j].depth == 24) && (visual_table[6] == -1)) visual_table[6] = j; /* TrueColor */ } if (defpsindex != -1) visual_table[3] = defpsindex; log_color = getenv("MAGIC_COLOR"); if ((log_color == NULL) && (dispType != NULL) && (dispType[0] != 'X')) log_color = dispType; /* Allow environment variables to override the colormap base and */ /* number of reserved colors, as these depend on the terminal X */ /* server, NOT on the machine running magic. */ /* Note: ought to use strtod() in place of atoi(). */ env_str = getenv("X_COLORMAP_BASE"); if (env_str != NULL) color_base = (int)atoi(env_str); else color_base = X_COLORMAP_BASE; env_str = getenv("X_COLORMAP_RESERVED"); if (env_str != NULL) color_reserved = (int)atoi(env_str); else color_reserved = X_COLORMAP_RESERVED; gritems = -1; if (log_color != NULL) { if (strncmp(log_color, "8", 1) == 0) gritems = visual_table[3]; if (strncmp(log_color, "15", 2) == 0) gritems = visual_table[4]; if (strncmp(log_color, "16", 2) == 0) gritems = visual_table[5]; if (strncmp(log_color, "24", 2) == 0) gritems = visual_table[6]; if (gritems == -1) { printf("The visual mode %s is not available. Sorry.\n", log_color); XFree(grvisual_get); MainExit(1); } } else { if (visual_table[3] != -1) gritems = visual_table[3]; else if (visual_table[6] != -1) gritems = visual_table[6]; else if (visual_table[5] != -1) gritems = visual_table[5]; else if (visual_table[4] != -1) gritems = visual_table[4]; } if (gritems == -1) { TxPrintf("None of TrueColor 15, 16 or 24, or PseudoColor 8 found. " "Cannot initialize DISPLAY %s\n", getenv("DISPLAY")); XFree(grvisual_get); GrClosePtr = NULL; MainExit(1); } else { TxPrintf("Using %s, VisualID 0x%lx depth %d\n", visual_type[grvisual_get[gritems].class], grvisual_get[gritems].visualid, grvisual_get[gritems].depth); } grClass = grvisual_get[gritems].class; grVisual = grvisual_get[gritems].visual; grcolorCount = grvisual_get[gritems].colormap_size; grDisplay.depth = grvisual_get[gritems].depth; grDisplay.red_mask = grvisual_get[gritems].red_mask; grDisplay.green_mask = grvisual_get[gritems].green_mask; grDisplay.blue_mask = grvisual_get[gritems].blue_mask; grDisplay.colorCount = grcolorCount; } XFree(grvisual_get); grDisplay.planeCount = grDisplay.depth; grDisplay.realColors = grDisplay.colorCount; if (grDisplay.planeCount == 8) { grDisplay.depth = 7; grDisplay.planeCount = 7; /* This resets to old 7-plane mode */ grDisplay.colorCount = 1 << (grDisplay.planeCount); grDisplay.realColors = grDisplay.colorCount; } if (grDisplay.depth) { status = 0; if (grClass != 4) status= XAllocColorCells(grXdpy, grXcmap, TRUE, grDisplay.planes, grDisplay.planeCount, &grDisplay.basepixel, 1); if (status == 0) { /* * Ok, we tried to be nice; now lets whack the default colormap * and put in one of our own. */ int actualColors = grcolorCount; int usableColors = actualColors - color_reserved; /* Need the window ID. If grCurrent.window is not a valid window, */ /* Use the default Tk main window. */ xwind = grCurrent.windowid; if (xwind == 0) { xwind = Tk_WindowId(Tk_MainWindow(magicinterp)); if (xwind == 0) xwind = DefaultRootWindow(grXdpy); } if (usableColors > 256) usableColors = 256; if (grClass != 4) TxPrintf("Unable to allocate %d planes in default colormap; " "making a new one.\n", grDisplay.planeCount); #ifdef MAGIC_WRAPPER if (grClass == 3) GrTkInstalledCMap = TRUE; #endif if (grDisplay.planeCount <= 8) { grDisplay.basepixel = color_base; grXcmap = XCreateColormap(grXdpy, xwind, grVisual, AllocAll); } else { grDisplay.basepixel = 0; grXcmap = XCreateColormap(grXdpy, xwind, grVisual, AllocNone); } for (j = 0; j < grDisplay.planeCount; j++) grDisplay.planes[j] = 1 << j; status = 1; for (i = 0; i < usableColors; i++) colors[i].pixel = i; XQueryColors(grXdpy, XDefaultColormap(grXdpy, grXscrn), colors, usableColors); if (grDisplay.planeCount <= 8) XStoreColors(grXdpy, grXcmap, colors, usableColors); grDisplay.realColors = (grDisplay.basepixel + grDisplay.colorCount > usableColors) ? usableColors - grDisplay.basepixel: grDisplay.colorCount; if ((grDisplay.realColors != grDisplay.colorCount) && (grDisplay.planeCount <= 8)) { TxPrintf("Only %d contiguous colors were available.\n", grDisplay.realColors); grDisplay.colorCount = grDisplay.realColors; } } if (grXcmap == 0 || status ==0) { TxError( "Tk/X11 setup: Unable to allocate %d planes\n", grDisplay.planeCount); MainExit(1); } } /* There is a non-obvious mapping between plane depth and the names */ /* of the corresponding style and cmap filenames to load for each */ /* display type. */ switch(grDisplay.depth) { case 0: case 1: grDStyleType = "bw"; grCMapType = NULL; /* This must be called here; because in B/W the colormap */ /* is useless, it will not be called by the style file */ /* load procedure. */ GrTkSetCMap(); break; case 7: case 8: grDStyleType = "7bit"; grCMapType = "7bit"; break; default: grDStyleType = "24bit"; grCMapType = "24bit"; break; } /* Globally-accessed variables */ grNumBitPlanes = grDisplay.depth; grBitPlaneMask = (1 << grDisplay.depth) - 1; HashInit(&grTkWindowTable,8,HT_WORDKEYS); rstatus = grTkLoadFont(); return rstatus; } /*--------------------------------------------------------- * GrTkClose: * * Results: * None. * * Side Effects: * Frees fonts from the Tk font cache. *--------------------------------------------------------- */ void GrTkClose () { int i; if (grXdpy == NULL) return; XFreeGC(grXdpy, grGCStipple); grGCStipple = NULL; /* Free Tk fonts from the font cache */ grTkFreeFonts(); /* XCloseDisplay(grXdpy); */ /* Pop down Tk window but let Tcl/Tk */ /* do XCloseDisplay() */ } /*--------------------------------------------------------- * GrTkFlush: * Flush output to display. * * Flushing is done automatically the next time input is read, * so this procedure should not be used very often. * * Results: None. * * Side Effects: None. *--------------------------------------------------------- */ void GrTkFlush () { GR_TK_FLUSH_BATCH(); } /* * --------------------------------------------------------------------------- * * MagicEventProc --- * * Tk Event Handler * * Results: * None. * * Side Effects: * Calls functions in response to Tk events. * * --------------------------------------------------------------------------- */ void MagicEventProc(clientData, xevent) ClientData clientData; XEvent *xevent; { HashEntry *entry; Tk_Window wind = (Tk_Window)clientData; MagWindow *mw; unsigned char LocRedirect = TxInputRedirect; XKeyPressedEvent *KeyPressedEvent = (XKeyPressedEvent *) xevent; KeySym keysym; int nbytes; /* Keys and Buttons: Determine expansion of macros or redirect * keys to the terminal/console. */ switch (xevent->type) { case ButtonPress: { XButtonEvent *ButtonEvent = (XButtonEvent *) xevent; int txbutton; switch (ButtonEvent->button) { case Button1: txbutton = TX_LEFT_BUTTON; keysym = XK_Pointer_Button1; break; case Button2: txbutton = TX_MIDDLE_BUTTON; keysym = XK_Pointer_Button2; break; case Button3: txbutton = TX_RIGHT_BUTTON; keysym = XK_Pointer_Button3; break; case Button4: txbutton = TX_BUTTON_4; keysym = XK_Pointer_Button4; break; case Button5: txbutton = TX_BUTTON_5; keysym = XK_Pointer_Button5; break; } nbytes = 0; entry = HashLookOnly(&grTkWindowTable, (char *)wind); mw = (entry)?(MagWindow *)HashGetValue(entry):0; /* Hack---non-wrapper versions of magic have */ /* fixed handlers for the frame. */ if (mw && (mw->w_flags & WIND_SCROLLBARS)) if (WindButtonInFrame(mw, ButtonEvent->x, grXToMagic(ButtonEvent->y), txbutton)) break; goto keys_and_buttons; } break; case KeyPress: { int keywstate, keymod, modifier, idx, idxmax; char inChar[10]; Tcl_Channel outChannel = Tcl_GetStdChannel(TCL_STDOUT); nbytes = XLookupString(KeyPressedEvent, inChar, sizeof(inChar), &keysym, NULL); if (IsModifierKey(keysym)) break; /* Don't handle modifiers */ entry = HashLookOnly(&grTkWindowTable, (char *)wind); mw = (entry)?(MagWindow *)HashGetValue(entry):0; keys_and_buttons: grCurrent.window = wind; grCurrent.windowid = Tk_WindowId(wind); grCurrent.mw = mw; keymod = (LockMask | ControlMask | ShiftMask) & KeyPressedEvent->state; #ifdef __APPLE__ if (KeyPressedEvent->state & (Mod1Mask | Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask)) keymod |= Mod1Mask; #else keymod |= (Mod1Mask & KeyPressedEvent->state); #endif modifier = keymod; if (nbytes == 0) { /* No ASCII equivalent */ keywstate = (keymod << 16) | (keysym & 0xffff); } else if (!strncmp(XKeysymToString(keysym), "KP_", 3)) { /* keypad key (special case---would like to */ /* differentiate between shift-KP-# and # itself) */ keymod &= ~ShiftMask; keywstate = (keymod << 16) | (keysym & 0xffff); nbytes = 0; } else /* ASCII-valued character */ { if (!(keymod & (LockMask | Mod1Mask))) { if (!(keymod & ControlMask)) keymod &= ~ShiftMask; else if (!(keymod & ShiftMask)) keymod &= ~ControlMask; } } idxmax = (nbytes == 0) ? 1 : nbytes; for (idx = 0; idx < idxmax; idx++) { if (xevent->type == KeyPress && inChar[idx] == 3) { /* Ctrl-C interrupt */ if (SigInterruptPending) MainExit(0); /* double Ctrl-C */ else sigOnInterrupt(0); /* Set InterruptPending */ TxError("Tk Caught Ctrl-C Interrupt\n"); TxFlush(); /* MainExit(0); */ break; } else if (nbytes > 0) { if ((keymod & ControlMask) && (inChar[idx] < 32)) inChar[idx] += 'A' - 1; keywstate = (keymod << 16) | ((int)inChar[idx] & 0xff); } /* Allow buttons to bypass the console and be */ /* treated as macros. */ if (LocRedirect == TX_INPUT_REDIRECTED) { switch (keysym) { case XK_Pointer_Button1: case XK_Pointer_Button2: case XK_Pointer_Button3: case XK_Pointer_Button4: case XK_Pointer_Button5: LocRedirect = TX_INPUT_NORMAL;; break; } } if ((LocRedirect == TX_INPUT_REDIRECTED) && TxTkConsole) { #if TCL_MAJOR_VERSION < 9 Tcl_SavedResult state; #else Tcl_InterpState state; #endif static char outstr[] = "::tkcon::Insert .text \"x\" "; /* Translate Control-H to BackSpace */ if ((keymod & ControlMask) && (keysym == XK_h)) keysym = XK_BackSpace; switch (keysym) { case XK_Return: TxSetPoint(KeyPressedEvent->x, grXToMagic(KeyPressedEvent->y), grCurrent.mw->w_wid); TxInputRedirect = TX_INPUT_PROCESSING; Tcl_EvalEx(consoleinterp, "::tkcon::Eval .text", 19, 0); TxInputRedirect = TX_INPUT_NORMAL; TxSetPrompt('%'); #if TCL_MAJOR_VERSION < 9 Tcl_SaveResult(magicinterp, &state); #else state = Tcl_SaveInterpState(magicinterp, TCL_OK); #endif Tcl_EvalEx(magicinterp, "history event 0", 15, 0); MacroDefine(mw->w_client, (int)'.', Tcl_GetStringResult(magicinterp), NULL, FALSE); #if TCL_MAJOR_VERSION < 9 Tcl_RestoreResult(magicinterp, &state); #else Tcl_RestoreInterpState(magicinterp, state); #endif break; case XK_Up: Tcl_EvalEx(consoleinterp, "::tckon::Event -1", 17, 0); break; case XK_Down: Tcl_EvalEx(consoleinterp, "::tckon::Event 1", 16, 0); break; case XK_Left: Tcl_EvalEx(consoleinterp, ".text mark set insert " "insert-1c ; .text see insert", 50, 0); break; case XK_Right: Tcl_EvalEx(consoleinterp, ".text mark set insert " "insert+1c ; .text see insert", 50, 0); break; case XK_BackSpace: case XK_Delete: Tcl_EvalEx(consoleinterp, ".text delete insert-1c ; " ".text see insert", 41, 0); break; case XK_quotedbl: case XK_backslash: case XK_bracketleft: outstr[23] = '\\'; outstr[24] = inChar[idx]; outstr[25] = '\"'; Tcl_EvalEx(consoleinterp, outstr, 26, 0); outstr[24] = '\"'; outstr[25] = '\0'; /* fall through */ default: /* Handle Ctrl-u: Delete entire command */ if ((keysym == XK_u) && (modifier == ControlMask)) { Tcl_EvalEx(consoleinterp, ".text delete limit end", 22, 0); } else { outstr[23] = inChar[idx]; Tcl_EvalEx(consoleinterp, outstr, 25, 0); } break; } } else if (LocRedirect == TX_INPUT_REDIRECTED) { int tl; if (TxBuffer == NULL) { TxBuffer = Tcl_Alloc(2); *TxBuffer = '\0'; tl = 0; } else { tl = strlen(TxBuffer); TxBuffer = Tcl_Realloc(TxBuffer, tl + 2); } if (keysym == XK_BackSpace || keysym == XK_Delete) { if (tl >= 0) { if (tl > 0) { *(TxBuffer + tl - 1) = '\0'; TxPrintf("\b"); } TxPrintf(" \b"); TxFlushOut(); } } else if (keysym == XK_Return) { *(TxBuffer + tl) = '\n'; *(TxBuffer + tl + 1) = '\0'; if (tl != 0) MacroDefine(mw->w_client, (int)'.', TxBuffer, NULL, FALSE); TxInputRedirect = TX_INPUT_NORMAL; TxSetPoint(KeyPressedEvent->x, grXToMagic(KeyPressedEvent->y), grCurrent.mw->w_wid); TxPrintf("\n"); TxFlushOut(); Tcl_NotifyChannel(Tcl_GetStdChannel(TCL_STDIN), TCL_READABLE); } else { *(TxBuffer + tl) = *(inChar + idx); *(TxBuffer + tl + 1) = '\0'; TxPrintf("%c", *(inChar + idx)); TxFlushOut(); } } else { bool iMacro; char *macroDef; macroDef = MacroRetrieve(mw->w_client, keywstate, &iMacro); /* Special handling: An imacro beginning with ':' */ /* sets the prompt to ':' and moves to the next char. */ if (macroDef != NULL && *macroDef == ':' && iMacro) { if (TxTkConsole) TxSetPrompt(':'); else { TxPrintf("\b\b: "); TxFlushOut(); } memmove(macroDef, macroDef + 1, strlen(macroDef + 1) + 1); } macroDef = MacroSubstitute(macroDef, "%W", Tk_PathName(wind)); if (macroDef == NULL) { if (keysym != XK_Return) { char *vis = MacroName(keywstate); TxError("Unknown macro or short command: '%s'\n", vis); freeMagic(vis); } /* Print Carriage Return & Put back Tcl/Tk prompt */ TxParseString(""); } else { int sl = strlen(macroDef); if (iMacro) { /* Echo macro to interpreter, then redirect keys */ if (TxTkConsole) { char *outstring = Tcl_Alloc(sl + 20); sprintf(outstring, ".text insert end \"%s\"", macroDef); Tcl_EvalEx(consoleinterp, outstring, -1, 0); Tcl_Free(outstring); } else { TxBuffer = Tcl_Alloc(sl + 1); strcpy(TxBuffer, macroDef); TxPrintf("%s", macroDef); TxFlushOut(); } TxInputRedirect = TX_INPUT_REDIRECTED; } else { /* TxParseString is defined by tcltk/tclmagic.c * and calls Tcl_Eval(). */ TxSetPoint(KeyPressedEvent->x, grXToMagic(KeyPressedEvent->y), grCurrent.mw->w_wid); TxParseString(macroDef); } freeMagic(macroDef); } } } } break; case VisibilityNotify: { XVisibilityEvent *VisEvent = (XVisibilityEvent*) xevent; entry = HashLookOnly(&grTkWindowTable, (char *)wind); mw = (entry)?(MagWindow *)HashGetValue(entry):0; switch(VisEvent->state) { case VisibilityUnobscured: mw->w_flags &= ~WIND_OBSCURED; if (mw->w_backingStore == (ClientData)NULL) { grtkCreateBackingStore(mw); if (mw->w_backingStore != (ClientData)NULL) { WindAreaChanged(mw, &mw->w_allArea); WindUpdate(); } } break; case VisibilityPartiallyObscured: case VisibilityFullyObscured: mw->w_flags |= WIND_OBSCURED; break; } } break; case Expose: { XExposeEvent *ExposeEvent = (XExposeEvent*) xevent; Rect screenRect; entry = HashLookOnly(&grTkWindowTable, (char *)wind); mw = (entry)?(MagWindow *)HashGetValue(entry):0; grCurrent.window = wind; grCurrent.windowid = Tk_WindowId(wind); grCurrent.mw = mw; screenRect.r_xbot = ExposeEvent->x; screenRect.r_xtop = ExposeEvent->x+ExposeEvent->width; screenRect.r_ytop = mw->w_allArea.r_ytop-ExposeEvent->y; screenRect.r_ybot = mw->w_allArea.r_ytop - (ExposeEvent->y + ExposeEvent->height); if (mw->w_backingStore != (ClientData)NULL) { Rect surface; (*GrLockPtr)(mw, FALSE); (*GrGetBackingStorePtr)(mw, &screenRect); (*GrUnlockPtr)(mw); WindScreenToSurface(mw, &screenRect, &surface); DBWHLRedrawPrepWindow(mw, &surface); WindDrawBorder(mw, &screenRect); } else WindAreaChanged(mw, &screenRect); WindUpdate(); } break; case ConfigureNotify: { XConfigureEvent *ConfigureEvent = (XConfigureEvent*) xevent; Rect screenRect; bool need_resize; entry = HashLookOnly(&grTkWindowTable, (char *)wind); mw = (entry)?(MagWindow *)HashGetValue(entry):0; grCurrent.window = wind; grCurrent.windowid = Tk_WindowId(wind); grCurrent.mw = mw; screenRect.r_xbot = ConfigureEvent->x; screenRect.r_xtop = ConfigureEvent->x + ConfigureEvent->width; screenRect.r_ytop = grXsToMagic(ConfigureEvent->y); screenRect.r_ybot = grXsToMagic(ConfigureEvent->y+ ConfigureEvent->height); need_resize = (screenRect.r_xbot != mw->w_screenArea.r_xbot || screenRect.r_xtop != mw->w_screenArea.r_xtop || screenRect.r_ybot != mw->w_screenArea.r_ybot || screenRect.r_ytop != mw->w_screenArea.r_ytop); WindReframe(mw, &screenRect, FALSE, FALSE); WindRedisplay(mw); if (need_resize) grtkCreateBackingStore(mw); } break; case MapNotify: case UnmapNotify: case DestroyNotify: /* Do nothing */ break; default: TxError("Tk Event: Unknown (%d)\n", xevent->type); TxFlush(); break; } } /*--------------------------------------------------------- * x11SetDisplay: * This routine sets the appropriate parameters so that * Magic will work with the X display under Tcl/Tk. * * Under Xlib, all input events (mouse and keyboard) are * sent to one queue which has to be polled to discover * whether there is any input or not. To fit the Magic * interrupt-driven input model, a helper process is * spawned which reads and blocks on the event queue, * sending SIGIO's to Magic when it detects input. The * input read in the helper process is then sent to Magic * via a communication pipe. * * Results: success / fail * * Side Effects: *--------------------------------------------------------- */ bool x11SetDisplay (dispType, outFileName, mouseFileName) char *dispType; char *outFileName; char *mouseFileName; { char *planecount; char *fullname; FILE* f; bool execFailed = FALSE; int x, y, width, height; WindPackageType = WIND_X_WINDOWS; TxInputRedirect = TX_INPUT_NORMAL; grCursorType = "color"; WindScrollBarWidth = 14; /* Set up the procedure values in the indirection table. */ GrLockPtr = GrTkLock; GrUnlockPtr = GrTkUnlock; GrInitPtr = GrTkInit; GrClosePtr = GrTkClose; GrSetCMapPtr = GrTkSetCMap; GrEnableTabletPtr = GrTkEnableTablet; GrDisableTabletPtr = GrTkDisableTablet; GrSetCursorPtr = GrTkSetCursor; GrTextSizePtr = GrTkTextSize; GrDrawGlyphPtr = GrTkDrawGlyph; GrReadPixelPtr = GrTkReadPixel; GrFlushPtr = GrTkFlush; GrCreateWindowPtr = GrTkCreate; GrDeleteWindowPtr = GrTkDelete; GrConfigureWindowPtr = GrTkConfigure; GrOverWindowPtr = GrTkRaise; GrUnderWindowPtr = GrTkLower; GrUpdateIconPtr = GrTkIconUpdate; GrEventPendingPtr = GrTkEventPending; GrWindowIdPtr = GrTkWindowId; GrWindowNamePtr = GrTkWindowName; GrGetCursorPosPtr = grtkGetCursorPos; GrGetCursorRootPosPtr = grtkGetCursorRootPos; /* local indirections */ grSetSPatternPtr = grtkSetSPattern; grPutTextPtr = grtkPutText; grFontTextPtr = grtkFontText; grDefineCursorPtr = grTkDefineCursor; grFreeCursorPtr = grTkFreeCursors; GrBitBltPtr = GrTkBitBlt; grDrawGridPtr = grtkDrawGrid; grDrawLinePtr = grtkDrawLine; grSetWMandCPtr = grtkSetWMandC; grFillRectPtr = grtkFillRect; grSetStipplePtr = grtkSetStipple; grSetLineStylePtr = grtkSetLineStyle; grSetCharSizePtr = grtkSetCharSize; grFillPolygonPtr = grtkFillPolygon; GrFreeBackingStorePtr = grtkFreeBackingStore; GrCreateBackingStorePtr = grtkCreateBackingStore; GrGetBackingStorePtr = grtkGetBackingStore; GrPutBackingStorePtr = grtkPutBackingStore; GrScrollBackingStorePtr = grtkScrollBackingStore; if (execFailed) { TxError("Execution failed!\n"); return FALSE; } if (!GrTkInit(dispType)) { return FALSE; }; Tk_GetVRootGeometry(Tk_MainWindow(magicinterp), &x, &y, &width, &height); GrScreenRect.r_xbot = x; GrScreenRect.r_ybot = y; GrScreenRect.r_xtop = x + width; GrScreenRect.r_ytop = y + height; return Tk_MainWindow(magicinterp) ? TRUE : FALSE; } extern void MakeWindowCommand(); /* * ---------------------------------------------------------------------------- * * GrTkCreate -- * Create a new window under Tk. * Bind Tk window to Magic Window w. * * Results: * Success/Failure * * Side Effects: * Window created and mapped. * * ---------------------------------------------------------------------------- */ bool GrTkCreate(w, name) MagWindow *w; char *name; { Tk_Window tkwind, tktop; Window wind; static int WindowNumber = 0; HashEntry *entry; char *windowplace; char windowname[32]; int x = w->w_frameArea.r_xbot; int y = grMagicToXs(w->w_frameArea.r_ytop); int width = w->w_frameArea.r_xtop - w->w_frameArea.r_xbot; int height = w->w_frameArea.r_ytop - w->w_frameArea.r_ybot; unsigned long attribmask = CWBackPixel | CWBorderPixel; XSetWindowAttributes grAttributes; int grDepth; GrTkFlush(); WindSeparateRedisplay(w); sprintf(windowname, ".magic%d", WindowNumber + 1); if ((windowplace = XGetDefault(grXdpy, "magic", windowname))) { XParseGeometry(windowplace,&x,&y, (unsigned int *)&width,(unsigned int *)&height); w->w_frameArea.r_xbot = x; w->w_frameArea.r_xtop = x+width; w->w_frameArea.r_ytop = grXsToMagic(y); w->w_frameArea.r_ybot = grXsToMagic(y+height); WindReframe(w,&(w->w_frameArea),FALSE,FALSE); } grAttributes.background_pixel = WhitePixel(grXdpy,grXscrn); grAttributes.border_pixel = BlackPixel(grXdpy,grXscrn); grDepth = grDisplay.depth; if(grClass == 3) grDepth = 8; /* Needed since grDisplay.depth is reset to 7 if Pseudocolor */ if ((tktop = Tk_MainWindow(magicinterp))) { if (!WindowNumber) { /* To do: deal with grVisual---destroy and recreate */ /* top frame if necessary */ if (Tk_WindowId(tktop) == 0) { Tk_SetWindowVisual(tktop, grVisual, grDepth, grXcmap); } else { /* The Top-level window has already been mapped. We can't mess */ /* with it's visual. If the title is "wish", we'll assume that */ /* nobody else is claiming it, and unmap it. */ if (!strcmp(Tk_Name(tktop), "wish")) Tk_UnmapWindow(tktop); } } } else return 0; /* Failure condition */ /* Last parameter "" indicates a top-level window in the space of */ /* the parent. */ if (name == NULL) tkwind = Tk_CreateWindowFromPath(magicinterp, tktop, windowname, ""); else tkwind = Tk_CreateWindowFromPath(magicinterp, tktop, name, NULL); /* TxError("Created window named \"%s\", tkwind = 0x%x\n", windowname, tkwind); */ if (tkwind != 0) { grCurrent.window = tkwind; grCurrent.mw = w; w->w_grdata = (ClientData) tkwind; entry = HashFind(&grTkWindowTable, (char *)tkwind); HashSetValue(entry,w); /* set the window attributes, since Tk doesn't do this in the */ /* Tk_CreateWindowFromPath() function. */ Tk_ChangeWindowAttributes(tkwind, attribmask, &grAttributes); /* ensure that the visual is what we wanted, if possible to change */ Tk_SetWindowVisual(tkwind, grVisual, grDepth, grXcmap); /* map the window, if necessary */ Tk_MapWindow(tkwind); /* use x, y, width, height to size and position the window */ Tk_GeometryRequest(tkwind, width, height); /* Tk_MoveResizeWindow(tkwind, x, y, width, height); */ wind = Tk_WindowId(tkwind); grCurrent.windowid = wind; if (!WindowNumber) { grGCFill = XCreateGC(grXdpy, wind, 0, 0); grGCDraw = XCreateGC(grXdpy, wind, 0, 0); grGCText = XCreateGC(grXdpy, wind, 0, 0); grGCCopy = XCreateGC(grXdpy, wind, 0, 0); grGCGlyph = XCreateGC(grXdpy, wind, 0, 0); } XSetPlaneMask(grXdpy, grGCGlyph, AllPlanes); Tk_DefineCursor(tkwind, grCurrent.cursor); GrTkIconUpdate(w, w->w_caption); /*----------------------------------------------------------------------*/ /* If we're using TkCon in a PseudoColor visual, we need to set the */ /* console's colormap to match the magic window. TkCon need know */ /* nothing about this action (i.e., don't call Tk_SetWindowColormap()). */ /* Make a call to a routine to reassign colors to match magic's solid */ /* colors. */ /*----------------------------------------------------------------------*/ if (TxTkConsole && !WindowNumber) { Window parent, rret, *clist; unsigned int iret; if (Tk_Visual(tktop) == Tk_Visual(tkwind)) { XQueryTree(grXdpy, Tk_WindowId(tktop), &rret, &parent, &clist, &iret); /* Overlay plane windows won't have the same visual in both the */ /* console and the layout windows; in such case, there should */ /* be no need to set the colormap. */ XSetWindowColormap(grXdpy, parent, grXcmap); if (clist != NULL) XFree(clist); } else GrTkInstalledCMap = FALSE; Tcl_EvalEx(consoleinterp, "catch repaintconsole", 20, 0); } WindowNumber++; /* set up Tk event handler to start processing */ Tk_CreateEventHandler(tkwind, ExposureMask | StructureNotifyMask | ButtonPressMask | KeyPressMask | VisibilityChangeMask, (Tk_EventProc *)MagicEventProc, (ClientData) tkwind); /* set up commands to be passed expressly to this window */ MakeWindowCommand((name == NULL) ? windowname : name, w); /*--------------------------------------------------------------*/ /* In a PseudoColor visual + GUI wrapper environment, we need */ /* to make sure that the GUI window (layout's parent) has the */ /* same colormap as the layout. Tk need know nothing about */ /* this sleight-of-hand. */ /*--------------------------------------------------------------*/ if ((grClass == 3) && !Tk_IsTopLevel(tkwind)) { Tk_Window tklayout = tkwind; Window parent, rret, *clist; unsigned int iret; while (!Tk_IsTopLevel(tklayout)) tklayout = Tk_Parent(tklayout); XQueryTree(grXdpy, Tk_WindowId(tklayout), &rret, &parent, &clist, &iret); /* Avoid trying to set the colormap of a window with an */ /* incompatible visual (see above). */ if (Tk_Visual(tklayout) == Tk_Visual(tkwind)) { XSetWindowColormap(grXdpy, parent, grXcmap); Tk_SetWindowColormap(tklayout, grXcmap); } else { GrTkInstalledCMap = FALSE; TxError("Warning: Cannot match colormap of wrapper to layout.\n"); } if (clist != NULL) XFree(clist); } /* Install the colormap in all associated windows, for pseudocolor */ /* visuals, if required */ if (grClass == 3) XInstallColormap(grXdpy, grXcmap); return 1; } else { TxError("Could not open new Tk window\n"); } return 0; } /* * ---------------------------------------------------------------------------- * * GrTkDelete -- * Destroy an X window. * * Results: * None. * * Side effects: * Window destroyed. * * ---------------------------------------------------------------------------- */ void GrTkDelete(w) MagWindow *w; { Tk_Window xw; HashEntry *entry; xw = (Tk_Window) w->w_grdata; entry = HashLookOnly(&grTkWindowTable, (char *)xw); HashSetValue(entry,NULL); Tcl_DeleteCommand(magicinterp, Tk_PathName(xw)); Tk_DestroyWindow(xw); } /* * ---------------------------------------------------------------------------- * * GrTkConfigure -- * Resize/ Move an existing X window. * * Results: * None. * * Side Effects: * Window reconfigured to w->w_frameArea. * * ---------------------------------------------------------------------------- */ void GrTkConfigure(w) MagWindow *w; { if (w->w_flags & WIND_OFFSCREEN) return; Tk_MoveResizeWindow((Tk_Window)w->w_grdata, w->w_frameArea.r_xbot, grMagicToXs(w->w_frameArea.r_ytop), w->w_frameArea.r_xtop - w->w_frameArea.r_xbot, w->w_frameArea.r_ytop - w->w_frameArea.r_ybot); } /* * ---------------------------------------------------------------------------- * * GrTkRaise -- * Raise a window to the top of the screen such that nothing * obscures it. * * Results: * None. * * Side Effects: * Window raised. * * ---------------------------------------------------------------------------- */ void GrTkRaise(w) MagWindow *w; { Tk_Window tkwind; if (w->w_flags & WIND_OFFSCREEN) return; tkwind = (Tk_Window)w->w_grdata; Tk_RestackWindow(tkwind, Above, NULL); } /* * ---------------------------------------------------------------------------- * * GrTkLower -- * Lower a window below all other X windows. * obscures it. * * Results: * None. * * Side Effects: * Window lowered. * * ---------------------------------------------------------------------------- */ void GrTkLower(w) MagWindow *w; { Tk_Window tkwind; if (w->w_flags & WIND_OFFSCREEN) return; tkwind = (Tk_Window)w->w_grdata; Tk_RestackWindow(tkwind, Below, NULL); } /* * ---------------------------------------------------------------------------- * * GrTkLock -- * Lock a window and set global variables "grCurrent.window" * and "grCurrent.mw" to reference the locked window. * * Results: * None. * * Side Effects: * Window locked. * * ---------------------------------------------------------------------------- */ void GrTkLock(w, flag) MagWindow *w; bool flag; { grSimpleLock(w, flag); if ( w != GR_LOCK_SCREEN ) { grCurrent.mw = w; if (w->w_flags & WIND_OFFSCREEN) { grCurrent.window = NULL; grCurrent.windowid = (Pixmap) w->w_grdata; } else { grCurrent.window = (Tk_Window) w->w_grdata; grCurrent.windowid = Tk_WindowId(grCurrent.window); } } } /* * ---------------------------------------------------------------------------- * * GrTkUnlock -- * Unlock a window, flushing stuff out to the display. * * Results: * None. * * Side Effects: * Window unlocked. * Display update. * * ---------------------------------------------------------------------------- */ void GrTkUnlock(w) MagWindow *w; { GR_TK_FLUSH_BATCH(); grSimpleUnlock(w); } /* *------------------------------------------------------------------------- * GrTkEventPending -- check for pending graphics events. * Here we use the X11 check for window events, because Tcl/Tk doesn't * allows peeking into its event queue without executing whatever is * in the queue. * * Results: * TRUE if an event is in the event queue. * * Side effects: * Hopefully none (put back the event!) * *------------------------------------------------------------------------- */ bool GrTkEventPending() { Tk_Window tkwind = grCurrent.window; Window wind = grCurrent.windowid; XEvent genEvent; bool retval; if (wind == 0) return FALSE; retval = XCheckWindowEvent(grXdpy, wind, ExposureMask | StructureNotifyMask | ButtonPressMask | KeyPressMask, &genEvent); if (retval) XPutBackEvent(grXdpy, &genEvent); return retval; } /* *------------------------------------------------------------------------- * * GrTkIconUpdate -- updates the icon text with the window script * * Results: none * * Side Effects: changes the icon text * *------------------------------------------------------------------------- */ void GrTkIconUpdate(w, text) /* See Blt code */ MagWindow *w; char *text; { Tk_Window tkwind; Window wind; XClassHint class; char *brack; if (w->w_flags & WIND_OFFSCREEN) return; tkwind = (Tk_Window)(w->w_grdata); if (tkwind == NULL) { tkwind = Tk_MainWindow(magicinterp); if (tkwind == NULL) return; } wind = Tk_WindowId(tkwind); if (wind == 0) return; class.res_name = "magic"; class.res_class = "magic"; XSetClassHint( grXdpy, wind, &class); if (text == NULL) return; if ((brack = strchr(text,'[')) && brack != text) { brack--; *brack = 0; XSetIconName(grXdpy,wind,text); XStoreName(grXdpy,wind,text); *brack = ' '; return; } if ((brack = strrchr(text,' '))) text = brack+1; XSetIconName(grXdpy,wind,text); XStoreName(grXdpy,wind,text); } /* *------------------------------------------------------------------------- * GrTkWindowId -- * Get magic's ID number from the indicated MagWindow structure * * Results: * The window ID number * * Side effects: * None. *------------------------------------------------------------------------- */ int GrTkWindowId(tkname) char *tkname; { Tk_Window tkwind; MagWindow *mw; HashEntry *entry; int id = 0; tkwind = Tk_NameToWindow(magicinterp, tkname, Tk_MainWindow(magicinterp)); if (tkwind != NULL) { entry = HashLookOnly(&grTkWindowTable, (char *)tkwind); mw = (entry) ? (MagWindow *)HashGetValue(entry) : 0; if (mw) id = mw->w_wid; } return id; }