magic/graphics/grTOGL1.c

1601 lines
40 KiB
C

/* grTOGL1.c
*
* Copyright (C) 2003 Open Circuit Design, Inc., for MultiGiG Ltd.
*
* This file contains primitive functions for OpenGL running under
* an X window system in a Tcl/Tk interpreter environment
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/keysym.h>
#include <GL/gl.h>
#include <GL/glx.h>
#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 "utils/utils.h"
#include "tiles/tile.h"
#include "utils/hash.h"
#include "database/database.h"
#include "drc/drc.h"
#include "utils/macros.h"
#include "graphics/grTOGLInt.h"
#include "utils/paths.h"
#include "graphics/grTkCommon.h"
/* C99 compat */
#include "dbwind/dbwind.h"
GLubyte **grTOGLStipples;
HashTable grTOGLWindowTable;
GLXContext grXcontext;
XVisualInfo *grVisualInfo;
TOGL_CURRENT toglCurrent= {(Tk_Font)0, 0, 0, 0, 0,
(Tk_Window)0, (Window)0, (MagWindow *)NULL};
/* 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 GrTOGLClose(), GrTOGLFlush();
extern void GrTOGLDelete(), GrTOGLConfigure(), GrTOGLRaise(), GrTOGLLower();
extern void GrTOGLLock(), GrTOGLUnlock(), GrTOGLIconUpdate();
extern bool GrTOGLInit();
extern bool GrTOGLEventPending(), GrTOGLCreate(), grtoglGetCursorPos();
extern int GrTOGLWindowId();
extern char *GrTkWindowName();
extern void toglSetProjection();
/*---------------------------------------------------------
* grtoglSetWMandC:
* This is a local routine that resets the value of the current
* write alpha (mask) and color, if necessary.
*
* Results: None.
*
* Side Effects: None.
*
* Errors: None.
*---------------------------------------------------------
*/
void
grtoglSetWMandC (mask, c)
int mask; /* New value for write mask */
int c; /* New value for current color */
{
static int oldColor = -1;
static int oldMask = -1;
int lr, lb, lg;
GLfloat fr, fb, fg;
GLfloat aval; /* Alpha default value was 0.75 */
if (mask == -65) mask = 127; /* All planes */
if (mask == oldMask && c == oldColor) return;
GR_TOGL_FLUSH_BATCH();
GrGetColor(c, &lr, &lb, &lg);
fr = ((GLfloat)lr / 255);
fg = ((GLfloat)lg / 255);
fb = ((GLfloat)lb / 255);
if (mask == 127)
{
glDisable(GL_BLEND);
aval = 1.0;
}
else
{
/* Calculate a "supercolor", outside of the normal color range, */
/* but which results in the desired color after a blend with */
/* the background color. */
fr = fr * 2 - 0.8;
fg = fg * 2 - 0.8;
fb = fb * 2 - 0.8;
aval = (GLfloat)mask / 127.0; /* mask translates to alpha in */
/* the OpenGL version. */
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}
glColor4f(fr, fb, fg, aval);
oldColor = c;
oldMask = mask;
}
/*---------------------------------------------------------
* grtoglSetLineStyle:
* This local routine sets the current line style.
*
* Results: None.
*
* Side Effects:
* A new line style is output to the display.
*
*---------------------------------------------------------
*/
void
grtoglSetLineStyle (style)
int style; /* New stipple pattern for lines. */
{
static int oldStyle = -1;
GLushort glstyle;
style &= 0xFF;
if (style == oldStyle) return;
oldStyle = style;
GR_TOGL_FLUSH_BATCH();
switch (style) {
case 0xFF:
case 0x00:
glDisable(GL_LINE_STIPPLE);
break;
default:
glstyle = style | (style << 8);
glEnable(GL_LINE_STIPPLE);
glLineStipple(1, glstyle);
break;
}
}
/*---------------------------------------------------------
* grtoglSetSPattern:
* toglSetSPattern associates a stipple pattern with a given
* stipple number. This is a local routine called from
* grStyle.c .
*
* Results: None.
*
* Side Effects: None.
*---------------------------------------------------------
*/
void
grtoglSetSPattern (sttable, numstipples)
int **sttable; /* The table of patterns */
int numstipples; /* Number of stipples */
{
int i, j, k, n;
GLubyte *pdata;
grTOGLStipples = (GLubyte **)mallocMagic(numstipples * sizeof(GLubyte *));
for (k = 0; k < numstipples; k++)
{
pdata = (GLubyte *)mallocMagic(128 * sizeof(GLubyte));
n = 0;
/* expand magic's default 8x8 stipple to OpenGL's 32x32 */
for (i = 0; i < 32; i++)
for (j = 0; j < 4; j++)
pdata[n++] = (GLubyte)sttable[k][i % 8];
grTOGLStipples[k] = pdata;
}
}
/*---------------------------------------------------------
* grtoglSetStipple:
* 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
grtoglSetStipple (stipple)
int stipple; /* The stipple number to be used. */
{
static int oldStip = -1;
if (stipple == oldStip) return;
oldStip = stipple;
GR_TOGL_FLUSH_BATCH();
if (stipple == 0 || stipple > grNumStipples) {
glDisable(GL_POLYGON_STIPPLE);
} else {
if (grTOGLStipples[stipple] == (GLubyte *)NULL) MainExit(1);
glEnable(GL_POLYGON_STIPPLE);
glPolygonStipple(grTOGLStipples[stipple]);
}
}
/*------------------------------------------------------------------------
* GrTOGLInit:
* GrTOGLInit initializes the graphics display and clears its screen.
* Files must have been previously opened with GrSetDisplay();
*
* Results: TRUE if successful.
*
* Notes: When 3D rendering is compiled in, we search for a double-buffered
* configuration first, because it generates the smoothest graphics,
* and fall back on a single-buffered configuration if necessary.
* For normal, 2D-only rendering, we look for a single-buffered
* configuration first because we don't use the back buffer, so a
* double-buffered configuration just wastes space.
*------------------------------------------------------------------------
*/
bool
GrTOGLInit ()
{
bool rstatus;
#ifdef THREE_D
static int attributeList[] = { GLX_RGBA, GLX_DOUBLEBUFFER, None };
#else
static int attributeList[] = { GLX_RGBA, None, None };
#endif
if (Tk_InitStubs(magicinterp, Tclmagic_InitStubsVersion, 0) == NULL) return FALSE;
toglCurrent.window = Tk_MainWindow(magicinterp);
if (toglCurrent.window == NULL)
{
TxError("No Top-Level Tk window available. . . is Tk running?\n");
return FALSE;
}
toglCurrent.windowid = Tk_WindowId(toglCurrent.window);
grXdpy = Tk_Display(toglCurrent.window);
toglCurrent.depth = Tk_Depth(toglCurrent.window);
grXscrn = DefaultScreen(grXdpy);
grVisualInfo = glXChooseVisual(grXdpy, grXscrn, attributeList);
if (!grVisualInfo)
{
/* Try for a double-buffered configuration */
#ifdef THREE_D
attributeList[1] = None;
#else
attributeList[1] = GLX_DOUBLEBUFFER;
#endif
grVisualInfo = glXChooseVisual(grXdpy, grXscrn, attributeList);
if (!grVisualInfo)
{
TxError("No suitable visual!\n");
return FALSE;
}
}
grXscrn = grVisualInfo->screen;
toglCurrent.depth = grVisualInfo->depth;
/* glXCreateContext() 4th argument: */
/* TRUE = Direct rendering, FALSE = Indirect rendering */
/* Direct rendering may not be able to deal with pixmaps. */
/* Normally, magic should compile with OpenGL framebuffer */
/* backing store and pbuffer offscreen rendering. However, */
/* if X11_BACKING_STORE is selected then force indirect */
/* rendering. This can be avoided if the backing store */
/* functions sync between OpenGL and X11; this has not */
/* been done yet. */
/* The CAIRO_OFFSCREEN_RENDER compile-time option uses */
/* Cairo to do the off-screen rendering, which allows */
/* OpenGL to run in direct-rendering mode only. To do: */
/* determine from OpenGL attributes if indirect rendering */
/* is allowed, and handle automatically. */
#ifdef X11_BACKING_STORE
grXcontext = glXCreateContext(grXdpy, grVisualInfo, NULL, GL_FALSE);
#else
grXcontext = glXCreateContext(grXdpy, grVisualInfo, NULL, GL_TRUE);
#endif
/* Basic GL parameters */
glLineWidth(1.0);
glShadeModel (GL_FLAT);
glPixelStorei(GL_PACK_LSB_FIRST, TRUE);
/* OpenGL sets its own names for colormap and dstyle file types */
grCMapType = "OpenGL";
grDStyleType = "OpenGL";
/* Globally-accessed variables */
grNumBitPlanes = toglCurrent.depth;
grBitPlaneMask = (1 << toglCurrent.depth) - 1;
HashInit(&grTOGLWindowTable,8,HT_WORDKEYS);
return grTkLoadFont();
}
/*---------------------------------------------------------
* GrTOGLClose:
*
* Results: None.
*
* Side Effects:
*---------------------------------------------------------
*/
void
GrTOGLClose ()
{
if (grXdpy == NULL) return;
if (grVisualInfo != NULL) XFree(grVisualInfo);
grTkFreeFonts();
/* Pop down Tk window but let Tcl/Tk */
/* do XCloseDisplay() */
}
/*---------------------------------------------------------
* GrTOGLFlush:
* 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
GrTOGLFlush ()
{
GR_TOGL_FLUSH_BATCH();
glFlush();
}
/*
*---------------------------------------------------------
*/
static GLXPbuffer pbuffer = None;
#define glTransYs(n) (DisplayHeight(grXdpy, grXscrn)-(n))
/*
*---------------------------------------------------------
* Set the OpenGL viewport (projection matrix) for a window
*---------------------------------------------------------
*/
void
toglSetProjection(llx, lly, width, height)
int llx, lly, width, height;
{
if (toglCurrent.mw->w_flags & WIND_OFFSCREEN)
{
int count = 0;
int PBattrib[] = {
GLX_PBUFFER_WIDTH, width,
GLX_PBUFFER_HEIGHT, height,
GLX_LARGEST_PBUFFER, False,
None
};
GLXFBConfig *config;
if (pbuffer != None) glXDestroyPbuffer(grXdpy, pbuffer);
config = glXGetFBConfigs(grXdpy, grXscrn, &count);
if (config != NULL && count != 0)
{
pbuffer = glXCreatePbuffer(grXdpy, config[0], PBattrib);
glXMakeCurrent(grXdpy, (GLXDrawable)pbuffer, grXcontext);
}
if (config != NULL) XFree(config);
}
else
glXMakeCurrent(grXdpy, (GLXDrawable)toglCurrent.windowid, grXcontext);
#ifndef OGL_SERVER_SIDE_ONLY
/* For batch-processing lines and rectangles */
glEnableClientState(GL_VERTEX_ARRAY);
#endif
/* Because this tends to result in thick lines, it has been moved */
/* the line drawing routine so it can be enabled for individual */
/* lines. */
/* glEnable(GL_LINE_SMOOTH); */
/* Force draw to front buffer (in case of double-buffered config) */
glDrawBuffer(GL_FRONT);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glViewport((GLsizei)llx, (GLsizei)lly, (GLsizei) width, (GLsizei) height);
/* scale to fit window */
#ifdef OGL_INVERT_Y
glScalef(1.0 / (float)(width >> 1), -1.0 / (float)(height >> 1), 1.0);
#else
glScalef(1.0 / (float)(width >> 1), 1.0 / (float)(height >> 1), 1.0);
#endif
/* magic origin maps to window center; move to window origin */
glTranslated(-(GLsizei)(width >> 1), -(GLsizei)(height >> 1), 0);
if (toglCurrent.mw->w_flags & WIND_OFFSCREEN)
glTranslatef((float)0.5, (float)0.5, 0);
/* Remaining transformations are done on the modelview matrix */
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
/*
* ---------------------------------------------------------------------------
*
* TOGLEventProc ---
*
* Tk Event Handler
*
* Results:
* None.
*
* Side Effects:
* Calls functions in response to X11 events.
*
* ---------------------------------------------------------------------------
*/
void
TOGLEventProc(clientData, xevent)
ClientData clientData;
XEvent *xevent;
{
TxInputEvent *event;
HashEntry *entry;
Tk_Window tkwind = (Tk_Window)clientData;
Window wind;
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 or 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(&grTOGLWindowTable, (char *)tkwind);
mw = (entry)?(MagWindow *)HashGetValue(entry):0;
if (mw && (mw->w_flags & WIND_SCROLLBARS))
if (WindButtonInFrame(mw, ButtonEvent->x,
grXtransY(mw, ButtonEvent->y),
txbutton))
break;
goto keys_and_buttons;
}
break;
case KeyPress:
{
int keywstate, keymod, 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(&grTOGLWindowTable, (char *)tkwind);
mw = (entry)?(MagWindow *)HashGetValue(entry):0;
keys_and_buttons:
keymod = (LockMask | ControlMask | ShiftMask)
& KeyPressedEvent->state;
#ifdef __APPLE__
if (KeyPressedEvent->state & (Mod1Mask | Mod2Mask |
Mod3Mask | Mod4Mask | Mod5Mask))
keymod |= Mod1Mask;
#else
keymod |= (Mod1Mask & KeyPressedEvent->state);
#endif
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 (nbytes > 0 && inChar[idx] == 3) /* Ctrl-C interrupt */
{
if (SigInterruptPending)
MainExit(0); /* double Ctrl-C */
else
sigOnInterrupt(0); /* Set InterruptPending */
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\" ";
switch (keysym)
{
case XK_Return:
TxSetPoint(KeyPressedEvent->x,
grXtransY(mw, KeyPressedEvent->y),
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, "::tkcon::Event -1",
17, 0);
break;
case XK_Down:
Tcl_EvalEx(consoleinterp, "::tkcon::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", 40, 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';
default:
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,
XK_period, TxBuffer, NULL, FALSE);
TxInputRedirect = TX_INPUT_NORMAL;
TxSetPoint(KeyPressedEvent->x,
grXtransY(mw, KeyPressedEvent->y),
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(tkwind));
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,
grXtransY(mw, KeyPressedEvent->y),
mw->w_wid);
TxParseString(macroDef);
}
freeMagic(macroDef);
}
}
}
}
break;
case ConfigureNotify:
{
XConfigureEvent *ConfigureEvent = (XConfigureEvent*) xevent;
Rect screenRect;
int width, height;
bool result, need_resize;
width = ConfigureEvent->width;
height = ConfigureEvent->height;
entry = HashLookOnly(&grTOGLWindowTable, (char *)tkwind);
mw = (entry)?(MagWindow *)HashGetValue(entry):0;
screenRect.r_xbot = ConfigureEvent->x;
screenRect.r_xtop = ConfigureEvent->x + width;
screenRect.r_ytop = glTransYs(ConfigureEvent->y);
screenRect.r_ybot = glTransYs(ConfigureEvent->y + 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);
/* Redraw the window */
WindReframe(mw, &screenRect, FALSE, FALSE);
WindRedisplay(mw);
if (need_resize) (*GrCreateBackingStorePtr)(mw);
}
break;
case VisibilityNotify:
{
XVisibilityEvent *VisEvent = (XVisibilityEvent*) xevent;
entry = HashLookOnly(&grTOGLWindowTable, (char *)tkwind);
mw = (entry)?(MagWindow *)HashGetValue(entry):0;
switch(VisEvent->state)
{
case VisibilityUnobscured:
mw->w_flags &= ~WIND_OBSCURED;
if (mw->w_backingStore == (ClientData)NULL)
{
(*GrCreateBackingStorePtr)(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(&grTOGLWindowTable, (char *)tkwind);
mw = (entry)?(MagWindow *)HashGetValue(entry):0;
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 MapNotify:
case UnmapNotify:
case DestroyNotify: /* Do nothing */
break;
default:
TxError("Tk Event: Unknown (%d)\n", xevent->type);
TxFlush();
break;
}
}
/* Set for on-screen display */
void
toglOnScreen()
{
GrSetCMapPtr = GrTOGLSetCMap;
GrFlushPtr = GrTOGLFlush;
grSetSPatternPtr = grtoglSetSPattern;
grDrawLinePtr = grtoglDrawLine;
grSetWMandCPtr = grtoglSetWMandC;
grFillRectPtr = grtoglFillRect;
grSetStipplePtr = grtoglSetStipple;
grSetLineStylePtr = grtoglSetLineStyle;
grFillPolygonPtr = grtoglFillPolygon;
}
/*---------------------------------------------------------
* oglSetDisplay:
* This routine sets the appropriate parameters so that
* Magic will work with the X display.
*
* 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: Sets up the pipe.
*---------------------------------------------------------
*/
bool
oglSetDisplay (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; /* to be changed? */
TxInputRedirect = TX_INPUT_NORMAL;
grCursorType = "color";
WindScrollBarWidth = 14;
/* Set up the procedure values in the indirection table. */
GrPixelCorrect = 0;
GrLockPtr = GrTOGLLock;
GrUnlockPtr = GrTOGLUnlock;
GrInitPtr = GrTOGLInit;
GrClosePtr = GrTOGLClose;
GrSetCMapPtr = GrTOGLSetCMap;
GrEnableTabletPtr = GrTOGLEnableTablet;
GrDisableTabletPtr = GrTOGLDisableTablet;
GrSetCursorPtr = GrTOGLSetCursor;
GrTextSizePtr = GrTOGLTextSize;
GrDrawGlyphPtr = GrTOGLDrawGlyph;
GrReadPixelPtr = GrTOGLReadPixel;
GrFlushPtr = GrTOGLFlush;
GrCreateWindowPtr = GrTOGLCreate;
GrDeleteWindowPtr = GrTOGLDelete;
GrConfigureWindowPtr = GrTOGLConfigure;
GrOverWindowPtr = GrTOGLRaise;
GrUnderWindowPtr = GrTOGLLower;
GrUpdateIconPtr = GrTOGLIconUpdate;
GrEventPendingPtr = GrTOGLEventPending;
GrWindowIdPtr = GrTOGLWindowId;
GrWindowNamePtr = GrTkWindowName; /* from grTkCommon.c */
GrGetCursorPosPtr = grtoglGetCursorPos;
GrGetCursorRootPosPtr = grtoglGetCursorRootPos;
/* local indirections */
grSetSPatternPtr = grtoglSetSPattern;
grPutTextPtr = grtoglPutText;
#ifdef VECTOR_FONTS
grFontTextPtr = grtoglFontText;
#endif
grDefineCursorPtr = grTkDefineCursor;
grFreeCursorPtr = grTkFreeCursors;
GrBitBltPtr = GrTOGLBitBlt;
grDrawGridPtr = grtoglDrawGrid;
grDrawLinePtr = grtoglDrawLine;
grSetWMandCPtr = grtoglSetWMandC;
grFillRectPtr = grtoglFillRect;
grSetStipplePtr = grtoglSetStipple;
grSetLineStylePtr = grtoglSetLineStyle;
grSetCharSizePtr = grtoglSetCharSize;
grFillPolygonPtr = grtoglFillPolygon;
#ifdef X11_BACKING_STORE
GrFreeBackingStorePtr = grtkFreeBackingStore;
GrCreateBackingStorePtr = grtkCreateBackingStore;
GrGetBackingStorePtr = grtkGetBackingStore;
GrPutBackingStorePtr = grtkPutBackingStore;
GrScrollBackingStorePtr = grtkScrollBackingStore;
#else
GrFreeBackingStorePtr = grtoglFreeBackingStore;
GrCreateBackingStorePtr = grtoglCreateBackingStore;
GrGetBackingStorePtr = grtoglGetBackingStore;
GrPutBackingStorePtr = grtoglPutBackingStore;
GrScrollBackingStorePtr = grtoglScrollBackingStore;
#endif
if (execFailed) {
TxError("Execution failed!\n");
return FALSE;
}
if(!GrTOGLInit()){
return FALSE;
};
Tk_GetVRootGeometry(Tk_MainWindow(magicinterp), &x, &y, &width, &height);
GrScreenRect.r_xbot = x;
GrScreenRect.r_ybot = y;
GrScreenRect.r_xtop = width + x;
GrScreenRect.r_ytop = height + y;
return Tk_MainWindow(magicinterp) ? TRUE : FALSE;
}
extern void MakeWindowCommand();
/*
* ----------------------------------------------------------------------------
*
* GrTOGLCreate --
* Create a new window under the X window system.
* Bind X window to Magic Window w.
*
* Results:
* Success/Fail
*
* Side Effects:
* Window created, window ID send to Xhelper.
*
* ----------------------------------------------------------------------------
*/
bool
GrTOGLCreate(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 = glTransYs(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 | CWColormap;
XSetWindowAttributes grAttributes;
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 = glTransYs(y);
w->w_frameArea.r_ybot = glTransYs(y+height);
WindReframe(w,&(w->w_frameArea),FALSE,FALSE);
}
grAttributes.colormap = XCreateColormap(grXdpy, RootWindow(grXdpy, grXscrn),
grVisualInfo->visual, AllocNone);
grAttributes.background_pixel = WhitePixel(grXdpy,grXscrn);
grAttributes.border_pixel = BlackPixel(grXdpy,grXscrn);
if ((tktop = Tk_MainWindow(magicinterp)))
{
if (!WindowNumber)
{
/* To do: deal with grVisualInfo---destroy and recreate top */
/* frame if necessary */
if (Tk_WindowId(tktop) == 0)
{
Tk_SetWindowVisual(tktop, grVisualInfo->visual,
toglCurrent.depth, grAttributes.colormap);
}
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 */
/* 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("Creating window named \"%s\", tkwind = 0x%x\n",
windowname, tkwind); TxFlush(); */
if (tkwind != 0)
{
bool result;
GrTOGLFlush();
toglCurrent.window = tkwind;
toglCurrent.mw = w;
w->w_grdata = (ClientData) tkwind;
entry = HashFind(&grTOGLWindowTable, (char *)tkwind);
HashSetValue(entry,w);
/* ensure that the visual is what we wanted, if possible to change */
Tk_SetWindowVisual(tkwind, grVisualInfo->visual, toglCurrent.depth,
grAttributes.colormap);
/* 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);
toglCurrent.windowid = wind;
glXMakeCurrent(grXdpy, (GLXDrawable)wind, grXcontext);
Tk_DefineCursor(tkwind, toglCurrent.cursor);
GrTOGLIconUpdate(w, w->w_caption);
WindowNumber++;
/* execute all Tk events up to current */
while (Tcl_DoOneEvent(TCL_DONT_WAIT) != 0);
/* set up Tk event handler to start processing */
Tk_CreateEventHandler(tkwind, ExposureMask | StructureNotifyMask
| ButtonPressMask | KeyPressMask | VisibilityChangeMask,
(Tk_EventProc *)TOGLEventProc, (ClientData) tkwind);
/* set up commands to be passed expressly to this window */
MakeWindowCommand((name == NULL) ? windowname : name, w);
return (WindowNumber == 1) ? grtoglLoadFont() : 1;
}
else
{
TxError("Could not open new Tk window\n");
}
return 0;
}
/*
* ----------------------------------------------------------------------------
*
* GrTOGLDelete --
* Destroy a Tk/OpenGL window.
*
* Results:
* None.
*
* Side Effects:
* Window destroyed.
*
* ----------------------------------------------------------------------------
*/
void
GrTOGLDelete(w)
MagWindow *w;
{
Tk_Window xw;
HashEntry *entry;
xw = (Tk_Window) w->w_grdata;
entry = HashLookOnly(&grTOGLWindowTable, (char *)xw);
HashSetValue(entry,NULL);
Tcl_DeleteCommand(magicinterp, Tk_PathName(xw));
Tk_DestroyWindow(xw);
}
/*
* ----------------------------------------------------------------------------
*
* GrTOGLConfigure --
* Resize/ Move an existing X window.
*
* Results:
* None.
*
* Side Effects:
* Window reconfigured to w->w_frameArea.
*
* ----------------------------------------------------------------------------
*/
void
GrTOGLConfigure(w)
MagWindow *w;
{
if (w->w_flags & WIND_OFFSCREEN) return;
Tk_MoveResizeWindow((Tk_Window)w->w_grdata,
w->w_frameArea.r_xbot, glTransYs(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);
}
/*
* ----------------------------------------------------------------------------
*
* GrTOGLRaise --
* Raise a window to the top of the screen such that nothing
* obscures it.
*
* Results:
* None.
*
* Side Effects:
* Window raised.
*
* ----------------------------------------------------------------------------
*/
void
GrTOGLRaise(w)
MagWindow *w;
{
Tk_Window tkwind;
if (w->w_flags & WIND_OFFSCREEN) return;
tkwind = (Tk_Window)w->w_grdata;
Tk_RestackWindow(tkwind, Above, NULL);
}
/*
* ----------------------------------------------------------------------------
*
* GrTOGLLower --
* Lower a window below all other Tk windows.
* obscures it.
*
* Results:
* None.
*
* Side Effects:
* Window lowered.
*
* ----------------------------------------------------------------------------
*/
void
GrTOGLLower(w)
MagWindow *w;
{
Tk_Window tkwind;
if (w->w_flags & WIND_OFFSCREEN) return;
tkwind = (Tk_Window)w->w_grdata;
Tk_RestackWindow(tkwind, Below, NULL);
}
/*
* ----------------------------------------------------------------------------
*
* GrTOGLLock --
* Lock a window and set global variables "toglCurrent.window"
* and "toglCurrent.mw" to reference the locked window.
*
* Results:
* None.
*
* Side Effects:
* Window locked.
*
* ----------------------------------------------------------------------------
*/
#ifdef CAIRO_OFFSCREEN_RENDER
extern void GrTCairoLock();
extern void GrTCairoUnlock();
extern void TCairoOffScreen();
#endif
void
GrTOGLLock(w, flag)
MagWindow *w;
bool flag;
{
Window wind;
#ifdef CAIRO_OFFSCREEN_RENDER
/* Use Cairo graphics for off-screen rendering */
if ((w != GR_LOCK_SCREEN) && (w->w_flags & WIND_OFFSCREEN))
{
GrTCairoLock(w, flag);
TCairoOffScreen();
return;
}
#endif
grSimpleLock(w, flag);
if ( w != GR_LOCK_SCREEN )
{
toglCurrent.mw = w;
if (w->w_flags & WIND_OFFSCREEN)
{
toglCurrent.window = (Tk_Window) NULL;
toglCurrent.windowid = (Pixmap) w->w_grdata;
}
else
{
toglCurrent.window = (Tk_Window) w->w_grdata;
toglCurrent.windowid = Tk_WindowId(toglCurrent.window);
}
toglSetProjection(w->w_allArea.r_xbot, w->w_allArea.r_ybot,
w->w_allArea.r_xtop - w->w_allArea.r_xbot,
w->w_allArea.r_ytop - w->w_allArea.r_ybot);
}
}
/*
* ----------------------------------------------------------------------------
*
* GrTOGLUnlock --
* Unlock a window, flushing stuff out to the display.
*
* Results:
* None.
*
* Side Effects:
* Window unlocked.
* Display update.
*
* ----------------------------------------------------------------------------
*/
void
GrTOGLUnlock(w)
MagWindow *w;
{
#ifdef CAIRO_OFFSCREEN_RENDER
/* Use Cairo graphics for off-screen rendering */
if ((w != GR_LOCK_SCREEN) && (w->w_flags & WIND_OFFSCREEN))
{
GrTCairoUnlock(w);
toglOnScreen();
return;
}
#endif
GrTOGLFlush();
if ((w != GR_LOCK_SCREEN) && (w->w_flags & WIND_OFFSCREEN))
{
GC grXcopyGC;
XGCValues gcValues;
unsigned char *pdata, *tdata;
int i, j;
Window root_return;
int x_return, y_return;
unsigned int pbwidth, pbheight, wborder, depth;
XGetGeometry(grXdpy, (Drawable)toglCurrent.windowid, &root_return,
&x_return, &y_return, &pbwidth, &pbheight, &wborder, &depth);
pdata = (unsigned char *)mallocMagic((pbwidth * pbheight * 3)
* sizeof(unsigned int));
/* In offscreen-rendering mode, copy Pbuffer back to window */
glReadBuffer(GL_FRONT);
glReadPixels(0, 0, pbwidth, pbheight, GL_RGB, GL_UNSIGNED_BYTE, pdata);
gcValues.graphics_exposures = FALSE;
grXcopyGC = XCreateGC(grXdpy, (Drawable)toglCurrent.windowid,
GCGraphicsExposures, &gcValues);
/* This is very slow, but the only way I've found to copy data */
/* from a Pbuffer into a Pixmap. It is only used to make the */
/* icon images for the toolbar, so it does not need to be */
/* efficient. */
tdata = pdata;
for (i = 0; i < pbwidth; i++) {
for (j = 0; j < pbheight; j++)
{
unsigned long pcolor;
pcolor = *tdata++;
pcolor <<= 8;
pcolor |= *tdata++;
pcolor <<= 8;
pcolor |= *tdata++;
XSetForeground(grXdpy, grXcopyGC, pcolor);
XDrawPoint(grXdpy, (Drawable)toglCurrent.windowid, grXcopyGC,
pbwidth - i - 1, j);
}
}
freeMagic(pdata);
XFreeGC(grXdpy, grXcopyGC);
}
grSimpleUnlock(w);
}
/*
*-------------------------------------------------------------------------
* GrTOGLEventPending --
* 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 waiting in the event queue.
*
* Side effects:
* None, hopefully (put back the event!)
*
*-------------------------------------------------------------------------
*/
bool
GrTOGLEventPending()
{
Window wind = toglCurrent.windowid;
XEvent genEvent;
bool retval;
XSync(grXdpy, FALSE); /* Necessary, or it won't catch mouse/key events */
retval = XCheckWindowEvent(grXdpy, wind, ExposureMask
| StructureNotifyMask | ButtonPressMask
| KeyPressMask, &genEvent);
if (retval) XPutBackEvent(grXdpy, &genEvent);
return retval;
}
/*
*-------------------------------------------------------------------------
*
* GrTOGLIconUpdate -- updates the icon text with the window script
*
* Results: none
*
* Side Effects: changes the icon text
*
*-------------------------------------------------------------------------
*/
void
GrTOGLIconUpdate(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)
{
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);
}
}
/*
*-------------------------------------------------------------------------
* GrTOGLWindowId --
* Get magic's ID number from the indicated MagWindow structure
*
* Results:
* The window ID number.
*
* Side effects:
* None.
*
*-------------------------------------------------------------------------
*/
int
GrTOGLWindowId(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(&grTOGLWindowTable, (char *)tkwind);
mw = (entry) ? (MagWindow *)HashGetValue(entry) : 0;
if (mw) id = mw->w_wid;
}
return id;
}