1 /* grTOGL1.c
2  *
3  * Copyright (C) 2003 Open Circuit Design, Inc., for MultiGiG Ltd.
4  *
5  * This file contains primitive functions for OpenGL running under
6  * an X window system in a Tcl/Tk interpreter environment
7  */
8 
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <unistd.h>
13 #include <sys/types.h>
14 #include <sys/stat.h>
15 #include <fcntl.h>
16 #include <signal.h>
17 
18 #include <X11/Xlib.h>
19 #include <X11/Xutil.h>
20 #include <X11/keysym.h>
21 
22 #include <GL/gl.h>
23 #include <GL/glx.h>
24 
25 #include "tcltk/tclmagic.h"
26 #include "utils/main.h"
27 #include "utils/magic.h"
28 #include "utils/malloc.h"
29 #include "utils/magsgtty.h"
30 #include "utils/geometry.h"
31 #include "windows/windows.h"
32 #include "graphics/graphics.h"
33 #include "graphics/graphicsInt.h"
34 #include "textio/textio.h"
35 #include "textio/txcommands.h"
36 #include "utils/signals.h"
37 #include "utils/utils.h"
38 #include "tiles/tile.h"
39 #include "utils/hash.h"
40 #include "database/database.h"
41 #include "drc/drc.h"
42 #include "utils/macros.h"
43 #include "graphics/grTOGLInt.h"
44 #include "utils/paths.h"
45 #include "graphics/grTkCommon.h"
46 
47 GLubyte		**grTOGLStipples;
48 HashTable	grTOGLWindowTable;
49 GLXContext	grXcontext;
50 XVisualInfo	*grVisualInfo;
51 
52 TOGL_CURRENT toglCurrent= {(Tk_Font)0, 0, 0, 0, 0,
53 	(Tk_Window)0, (Window)0, (MagWindow *)NULL};
54 
55 /* This is kind of a long story, and very kludgy, but the following
56  * things need to be defined as externals because of the way lint
57  * libraries are made by taking this module and changing all procedures
58  * names "Xxxx" to "Grxxx".  The change is only done at the declaration
59  * of the procedure, so we need these declarations to handle uses
60  * of those names, which don't get modified.  Check out the Makefile
61  * for details on this.
62  */
63 extern void GrTOGLClose(), GrTOGLFlush();
64 extern void GrTOGLDelete(), GrTOGLConfigure(), GrTOGLRaise(), GrTOGLLower();
65 extern void GrTOGLLock(), GrTOGLUnlock(), GrTOGLIconUpdate();
66 extern bool GrTOGLInit();
67 extern bool GrTOGLEventPending(), GrTOGLCreate(), grtoglGetCursorPos();
68 extern int  GrTOGLWindowId();
69 extern char *GrTkWindowName();
70 
71 extern void toglSetProjection();
72 
73 
74 /*---------------------------------------------------------
75  * grtoglSetWMandC:
76  *	This is a local routine that resets the value of the current
77  *	write alpha (mask) and color, if necessary.
78  *
79  * Results:	None.
80  *
81  * Side Effects:    None.
82  *
83  * Errors:		None.
84  *---------------------------------------------------------
85  */
86 
87 void
grtoglSetWMandC(mask,c)88 grtoglSetWMandC (mask, c)
89     int mask;			/* New value for write mask */
90     int c;			/* New value for current color */
91 {
92     static int oldColor = -1;
93     static int oldMask = -1;
94 
95     int lr, lb, lg;
96     GLfloat fr, fb, fg;
97     GLfloat aval; 	 /* Alpha default value was 0.75 */
98 
99     if (mask == -65) mask = 127;	/* All planes */
100     if (mask == oldMask && c == oldColor) return;
101 
102     GR_TOGL_FLUSH_BATCH();
103 
104     GrGetColor(c, &lr, &lb, &lg);
105 
106     fr = ((GLfloat)lr / 255);
107     fg = ((GLfloat)lg / 255);
108     fb = ((GLfloat)lb / 255);
109 
110     if (mask == 127)
111     {
112 	glDisable(GL_BLEND);
113 	aval = 1.0;
114     }
115     else
116     {
117 	/* Calculate a "supercolor", outside of the normal color range, */
118 	/* but which results in the desired color after a blend with	*/
119 	/* the background color.					*/
120 
121 	fr = fr * 2 - 0.8;
122 	fg = fg * 2 - 0.8;
123 	fb = fb * 2 - 0.8;
124 
125 	aval = (GLfloat)mask / 127.0;	/* mask translates to alpha in	*/
126 					/* the OpenGL version.		*/
127 
128 	glEnable(GL_BLEND);
129 	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
130     }
131     glColor4f(fr, fb, fg, aval);
132 
133     oldColor = c;
134     oldMask = mask;
135 }
136 
137 
138 /*---------------------------------------------------------
139  * grtoglSetLineStyle:
140  *	This local routine sets the current line style.
141  *
142  * Results:	None.
143  *
144  * Side Effects:
145  *	A new line style is output to the display.
146  *
147  *---------------------------------------------------------
148  */
149 
150 void
grtoglSetLineStyle(style)151 grtoglSetLineStyle (style)
152     int style;			/* New stipple pattern for lines. */
153 {
154     static int oldStyle = -1;
155     GLushort glstyle;
156 
157     style &= 0xFF;
158     if (style == oldStyle) return;
159     oldStyle = style;
160     GR_TOGL_FLUSH_BATCH();
161 
162     switch (style) {
163 	case 0xFF:
164 	case 0x00:
165 	    glDisable(GL_LINE_STIPPLE);
166 	    break;
167 	default:
168 	    glstyle = style | (style << 8);
169 	    glEnable(GL_LINE_STIPPLE);
170 	    glLineStipple(1, glstyle);
171 	    break;
172     }
173 }
174 
175 
176 /*---------------------------------------------------------
177  * grtoglSetSPattern:
178  *	toglSetSPattern associates a stipple pattern with a given
179  *	stipple number.  This is a local routine called from
180  *	grStyle.c .
181  *
182  * Results:	None.
183  *
184  * Side Effects:    None.
185  *---------------------------------------------------------
186  */
187 
188 void
grtoglSetSPattern(sttable,numstipples)189 grtoglSetSPattern (sttable, numstipples)
190     int **sttable;			/* The table of patterns */
191     int numstipples;			/* Number of stipples */
192 {
193     int i, j, k, n;
194     GLubyte *pdata;
195 
196     grTOGLStipples = (GLubyte **)mallocMagic(numstipples * sizeof(GLubyte *));
197     for (k = 0; k < numstipples; k++)
198     {
199 	pdata = (GLubyte *)mallocMagic(128 * sizeof(GLubyte));
200 	n = 0;
201 
202 	/* expand magic's default 8x8 stipple to OpenGL's 32x32 */
203 
204 	for (i = 0; i < 32; i++)
205 	    for (j = 0; j < 4; j++)
206 		pdata[n++] = (GLubyte)sttable[k][i % 8];
207 
208 	grTOGLStipples[k] = pdata;
209     }
210 }
211 
212 
213 /*---------------------------------------------------------
214  * grtoglSetStipple:
215  *	This routine sets the Xs current stipple number.
216  *
217  * Results: None.
218  *
219  * Side Effects:
220  *	The current clipmask in the X is set to stipple,
221  *	if it wasn't that already.
222  *---------------------------------------------------------
223  */
224 
225 void
grtoglSetStipple(stipple)226 grtoglSetStipple (stipple)
227     int stipple;			/* The stipple number to be used. */
228 {
229     static int oldStip = -1;
230     if (stipple == oldStip) return;
231     oldStip = stipple;
232     GR_TOGL_FLUSH_BATCH();
233     if (stipple == 0 || stipple > grNumStipples) {
234 	glDisable(GL_POLYGON_STIPPLE);
235     } else {
236 	if (grTOGLStipples[stipple] == (GLubyte *)NULL) MainExit(1);
237 	glEnable(GL_POLYGON_STIPPLE);
238 	glPolygonStipple(grTOGLStipples[stipple]);
239     }
240 }
241 
242 
243 /*------------------------------------------------------------------------
244  * GrTOGLInit:
245  *	GrTOGLInit initializes the graphics display and clears its screen.
246  *	Files must have been previously opened with GrSetDisplay();
247  *
248  * Results: TRUE if successful.
249  *
250  * Notes: When 3D rendering is compiled in, we search for a double-buffered
251  *	configuration first, because it generates the smoothest graphics,
252  *	and fall back on a single-buffered configuration if necessary.
253  *	For normal, 2D-only rendering, we look for a single-buffered
254  *	configuration first because we don't use the back buffer, so a
255  *	double-buffered configuration just wastes space.
256  *------------------------------------------------------------------------
257  */
258 
259 bool
GrTOGLInit()260 GrTOGLInit ()
261 {
262     bool rstatus;
263 #ifdef THREE_D
264     static int attributeList[] = { GLX_RGBA, GLX_DOUBLEBUFFER, None };
265 #else
266     static int attributeList[] = { GLX_RGBA, None, None };
267 #endif
268 
269     if (Tk_InitStubs(magicinterp, "8.5", 0) == NULL) return FALSE;
270 
271     toglCurrent.window = Tk_MainWindow(magicinterp);
272     if (toglCurrent.window == NULL)
273     {
274     	 TxError("No Top-Level Tk window available. . . is Tk running?\n");
275 	 return FALSE;
276     }
277 
278     toglCurrent.windowid = Tk_WindowId(toglCurrent.window);
279     grXdpy = Tk_Display(toglCurrent.window);
280     toglCurrent.depth = Tk_Depth(toglCurrent.window);
281 
282     grXscrn = DefaultScreen(grXdpy);
283 
284     grVisualInfo = glXChooseVisual(grXdpy, grXscrn, attributeList);
285 
286     if (!grVisualInfo)
287     {
288 	/* Try for a double-buffered configuration */
289 #ifdef THREE_D
290 	attributeList[1] = None;
291 #else
292 	attributeList[1] = GLX_DOUBLEBUFFER;
293 #endif
294 	grVisualInfo = glXChooseVisual(grXdpy, grXscrn, attributeList);
295 	if (!grVisualInfo)
296 	{
297 	    TxError("No suitable visual!\n");
298 	    return FALSE;
299 	}
300     }
301     grXscrn = grVisualInfo->screen;
302     toglCurrent.depth = grVisualInfo->depth;
303 
304     /* glXCreateContext() 4th argument:				*/
305     /* TRUE = Direct rendering, FALSE = Indirect rendering 	*/
306 
307     /* Direct rendering may not be able to deal with pixmaps. 	*/
308     /* Normally, magic should compile with OpenGL framebuffer	*/
309     /* backing store and pbuffer offscreen rendering.  However,	*/
310     /* if X11_BACKING_STORE is selected then force indirect	*/
311     /* rendering.  This can be avoided if the backing store	*/
312     /* functions sync between OpenGL and X11;  this has not	*/
313     /* been done yet.						*/
314 
315     /* The CAIRO_OFFSCREEN_RENDER compile-time option uses	*/
316     /* Cairo to do the off-screen rendering, which allows	*/
317     /* OpenGL to run in direct-rendering mode only.  To do:	*/
318     /* determine from OpenGL attributes if indirect rendering	*/
319     /* is allowed, and handle automatically.			*/
320 
321 #ifdef X11_BACKING_STORE
322     grXcontext = glXCreateContext(grXdpy, grVisualInfo, NULL, GL_FALSE);
323 #else
324     grXcontext = glXCreateContext(grXdpy, grVisualInfo, NULL, GL_TRUE);
325 #endif
326 
327     /* Basic GL parameters */
328 
329     glLineWidth(1.0);
330     glShadeModel (GL_FLAT);
331     glPixelStorei(GL_PACK_LSB_FIRST, TRUE);
332 
333     /* OpenGL sets its own names for colormap and dstyle file types */
334     grCMapType = "OpenGL";
335     grDStyleType = "OpenGL";
336 
337     /* Globally-accessed variables */
338     grNumBitPlanes = toglCurrent.depth;
339     grBitPlaneMask = (1 << toglCurrent.depth) - 1;
340 
341     HashInit(&grTOGLWindowTable,8,HT_WORDKEYS);
342 
343     return grTkLoadFont();
344 }
345 
346 /*---------------------------------------------------------
347  * GrTOGLClose:
348  *
349  * Results:	None.
350  *
351  * Side Effects:
352  *---------------------------------------------------------
353  */
354 
355 void
GrTOGLClose()356 GrTOGLClose ()
357 {
358     if (grXdpy == NULL) return;
359     if (grVisualInfo != NULL) XFree(grVisualInfo);
360 
361     grTkFreeFonts();
362 
363     /* Pop down Tk window but let Tcl/Tk */
364     /* do XCloseDisplay()		 */
365 }
366 
367 
368 /*---------------------------------------------------------
369  * GrTOGLFlush:
370  * 	Flush output to display.
371  *
372  *	Flushing is done automatically the next time input is read,
373  *	so this procedure should not be used very often.
374  *
375  * Results:	None.
376  *
377  * Side Effects:    None.
378  *---------------------------------------------------------
379  */
380 
381 void
GrTOGLFlush()382 GrTOGLFlush ()
383 {
384     GR_TOGL_FLUSH_BATCH();
385     glFlush();
386 }
387 
388 /*
389  *---------------------------------------------------------
390  */
391 
392 static GLXPbuffer pbuffer = None;
393 
394 #define glTransYs(n) (DisplayHeight(grXdpy, grXscrn)-(n))
395 
396 /*
397  *---------------------------------------------------------
398  * Set the OpenGL viewport (projection matrix) for a window
399  *---------------------------------------------------------
400  */
401 
402 void
toglSetProjection(llx,lly,width,height)403 toglSetProjection(llx, lly, width, height)
404     int llx, lly, width, height;
405 {
406     if (toglCurrent.mw->w_flags & WIND_OFFSCREEN)
407     {
408 	int count = 0;
409 	int PBattrib[] = {
410 	    GLX_PBUFFER_WIDTH, width,
411 	    GLX_PBUFFER_HEIGHT, height,
412 	    GLX_LARGEST_PBUFFER, False,
413 	    None
414 	};
415 	GLXFBConfig *config;
416 
417 	if (pbuffer != None) glXDestroyPbuffer(grXdpy, pbuffer);
418 	config = glXGetFBConfigs(grXdpy, grXscrn, &count);
419 	if (config != NULL && count != 0)
420 	{
421 	    pbuffer = glXCreatePbuffer(grXdpy, config[0], PBattrib);
422 	    glXMakeCurrent(grXdpy, (GLXDrawable)pbuffer, grXcontext);
423 	}
424 	if (config != NULL) XFree(config);
425     }
426     else
427 	glXMakeCurrent(grXdpy, (GLXDrawable)toglCurrent.windowid, grXcontext);
428 
429 #ifndef OGL_SERVER_SIDE_ONLY
430     /* For batch-processing lines and rectangles */
431     glEnableClientState(GL_VERTEX_ARRAY);
432 #endif
433 
434     /* Because this tends to result in thick lines, it has been moved	*/
435     /* the line drawing routine so it can be enabled for individual	*/
436     /* lines.								*/
437     /* glEnable(GL_LINE_SMOOTH); */
438 
439     /* Force draw to front buffer (in case of double-buffered config) */
440     glDrawBuffer(GL_FRONT);
441 
442     glMatrixMode(GL_PROJECTION);
443     glLoadIdentity();
444 
445     glViewport((GLsizei)llx, (GLsizei)lly, (GLsizei) width, (GLsizei) height);
446 
447     /* scale to fit window */
448 
449 #ifdef OGL_INVERT_Y
450     glScalef(1.0 / (float)(width >> 1), -1.0 / (float)(height >> 1), 1.0);
451 #else
452     glScalef(1.0 / (float)(width >> 1), 1.0 / (float)(height >> 1), 1.0);
453 #endif
454 
455     /* magic origin maps to window center; move to window origin */
456 
457     glTranslated(-(GLsizei)(width >> 1), -(GLsizei)(height >> 1), 0);
458 
459     if (toglCurrent.mw->w_flags & WIND_OFFSCREEN)
460 	glTranslatef((float)0.5, (float)0.5, 0);
461 
462     /* Remaining transformations are done on the modelview matrix */
463 
464     glMatrixMode(GL_MODELVIEW);
465     glLoadIdentity();
466 }
467 
468 
469 /*
470  * ---------------------------------------------------------------------------
471  *
472  * TOGLEventProc ---
473  *
474  *	Tk Event Handler
475  *
476  * Results:
477  *      None.
478  *
479  * Side Effects:
480  *      Calls functions in response to X11 events.
481  *
482  * ---------------------------------------------------------------------------
483  */
484 
485 void
TOGLEventProc(clientData,xevent)486 TOGLEventProc(clientData, xevent)
487     ClientData clientData;
488     XEvent *xevent;
489 {
490     TxInputEvent *event;
491     HashEntry	*entry;
492     Tk_Window tkwind = (Tk_Window)clientData;
493     Window wind;
494     MagWindow *mw;
495     unsigned char LocRedirect = TxInputRedirect;
496 
497     XKeyPressedEvent *KeyPressedEvent = (XKeyPressedEvent *) xevent;
498     KeySym keysym;
499     int nbytes;
500 
501     /* Keys and Buttons:  Determine expansion of macros or redirect
502      * keys to the terminal or console.
503      */
504 
505     switch (xevent->type)
506     {
507 	case ButtonPress:
508 	    {
509 		XButtonEvent *ButtonEvent = (XButtonEvent *) xevent;
510 		int txbutton;
511 
512 		switch (ButtonEvent->button) {
513 		case Button1:
514 		    txbutton = TX_LEFT_BUTTON;
515 		    keysym = XK_Pointer_Button1;
516 		    break;
517 		case Button2:
518 		    txbutton = TX_MIDDLE_BUTTON;
519 		    keysym = XK_Pointer_Button2;
520 		    break;
521 		case Button3:
522 		    txbutton = TX_RIGHT_BUTTON;
523 		    keysym = XK_Pointer_Button3;
524 		    break;
525 		case Button4:
526 		    txbutton = TX_BUTTON_4;
527 		    keysym = XK_Pointer_Button4;
528 		    break;
529 		case Button5:
530 		    txbutton = TX_BUTTON_5;
531 		    keysym = XK_Pointer_Button5;
532 		    break;
533 		}
534 		nbytes = 0;
535 
536 		entry = HashLookOnly(&grTOGLWindowTable, (char *)tkwind);
537 		mw = (entry)?(MagWindow *)HashGetValue(entry):0;
538 
539 		if (mw && (mw->w_flags & WIND_SCROLLBARS))
540 		    if (WindButtonInFrame(mw, ButtonEvent->x,
541 				grXtransY(mw, ButtonEvent->y),
542 				txbutton))
543 			break;
544 
545 		goto keys_and_buttons;
546 	    }
547 	    break;
548 	case KeyPress:
549 	    {
550 		int keywstate, keymod, idx, idxmax;
551 		char inChar[10];
552 		Tcl_Channel outChannel = Tcl_GetStdChannel(TCL_STDOUT);
553 
554 		nbytes = XLookupString(KeyPressedEvent, inChar, sizeof(inChar),
555 			&keysym, NULL);
556 
557 		if (IsModifierKey(keysym)) break;	/* Don't handle modifiers */
558 
559 		entry = HashLookOnly(&grTOGLWindowTable, (char *)tkwind);
560 		mw = (entry)?(MagWindow *)HashGetValue(entry):0;
561 
562 keys_and_buttons:
563 
564 		keymod = (LockMask | ControlMask | ShiftMask)
565 				& KeyPressedEvent->state;
566 #ifdef __APPLE__
567 		if (KeyPressedEvent->state & (Mod1Mask | Mod2Mask |
568 				Mod3Mask | Mod4Mask | Mod5Mask))
569 		    keymod |= Mod1Mask;
570 #else
571 		keymod |= (Mod1Mask & KeyPressedEvent->state);
572 #endif
573 
574 		if (nbytes == 0)	/* No ASCII equivalent */
575 		{
576 		    keywstate = (keymod << 16) | (keysym & 0xffff);
577 		}
578 		else if (!strncmp(XKeysymToString(keysym), "KP_", 3))
579 		{
580 		    /* keypad key (special case---would like to		*/
581 		    /* differentiate between shift-KP-# and # itself)	*/
582 		    keymod &= ~ShiftMask;
583 		    keywstate = (keymod << 16) | (keysym & 0xffff);
584 		    nbytes = 0;
585 		}
586 		else			/* ASCII-valued character */
587 		{
588 		    if (!(keymod & (LockMask | Mod1Mask))) {
589 			if (!(keymod & ControlMask))
590 			    keymod &= ~ShiftMask;
591 			else if (!(keymod & ShiftMask))
592 			    keymod &= ~ControlMask;
593 		    }
594 		}
595 
596 		idxmax = (nbytes == 0) ? 1 : nbytes;
597 		for (idx = 0; idx < idxmax; idx++)
598 		{
599 		    if (inChar[idx] == 3)	/* Ctrl-C interrupt */
600 		    {
601 			if (SigInterruptPending)
602 			    MainExit(0);	/* double Ctrl-C */
603 			else
604 			    sigOnInterrupt(0);	/* Set InterruptPending */
605 			break;
606 		    }
607 		    else if (nbytes > 0)
608 		    {
609 			if ((keymod & ControlMask) && (inChar[idx] < 32))
610 			    inChar[idx] += 'A' - 1;
611 
612 			keywstate = (keymod << 16) | ((int)inChar[idx] & 0xff);
613 		    }
614 
615 		    /* Allow buttons to bypass the console and be	*/
616 		    /* treated as macros.				*/
617 
618 		    if (LocRedirect == TX_INPUT_REDIRECTED)
619 		    {
620 			switch (keysym)
621 			{
622 			    case XK_Pointer_Button1:
623 			    case XK_Pointer_Button2:
624 			    case XK_Pointer_Button3:
625 			    case XK_Pointer_Button4:
626 			    case XK_Pointer_Button5:
627 				LocRedirect = TX_INPUT_NORMAL;;
628 				break;
629 			}
630 		    }
631 
632 		    if ((LocRedirect == TX_INPUT_REDIRECTED) && TxTkConsole)
633 		    {
634 			Tcl_SavedResult state;
635 			static char outstr[] = "::tkcon::Insert .text \"x\" ";
636 
637 			switch (keysym)
638 			{
639 			    case XK_Return:
640 			        TxSetPoint(KeyPressedEvent->x,
641 					grXtransY(mw, KeyPressedEvent->y),
642 					mw->w_wid);
643 				TxInputRedirect = TX_INPUT_PROCESSING;
644 				Tcl_EvalEx(consoleinterp, "::tkcon::Eval .text",
645 						19, 0);
646 				TxInputRedirect = TX_INPUT_NORMAL;
647 				TxSetPrompt('%');
648 
649 				Tcl_SaveResult(magicinterp, &state);
650 				Tcl_EvalEx(magicinterp, "history event 0", 15, 0);
651 			        MacroDefine(mw->w_client, (int)'.',
652 					Tcl_GetStringResult(magicinterp), NULL,
653 					FALSE);
654 				Tcl_RestoreResult(magicinterp, &state);
655 				break;
656 			    case XK_Up:
657 				Tcl_EvalEx(consoleinterp, "::tkcon::Event -1",
658 					17, 0);
659 				break;
660 			    case XK_Down:
661 				Tcl_EvalEx(consoleinterp, "::tkcon::Event 1",
662 					16, 0);
663 				break;
664 			    case XK_Left:
665 				Tcl_EvalEx(consoleinterp, ".text mark set insert "
666 					"insert-1c ; .text see insert", 50, 0);
667 				break;
668 			    case XK_Right:
669 				Tcl_EvalEx(consoleinterp, ".text mark set insert "
670 					"insert+1c ; .text see insert", 50, 0);
671 				break;
672 			    case XK_BackSpace: case XK_Delete:
673 				Tcl_EvalEx(consoleinterp, ".text delete insert-1c ;"
674 					".text see insert", 40, 0);
675 				break;
676 			    case XK_quotedbl: case XK_backslash: case XK_bracketleft:
677 				outstr[23] = '\\';
678 				outstr[24] = inChar[idx];
679 				outstr[25] = '\"';
680 				Tcl_EvalEx(consoleinterp, outstr, 26, 0);
681 				outstr[24] = '\"';
682 				outstr[25] = '\0';
683 			    default:
684 				outstr[23] = inChar[idx];
685 				Tcl_EvalEx(consoleinterp, outstr, 25, 0);
686 				break;
687 			}
688 		    }
689 		    else if (LocRedirect == TX_INPUT_REDIRECTED) {
690 			int tl;
691 			if (TxBuffer == NULL)
692 			{
693 			    TxBuffer = Tcl_Alloc(2);
694 			    *TxBuffer = '\0';
695 			    tl = 0;
696 			}
697 			else
698 			{
699 			    tl = strlen(TxBuffer);
700 			    TxBuffer = Tcl_Realloc(TxBuffer, tl + 2);
701 			}
702 			if (keysym == XK_BackSpace || keysym == XK_Delete)
703 			{
704 			    if (tl >= 0)
705 			    {
706 				if (tl > 0)
707 				{
708 				    *(TxBuffer + tl - 1) = '\0';
709 				    TxPrintf("\b");
710 				}
711 				TxPrintf(" \b");
712 				TxFlushOut();
713 			    }
714 			}
715 			else if (keysym == XK_Return)
716 			{
717 			    *(TxBuffer + tl) = '\n';
718 			    *(TxBuffer + tl + 1) = '\0';
719 			    if (tl != 0) MacroDefine(mw->w_client,
720 					XK_period, TxBuffer, NULL, FALSE);
721 			    TxInputRedirect = TX_INPUT_NORMAL;
722 			    TxSetPoint(KeyPressedEvent->x,
723 					grXtransY(mw, KeyPressedEvent->y),
724 					mw->w_wid);
725 			    TxPrintf("\n");
726 			    TxFlushOut();
727 			    Tcl_NotifyChannel(Tcl_GetStdChannel(TCL_STDIN),
728 					TCL_READABLE);
729 			}
730 			else
731 			{
732 			    *(TxBuffer + tl) = *(inChar + idx);
733 			    *(TxBuffer + tl + 1) = '\0';
734 			    TxPrintf("%c", *(inChar + idx));
735 			    TxFlushOut();
736 			}
737 		    }
738 		    else
739 		    {
740 			bool iMacro;
741 			char *macroDef;
742 
743 			macroDef = MacroRetrieve(mw->w_client, keywstate, &iMacro);
744 
745 			/* Special handling:  An imacro beginning with ':'	*/
746 			/* sets the prompt to ':' and moves to the next char.	*/
747 
748 			if (macroDef != NULL && *macroDef == ':' && iMacro)
749 			{
750 			    if (TxTkConsole)
751 				TxSetPrompt(':');
752 			    else
753 			    {
754 				TxPrintf("\b\b: ");
755 				TxFlushOut();
756 			    }
757 			    memmove(macroDef, macroDef + 1, strlen(macroDef + 1) + 1);
758 			}
759 
760 			macroDef = MacroSubstitute(macroDef, "%W", Tk_PathName(tkwind));
761 
762 			if (macroDef == NULL)
763 			{
764 			    if (keysym != XK_Return)
765 			    {
766 				char *vis = MacroName(keywstate);
767 				TxError("Unknown macro or short command: '%s'\n", vis);
768 
769 				freeMagic(vis);
770 			    }
771 			    /* Print Carriage Return & Put back Tcl/Tk prompt */
772 			    TxParseString("", NULL, NULL);
773 			}
774 			else
775 			{
776 			    int sl = strlen(macroDef);
777 
778 			    if (iMacro)
779 			    {
780 				/* Echo macro to interpreter, then redirect keys */
781 
782 				if (TxTkConsole)
783 				{
784 				    char *outstring = Tcl_Alloc(sl + 20);
785 				    sprintf(outstring, ".text insert end \"%s\"",
786 						macroDef);
787 				    Tcl_EvalEx(consoleinterp, outstring, -1, 0);
788 				    Tcl_Free(outstring);
789 				}
790 				else
791 				{
792 				    TxBuffer = Tcl_Alloc(sl + 1);
793 				    strcpy(TxBuffer, macroDef);
794 				    TxPrintf("%s", macroDef);
795 				    TxFlushOut();
796 				}
797 				TxInputRedirect = TX_INPUT_REDIRECTED;
798 			    }
799 			    else
800 			    {
801 				/* TxParseString is defined by tcltk/tclmagic.c
802 				 * and calls Tcl_Eval()
803 				 */
804 
805 				TxSetPoint(KeyPressedEvent->x,
806 					grXtransY(mw, KeyPressedEvent->y),
807 					mw->w_wid);
808 				TxParseString(macroDef, NULL, NULL);
809 			    }
810 			    freeMagic(macroDef);
811 			}
812 		    }
813 		}
814 	    }
815 	    break;
816 	case ConfigureNotify:
817 	    {
818 		XConfigureEvent *ConfigureEvent = (XConfigureEvent*) xevent;
819 		Rect	screenRect;
820 		int width, height;
821 		bool result, need_resize;
822 
823 		width = ConfigureEvent->width;
824 		height = ConfigureEvent->height;
825 
826 		entry = HashLookOnly(&grTOGLWindowTable, (char *)tkwind);
827 		mw = (entry)?(MagWindow *)HashGetValue(entry):0;
828 
829 		screenRect.r_xbot = ConfigureEvent->x;
830             	screenRect.r_xtop = ConfigureEvent->x + width;
831             	screenRect.r_ytop = glTransYs(ConfigureEvent->y);
832             	screenRect.r_ybot = glTransYs(ConfigureEvent->y + height);
833 
834 		need_resize = (screenRect.r_xbot != mw->w_screenArea.r_xbot ||
835 			screenRect.r_xtop != mw->w_screenArea.r_xtop ||
836 			screenRect.r_ybot != mw->w_screenArea.r_ybot ||
837 			screenRect.r_ytop != mw->w_screenArea.r_ytop);
838 
839 		/* Redraw the window */
840 
841 		WindReframe(mw, &screenRect, FALSE, FALSE);
842 		WindRedisplay(mw);
843 		if (need_resize) (*GrCreateBackingStorePtr)(mw);
844             }
845             break;
846 	case VisibilityNotify:
847 	    {
848 		XVisibilityEvent *VisEvent = (XVisibilityEvent*) xevent;
849 
850 		entry = HashLookOnly(&grTOGLWindowTable, (char *)tkwind);
851 		mw = (entry)?(MagWindow *)HashGetValue(entry):0;
852 
853 		switch(VisEvent->state)
854 		{
855 		    case VisibilityUnobscured:
856 			mw->w_flags &= ~WIND_OBSCURED;
857 			if (mw->w_backingStore == (ClientData)NULL)
858 			{
859 			    (*GrCreateBackingStorePtr)(mw);
860 			    if (mw->w_backingStore != (ClientData)NULL)
861 			    {
862 				WindAreaChanged(mw, &mw->w_allArea);
863 				WindUpdate();
864 			    }
865 			}
866 			break;
867 		    case VisibilityPartiallyObscured:
868 		    case VisibilityFullyObscured:
869 			mw->w_flags |= WIND_OBSCURED;
870 			break;
871 		}
872 	    }
873 	    break;
874 	case Expose:
875 	    {
876 		XExposeEvent *ExposeEvent = (XExposeEvent*) xevent;
877 		Rect screenRect;
878 
879 		entry = HashLookOnly(&grTOGLWindowTable, (char *)tkwind);
880 		mw = (entry)?(MagWindow *)HashGetValue(entry):0;
881 
882 		screenRect.r_xbot = ExposeEvent->x;
883             	screenRect.r_xtop = ExposeEvent->x+ExposeEvent->width;
884             	screenRect.r_ytop = mw->w_allArea.r_ytop-ExposeEvent->y;
885             	screenRect.r_ybot = mw->w_allArea.r_ytop -
886 				(ExposeEvent->y + ExposeEvent->height);
887 
888 		if (mw->w_backingStore != (ClientData)NULL)
889 		{
890 		    Rect surface;
891 		    (*GrLockPtr)(mw, FALSE);
892 		    (*GrGetBackingStorePtr)(mw, &screenRect);
893 		    (*GrUnlockPtr)(mw);
894 		    WindScreenToSurface(mw, &screenRect, &surface);
895 		    DBWHLRedrawPrepWindow(mw, &surface);
896 		    WindDrawBorder(mw, &screenRect);
897 		}
898 		else
899 		    WindAreaChanged(mw, &screenRect);
900 		WindUpdate();
901             }
902 	    break;
903 
904 	case MapNotify:
905 	case UnmapNotify:
906 	case DestroyNotify:	/* Do nothing */
907             break;
908 
909 	default:
910 	    TxError("Tk Event: Unknown (%d)\n", xevent->type);
911 	    TxFlush();
912 	    break;
913     }
914 }
915 
916 
917 
918 /* Set for on-screen display */
919 
920 void
toglOnScreen()921 toglOnScreen()
922 {
923     GrSetCMapPtr = GrTOGLSetCMap;
924     GrFlushPtr = GrTOGLFlush;
925 
926     grSetSPatternPtr = grtoglSetSPattern;
927     grDrawLinePtr = grtoglDrawLine;
928     grSetWMandCPtr = grtoglSetWMandC;
929     grFillRectPtr = grtoglFillRect;
930     grSetStipplePtr = grtoglSetStipple;
931     grSetLineStylePtr = grtoglSetLineStyle;
932     grFillPolygonPtr = grtoglFillPolygon;
933 }
934 
935 /*---------------------------------------------------------
936  * oglSetDisplay:
937  *	This routine sets the appropriate parameters so that
938  *	Magic will work with the X display.
939  *
940  *      Under Xlib, all input events (mouse and keyboard) are
941  *	sent to one queue which has to be polled to discover
942  *	whether there is any input or not.  To fit the Magic
943  *	interrupt-driven input model, a helper process is
944  *	spawned which reads and blocks on the event queue,
945  *	sending SIGIO's to Magic when it detects input.  The
946  *	input read in the helper process is then sent to Magic
947  *	via a communication pipe.
948  *
949  * Results:  success / fail
950  *
951  * Side Effects:	Sets up the pipe.
952  *---------------------------------------------------------
953  */
954 
955 bool
oglSetDisplay(dispType,outFileName,mouseFileName)956 oglSetDisplay (dispType, outFileName, mouseFileName)
957     char *dispType;
958     char *outFileName;
959     char *mouseFileName;
960 {
961     char *planecount;
962     char *fullname;
963     FILE* f;
964     bool execFailed = FALSE;
965     int x, y, width, height;
966 
967     WindPackageType = WIND_X_WINDOWS;  /* to be changed? */
968     TxInputRedirect = TX_INPUT_NORMAL;
969 
970     grCursorType = "color";
971     WindScrollBarWidth = 14;
972 
973     /* Set up the procedure values in the indirection table. */
974 
975     GrPixelCorrect = 0;
976 
977     GrLockPtr = GrTOGLLock;
978     GrUnlockPtr = GrTOGLUnlock;
979     GrInitPtr = GrTOGLInit;
980     GrClosePtr = GrTOGLClose;
981     GrSetCMapPtr = GrTOGLSetCMap;
982 
983     GrEnableTabletPtr = GrTOGLEnableTablet;
984     GrDisableTabletPtr = GrTOGLDisableTablet;
985     GrSetCursorPtr = GrTOGLSetCursor;
986     GrTextSizePtr = GrTOGLTextSize;
987     GrDrawGlyphPtr = GrTOGLDrawGlyph;
988     GrReadPixelPtr = GrTOGLReadPixel;
989     GrFlushPtr = GrTOGLFlush;
990 
991     GrCreateWindowPtr = GrTOGLCreate;
992     GrDeleteWindowPtr = GrTOGLDelete;
993     GrConfigureWindowPtr = GrTOGLConfigure;
994     GrOverWindowPtr = GrTOGLRaise;
995     GrUnderWindowPtr = GrTOGLLower;
996     GrUpdateIconPtr = GrTOGLIconUpdate;
997     GrEventPendingPtr = GrTOGLEventPending;
998     GrWindowIdPtr = GrTOGLWindowId;
999     GrWindowNamePtr = GrTkWindowName;		/* from grTkCommon.c */
1000     GrGetCursorPosPtr = grtoglGetCursorPos;
1001     GrGetCursorRootPosPtr = grtoglGetCursorRootPos;
1002 
1003     /* local indirections */
1004     grSetSPatternPtr = grtoglSetSPattern;
1005     grPutTextPtr = grtoglPutText;
1006 #ifdef VECTOR_FONTS
1007     grFontTextPtr = grtoglFontText;
1008 #endif
1009     grDefineCursorPtr = grTkDefineCursor;
1010     grFreeCursorPtr = grTkFreeCursors;
1011     GrBitBltPtr = GrTOGLBitBlt;
1012     grDrawGridPtr = grtoglDrawGrid;
1013     grDrawLinePtr = grtoglDrawLine;
1014     grSetWMandCPtr = grtoglSetWMandC;
1015     grFillRectPtr = grtoglFillRect;
1016     grSetStipplePtr = grtoglSetStipple;
1017     grSetLineStylePtr = grtoglSetLineStyle;
1018     grSetCharSizePtr = grtoglSetCharSize;
1019     grFillPolygonPtr = grtoglFillPolygon;
1020 
1021 #ifdef X11_BACKING_STORE
1022     GrFreeBackingStorePtr = grtkFreeBackingStore;
1023     GrCreateBackingStorePtr = grtkCreateBackingStore;
1024     GrGetBackingStorePtr = grtkGetBackingStore;
1025     GrPutBackingStorePtr = grtkPutBackingStore;
1026     GrScrollBackingStorePtr = grtkScrollBackingStore;
1027 #else
1028     GrFreeBackingStorePtr = grtoglFreeBackingStore;
1029     GrCreateBackingStorePtr = grtoglCreateBackingStore;
1030     GrGetBackingStorePtr = grtoglGetBackingStore;
1031     GrPutBackingStorePtr = grtoglPutBackingStore;
1032     GrScrollBackingStorePtr = grtoglScrollBackingStore;
1033 #endif
1034 
1035     if (execFailed) {
1036 	TxError("Execution failed!\n");
1037 	return FALSE;
1038     }
1039 
1040     if(!GrTOGLInit()){
1041 	return FALSE;
1042     };
1043 
1044     Tk_GetVRootGeometry(Tk_MainWindow(magicinterp), &x, &y, &width, &height);
1045     GrScreenRect.r_xbot = x;
1046     GrScreenRect.r_ybot = y;
1047     GrScreenRect.r_xtop = width + x;
1048     GrScreenRect.r_ytop = height + y;
1049 
1050     return Tk_MainWindow(magicinterp) ? TRUE : FALSE;
1051 }
1052 
1053 extern void MakeWindowCommand();
1054 
1055 /*
1056  * ----------------------------------------------------------------------------
1057  *
1058  * GrTOGLCreate --
1059  *      Create a new window under the X window system.
1060  *	Bind X window to Magic Window w.
1061  *
1062  * Results:
1063  *	Success/Fail
1064  *
1065  * Side Effects:
1066  *      Window created, window ID send to Xhelper.
1067  *
1068  * ----------------------------------------------------------------------------
1069  */
1070 
1071 bool
GrTOGLCreate(w,name)1072 GrTOGLCreate(w, name)
1073     MagWindow *w;
1074     char *name;
1075 {
1076     Tk_Window tkwind, tktop;
1077     Window wind;
1078     static int WindowNumber = 0;
1079     HashEntry	*entry;
1080     char	*windowplace;
1081     char	windowname[10];
1082     int		x      = w->w_frameArea.r_xbot;
1083     int		y      = glTransYs(w->w_frameArea.r_ytop);
1084     int		width  = w->w_frameArea.r_xtop - w->w_frameArea.r_xbot;
1085     int		height = w->w_frameArea.r_ytop - w->w_frameArea.r_ybot;
1086     unsigned long        attribmask = CWBackPixel | CWBorderPixel | CWColormap;
1087     XSetWindowAttributes grAttributes;
1088 
1089     WindSeparateRedisplay(w);
1090 
1091     sprintf(windowname, ".magic%d", WindowNumber + 1);
1092     if (windowplace = XGetDefault(grXdpy, "magic", windowname))
1093     {
1094 	XParseGeometry(windowplace,&x,&y,
1095 		(unsigned int *)&width,(unsigned int *)&height);
1096 	w->w_frameArea.r_xbot = x;
1097 	w->w_frameArea.r_xtop = x+width;
1098 	w->w_frameArea.r_ytop = glTransYs(y);
1099 	w->w_frameArea.r_ybot = glTransYs(y+height);
1100 	WindReframe(w,&(w->w_frameArea),FALSE,FALSE);
1101     }
1102 
1103     grAttributes.colormap = XCreateColormap(grXdpy, RootWindow(grXdpy, grXscrn),
1104 		grVisualInfo->visual, AllocNone);
1105     grAttributes.background_pixel = WhitePixel(grXdpy,grXscrn);
1106     grAttributes.border_pixel = BlackPixel(grXdpy,grXscrn);
1107 
1108     if (tktop = Tk_MainWindow(magicinterp))
1109     {
1110         if (!WindowNumber)
1111 	{
1112 	    /* To do: deal with grVisualInfo---destroy and recreate top	*/
1113 	    /* frame if necessary					*/
1114 
1115 	    if (Tk_WindowId(tktop) == 0)
1116 	    {
1117 		Tk_SetWindowVisual(tktop, grVisualInfo->visual,
1118 			toglCurrent.depth, grAttributes.colormap);
1119 	    }
1120 	    else
1121 	    {
1122 	        /* The Top-level window has already been mapped.  We can't mess */
1123 	        /* with it's visual.  If the title is "wish", we'll assume that */
1124 	        /* nobody else is claiming it, and unmap it.		    	*/
1125 
1126 	        if (!strcmp(Tk_Name(tktop), "wish")) Tk_UnmapWindow(tktop);
1127 	    }
1128 	}
1129     }
1130     else
1131         return 0;  /* failure */
1132 
1133     /* Last parameter "" indicates a top-level window in the space of	*/
1134     /* the parent.							*/
1135 
1136     if (name == NULL)
1137         tkwind = Tk_CreateWindowFromPath(magicinterp, tktop, windowname, "");
1138     else
1139         tkwind = Tk_CreateWindowFromPath(magicinterp, tktop, name, NULL);
1140 
1141     /* TxError("Creating window named \"%s\", tkwind = 0x%x\n",
1142 		windowname, tkwind); TxFlush(); */
1143 
1144     if (tkwind != 0)
1145     {
1146 	bool result;
1147 
1148 	GrTOGLFlush();
1149 
1150 	toglCurrent.window = tkwind;
1151 	toglCurrent.mw = w;
1152 
1153 	w->w_grdata = (ClientData) tkwind;
1154 
1155 	entry = HashFind(&grTOGLWindowTable, (char *)tkwind);
1156 	HashSetValue(entry,w);
1157 
1158 	/* ensure that the visual is what we wanted, if possible to change */
1159 
1160 	Tk_SetWindowVisual(tkwind, grVisualInfo->visual, toglCurrent.depth,
1161 			grAttributes.colormap);
1162 
1163 	/* map the window, if necessary */
1164 
1165 	Tk_MapWindow(tkwind);
1166 
1167 	/* use x, y, width, height to size and position the window */
1168 
1169 	Tk_GeometryRequest(tkwind, width, height);
1170 	/* Tk_MoveResizeWindow(tkwind, x, y, width, height); */
1171 
1172 	wind = Tk_WindowId(tkwind);
1173 	toglCurrent.windowid = wind;
1174 	glXMakeCurrent(grXdpy, (GLXDrawable)wind, grXcontext);
1175 
1176         Tk_DefineCursor(tkwind, toglCurrent.cursor);
1177 	GrTOGLIconUpdate(w, w->w_caption);
1178 
1179 	WindowNumber++;
1180 
1181 	/* execute all Tk events up to current */
1182 
1183 	while (Tcl_DoOneEvent(TCL_DONT_WAIT) != 0);
1184 
1185 	/* set up Tk event handler to start processing */
1186 
1187 	Tk_CreateEventHandler(tkwind, ExposureMask | StructureNotifyMask
1188 		| ButtonPressMask | KeyPressMask | VisibilityChangeMask,
1189 		 (Tk_EventProc *)TOGLEventProc, (ClientData) tkwind);
1190 
1191 	/* set up commands to be passed expressly to this window */
1192 
1193 	MakeWindowCommand((name == NULL) ? windowname : name, w);
1194 
1195 	return (WindowNumber == 1) ? grtoglLoadFont() : 1;
1196     }
1197     else
1198     {
1199 	TxError("Could not open new Tk window\n");
1200     }
1201 
1202     return 0;
1203 }
1204 
1205 /*
1206  * ----------------------------------------------------------------------------
1207  *
1208  * GrTOGLDelete --
1209  *      Destroy a Tk/OpenGL window.
1210  *
1211  * Results:
1212  *	None.
1213  *
1214  * Side Effects:
1215  *      Window destroyed.
1216  *
1217  * ----------------------------------------------------------------------------
1218  */
1219 
1220 void
GrTOGLDelete(w)1221 GrTOGLDelete(w)
1222     MagWindow *w;
1223 {
1224     Tk_Window xw;
1225     HashEntry	*entry;
1226 
1227     xw = (Tk_Window) w->w_grdata;
1228     entry = HashLookOnly(&grTOGLWindowTable, (char *)xw);
1229     HashSetValue(entry,NULL);
1230 
1231     Tcl_DeleteCommand(magicinterp, Tk_PathName(xw));
1232     Tk_DestroyWindow(xw);
1233 }
1234 
1235 /*
1236  * ----------------------------------------------------------------------------
1237  *
1238  * GrTOGLConfigure --
1239  *      Resize/ Move an existing X window.
1240  *
1241  * Results:
1242  *      None.
1243  *
1244  * Side Effects:
1245  *      Window reconfigured to w->w_frameArea.
1246  *
1247  * ----------------------------------------------------------------------------
1248  */
1249 
1250 void
GrTOGLConfigure(w)1251 GrTOGLConfigure(w)
1252     MagWindow *w;
1253 {
1254     if (w->w_flags & WIND_OFFSCREEN) return;
1255 
1256     Tk_MoveResizeWindow((Tk_Window)w->w_grdata,
1257 	    w->w_frameArea.r_xbot, glTransYs(w->w_frameArea.r_ytop),
1258 		w->w_frameArea.r_xtop - w->w_frameArea.r_xbot,
1259 		    w->w_frameArea.r_ytop - w->w_frameArea.r_ybot);
1260 }
1261 
1262 /*
1263  * ----------------------------------------------------------------------------
1264  *
1265  * GrTOGLRaise --
1266  *      Raise a window to the top of the screen such that nothing
1267  *	obscures it.
1268  *
1269  * Results:
1270  *      None.
1271  *
1272  * Side Effects:
1273  *      Window raised.
1274  *
1275  * ----------------------------------------------------------------------------
1276  */
1277 
1278 void
GrTOGLRaise(w)1279 GrTOGLRaise(w)
1280     MagWindow *w;
1281 {
1282     Tk_Window tkwind;
1283 
1284     if (w->w_flags & WIND_OFFSCREEN) return;
1285 
1286     tkwind = (Tk_Window)w->w_grdata;
1287     Tk_RestackWindow(tkwind, Above, NULL);
1288 }
1289 
1290 /*
1291  * ----------------------------------------------------------------------------
1292  *
1293  * GrTOGLLower --
1294  *      Lower a window below all other Tk windows.
1295  *	obscures it.
1296  *
1297  * Results:
1298  *      None.
1299  *
1300  * Side Effects:
1301  *      Window lowered.
1302  *
1303  * ----------------------------------------------------------------------------
1304  */
1305 
1306 void
GrTOGLLower(w)1307 GrTOGLLower(w)
1308     MagWindow *w;
1309 {
1310     Tk_Window tkwind;
1311 
1312     if (w->w_flags & WIND_OFFSCREEN) return;
1313 
1314     tkwind = (Tk_Window)w->w_grdata;
1315     Tk_RestackWindow(tkwind, Below, NULL);
1316 }
1317 
1318 
1319 /*
1320  * ----------------------------------------------------------------------------
1321  *
1322  * GrTOGLLock --
1323  *      Lock a window and set global variables "toglCurrent.window"
1324  *	and "toglCurrent.mw" to reference the locked window.
1325  *
1326  * Results:
1327  *	None.
1328  *
1329  * Side Effects:
1330  *      Window locked.
1331  *
1332  * ----------------------------------------------------------------------------
1333  */
1334 
1335 #ifdef CAIRO_OFFSCREEN_RENDER
1336 extern void GrTCairoLock();
1337 extern void TCairoOffScreen();
1338 #endif
1339 
1340 void
GrTOGLLock(w,flag)1341 GrTOGLLock(w, flag)
1342     MagWindow *w;
1343     bool flag;
1344 {
1345     Window wind;
1346 
1347 #ifdef CAIRO_OFFSCREEN_RENDER
1348     /* Use Cairo graphics for off-screen rendering */
1349 
1350     if ((w != GR_LOCK_SCREEN) && (w->w_flags & WIND_OFFSCREEN))
1351     {
1352 	GrTCairoLock(w, flag);
1353 	TCairoOffScreen();
1354 	return;
1355     }
1356 #endif
1357 
1358     grSimpleLock(w, flag);
1359     if ( w != GR_LOCK_SCREEN )
1360     {
1361 	toglCurrent.mw = w;
1362 
1363 	if (w->w_flags & WIND_OFFSCREEN)
1364 	{
1365 	    toglCurrent.window = (Tk_Window) NULL;
1366 	    toglCurrent.windowid = (Pixmap) w->w_grdata;
1367 	}
1368 	else
1369 	{
1370 	    toglCurrent.window = (Tk_Window) w->w_grdata;
1371 	    toglCurrent.windowid = Tk_WindowId(toglCurrent.window);
1372 	}
1373 
1374 	toglSetProjection(w->w_allArea.r_xbot, w->w_allArea.r_ybot,
1375 			w->w_allArea.r_xtop - w->w_allArea.r_xbot,
1376 			w->w_allArea.r_ytop - w->w_allArea.r_ybot);
1377     }
1378 }
1379 
1380 /*
1381  * ----------------------------------------------------------------------------
1382  *
1383  * GrTOGLUnlock --
1384  *      Unlock a window, flushing stuff out to the display.
1385  *
1386  * Results:
1387  *	None.
1388  *
1389  * Side Effects:
1390  *      Window unlocked.
1391  *	Display update.
1392  *
1393  * ----------------------------------------------------------------------------
1394  */
1395 
1396 void
GrTOGLUnlock(w)1397 GrTOGLUnlock(w)
1398     MagWindow *w;
1399 {
1400 
1401 #ifdef CAIRO_OFFSCREEN_RENDER
1402     /* Use Cairo graphics for off-screen rendering */
1403 
1404     if ((w != GR_LOCK_SCREEN) && (w->w_flags & WIND_OFFSCREEN))
1405     {
1406 	GrTCairoUnlock(w);
1407 	toglOnScreen();
1408 	return;
1409     }
1410 #endif
1411 
1412     GrTOGLFlush();
1413 
1414     if ((w != GR_LOCK_SCREEN) && (w->w_flags & WIND_OFFSCREEN))
1415     {
1416 	GC grXcopyGC;
1417 	XGCValues gcValues;
1418 	unsigned char *pdata, *tdata;
1419 	int i, j;
1420 
1421         Window root_return;
1422         int x_return, y_return;
1423         unsigned int pbwidth, pbheight, wborder, depth;
1424 
1425 	XGetGeometry(grXdpy, (Drawable)toglCurrent.windowid, &root_return,
1426 		&x_return, &y_return, &pbwidth, &pbheight, &wborder, &depth);
1427 
1428 	pdata = (unsigned char *)mallocMagic((pbwidth * pbheight * 3)
1429 			* sizeof(unsigned int));
1430 
1431 	/* In offscreen-rendering mode, copy Pbuffer back to window */
1432 	glReadBuffer(GL_FRONT);
1433 	glReadPixels(0, 0, pbwidth, pbheight, GL_RGB, GL_UNSIGNED_BYTE, pdata);
1434 
1435         gcValues.graphics_exposures = FALSE;
1436         grXcopyGC = XCreateGC(grXdpy, (Drawable)toglCurrent.windowid,
1437 		GCGraphicsExposures, &gcValues);
1438 
1439 	/* This is very slow, but the only way I've found to copy data	*/
1440 	/* from a Pbuffer into a Pixmap.  It is only used to make the	*/
1441 	/* icon images for the toolbar, so it does not need to be	*/
1442 	/* efficient.							*/
1443 
1444 	tdata = pdata;
1445 	for (i = 0; i < pbwidth; i++) {
1446 	    for (j = 0; j < pbheight; j++)
1447 	    {
1448 		unsigned long pcolor;
1449 		pcolor = *tdata++;
1450 		pcolor <<= 8;
1451 		pcolor |= *tdata++;
1452 		pcolor <<= 8;
1453 		pcolor |= *tdata++;
1454 		XSetForeground(grXdpy, grXcopyGC, pcolor);
1455 		XDrawPoint(grXdpy, (Drawable)toglCurrent.windowid, grXcopyGC,
1456 			pbwidth - i - 1, j);
1457 	    }
1458 	}
1459 
1460 	freeMagic(pdata);
1461 	XFreeGC(grXdpy, grXcopyGC);
1462     }
1463     grSimpleUnlock(w);
1464 }
1465 
1466 
1467 
1468 /*
1469  *-------------------------------------------------------------------------
1470  * GrTOGLEventPending --
1471  *	check for pending graphics events.
1472  *      Here we use the X11 check for window events, because Tcl/Tk doesn't
1473  *      allows peeking into its event queue without executing whatever is
1474  *      in the queue.
1475  *
1476  * Results:
1477  *	TRUE if an event is waiting in the event queue.
1478  *
1479  * Side effects:
1480  *	None, hopefully (put back the event!)
1481  *
1482  *-------------------------------------------------------------------------
1483  */
1484 
1485 bool
GrTOGLEventPending()1486 GrTOGLEventPending()
1487 {
1488    Window       wind = toglCurrent.windowid;
1489    XEvent       genEvent;
1490    bool         retval;
1491 
1492    XSync(grXdpy, FALSE); /* Necessary, or it won't catch mouse/key events */
1493    retval = XCheckWindowEvent(grXdpy, wind, ExposureMask
1494                 | StructureNotifyMask | ButtonPressMask
1495                 | KeyPressMask, &genEvent);
1496    if (retval) XPutBackEvent(grXdpy, &genEvent);
1497    return retval;
1498 }
1499 
1500 /*
1501  *-------------------------------------------------------------------------
1502  *
1503  * GrTOGLIconUpdate -- updates the icon text with the window script
1504  *
1505  * Results: none
1506  *
1507  * Side Effects: changes the icon text
1508  *
1509  *-------------------------------------------------------------------------
1510  */
1511 
1512 void
GrTOGLIconUpdate(w,text)1513 GrTOGLIconUpdate(w,text)		/* See Blt code */
1514     MagWindow	*w;
1515     char	*text;
1516 {
1517     Tk_Window	tkwind;
1518     Window	wind;
1519     XClassHint	class;
1520     char	*brack;
1521 
1522     if (w->w_flags & WIND_OFFSCREEN) return;
1523 
1524     tkwind = (Tk_Window)(w->w_grdata);
1525     if (tkwind == NULL) {
1526 	tkwind = Tk_MainWindow(magicinterp);
1527         if (tkwind == NULL) return;
1528     }
1529     wind = Tk_WindowId(tkwind);
1530     if (wind == 0) return;
1531 
1532     class.res_name = "magic";
1533     class.res_class = "magic";
1534 
1535     XSetClassHint( grXdpy, wind, &class);
1536     if (text)
1537     {
1538 	if (brack = strchr(text,'['))
1539 	{
1540 	    brack--;
1541 	    *brack = 0;
1542             XSetIconName(grXdpy,wind,text);
1543 	    XStoreName(grXdpy,wind,text);
1544      	    *brack = ' ';
1545 	    return;
1546 	}
1547 	if (brack = strrchr(text,' ')) text = brack+1;
1548 	XSetIconName(grXdpy,wind,text);
1549 	XStoreName(grXdpy,wind,text);
1550     }
1551 }
1552 
1553 /*
1554  *-------------------------------------------------------------------------
1555  * GrTOGLWindowId --
1556  *	Get magic's ID number from the indicated MagWindow structure
1557  *
1558  * Results:
1559  *	The window ID number.
1560  *
1561  * Side effects:
1562  *	None.
1563  *
1564  *-------------------------------------------------------------------------
1565  */
1566 
1567 int
GrTOGLWindowId(tkname)1568 GrTOGLWindowId(tkname)
1569     char *tkname;
1570 {
1571     Tk_Window tkwind;
1572     MagWindow *mw;
1573     HashEntry *entry;
1574     int id = 0;
1575 
1576     tkwind = Tk_NameToWindow(magicinterp, tkname, Tk_MainWindow(magicinterp));
1577     if (tkwind != NULL)
1578     {
1579 	entry = HashLookOnly(&grTOGLWindowTable, (char *)tkwind);
1580 	mw = (entry) ? (MagWindow *)HashGetValue(entry) : 0;
1581 	if (mw) id = mw->w_wid;
1582     }
1583     return id;
1584 }
1585