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