1 /*
2  * tkTest.c --
3  *
4  *	This file contains C command procedures for a bunch of additional
5  *	Tcl commands that are used for testing out Tcl's C interfaces.
6  *	These commands are not normally included in Tcl applications;
7  *	they're only used for testing.
8  *
9  * Copyright (c) 1993-1994 The Regents of the University of California.
10  * Copyright (c) 1994-1996 Sun Microsystems, Inc.
11  *
12  * See the file "license.terms" for information on usage and redistribution
13  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
14  *
15  * SCCS: @(#) tkTest.c 1.35 96/10/03 11:22:26
16  */
17 
18 #include "tkInt.h"
19 
20 #ifdef WIN_TCL
21 #include "tkWinInt.h"
22 #endif
23 
24 /*
25  * The table below describes events and is used by the "testevent"
26  * command.
27  */
28 
29 typedef struct {
30     char *name;			/* Name of event. */
31     int type;			/* Event type for X, such as
32 				 * ButtonPress. */
33 } EventInfo;
34 
35 static EventInfo eventArray[] = {
36     {"Motion",		MotionNotify},
37     {"Button",		ButtonPress},
38     {"ButtonPress",	ButtonPress},
39     {"ButtonRelease",	ButtonRelease},
40     {"Colormap",	ColormapNotify},
41     {"Enter",		EnterNotify},
42     {"Leave",		LeaveNotify},
43     {"Expose",		Expose},
44     {"FocusIn",		FocusIn},
45     {"FocusOut",	FocusOut},
46     {"Keymap",		KeymapNotify},
47     {"Key",		KeyPress},
48     {"KeyPress",	KeyPress},
49     {"KeyRelease",	KeyRelease},
50     {"Property",	PropertyNotify},
51     {"ResizeRequest",	ResizeRequest},
52     {"Circulate",	CirculateNotify},
53     {"Configure",	ConfigureNotify},
54     {"Destroy",		DestroyNotify},
55     {"Gravity",		GravityNotify},
56     {"Map",		MapNotify},
57     {"Reparent",	ReparentNotify},
58     {"Unmap",		UnmapNotify},
59     {"Visibility",	VisibilityNotify},
60     {"CirculateRequest",CirculateRequest},
61     {"ConfigureRequest",ConfigureRequest},
62     {"MapRequest",	MapRequest},
63     {(char *) NULL,	0}
64 };
65 
66 /*
67  * The defines and table below are used to classify events into
68  * various groups.  The reason for this is that logically identical
69  * fields (e.g. "state") appear at different places in different
70  * types of events.  The classification masks can be used to figure
71  * out quickly where to extract information from events.
72  */
73 
74 #define KEY_BUTTON_MOTION	0x1
75 #define CROSSING		0x2
76 #define FOCUS			0x4
77 #define EXPOSE			0x8
78 #define VISIBILITY		0x10
79 #define CREATE			0x20
80 #define MAP			0x40
81 #define REPARENT		0x80
82 #define CONFIG			0x100
83 #define CONFIG_REQ		0x200
84 #define RESIZE_REQ		0x400
85 #define GRAVITY			0x800
86 #define PROP			0x1000
87 #define SEL_CLEAR		0x2000
88 #define SEL_REQ			0x4000
89 #define SEL_NOTIFY		0x8000
90 #define COLORMAP		0x10000
91 #define MAPPING			0x20000
92 
93 static int flagArray[LASTEvent] = {
94    /* Not used */		0,
95    /* Not used */		0,
96    /* KeyPress */		KEY_BUTTON_MOTION,
97    /* KeyRelease */		KEY_BUTTON_MOTION,
98    /* ButtonPress */		KEY_BUTTON_MOTION,
99    /* ButtonRelease */		KEY_BUTTON_MOTION,
100    /* MotionNotify */		KEY_BUTTON_MOTION,
101    /* EnterNotify */		CROSSING,
102    /* LeaveNotify */		CROSSING,
103    /* FocusIn */		FOCUS,
104    /* FocusOut */		FOCUS,
105    /* KeymapNotify */		0,
106    /* Expose */			EXPOSE,
107    /* GraphicsExpose */		EXPOSE,
108    /* NoExpose */		0,
109    /* VisibilityNotify */	VISIBILITY,
110    /* CreateNotify */		CREATE,
111    /* DestroyNotify */		0,
112    /* UnmapNotify */		0,
113    /* MapNotify */		MAP,
114    /* MapRequest */		0,
115    /* ReparentNotify */		REPARENT,
116    /* ConfigureNotify */	CONFIG,
117    /* ConfigureRequest */	CONFIG_REQ,
118    /* GravityNotify */		0,
119    /* ResizeRequest */		RESIZE_REQ,
120    /* CirculateNotify */	0,
121    /* CirculateRequest */	0,
122    /* PropertyNotify */		PROP,
123    /* SelectionClear */		SEL_CLEAR,
124    /* SelectionRequest */	SEL_REQ,
125    /* SelectionNotify */	SEL_NOTIFY,
126    /* ColormapNotify */		COLORMAP,
127    /* ClientMessage */		0,
128    /* MappingNotify */		MAPPING
129 };
130 
131 /*
132  * The following data structure represents the master for a test
133  * image:
134  */
135 
136 typedef struct TImageMaster {
137     Tk_ImageMaster master;	/* Tk's token for image master. */
138     Tcl_Interp *interp;		/* Interpreter for application. */
139     int width, height;		/* Dimensions of image. */
140     char *imageName;		/* Name of image (malloc-ed). */
141     char *varName;		/* Name of variable in which to log
142 				 * events for image (malloc-ed). */
143 } TImageMaster;
144 
145 /*
146  * The following data structure represents a particular use of a
147  * particular test image.
148  */
149 
150 typedef struct TImageInstance {
151     TImageMaster *masterPtr;	/* Pointer to master for image. */
152     XColor *fg;			/* Foreground color for drawing in image. */
153     GC gc;			/* Graphics context for drawing in image. */
154 } TImageInstance;
155 
156 /*
157  * The type record for test images:
158  */
159 
160 static int		ImageCreate _ANSI_ARGS_((Tcl_Interp *interp,
161 			    char *name, int argc, char **argv,
162 			    Tk_ImageType *typePtr, Tk_ImageMaster master,
163 			    ClientData *clientDataPtr));
164 static ClientData	ImageGet _ANSI_ARGS_((Tk_Window tkwin,
165 			    ClientData clientData));
166 static void		ImageDisplay _ANSI_ARGS_((ClientData clientData,
167 			    Display *display, Drawable drawable,
168 			    int imageX, int imageY, int width,
169 			    int height, int drawableX,
170 			    int drawableY));
171 static void		ImageFree _ANSI_ARGS_((ClientData clientData,
172 			    Display *display));
173 static void		ImageDelete _ANSI_ARGS_((ClientData clientData));
174 
175 static Tk_ImageType imageType = {
176     "test",			/* name */
177     ImageCreate,		/* createProc */
178     ImageGet,			/* getProc */
179     ImageDisplay,		/* displayProc */
180     ImageFree,			/* freeProc */
181     ImageDelete,		/* deleteProc */
182     (Tk_ImageType *) NULL	/* nextPtr */
183 };
184 
185 /*
186  * One of the following structures describes each of the interpreters
187  * created by the "testnewapp" command.  This information is used by
188  * the "testdeleteinterps" command to destroy all of those interpreters.
189  */
190 
191 typedef struct NewApp {
192     Tcl_Interp *interp;		/* Token for interpreter. */
193     struct NewApp *nextPtr;	/* Next in list of new interpreters. */
194 } NewApp;
195 
196 static NewApp *newAppPtr = NULL;
197 				/* First in list of all new interpreters. */
198 
199 /*
200  * Declaration for the square widget's class command procedure:
201  */
202 
203 extern int SquareCmd _ANSI_ARGS_((ClientData clientData,
204 	Tcl_Interp *interp, int argc, char *argv[]));
205 
206 /*
207  * Forward declarations for procedures defined later in this file:
208  */
209 
210 int			Tktest_Init _ANSI_ARGS_((Tcl_Interp *interp));
211 static int		ImageCmd _ANSI_ARGS_((ClientData dummy,
212 			    Tcl_Interp *interp, int argc, char **argv));
213 #ifdef WIN_TCL
214 static int		TestclipboardCmd _ANSI_ARGS_((ClientData dummy,
215 			    Tcl_Interp *interp, int argc, char **argv));
216 #endif
217 static int		TestdeleteappsCmd _ANSI_ARGS_((ClientData dummy,
218 			    Tcl_Interp *interp, int argc, char **argv));
219 static int		TesteventCmd _ANSI_ARGS_((ClientData dummy,
220 			    Tcl_Interp *interp, int argc, char **argv));
221 static int		TestmakeexistCmd _ANSI_ARGS_((ClientData dummy,
222 			    Tcl_Interp *interp, int argc, char **argv));
223 static int		TestsendCmd _ANSI_ARGS_((ClientData dummy,
224 			    Tcl_Interp *interp, int argc, char **argv));
225 
226 /*
227  *----------------------------------------------------------------------
228  *
229  * Tktest_Init --
230  *
231  *	This procedure performs intialization for the Tk test
232  *	suite exensions.
233  *
234  * Results:
235  *	Returns a standard Tcl completion code, and leaves an error
236  *	message in interp->result if an error occurs.
237  *
238  * Side effects:
239  *	Creates several test commands.
240  *
241  *----------------------------------------------------------------------
242  */
243 
244 int
Tktest_Init(interp)245 Tktest_Init(interp)
246     Tcl_Interp *interp;		/* Interpreter for application. */
247 {
248     static int initialized = 0;
249 
250     /*
251      * Create additional commands for testing Tk.
252      */
253 
254     if (Tcl_PkgProvide(interp, "Tktest", TK_VERSION) == TCL_ERROR) {
255         return TCL_ERROR;
256     }
257 
258     Tcl_CreateCommand(interp, "square", SquareCmd,
259 	    (ClientData) Tk_MainWindow(interp), (Tcl_CmdDeleteProc *) NULL);
260 #ifdef WIN_TCL
261     Tcl_CreateCommand(interp, "testclipboard", TestclipboardCmd,
262 	    (ClientData) Tk_MainWindow(interp), (Tcl_CmdDeleteProc *) NULL);
263 #endif
264     Tcl_CreateCommand(interp, "testdeleteapps", TestdeleteappsCmd,
265 	    (ClientData) Tk_MainWindow(interp), (Tcl_CmdDeleteProc *) NULL);
266     Tcl_CreateCommand(interp, "testevent", TesteventCmd,
267 	    (ClientData) Tk_MainWindow(interp), (Tcl_CmdDeleteProc *) NULL);
268     Tcl_CreateCommand(interp, "testmakeexist", TestmakeexistCmd,
269 	    (ClientData) Tk_MainWindow(interp), (Tcl_CmdDeleteProc *) NULL);
270     Tcl_CreateCommand(interp, "testsend", TestsendCmd,
271 	    (ClientData) Tk_MainWindow(interp), (Tcl_CmdDeleteProc *) NULL);
272 
273     /*
274      * Create test image type.
275      */
276 
277     if (!initialized) {
278 	initialized = 1;
279 	Tk_CreateImageType(&imageType);
280     }
281     return TCL_OK;
282 }
283 
284 /*
285  *----------------------------------------------------------------------
286  *
287  * TestclipboardCmd --
288  *
289  *	This procedure implements the testclipboard command. It provides
290  *	a way to determine the actual contents of the Windows clipboard.
291  *
292  * Results:
293  *	A standard Tcl result.
294  *
295  * Side effects:
296  *	None.
297  *
298  *----------------------------------------------------------------------
299  */
300 
301 #ifdef WIN_TCL
302 static int
TestclipboardCmd(clientData,interp,argc,argv)303 TestclipboardCmd(clientData, interp, argc, argv)
304     ClientData clientData;		/* Main window for application. */
305     Tcl_Interp *interp;			/* Current interpreter. */
306     int argc;				/* Number of arguments. */
307     char **argv;			/* Argument strings. */
308 {
309     TkWindow *winPtr = (TkWindow *) clientData;
310     HGLOBAL handle;
311     char *data;
312 
313     if (OpenClipboard(NULL)) {
314 	handle = GetClipboardData(CF_TEXT);
315 	if (handle != NULL) {
316 	    data = GlobalLock(handle);
317 	    Tcl_AppendResult(interp, data, (char *) NULL);
318 	    GlobalUnlock(handle);
319 	}
320 	CloseClipboard();
321     }
322     return TCL_OK;
323 }
324 #endif
325 
326 /*
327  *----------------------------------------------------------------------
328  *
329  * TestdeleteappsCmd --
330  *
331  *	This procedure implements the "testdeleteapps" command.  It cleans
332  *	up all the interpreters left behind by the "testnewapp" command.
333  *
334  * Results:
335  *	A standard Tcl result.
336  *
337  * Side effects:
338  *	All the intepreters created by previous calls to "testnewapp"
339  *	get deleted.
340  *
341  *----------------------------------------------------------------------
342  */
343 
344 	/* ARGSUSED */
345 static int
TestdeleteappsCmd(clientData,interp,argc,argv)346 TestdeleteappsCmd(clientData, interp, argc, argv)
347     ClientData clientData;		/* Main window for application. */
348     Tcl_Interp *interp;			/* Current interpreter. */
349     int argc;				/* Number of arguments. */
350     char **argv;			/* Argument strings. */
351 {
352     NewApp *nextPtr;
353 
354     while (newAppPtr != NULL) {
355 	nextPtr = newAppPtr->nextPtr;
356 	Tcl_DeleteInterp(newAppPtr->interp);
357 	ckfree((char *) newAppPtr);
358 	newAppPtr = nextPtr;
359     }
360     return TCL_OK;
361 }
362 
363 /*
364  *----------------------------------------------------------------------
365  *
366  * TesteventCmd --
367  *
368  *	This procedure implements the "testevent" command.  It allows
369  *	events to be generated on the fly, for testing event-handling.
370  *
371  * Results:
372  *	A standard Tcl result.
373  *
374  * Side effects:
375  *	Creates and handles events.
376  *
377  *----------------------------------------------------------------------
378  */
379 
380 	/* ARGSUSED */
381 static int
TesteventCmd(clientData,interp,argc,argv)382 TesteventCmd(clientData, interp, argc, argv)
383     ClientData clientData;		/* Main window for application. */
384     Tcl_Interp *interp;			/* Current interpreter. */
385     int argc;				/* Number of arguments. */
386     char **argv;			/* Argument strings. */
387 {
388     Tk_Window main = (Tk_Window) clientData;
389     Tk_Window tkwin, tkwin2;
390     XEvent event;
391     EventInfo *eiPtr;
392     char *field, *value;
393     int i, number, flags;
394     KeySym keysym;
395 
396     if ((argc < 3) || !(argc & 1)) {
397 	Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
398 		" window type ?field value field value ...?\"",
399 		(char *) NULL);
400 	return TCL_ERROR;
401     }
402     tkwin = Tk_NameToWindow(interp, argv[1], main);
403     if (tkwin == NULL) {
404 	return TCL_ERROR;
405     }
406 
407     /*
408      * Get the type of the event.
409      */
410 
411     memset((VOID *) &event, 0, sizeof(event));
412     for (eiPtr = eventArray; ; eiPtr++) {
413 	if (eiPtr->name == NULL) {
414 	    Tcl_AppendResult(interp, "bad event type \"", argv[2],
415 		    "\"", (char *) NULL);
416 	    return TCL_ERROR;
417 	}
418 	if (strcmp(eiPtr->name, argv[2]) == 0) {
419 	    event.xany.type = eiPtr->type;
420 	    break;
421 	}
422     }
423 
424     /*
425      * Fill in fields that are common to all events.
426      */
427 
428     event.xany.serial = NextRequest(Tk_Display(tkwin));
429     event.xany.send_event = False;
430     event.xany.window = Tk_WindowId(tkwin);
431     event.xany.display = Tk_Display(tkwin);
432 
433     /*
434      * Process the remaining arguments to fill in additional fields
435      * of the event.
436      */
437 
438     flags = flagArray[event.xany.type];
439     for (i = 3; i < argc; i += 2) {
440 	field = argv[i];
441 	value = argv[i+1];
442 	if (strcmp(field, "-above") == 0) {
443 	    tkwin2 = Tk_NameToWindow(interp, value, main);
444 	    if (tkwin2 == NULL) {
445 		return TCL_ERROR;
446 	    }
447 	    event.xconfigure.above = Tk_WindowId(tkwin2);
448 	} else if (strcmp(field, "-borderwidth") == 0) {
449 	    if (Tcl_GetInt(interp, value, &number) != TCL_OK) {
450 		return TCL_ERROR;
451 	    }
452 	    event.xcreatewindow.border_width = number;
453 	} else if (strcmp(field, "-button") == 0) {
454 	    if (Tcl_GetInt(interp, value, &number) != TCL_OK) {
455 		return TCL_ERROR;
456 	    }
457 	    event.xbutton.button = number;
458 	} else if (strcmp(field, "-count") == 0) {
459 	    if (Tcl_GetInt(interp, value, &number) != TCL_OK) {
460 		return TCL_ERROR;
461 	    }
462 	    if (flags & EXPOSE) {
463 		event.xexpose.count = number;
464 	    } else if (flags & MAPPING) {
465 		event.xmapping.count = number;
466 	    }
467 	} else if (strcmp(field, "-detail") == 0) {
468 	    if (flags & (CROSSING|FOCUS)) {
469 		if (strcmp(value, "NotifyAncestor") == 0) {
470 		    number = NotifyAncestor;
471 		} else if (strcmp(value, "NotifyVirtual") == 0) {
472 		    number = NotifyVirtual;
473 		} else if (strcmp(value, "NotifyInferior") == 0) {
474 		    number = NotifyInferior;
475 		} else if (strcmp(value, "NotifyNonlinear") == 0) {
476 		    number = NotifyNonlinear;
477 		} else if (strcmp(value, "NotifyNonlinearVirtual") == 0) {
478 		    number = NotifyNonlinearVirtual;
479 		} else if (strcmp(value, "NotifyPointer") == 0) {
480 		    number = NotifyPointer;
481 		} else if (strcmp(value, "NotifyPointerRoot") == 0) {
482 		    number = NotifyPointerRoot;
483 		} else if (strcmp(value, "NotifyDetailNone") == 0) {
484 		    number = NotifyDetailNone;
485 		} else {
486 		    Tcl_AppendResult(interp, "bad detail \"", value, "\"",
487 			    (char *) NULL);
488 		    return TCL_ERROR;
489 		}
490 		if (flags & FOCUS) {
491 		    event.xfocus.detail = number;
492 		} else {
493 		    event.xcrossing.detail = number;
494 		}
495 	    } else if (flags & CONFIG_REQ) {
496 		if (strcmp(value, "Above") == 0) {
497 		    number = Above;
498 		} else if (strcmp(value, "Below") == 0) {
499 		    number = Below;
500 		} else if (strcmp(value, "TopIf") == 0) {
501 		    number = TopIf;
502 		} else if (strcmp(value, "BottomIf") == 0) {
503 		    number = BottomIf;
504 		} else if (strcmp(value, "Opposite") == 0) {
505 		    number = Opposite;
506 		} else {
507 		    Tcl_AppendResult(interp, "bad detail \"", value, "\"",
508 			    (char *) NULL);
509 		    return TCL_ERROR;
510 		}
511 		event.xconfigurerequest.detail = number;
512 	    }
513 	} else if (strcmp(field, "-focus") == 0) {
514 	    if (Tcl_GetInt(interp, value, &number) != TCL_OK) {
515 		return TCL_ERROR;
516 	    }
517 	    event.xcrossing.focus = number;
518 	} else if (strcmp(field, "-height") == 0) {
519 	    if (Tcl_GetInt(interp, value, &number) != TCL_OK) {
520 		return TCL_ERROR;
521 	    }
522 	    if (flags & EXPOSE) {
523 		 event.xexpose.height = number;
524 	    } else if (flags & (CONFIG|CONFIG_REQ)) {
525 		event.xconfigure.height = number;
526 	    } else if (flags & RESIZE_REQ) {
527 		event.xresizerequest.height = number;
528 	    }
529 	} else if (strcmp(field, "-keycode") == 0) {
530 	    if (Tcl_GetInt(interp, value, &number) != TCL_OK) {
531 		return TCL_ERROR;
532 	    }
533 	    event.xkey.keycode = number;
534 	} else if (strcmp(field, "-keysym") == 0) {
535 	    keysym = TkStringToKeysym(value);
536 	    if (keysym == NoSymbol) {
537 		Tcl_AppendResult(interp, "unknown keysym \"", value,
538 			"\"", (char *) NULL);
539 		return TCL_ERROR;
540 	    }
541 	    number = XKeysymToKeycode(event.xany.display, keysym);
542 	    if (number == 0) {
543 		Tcl_AppendResult(interp, "no keycode for keysym \"", value,
544 			"\"", (char *) NULL);
545 		return TCL_ERROR;
546 	    }
547 	    event.xkey.keycode = number;
548 	} else if (strcmp(field, "-mode") == 0) {
549 	    if (strcmp(value, "NotifyNormal") == 0) {
550 		number = NotifyNormal;
551 	    } else if (strcmp(value, "NotifyGrab") == 0) {
552 		number = NotifyGrab;
553 	    } else if (strcmp(value, "NotifyUngrab") == 0) {
554 		number = NotifyUngrab;
555 	    } else if (strcmp(value, "NotifyWhileGrabbed") == 0) {
556 		number = NotifyWhileGrabbed;
557 	    } else {
558 		Tcl_AppendResult(interp, "bad mode \"", value, "\"",
559 			(char *) NULL);
560 		return TCL_ERROR;
561 	    }
562 	    if (flags & CROSSING) {
563 		event.xcrossing.mode = number;
564 	    } else if (flags & FOCUS) {
565 		event.xfocus.mode = number;
566 	    }
567 	} else if (strcmp(field, "-override") == 0) {
568 	    if (Tcl_GetInt(interp, value, &number) != TCL_OK) {
569 		return TCL_ERROR;
570 	    }
571 	    if (flags & CREATE) {
572 		event.xcreatewindow.override_redirect = number;
573 	    } else if (flags & MAP) {
574 		event.xmap.override_redirect = number;
575 	    } else if (flags & REPARENT) {
576 		event.xreparent.override_redirect = number;
577 	    } else if (flags & CONFIG) {
578 		event.xconfigure.override_redirect = number;
579 	    }
580 	} else if (strcmp(field, "-place") == 0) {
581 	    if (strcmp(value, "PlaceOnTop") == 0) {
582 		event.xcirculate.place = PlaceOnTop;
583 	    } else if (strcmp(value, "PlaceOnBottom") == 0) {
584 		event.xcirculate.place = PlaceOnBottom;
585 	    } else if (strcmp(value, "bogus") == 0) {
586 		event.xcirculate.place = 147;
587 	    } else {
588 		Tcl_AppendResult(interp, "bad place \"", value, "\"",
589 			(char *) NULL);
590 		return TCL_ERROR;
591 	    }
592 	} else if (strcmp(field, "-root") == 0) {
593 	    if (Tcl_GetInt(interp, value, &number) != TCL_OK) {
594 		return TCL_ERROR;
595 	    }
596 	    event.xkey.root = number;
597 	} else if (strcmp(field, "-rootx") == 0) {
598 	    if (Tcl_GetInt(interp, value, &number) != TCL_OK) {
599 		return TCL_ERROR;
600 	    }
601 	    event.xkey.x_root = number;
602 	} else if (strcmp(field, "-rooty") == 0) {
603 	    if (Tcl_GetInt(interp, value, &number) != TCL_OK) {
604 		return TCL_ERROR;
605 	    }
606 	    event.xkey.y_root = number;
607 	} else if (strcmp(field, "-sendevent") == 0) {
608 	    if (Tcl_GetInt(interp, value, &number) != TCL_OK) {
609 		return TCL_ERROR;
610 	    }
611 	    event.xany.send_event = number;
612 	} else if (strcmp(field, "-serial") == 0) {
613 	    if (Tcl_GetInt(interp, value, &number) != TCL_OK) {
614 		return TCL_ERROR;
615 	    }
616 	    event.xany.serial = number;
617 	} else if (strcmp(field, "-state") == 0) {
618 	    if (flags & KEY_BUTTON_MOTION) {
619 		if (Tcl_GetInt(interp, value, &number) != TCL_OK) {
620 		    return TCL_ERROR;
621 		}
622 		event.xkey.state = number;
623 	    } else if (flags & CROSSING) {
624 		if (Tcl_GetInt(interp, value, &number) != TCL_OK) {
625 		    return TCL_ERROR;
626 		}
627 		event.xcrossing.state = number;
628 	    } else if (flags & VISIBILITY) {
629 		if (strcmp(value, "VisibilityUnobscured") == 0) {
630 		    number = VisibilityUnobscured;
631 		} else if (strcmp(value, "VisibilityPartiallyObscured") == 0) {
632 		    number = VisibilityPartiallyObscured;
633 		} else if (strcmp(value, "VisibilityFullyObscured") == 0) {
634 		    number = VisibilityFullyObscured;
635 		} else {
636 		    Tcl_AppendResult(interp, "bad state \"", value, "\"",
637 			    (char *) NULL);
638 		    return TCL_ERROR;
639 		}
640 		event.xvisibility.state = number;
641 	    }
642 	} else if (strcmp(field, "-subwindow") == 0) {
643 	    tkwin2 = Tk_NameToWindow(interp, value, main);
644 	    if (tkwin2 == NULL) {
645 		return TCL_ERROR;
646 	    }
647 	    event.xkey.subwindow = Tk_WindowId(tkwin2);
648 	} else if (strcmp(field, "-time") == 0) {
649 	    if (Tcl_GetInt(interp, value, &number) != TCL_OK) {
650 		return TCL_ERROR;
651 	    }
652 	    if (flags & (KEY_BUTTON_MOTION|PROP|SEL_CLEAR)) {
653 		event.xkey.time = (Time) number;
654 	    } else if (flags & SEL_REQ) {
655 		event.xselectionrequest.time = (Time) number;
656 	    } else if (flags & SEL_NOTIFY) {
657 		event.xselection.time = (Time) number;
658 	    }
659 	} else if (strcmp(field, "-valueMask") == 0) {
660 	    if (Tcl_GetInt(interp, value, &number) != TCL_OK) {
661 		return TCL_ERROR;
662 	    }
663 	    event.xconfigurerequest.value_mask = number;
664 	} else if (strcmp(field, "-width") == 0) {
665 	    if (Tcl_GetInt(interp, value, &number) != TCL_OK) {
666 		return TCL_ERROR;
667 	    }
668 	    if (flags & EXPOSE) {
669 		event.xexpose.width = number;
670 	    } else if (flags & (CONFIG|CONFIG_REQ)) {
671 		event.xconfigure.width = number;
672 	    } else if (flags & RESIZE_REQ) {
673 		event.xresizerequest.width = number;
674 	    }
675 	} else if (strcmp(field, "-window") == 0) {
676 	    tkwin2 = Tk_NameToWindow(interp, value, main);
677 	    if (tkwin2 == NULL) {
678 		return TCL_ERROR;
679 	    }
680 	    event.xmap.window = Tk_WindowId(tkwin2);
681 	} else if (strcmp(field, "-x") == 0) {
682 	    int rootX, rootY;
683 	    if (Tcl_GetInt(interp, value, &number) != TCL_OK) {
684 		return TCL_ERROR;
685 	    }
686 	    Tk_GetRootCoords(tkwin, &rootX, &rootY);
687 	    rootX += number;
688 	    if (flags & KEY_BUTTON_MOTION) {
689 		event.xkey.x = number;
690 		event.xkey.x_root = rootX;
691 	    } else if (flags & EXPOSE) {
692 		event.xexpose.x = number;
693 	    } else if (flags & (CREATE|CONFIG|GRAVITY|CONFIG_REQ)) {
694 		event.xcreatewindow.x = number;
695 	    } else if (flags & REPARENT) {
696 		event.xreparent.x = number;
697 	    } else if (flags & CROSSING) {
698 		event.xcrossing.x = number;
699 		event.xcrossing.x_root = rootY;
700 	    }
701 	} else if (strcmp(field, "-y") == 0) {
702 	    int rootX, rootY;
703 	    if (Tcl_GetInt(interp, value, &number) != TCL_OK) {
704 		return TCL_ERROR;
705 	    }
706 	    Tk_GetRootCoords(tkwin, &rootX, &rootY);
707 	    rootY += number;
708 	    if (flags & KEY_BUTTON_MOTION) {
709 		event.xkey.y = number;
710 		event.xkey.y_root = rootY;
711 	    } else if (flags & EXPOSE) {
712 		event.xexpose.y = number;
713 	    } else if (flags & (CREATE|CONFIG|GRAVITY|CONFIG_REQ)) {
714 		event.xcreatewindow.y = number;
715 	    } else if (flags & REPARENT) {
716 		event.xreparent.y = number;
717 	    } else if (flags & CROSSING) {
718 		event.xcrossing.y = number;
719 		event.xcrossing.y_root = rootY;
720 	    }
721 	} else {
722 	    Tcl_AppendResult(interp, "bad option \"", field, "\"",
723 		    (char *) NULL);
724 	    return TCL_ERROR;
725 	}
726     }
727     Tk_HandleEvent(&event);
728     return TCL_OK;
729 }
730 
731 /*
732  *----------------------------------------------------------------------
733  *
734  * TestmakeexistCmd --
735  *
736  *	This procedure implements the "testmakeexist" command.  It calls
737  *	Tk_MakeWindowExist on each of its arguments to force the windows
738  *	to be created.
739  *
740  * Results:
741  *	A standard Tcl result.
742  *
743  * Side effects:
744  *	Forces windows to be created.
745  *
746  *----------------------------------------------------------------------
747  */
748 
749 	/* ARGSUSED */
750 static int
TestmakeexistCmd(clientData,interp,argc,argv)751 TestmakeexistCmd(clientData, interp, argc, argv)
752     ClientData clientData;		/* Main window for application. */
753     Tcl_Interp *interp;			/* Current interpreter. */
754     int argc;				/* Number of arguments. */
755     char **argv;			/* Argument strings. */
756 {
757     Tk_Window main = (Tk_Window) clientData;
758     int i;
759     Tk_Window tkwin;
760 
761     for (i = 1; i < argc; i++) {
762 	tkwin = Tk_NameToWindow(interp, argv[i], main);
763 	if (tkwin == NULL) {
764 	    return TCL_ERROR;
765 	}
766 	Tk_MakeWindowExist(tkwin);
767     }
768 
769     return TCL_OK;
770 }
771 
772 /*
773  *----------------------------------------------------------------------
774  *
775  * ImageCreate --
776  *
777  *	This procedure is called by the Tk image code to create "test"
778  *	images.
779  *
780  * Results:
781  *	A standard Tcl result.
782  *
783  * Side effects:
784  *	The data structure for a new image is allocated.
785  *
786  *----------------------------------------------------------------------
787  */
788 
789 	/* ARGSUSED */
790 static int
ImageCreate(interp,name,argc,argv,typePtr,master,clientDataPtr)791 ImageCreate(interp, name, argc, argv, typePtr, master, clientDataPtr)
792     Tcl_Interp *interp;		/* Interpreter for application containing
793 				 * image. */
794     char *name;			/* Name to use for image. */
795     int argc;			/* Number of arguments. */
796     char **argv;		/* Argument strings for options (doesn't
797 				 * include image name or type). */
798     Tk_ImageType *typePtr;	/* Pointer to our type record (not used). */
799     Tk_ImageMaster master;	/* Token for image, to be used by us in
800 				 * later callbacks. */
801     ClientData *clientDataPtr;	/* Store manager's token for image here;
802 				 * it will be returned in later callbacks. */
803 {
804     TImageMaster *timPtr;
805     char *varName;
806     int i;
807 
808     varName = "log";
809     for (i = 0; i < argc; i += 2) {
810 	if (strcmp(argv[i], "-variable") != 0) {
811 	    Tcl_AppendResult(interp, "bad option name \"", argv[i],
812 		    "\"", (char *) NULL);
813 	    return TCL_ERROR;
814 	}
815 	if ((i+1) == argc) {
816 	    Tcl_AppendResult(interp, "no value given for \"", argv[i],
817 		    "\" option", (char *) NULL);
818 	    return TCL_ERROR;
819 	}
820 	varName = argv[i+1];
821     }
822     timPtr = (TImageMaster *) ckalloc(sizeof(TImageMaster));
823     timPtr->master = master;
824     timPtr->interp = interp;
825     timPtr->width = 30;
826     timPtr->height = 15;
827     timPtr->imageName = (char *) ckalloc((unsigned) (strlen(name) + 1));
828     strcpy(timPtr->imageName, name);
829     timPtr->varName = (char *) ckalloc((unsigned) (strlen(varName) + 1));
830     strcpy(timPtr->varName, varName);
831     Tcl_CreateCommand(interp, name, ImageCmd, (ClientData) timPtr,
832 	    (Tcl_CmdDeleteProc *) NULL);
833     *clientDataPtr = (ClientData) timPtr;
834     Tk_ImageChanged(master, 0, 0, 30, 15, 30, 15);
835     return TCL_OK;
836 }
837 
838 /*
839  *----------------------------------------------------------------------
840  *
841  * ImageCmd --
842  *
843  *	This procedure implements the commands corresponding to individual
844  *	images.
845  *
846  * Results:
847  *	A standard Tcl result.
848  *
849  * Side effects:
850  *	Forces windows to be created.
851  *
852  *----------------------------------------------------------------------
853  */
854 
855 	/* ARGSUSED */
856 static int
ImageCmd(clientData,interp,argc,argv)857 ImageCmd(clientData, interp, argc, argv)
858     ClientData clientData;		/* Main window for application. */
859     Tcl_Interp *interp;			/* Current interpreter. */
860     int argc;				/* Number of arguments. */
861     char **argv;			/* Argument strings. */
862 {
863     TImageMaster *timPtr = (TImageMaster *) clientData;
864     int x, y, width, height;
865 
866     if (argc < 2) {
867 	Tcl_AppendResult(interp, "wrong # args: should be \"",
868 		argv[0], "option ?arg arg ...?", (char *) NULL);
869 	return TCL_ERROR;
870     }
871     if (strcmp(argv[1], "changed") == 0) {
872 	if (argc != 8) {
873 	    Tcl_AppendResult(interp, "wrong # args: should be \"",
874 		    argv[0], " changed x y width height imageWidth imageHeight",
875 		    (char *) NULL);
876 	    return TCL_ERROR;
877 	}
878 	if ((Tcl_GetInt(interp, argv[2], &x) != TCL_OK)
879 		|| (Tcl_GetInt(interp, argv[3], &y) != TCL_OK)
880 		|| (Tcl_GetInt(interp, argv[4], &width) != TCL_OK)
881 		|| (Tcl_GetInt(interp, argv[5], &height) != TCL_OK)
882 		|| (Tcl_GetInt(interp, argv[6], &timPtr->width) != TCL_OK)
883 		|| (Tcl_GetInt(interp, argv[7], &timPtr->height) != TCL_OK)) {
884 	    return TCL_ERROR;
885 	}
886 	Tk_ImageChanged(timPtr->master, x, y, width, height, timPtr->width,
887 		timPtr->height);
888     } else {
889 	Tcl_AppendResult(interp, "bad option \"", argv[1],
890 		"\": must be changed", (char *) NULL);
891 	return TCL_ERROR;
892     }
893     return TCL_OK;
894 }
895 
896 /*
897  *----------------------------------------------------------------------
898  *
899  * ImageGet --
900  *
901  *	This procedure is called by Tk to set things up for using a
902  *	test image in a particular widget.
903  *
904  * Results:
905  *	The return value is a token for the image instance, which is
906  *	used in future callbacks to ImageDisplay and ImageFree.
907  *
908  * Side effects:
909  *	None.
910  *
911  *----------------------------------------------------------------------
912  */
913 
914 static ClientData
ImageGet(tkwin,clientData)915 ImageGet(tkwin, clientData)
916     Tk_Window tkwin;		/* Token for window in which image will
917 				 * be used. */
918     ClientData clientData;	/* Pointer to TImageMaster for image. */
919 {
920     TImageMaster *timPtr = (TImageMaster *) clientData;
921     TImageInstance *instPtr;
922     char buffer[100];
923     XGCValues gcValues;
924 
925     sprintf(buffer, "%s get", timPtr->imageName);
926     Tcl_SetVar(timPtr->interp, timPtr->varName, buffer,
927 	    TCL_GLOBAL_ONLY|TCL_APPEND_VALUE|TCL_LIST_ELEMENT);
928 
929     instPtr = (TImageInstance *) ckalloc(sizeof(TImageInstance));
930     instPtr->masterPtr = timPtr;
931     instPtr->fg = Tk_GetColor(timPtr->interp, tkwin, "#ff0000");
932     gcValues.foreground = instPtr->fg->pixel;
933     instPtr->gc = Tk_GetGC(tkwin, GCForeground, &gcValues);
934     return (ClientData) instPtr;
935 }
936 
937 /*
938  *----------------------------------------------------------------------
939  *
940  * ImageDisplay --
941  *
942  *	This procedure is invoked to redisplay part or all of an
943  *	image in a given drawable.
944  *
945  * Results:
946  *	None.
947  *
948  * Side effects:
949  *	The image gets partially redrawn, as an "X" that shows the
950  *	exact redraw area.
951  *
952  *----------------------------------------------------------------------
953  */
954 
955 static void
ImageDisplay(clientData,display,drawable,imageX,imageY,width,height,drawableX,drawableY)956 ImageDisplay(clientData, display, drawable, imageX, imageY, width, height,
957 	drawableX, drawableY)
958     ClientData clientData;	/* Pointer to TImageInstance for image. */
959     Display *display;		/* Display to use for drawing. */
960     Drawable drawable;		/* Where to redraw image. */
961     int imageX, imageY;		/* Origin of area to redraw, relative to
962 				 * origin of image. */
963     int width, height;		/* Dimensions of area to redraw. */
964     int drawableX, drawableY;	/* Coordinates in drawable corresponding to
965 				 * imageX and imageY. */
966 {
967     TImageInstance *instPtr = (TImageInstance *) clientData;
968     char buffer[200];
969 
970     sprintf(buffer, "%s display %d %d %d %d %d %d",
971 	    instPtr->masterPtr->imageName, imageX, imageY, width, height,
972 	    drawableX, drawableY);
973     Tcl_SetVar(instPtr->masterPtr->interp, instPtr->masterPtr->varName, buffer,
974 	    TCL_GLOBAL_ONLY|TCL_APPEND_VALUE|TCL_LIST_ELEMENT);
975     if (width > (instPtr->masterPtr->width - imageX)) {
976 	width = instPtr->masterPtr->width - imageX;
977     }
978     if (height > (instPtr->masterPtr->height - imageY)) {
979 	height = instPtr->masterPtr->height - imageY;
980     }
981     XDrawRectangle(display, drawable, instPtr->gc, drawableX, drawableY,
982 	    (unsigned) (width-1), (unsigned) (height-1));
983     XDrawLine(display, drawable, instPtr->gc, drawableX, drawableY,
984 	    (int) (drawableX + width - 1), (int) (drawableY + height - 1));
985     XDrawLine(display, drawable, instPtr->gc, drawableX,
986 	    (int) (drawableY + height - 1),
987 	    (int) (drawableX + width - 1), drawableY);
988 }
989 
990 /*
991  *----------------------------------------------------------------------
992  *
993  * ImageFree --
994  *
995  *	This procedure is called when an instance of an image is
996  * 	no longer used.
997  *
998  * Results:
999  *	None.
1000  *
1001  * Side effects:
1002  *	Information related to the instance is freed.
1003  *
1004  *----------------------------------------------------------------------
1005  */
1006 
1007 static void
ImageFree(clientData,display)1008 ImageFree(clientData, display)
1009     ClientData clientData;	/* Pointer to TImageInstance for instance. */
1010     Display *display;		/* Display where image was to be drawn. */
1011 {
1012     TImageInstance *instPtr = (TImageInstance *) clientData;
1013     char buffer[200];
1014 
1015     sprintf(buffer, "%s free", instPtr->masterPtr->imageName);
1016     Tcl_SetVar(instPtr->masterPtr->interp, instPtr->masterPtr->varName, buffer,
1017 	    TCL_GLOBAL_ONLY|TCL_APPEND_VALUE|TCL_LIST_ELEMENT);
1018     Tk_FreeColor(instPtr->fg);
1019     Tk_FreeGC(display, instPtr->gc);
1020     ckfree((char *) instPtr);
1021 }
1022 
1023 /*
1024  *----------------------------------------------------------------------
1025  *
1026  * ImageDelete --
1027  *
1028  *	This procedure is called to clean up a test image when
1029  *	an application goes away.
1030  *
1031  * Results:
1032  *	None.
1033  *
1034  * Side effects:
1035  *	Information about the image is deleted.
1036  *
1037  *----------------------------------------------------------------------
1038  */
1039 
1040 static void
ImageDelete(clientData)1041 ImageDelete(clientData)
1042     ClientData clientData;	/* Pointer to TImageMaster for image.  When
1043 				 * this procedure is called, no more
1044 				 * instances exist. */
1045 {
1046     TImageMaster *timPtr = (TImageMaster *) clientData;
1047     char buffer[100];
1048 
1049     sprintf(buffer, "%s delete", timPtr->imageName);
1050     Tcl_SetVar(timPtr->interp, timPtr->varName, buffer,
1051 	    TCL_GLOBAL_ONLY|TCL_APPEND_VALUE|TCL_LIST_ELEMENT);
1052 
1053     Tcl_DeleteCommand(timPtr->interp, timPtr->imageName);
1054     ckfree(timPtr->imageName);
1055     ckfree(timPtr->varName);
1056     ckfree((char *) timPtr);
1057 }
1058 
1059 /*
1060  *----------------------------------------------------------------------
1061  *
1062  * TestsendCmd --
1063  *
1064  *	This procedure implements the "testsend" command.  It provides
1065  *	a set of functions for testing the "send" command and support
1066  *	procedure in tkSend.c.
1067  *
1068  * Results:
1069  *	A standard Tcl result.
1070  *
1071  * Side effects:
1072  *	Depends on option;  see below.
1073  *
1074  *----------------------------------------------------------------------
1075  */
1076 
1077 	/* ARGSUSED */
1078 static int
TestsendCmd(clientData,interp,argc,argv)1079 TestsendCmd(clientData, interp, argc, argv)
1080     ClientData clientData;		/* Main window for application. */
1081     Tcl_Interp *interp;			/* Current interpreter. */
1082     int argc;				/* Number of arguments. */
1083     char **argv;			/* Argument strings. */
1084 {
1085     TkWindow *winPtr = (TkWindow *) clientData;
1086 
1087     if (argc < 2) {
1088 	Tcl_AppendResult(interp, "wrong # args;  must be \"", argv[0],
1089 		" option ?arg ...?\"", (char *) NULL);
1090 	return TCL_ERROR;
1091     }
1092 
1093 #ifndef WIN_TCL
1094     if (strcmp(argv[1], "bogus") == 0) {
1095 	XChangeProperty(winPtr->dispPtr->display,
1096 		RootWindow(winPtr->dispPtr->display, 0),
1097 		winPtr->dispPtr->registryProperty, XA_INTEGER, 32,
1098 		PropModeReplace,
1099 		(unsigned char *) "This is bogus information", 6);
1100     } else if (strcmp(argv[1], "prop") == 0) {
1101 	int result, actualFormat, length;
1102 	unsigned long bytesAfter;
1103 	Atom actualType, propName;
1104 	char *property, *p, *end;
1105 	Window w;
1106 
1107 	if ((argc != 4) && (argc != 5)) {
1108 	    Tcl_AppendResult(interp, "wrong # args;  must be \"", argv[0],
1109 		    " prop window name ?value ?\"", (char *) NULL);
1110 	    return TCL_ERROR;
1111 	}
1112 	if (strcmp(argv[2], "root") == 0) {
1113 	    w = RootWindow(winPtr->dispPtr->display, 0);
1114 	} else if (strcmp(argv[2], "comm") == 0) {
1115 	    w = Tk_WindowId(winPtr->dispPtr->commTkwin);
1116 	} else {
1117 	    w = strtoul(argv[2], &end, 0);
1118 	}
1119 	propName = Tk_InternAtom((Tk_Window) winPtr, argv[3]);
1120 	if (argc == 4) {
1121 	    property = NULL;
1122 	    result = XGetWindowProperty(winPtr->dispPtr->display,
1123 		    w, propName, 0, 100000, False, XA_STRING,
1124 		    &actualType, &actualFormat, (unsigned long *) &length,
1125 		    &bytesAfter, (unsigned char **) &property);
1126 	    if ((result == Success) && (actualType != None)
1127 		    && (actualFormat == 8) && (actualType == XA_STRING)) {
1128 		for (p = property; (p-property) < length; p++) {
1129 		    if (*p == 0) {
1130 			*p = '\n';
1131 		    }
1132 		}
1133 		Tcl_SetResult(interp, property, TCL_VOLATILE);
1134 	    }
1135 	    if (property != NULL) {
1136 		XFree(property);
1137 	    }
1138 	} else {
1139 	    if (argv[4][0] == 0) {
1140 		XDeleteProperty(winPtr->dispPtr->display, w, propName);
1141 	    } else {
1142 		for (p = argv[4]; *p != 0; p++) {
1143 		    if (*p == '\n') {
1144 			*p = 0;
1145 		    }
1146 		}
1147 		XChangeProperty(winPtr->dispPtr->display,
1148 			w, propName, XA_STRING, 8, PropModeReplace,
1149 			(unsigned char *) argv[4], p-argv[4]);
1150 	    }
1151 	}
1152     } else if (strcmp(argv[1], "serial") == 0) {
1153 	sprintf(interp->result, "%d", tkSendSerial+1);
1154     } else {
1155 	Tcl_AppendResult(interp, "bad option \"", argv[1],
1156 		"\": must be bogus, prop, or serial", (char *) NULL);
1157 	return TCL_ERROR;
1158     }
1159 #endif
1160     return TCL_OK;
1161 }
1162