]> cvs.zerfleddert.de Git - micropolis/blame_incremental - src/tk/tkwm.c
better search for matching visual
[micropolis] / src / tk / tkwm.c
... / ...
CommitLineData
1/*
2 * tkWm.c --
3 *
4 * This module takes care of the interactions between a Tk-based
5 * application and the window manager. Among other things, it
6 * implements the "wm" command and passes geometry information
7 * to the window manager.
8 *
9 * Copyright 1991 Regents of the University of California.
10 * Permission to use, copy, modify, and distribute this
11 * software and its documentation for any purpose and without
12 * fee is hereby granted, provided that the above copyright
13 * notice appear in all copies. The University of California
14 * makes no representations about the suitability of this
15 * software for any purpose. It is provided "as is" without
16 * express or implied warranty.
17 */
18
19#ifndef lint
20static char rcsid[] = "$Header: /user6/ouster/wish/RCS/tkWm.c,v 1.32 92/08/21 16:26:31 ouster Exp $ SPRITE (Berkeley)";
21#endif
22
23#include "tkconfig.h"
24#include "tkint.h"
25#include "tkwm.h"
26
27/*
28 * The definitions below compensate for the lack of some definitions
29 * under X11R3.
30 */
31
32#ifdef X11R3
33#define PBaseSize (1L<<8)
34#endif
35
36/*
37 * A data structure of the following type holds window-manager-related
38 * information for each top-level window in an application.
39 */
40
41typedef struct TkWmInfo {
42 TkWindow *winPtr; /* Pointer to main Tk information for
43 * this window. */
44 Window reparent; /* If the window has been reparented, this
45 * gives the ID of the ancestor of the window
46 * that is a child of the root window (may
47 * not be window's immediate parent). If
48 * the window isn't reparented, this has the
49 * value None. */
50 Tk_Uid titleUid; /* Title to display in window caption. If
51 * NULL, use name of widget. */
52 Tk_Uid iconName; /* Name to display in icon. */
53 Window master; /* Master window for TRANSIENT_FOR property,
54 * or None. */
55 XWMHints hints; /* Various pieces of information for
56 * window manager. */
57 Tk_Uid leaderName; /* Path name of leader of window group
58 * (corresponds to hints.window_group).
59 * Note: this field doesn't get updated
60 * if leader is destroyed. */
61 Tk_Uid iconWindowName; /* Path name of window specified as icon
62 * window for this window, or NULL. Note:
63 * this field doesn't get updated if
64 * iconWindowName is destroyed. */
65 Tk_Uid masterWindowName; /* Path name of window specified as master
66 * in "wm transient" command, or NULL.
67 * Note: this field doesn't get updated if
68 * masterWindowName is destroyed. */
69
70 /*
71 * Information used to construct an XSizeHints structure for
72 * the window manager:
73 */
74
75 int sizeHintsFlags; /* Flags word for XSizeHints structure.
76 * If the PBaseSize flag is set then the
77 * window is gridded; otherwise it isn't
78 * gridded. */
79 int minWidth, minHeight; /* Minimum dimensions of window, in
80 * grid units, not pixels. */
81 int maxWidth, maxHeight; /* Maximum dimensions of window, in
82 * grid units, not pixels. */
83 int widthInc, heightInc; /* Increments for size changes (# pixels
84 * per step). */
85 struct {
86 int x; /* numerator */
87 int y; /* denominator */
88 } minAspect, maxAspect; /* Min/max aspect ratios for window. */
89 int reqGridWidth, reqGridHeight;
90 /* The dimensions of the window (in
91 * grid units) requested through
92 * the geometry manager. */
93 int gravity; /* Desired window gravity. */
94
95 /*
96 * Information used to manage the size and location of a window.
97 */
98
99 int prevReqWidth, prevReqHeight;
100 /* Last known size preferences, as specified
101 * to Tk_GeometryRequest. Used to tell when
102 * the preferred dimensions have changed. */
103 int width, height; /* Desired dimensions of window, specified
104 * in grid units. These values are
105 * set by the "wm geometry" command and by
106 * ConfigureNotify events (for when wm
107 * resizes window). -1 means user hasn't
108 * requested dimensions. */
109 int x, y; /* Desired X and Y coordinates for window.
110 * These values are set by "wm geometry",
111 * plus by ConfigureNotify events (when wm
112 * moves window). These numbers are
113 * different than the numbers stored in
114 * winPtr->changes because (a) they could be
115 * measured from the right or bottom edge
116 * of the screen (see WM_NEGATIVE_X and
117 * WM_NEGATIVE_Y flags) and (b) if the window
118 * has been reparented then they refer to the
119 * parent rather than the window itself. */
120 int parentWidth, parentHeight;
121 /* Width and height of reparent, in pixels
122 * *including border*. If window hasn't been
123 * reparented then these will be the outer
124 * dimensions of the window, including
125 * border. */
126 int xInParent, yInParent; /* Offset of window within reparent, measured
127 * from upper-left outer corner of parent's
128 * border. If not reparented then these are
129 * zero. */
130 unsigned long configRequest;/* Serial number of last request that we
131 * issued to change geometry of window.
132 * Used to discard configure events that
133 * we know will be superceded. */
134 int configWidth, configHeight;
135 /* Dimensions passed to last request that we
136 * issued to change geometry of window. Used
137 * to eliminate redundant resize operations. */
138
139 int flags; /* Miscellaneous flags, defined below. */
140
141 char *deleteCmd; /* Command to execute when a WM_DELETE_WINDOW
142 * ICCCM ClientMessage arrives for this window.
143 *
144 * If it is the empty string "" or has never
145 * been set (is char *)NULL) via the "wm" tcl
146 * command the window is destroyed.
147 *
148 * If it is a non-empty string, the name of
149 * the window is appended on to the end
150 * of the string and it is executed
151 * within the interpreter associated with
152 * the top level window.
153 */
154 struct TkWmInfo *nextPtr; /* Next in list of all top-level windows. */
155} WmInfo;
156
157/*
158 * Flag values for WmInfo structures:
159 *
160 * WM_NEVER_MAPPED - non-zero means window has never been
161 * mapped; need to update all info when
162 * window is first mapped.
163 * WM_UPDATE_PENDING - non-zero means a call to UpdateGeometryInfo
164 * has already been scheduled for this
165 * window; no need to schedule another one.
166 * WM_NEGATIVE_X - non-zero means x-coordinate is measured in
167 * pixels from right edge of screen, rather
168 * than from left edge.
169 * WM_NEGATIVE_Y - non-zero means y-coordinate is measured in
170 * pixels up from bottom of screen, rather than
171 * down from top.
172 * WM_UPDATE_SIZE_HINTS - non-zero means that new size hints need to be
173 * propagated to window manager.
174 * WM_NESTED_REPARENT - non-zero means that the window has been
175 * reparented several levels deep in a hierarchy
176 * (i.e. reparent isn't the window's immediate
177 * parent).
178 * WM_CONFIG_PENDING - non-zero means we've asked for the top-level
179 * window to be resized but haven't seen a
180 * ConfigureNotify event to indicate that the
181 * resize occurred.
182 * WM_CONFIG_AGAIN - non-zero means we need to reconfigure the
183 * window again as soon as the current configure
184 * request has been processed by the window
185 * manager.
186 * WM_FULL_SCREEN - non-zero means that the window is in full screen mode.
187 */
188
189#define WM_NEVER_MAPPED 1
190#define WM_UPDATE_PENDING 2
191#define WM_NEGATIVE_X 4
192#define WM_NEGATIVE_Y 8
193#define WM_UPDATE_SIZE_HINTS 0x10
194#define WM_NESTED_REPARENT 0x20
195#define WM_CONFIG_PENDING 0x40
196#define WM_CONFIG_AGAIN 0x100
197#define WM_FULL_SCREEN 0x200
198
199/*
200 * This module keeps a list of all top-level windows, primarily to
201 * simplify the job of Tk_CoordsToWindow.
202 */
203
204static WmInfo *firstWmPtr = NULL; /* Points to first top-level window. */
205
206#define IS_GRIDDED(wmPtr) ((wmPtr)->sizeHintsFlags & PBaseSize)
207
208/*
209 * Forward declarations for procedures defined in this file:
210 */
211
212static int ParseGeometry _ANSI_ARGS_ ((Tcl_Interp *interp,
213 char *string, TkWindow *winPtr));
214static void TopLevelEventProc _ANSI_ARGS_((ClientData clientData,
215 XEvent *eventPtr));
216static void TopLevelReqProc _ANSI_ARGS_((ClientData dummy,
217 Tk_Window tkwin));
218static void UpdateGeometryInfo _ANSI_ARGS_((
219 ClientData clientData));
220static void UpdateHints _ANSI_ARGS_((TkWindow *winPtr));
221static void UpdateSizeHints _ANSI_ARGS_((TkWindow *winPtr));
222\f
223/*
224 *--------------------------------------------------------------
225 *
226 * TkWmNewWindow --
227 *
228 * This procedure is invoked whenever a new top-level
229 * window is created. Its job is to initialize the WmInfo
230 * structure for the window.
231 *
232 * Results:
233 * None.
234 *
235 * Side effects:
236 * A WmInfo structure gets allocated and initialized.
237 *
238 *--------------------------------------------------------------
239 */
240
241void
242TkWmNewWindow(winPtr)
243 TkWindow *winPtr; /* Newly-created top-level window. */
244{
245 register WmInfo *wmPtr;
246
247 wmPtr = (WmInfo *) ckalloc(sizeof(WmInfo));
248 wmPtr->winPtr = winPtr;
249 wmPtr->reparent = None;
250 wmPtr->titleUid = NULL;
251 wmPtr->iconName = NULL;
252 wmPtr->master = None;
253 wmPtr->hints.flags = InputHint | StateHint;
254 wmPtr->hints.input = True;
255 wmPtr->hints.initial_state = NormalState;
256 wmPtr->hints.icon_pixmap = None;
257 wmPtr->hints.icon_window = None;
258 wmPtr->hints.icon_x = wmPtr->hints.icon_y = 0;
259 wmPtr->hints.icon_mask = None;
260 wmPtr->hints.window_group = None;
261 wmPtr->leaderName = NULL;
262 wmPtr->iconWindowName = NULL;
263 wmPtr->masterWindowName = NULL;
264 wmPtr->sizeHintsFlags = 0;
265 wmPtr->minWidth = wmPtr->minHeight = 0;
266 wmPtr->maxWidth = wmPtr->maxHeight = 10000;
267 wmPtr->widthInc = wmPtr->heightInc = 1;
268 wmPtr->minAspect.x = wmPtr->minAspect.y = 1;
269 wmPtr->maxAspect.x = wmPtr->maxAspect.y = 1;
270 wmPtr->reqGridWidth = wmPtr->reqGridHeight = -1;
271 wmPtr->prevReqWidth = wmPtr->prevReqHeight = -1;
272 wmPtr->gravity = NorthWestGravity;
273 wmPtr->width = -1;
274 wmPtr->height = -1;
275 wmPtr->x = winPtr->changes.x;
276 wmPtr->y = winPtr->changes.y;
277 wmPtr->parentWidth = winPtr->changes.width
278 + 2*winPtr->changes.border_width;
279 wmPtr->parentHeight = winPtr->changes.height
280 + 2*winPtr->changes.border_width;
281 wmPtr->xInParent = wmPtr->yInParent = 0;
282 wmPtr->configRequest = 0;
283 wmPtr->configWidth = -1;
284 wmPtr->configHeight = -1;
285 wmPtr->flags = WM_NEVER_MAPPED;
286 wmPtr->deleteCmd = (char *)0;
287 wmPtr->nextPtr = firstWmPtr;
288 firstWmPtr = wmPtr;
289 winPtr->wmInfoPtr = wmPtr;
290
291 /*
292 * Tk must monitor certain events for top-level windows:
293 * (a) structure events, in order to detect size and position changes
294 * caused by window managers.
295 * (b) enter/level events, in order to perform focussing correctly.
296 */
297
298 Tk_CreateEventHandler((Tk_Window) winPtr,
299 StructureNotifyMask|EnterWindowMask|LeaveWindowMask,
300 TopLevelEventProc, (ClientData) winPtr);
301
302 /*
303 * Arrange for geometry requests to be reflected from the window
304 * to the window manager.
305 */
306
307 Tk_ManageGeometry((Tk_Window) winPtr, TopLevelReqProc, (ClientData) 0);
308}
309\f
310/*
311 *--------------------------------------------------------------
312 *
313 * TkWmMapWindow --
314 *
315 * This procedure is invoked just before a top-level window
316 * is mapped. It gives this module a chance to update all
317 * window-manager-related information in properties before
318 * the window manager sees the map event and checks the
319 * properties.
320 *
321 * Results:
322 * Returns non-zero if it's OK for the window to be mapped, 0
323 * if the caller shouldn't map the window after all (e.g. because
324 * it has been withdrawn).
325 *
326 * Side effects:
327 * Properties of winPtr may get updated to provide up-to-date
328 * information to the window manager.
329 *
330 *--------------------------------------------------------------
331 */
332
333int
334TkWmMapWindow(winPtr)
335 TkWindow *winPtr; /* Top-level window that's about to
336 * be mapped. */
337{
338 register WmInfo *wmPtr = winPtr->wmInfoPtr;
339#ifndef X11R3
340 XTextProperty textProp;
341#endif
342
343 /*
344 * Set the MAPPED flag if the window is going to appear in its normal
345 * state: if it's going to be iconified or withdrawn then it won't
346 * ever be mapped.
347 */
348
349 if (wmPtr->hints.initial_state == NormalState) {
350 winPtr->flags |= TK_MAPPED;
351 }
352 if (wmPtr->flags & WM_NEVER_MAPPED) {
353 wmPtr->flags &= ~WM_NEVER_MAPPED;
354
355 /*
356 * This is the first time this window has ever been mapped.
357 * Store all the window-manager-related information for the
358 * window.
359 */
360
361#ifndef X11R3
362 if (wmPtr->titleUid == NULL) {
363 wmPtr->titleUid = winPtr->nameUid;
364 }
365 if (XStringListToTextProperty(&wmPtr->titleUid, 1, &textProp) != 0) {
366 XSetWMName(winPtr->display, winPtr->window, &textProp);
367 XFree((char *) textProp.value);
368 }
369#endif
370
371 TkWmSetClass(winPtr);
372 TkWmSetWmProtocols(winPtr);
373
374 if (wmPtr->iconName != NULL) {
375 XSetIconName(winPtr->display, winPtr->window, wmPtr->iconName);
376 }
377
378 if (wmPtr->master != None) {
379 XSetTransientForHint(winPtr->display, winPtr->window, wmPtr->master);
380 }
381 }
382
383 wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
384 UpdateGeometryInfo((ClientData) winPtr);
385 UpdateHints(winPtr);
386 if (wmPtr->hints.initial_state == WithdrawnState) {
387 return 0;
388 }
389 return 1;
390}
391\f
392/*
393 *--------------------------------------------------------------
394 *
395 * TkWmDeadWindow --
396 *
397 * This procedure is invoked when a top-level window is
398 * about to be deleted. It cleans up the wm-related data
399 * structures for the window.
400 *
401 * Results:
402 * None.
403 *
404 * Side effects:
405 * The WmInfo structure for winPtr gets freed up.
406 *
407 *--------------------------------------------------------------
408 */
409
410void
411TkWmDeadWindow(winPtr)
412 TkWindow *winPtr; /* Newly-created top-level window. */
413{
414 register WmInfo *wmPtr = winPtr->wmInfoPtr;
415
416 if (wmPtr == NULL) {
417 return;
418 }
419 if (firstWmPtr == wmPtr) {
420 firstWmPtr = wmPtr->nextPtr;
421 } else {
422 register WmInfo *prevPtr;
423
424 for (prevPtr = firstWmPtr; ; prevPtr = prevPtr->nextPtr) {
425 if (prevPtr == NULL) {
426 panic("couldn't unlink window in TkWmDeadWindow");
427 }
428 if (prevPtr->nextPtr == wmPtr) {
429 prevPtr->nextPtr = wmPtr->nextPtr;
430 break;
431 }
432 }
433 }
434 if (wmPtr->hints.flags & IconPixmapHint) {
435 Tk_FreeBitmap(wmPtr->hints.icon_pixmap);
436 }
437 if (wmPtr->hints.flags & IconMaskHint) {
438 Tk_FreeBitmap(wmPtr->hints.icon_mask);
439 }
440 if (wmPtr->flags & WM_UPDATE_PENDING) {
441 Tk_CancelIdleCall(UpdateGeometryInfo, (ClientData) winPtr);
442 }
443 if (wmPtr->deleteCmd) {
444 ckfree(wmPtr->deleteCmd);
445 }
446 ckfree((char *) wmPtr);
447 winPtr->wmInfoPtr = NULL;
448}
449\f
450/*
451 *--------------------------------------------------------------
452 *
453 * TkWmSetClass --
454 *
455 * This procedure is invoked whenever a top-level window's
456 * class is changed. If the window has been mapped then this
457 * procedure updates the window manager property for the
458 * class. If the window hasn't been mapped, the update is
459 * deferred until just before the first mapping.
460 *
461 * Results:
462 * None.
463 *
464 * Side effects:
465 * A window property may get updated.
466 *
467 *--------------------------------------------------------------
468 */
469
470void
471TkWmSetClass(winPtr)
472 TkWindow *winPtr; /* Newly-created top-level window. */
473{
474 if (winPtr->wmInfoPtr->flags & WM_NEVER_MAPPED) {
475 return;
476 }
477
478#ifndef X11R3
479 if (winPtr->classUid != NULL) {
480 XClassHint *classPtr;
481
482 classPtr = XAllocClassHint();
483 classPtr->res_name = winPtr->nameUid;
484 classPtr->res_class = winPtr->classUid;
485 XSetClassHint(winPtr->display, winPtr->window, classPtr);
486 XFree((char *) classPtr);
487 }
488#endif
489}
490\f
491/*
492 *----------------------------------------------------------------------
493 *
494 * Tk_WmCmd --
495 *
496 * This procedure is invoked to process the "wm" Tcl command.
497 * See the user documentation for details on what it does.
498 *
499 * Results:
500 * A standard Tcl result.
501 *
502 * Side effects:
503 * See the user documentation.
504 *
505 *----------------------------------------------------------------------
506 */
507
508 /* ARGSUSED */
509int
510Tk_WmCmd(clientData, interp, argc, argv)
511 ClientData clientData; /* Main window associated with
512 * interpreter. */
513 Tcl_Interp *interp; /* Current interpreter. */
514 int argc; /* Number of arguments. */
515 char **argv; /* Argument strings. */
516{
517 Tk_Window tkwin = (Tk_Window) clientData;
518 TkWindow *winPtr;
519 register WmInfo *wmPtr;
520 char c;
521 int length;
522
523 if (argc < 3) {
524 Tcl_AppendResult(interp, "wrong # args: should be \"",
525 argv[0], " option window ?arg ...?\"", (char *) NULL);
526 return TCL_ERROR;
527 }
528 winPtr = (TkWindow *) Tk_NameToWindow(interp, argv[2], tkwin);
529 if (winPtr == NULL) {
530 return TCL_ERROR;
531 }
532 if (!(winPtr->flags & TK_TOP_LEVEL)) {
533 Tcl_AppendResult(interp, "window \"", winPtr->pathName,
534 "\" isn't a top-level window", (char *) NULL);
535 return TCL_ERROR;
536 }
537 wmPtr = winPtr->wmInfoPtr;
538 c = argv[1][0];
539 length = strlen(argv[1]);
540 if ((c == 'a') && (strncmp(argv[1], "aspect", length) == 0)) {
541 int numer1, denom1, numer2, denom2;
542
543 if ((argc != 3) && (argc != 7)) {
544 Tcl_AppendResult(interp, "wrong # arguments: must be \"",
545 argv[0], " aspect window ?minNumer minDenom ",
546 "maxNumer maxDenom?\"", (char *) NULL);
547 return TCL_ERROR;
548 }
549 if (argc == 3) {
550 if (wmPtr->sizeHintsFlags & PAspect) {
551 sprintf(interp->result, "%d %d %d %d", wmPtr->minAspect.x,
552 wmPtr->minAspect.y, wmPtr->maxAspect.x,
553 wmPtr->maxAspect.y);
554 }
555 return TCL_OK;
556 }
557 if (*argv[3] == '\0') {
558 wmPtr->sizeHintsFlags &= ~PAspect;
559 } else {
560 if ((Tcl_GetInt(interp, argv[3], &numer1) != TCL_OK)
561 || (Tcl_GetInt(interp, argv[4], &denom1) != TCL_OK)
562 || (Tcl_GetInt(interp, argv[5], &numer2) != TCL_OK)
563 || (Tcl_GetInt(interp, argv[6], &denom2) != TCL_OK)) {
564 return TCL_ERROR;
565 }
566 if ((numer1 <= 0) || (denom1 <= 0) || (numer2 <= 0) ||
567 (denom2 <= 0)) {
568 interp->result = "aspect number can't be <= 0";
569 return TCL_ERROR;
570 }
571 wmPtr->minAspect.x = numer1;
572 wmPtr->minAspect.y = denom1;
573 wmPtr->maxAspect.x = numer2;
574 wmPtr->maxAspect.y = denom2;
575 wmPtr->sizeHintsFlags |= PAspect;
576 }
577 wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
578 goto updateGeom;
579 } else if ((c == 'd') && (strncmp(argv[1], "deiconify", length) == 0)) {
580 if (argc != 3) {
581 Tcl_AppendResult(interp, "wrong # arguments: must be \"",
582 argv[0], " deiconify window\"", (char *) NULL);
583 return TCL_ERROR;
584 }
585 wmPtr->hints.initial_state = NormalState;
586 if (wmPtr->flags & WM_NEVER_MAPPED) {
587 return TCL_OK;
588 }
589 Tk_MapWindow((Tk_Window) winPtr);
590 } else if ((c == 'f') && (strncmp(argv[1], "focusmodel", length) == 0)) {
591 if ((argc != 3) && (argc != 4)) {
592 Tcl_AppendResult(interp, "wrong # arguments: must be \"",
593 argv[0], " focusmodel window ?active|passive?\"",
594 (char *) NULL);
595 return TCL_ERROR;
596 }
597 if (argc == 3) {
598 interp->result = wmPtr->hints.input ? "passive" : "active";
599 return TCL_OK;
600 }
601 c = argv[3][0];
602 length = strlen(argv[3]);
603 if ((c == 'a') && (strncmp(argv[3], "active", length) == 0)) {
604 wmPtr->hints.input = False;
605 } else if ((c == 'p') && (strncmp(argv[3], "passive", length) == 0)) {
606 wmPtr->hints.input = True;
607 } else {
608 Tcl_AppendResult(interp, "bad argument \"", argv[3],
609 "\": must be active or passive", (char *) NULL);
610 return TCL_ERROR;
611 }
612 UpdateHints(winPtr);
613 } else if ((c == 'f') && (strncmp(argv[1], "fullscreen", length) == 0)) {
614 if (argc != 4) {
615 Tcl_AppendResult(interp, "wrong # arguments: must be \"",
616 argv[0], " fullscreen window on|off\"",
617 (char *) NULL);
618 return TCL_ERROR;
619 }
620 c = argv[3][0];
621 length = strlen(argv[3]);
622 if (strncmp(argv[3], "on", length) == 0) {
623 wmPtr->flags &= ~WM_FULL_SCREEN;
624 } else if (strncmp(argv[3], "off", length) == 0) {
625 wmPtr->flags |= WM_FULL_SCREEN;
626 } else {
627 Tcl_AppendResult(interp, "bad argument \"", argv[3],
628 "\": must be on or off", (char *) NULL);
629 return TCL_ERROR;
630 }
631
632 static Atom _NET_WM_STATE;
633 static Atom _NET_WM_STATE_REMOVE;
634 static Atom _NET_WM_STATE_ADD;
635 static Atom _NET_WM_STATE_FULLSCREEN;
636
637 if (!_NET_WM_STATE) {
638#define MAX_ATOMS 30
639 Atom *atom_ptr[MAX_ATOMS];
640 char *names[MAX_ATOMS];
641 int i = 0;
642#define atom(a,b) atom_ptr[i] = &a; names[i] = b; i++
643 atom(_NET_WM_STATE, "_NET_WM_STATE");
644 atom(_NET_WM_STATE_REMOVE, "_NET_WM_STATE_REMOVE");
645 atom(_NET_WM_STATE_ADD, "_NET_WM_STATE_ADD");
646 atom(_NET_WM_STATE_FULLSCREEN, "_NET_WM_STATE_FULLSCREEN");
647#undef atom
648 Atom atoms[MAX_ATOMS];
649 XInternAtoms(winPtr->display, names, i, 0, atoms);
650 for (; i--;) {
651 *atom_ptr[i] = atoms[i];
652 }
653 }
654
655 XEvent e;
656 e.xany.type = ClientMessage;
657 e.xany.window = winPtr->window;
658 e.xclient.message_type = _NET_WM_STATE;
659 e.xclient.format = 32;
660 e.xclient.data.l[0] =
661 (wmPtr->flags & WM_FULL_SCREEN)
662 ? _NET_WM_STATE_ADD
663 : _NET_WM_STATE_REMOVE;
664 e.xclient.data.l[1] = (long)_NET_WM_STATE_FULLSCREEN;
665 e.xclient.data.l[2] = (long)0;
666 e.xclient.data.l[3] = (long)0;
667 e.xclient.data.l[4] = (long)0;
668 XSendEvent(winPtr->display, RootWindow(winPtr->display, winPtr->screenNum), 0,
669 SubstructureNotifyMask|SubstructureRedirectMask, &e);
670
671 } else if ((c == 'g') && (strncmp(argv[1], "geometry", length) == 0)
672 && (length >= 2)) {
673 char xSign, ySign;
674 int width, height;
675
676 if ((argc != 3) && (argc != 4)) {
677 Tcl_AppendResult(interp, "wrong # arguments: must be \"",
678 argv[0], " geometry window ?newGeometry?\"",
679 (char *) NULL);
680 return TCL_ERROR;
681 }
682 if (argc == 3) {
683 xSign = (wmPtr->flags & WM_NEGATIVE_X) ? '-' : '+';
684 ySign = (wmPtr->flags & WM_NEGATIVE_Y) ? '-' : '+';
685 if (wmPtr->width != -1) {
686 width = wmPtr->width;
687 height = wmPtr->height;
688 } else if (IS_GRIDDED(wmPtr)) {
689 width = wmPtr->reqGridWidth;
690 height = wmPtr->reqGridHeight;
691 } else {
692 width = winPtr->reqWidth;
693 height = winPtr->reqHeight;
694 }
695 sprintf(interp->result, "%dx%d%c%d%c%d", width, height,
696 xSign, wmPtr->x, ySign, wmPtr->y);
697 return TCL_OK;
698 }
699 if (*argv[3] == '\0') {
700 wmPtr->width = -1;
701 wmPtr->height = -1;
702 goto updateGeom;
703 }
704 return ParseGeometry(interp, argv[3], winPtr);
705 } else if ((c == 'g') && (strncmp(argv[1], "grid", length) == 0)
706 && (length >= 3)) {
707 int reqWidth, reqHeight, widthInc, heightInc;
708
709 if ((argc != 3) && (argc != 7)) {
710 Tcl_AppendResult(interp, "wrong # arguments: must be \"",
711 argv[0], " reqsize window ?baseWidth baseHeight ",
712 "widthInc heightInc?\"", (char *) NULL);
713 return TCL_ERROR;
714 }
715 if (argc == 3) {
716 if (wmPtr->sizeHintsFlags & PBaseSize) {
717 sprintf(interp->result, "%d %d %d %d", wmPtr->reqGridWidth,
718 wmPtr->reqGridHeight, wmPtr->widthInc,
719 wmPtr->heightInc);
720 }
721 return TCL_OK;
722 }
723 if (*argv[3] == '\0') {
724 /*
725 * Turn off gridding and reset the width and height
726 * to make sense as ungridded numbers.
727 */
728
729 wmPtr->sizeHintsFlags &= ~(PBaseSize|PResizeInc);
730 wmPtr->widthInc = 1;
731 wmPtr->heightInc = 1;
732 if (wmPtr->width != -1) {
733 wmPtr->width = winPtr->reqWidth + (wmPtr->width
734 - wmPtr->reqGridWidth)*wmPtr->widthInc;
735 wmPtr->height = winPtr->reqHeight + (wmPtr->height
736 - wmPtr->reqGridHeight)*wmPtr->heightInc;
737 }
738 } else {
739 if ((Tcl_GetInt(interp, argv[3], &reqWidth) != TCL_OK)
740 || (Tcl_GetInt(interp, argv[4], &reqHeight) != TCL_OK)
741 || (Tcl_GetInt(interp, argv[5], &widthInc) != TCL_OK)
742 || (Tcl_GetInt(interp, argv[6], &heightInc) != TCL_OK)) {
743 return TCL_ERROR;
744 }
745 if (reqWidth < 0) {
746 interp->result = "baseWidth can't be < 0";
747 return TCL_ERROR;
748 }
749 if (reqHeight < 0) {
750 interp->result = "baseHeight can't be < 0";
751 return TCL_ERROR;
752 }
753 if (widthInc < 0) {
754 interp->result = "widthInc can't be < 0";
755 return TCL_ERROR;
756 }
757 if (heightInc < 0) {
758 interp->result = "heightInc can't be < 0";
759 return TCL_ERROR;
760 }
761 Tk_SetGrid((Tk_Window) tkwin, reqWidth, reqHeight, widthInc,
762 heightInc);
763 }
764 wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
765 goto updateGeom;
766 } else if ((c == 'g') && (strncmp(argv[1], "group", length) == 0)
767 && (length >= 3)) {
768 Tk_Window tkwin2;
769
770 if ((argc != 3) && (argc != 4)) {
771 Tcl_AppendResult(interp, "wrong # arguments: must be \"",
772 argv[0], " group window ?pathName?\"",
773 (char *) NULL);
774 return TCL_ERROR;
775 }
776 if (argc == 3) {
777 if (wmPtr->hints.flags & WindowGroupHint) {
778 interp->result = wmPtr->leaderName;
779 }
780 return TCL_OK;
781 }
782 if (*argv[3] == '\0') {
783 wmPtr->hints.flags &= ~WindowGroupHint;
784 wmPtr->leaderName = NULL;
785 } else {
786 tkwin2 = Tk_NameToWindow(interp, argv[3], tkwin);
787 if (tkwin2 == NULL) {
788 return TCL_ERROR;
789 }
790 Tk_MakeWindowExist(tkwin2);
791 wmPtr->hints.window_group = Tk_WindowId(tkwin2);
792 wmPtr->hints.flags |= WindowGroupHint;
793 wmPtr->leaderName = Tk_PathName(tkwin2);
794 }
795 UpdateHints(winPtr);
796 } else if ((c == 'i') && (strncmp(argv[1], "iconbitmap", length) == 0)
797 && (length >= 5)) {
798 Pixmap pixmap;
799
800 if ((argc != 3) && (argc != 4)) {
801 Tcl_AppendResult(interp, "wrong # arguments: must be \"",
802 argv[0], " iconbitmap window ?bitmap?\"",
803 (char *) NULL);
804 return TCL_ERROR;
805 }
806 if (argc == 3) {
807 if (wmPtr->hints.flags & IconPixmapHint) {
808 interp->result = Tk_NameOfBitmap(wmPtr->hints.icon_pixmap);
809 }
810 return TCL_OK;
811 }
812 if (*argv[3] == '\0') {
813 if (wmPtr->hints.icon_pixmap != None) {
814 Tk_FreeBitmap(wmPtr->hints.icon_pixmap);
815 }
816 wmPtr->hints.flags &= ~IconPixmapHint;
817 } else {
818 pixmap = Tk_GetBitmap(interp, tkwin, Tk_GetUid(argv[3]));
819 if (pixmap == None) {
820 return TCL_ERROR;
821 }
822 wmPtr->hints.icon_pixmap = pixmap;
823 wmPtr->hints.flags |= IconPixmapHint;
824 }
825 UpdateHints(winPtr);
826 } else if ((c == 'i') && (strncmp(argv[1], "iconify", length) == 0)
827 && (length >= 5)) {
828 if (argc != 3) {
829 Tcl_AppendResult(interp, "wrong # arguments: must be \"",
830 argv[0], " iconify window\"", (char *) NULL);
831 return TCL_ERROR;
832 }
833 wmPtr->hints.initial_state = IconicState;
834 if (wmPtr->flags & WM_NEVER_MAPPED) {
835 return TCL_OK;
836 }
837#ifndef X11R3
838 if (XIconifyWindow(winPtr->display, winPtr->window,
839 winPtr->screenNum) == 0) {
840 interp->result =
841 "couldn't send iconify message to window manager";
842 return TCL_ERROR;
843 }
844#else
845 interp->result = "can't iconify under X11R3";
846 return TCL_ERROR;
847#endif
848 } else if ((c == 'i') && (strncmp(argv[1], "iconmask", length) == 0)
849 && (length >= 5)) {
850 Pixmap pixmap;
851
852 if ((argc != 3) && (argc != 4)) {
853 Tcl_AppendResult(interp, "wrong # arguments: must be \"",
854 argv[0], " iconmask window ?bitmap?\"",
855 (char *) NULL);
856 return TCL_ERROR;
857 }
858 if (argc == 3) {
859 if (wmPtr->hints.flags & IconMaskHint) {
860 interp->result = Tk_NameOfBitmap(wmPtr->hints.icon_mask);
861 }
862 return TCL_OK;
863 }
864 if (*argv[3] == '\0') {
865 if (wmPtr->hints.icon_mask != None) {
866 Tk_FreeBitmap(wmPtr->hints.icon_mask);
867 }
868 wmPtr->hints.flags &= ~IconMaskHint;
869 } else {
870 pixmap = Tk_GetBitmap(interp, tkwin, Tk_GetUid(argv[3]));
871 if (pixmap == None) {
872 return TCL_ERROR;
873 }
874 wmPtr->hints.icon_mask = pixmap;
875 wmPtr->hints.flags |= IconMaskHint;
876 }
877 UpdateHints(winPtr);
878 } else if ((c == 'i') && (strncmp(argv[1], "iconname", length) == 0)
879 && (length >= 5)) {
880 if (argc > 4) {
881 Tcl_AppendResult(interp, "wrong # arguments: must be \"",
882 argv[0], " iconname window ?newName?\"", (char *) NULL);
883 return TCL_ERROR;
884 }
885 if (argc == 3) {
886 interp->result = (wmPtr->iconName != NULL) ? wmPtr->iconName : "";
887 return TCL_OK;
888 } else {
889 wmPtr->iconName = Tk_GetUid(argv[3]);
890 if (!(wmPtr->flags & WM_NEVER_MAPPED)) {
891 XSetIconName(winPtr->display, winPtr->window, wmPtr->iconName);
892 }
893 }
894 } else if ((c == 'i') && (strncmp(argv[1], "iconposition", length) == 0)
895 && (length >= 5)) {
896 int x, y;
897
898 if ((argc != 3) && (argc != 5)) {
899 Tcl_AppendResult(interp, "wrong # arguments: must be \"",
900 argv[0], " iconposition window ?x y?\"",
901 (char *) NULL);
902 return TCL_ERROR;
903 }
904 if (argc == 3) {
905 if (wmPtr->hints.flags & IconPositionHint) {
906 sprintf(interp->result, "%d %d", wmPtr->hints.icon_x,
907 wmPtr->hints.icon_y);
908 }
909 return TCL_OK;
910 }
911 if (*argv[3] == '\0') {
912 wmPtr->hints.flags &= ~IconPositionHint;
913 } else {
914 if ((Tcl_GetInt(interp, argv[3], &x) != TCL_OK)
915 || (Tcl_GetInt(interp, argv[4], &y) != TCL_OK)){
916 return TCL_ERROR;
917 }
918 wmPtr->hints.icon_x = x;
919 wmPtr->hints.icon_y = y;
920 wmPtr->hints.flags |= IconPositionHint;
921 }
922 UpdateHints(winPtr);
923 } else if ((c == 'i') && (strncmp(argv[1], "iconwindow", length) == 0)
924 && (length >= 5)) {
925 Tk_Window tkwin2;
926
927 if ((argc != 3) && (argc != 4)) {
928 Tcl_AppendResult(interp, "wrong # arguments: must be \"",
929 argv[0], " iconwindow window ?pathName?\"",
930 (char *) NULL);
931 return TCL_ERROR;
932 }
933 if (argc == 3) {
934 if (wmPtr->hints.flags & IconWindowHint) {
935 interp->result = wmPtr->iconWindowName;
936 }
937 return TCL_OK;
938 }
939 if (*argv[3] == '\0') {
940 wmPtr->hints.flags &= ~IconWindowHint;
941 wmPtr->iconWindowName = NULL;
942 } else {
943 tkwin2 = Tk_NameToWindow(interp, argv[3], tkwin);
944 if (tkwin2 == NULL) {
945 return TCL_ERROR;
946 }
947 Tk_MakeWindowExist(tkwin2);
948 wmPtr->hints.icon_window = Tk_WindowId(tkwin2);
949 wmPtr->hints.flags |= IconWindowHint;
950 wmPtr->iconWindowName = Tk_PathName(tkwin2);
951 }
952 UpdateHints(winPtr);
953 } else if ((c == 'm') && (strncmp(argv[1], "maxsize", length) == 0)
954 && (length >= 2)) {
955 int width, height;
956 if ((argc != 3) && (argc != 5)) {
957 Tcl_AppendResult(interp, "wrong # arguments: must be \"",
958 argv[0], " maxsize window ?width height?\"", (char *) NULL);
959 return TCL_ERROR;
960 }
961 if (argc == 3) {
962 if (wmPtr->sizeHintsFlags & PMaxSize) {
963 sprintf(interp->result, "%d %d", wmPtr->maxWidth,
964 wmPtr->maxHeight);
965 }
966 return TCL_OK;
967 }
968 if (*argv[3] == '\0') {
969 wmPtr->sizeHintsFlags &= ~PMaxSize;
970 } else {
971 if ((Tcl_GetInt(interp, argv[3], &width) != TCL_OK)
972 || (Tcl_GetInt(interp, argv[4], &height) != TCL_OK)) {
973 return TCL_ERROR;
974 }
975 wmPtr->maxWidth = width;
976 wmPtr->maxHeight = height;
977 wmPtr->sizeHintsFlags |= PMaxSize;
978 }
979 wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
980 goto updateGeom;
981 } else if ((c == 'm') && (strncmp(argv[1], "minsize", length) == 0)
982 && (length >= 2)) {
983 int width, height;
984 if ((argc != 3) && (argc != 5)) {
985 Tcl_AppendResult(interp, "wrong # arguments: must be \"",
986 argv[0], " minsize window ?width height?\"", (char *) NULL);
987 return TCL_ERROR;
988 }
989 if (argc == 3) {
990 if (wmPtr->sizeHintsFlags & PMinSize) {
991 sprintf(interp->result, "%d %d", wmPtr->minWidth,
992 wmPtr->minHeight);
993 }
994 return TCL_OK;
995 }
996 if (*argv[3] == '\0') {
997 wmPtr->sizeHintsFlags &= ~PMinSize;
998 } else {
999 if ((Tcl_GetInt(interp, argv[3], &width) != TCL_OK)
1000 || (Tcl_GetInt(interp, argv[4], &height) != TCL_OK)) {
1001 return TCL_ERROR;
1002 }
1003 wmPtr->minWidth = width;
1004 wmPtr->minHeight = height;
1005 wmPtr->sizeHintsFlags |= PMinSize;
1006 }
1007 wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
1008 goto updateGeom;
1009 } else if ((c == 'p') && (strncmp(argv[1], "positionfrom", length) == 0)) {
1010 if ((argc != 3) && (argc != 4)) {
1011 Tcl_AppendResult(interp, "wrong # arguments: must be \"",
1012 argv[0], " positionfrom window ?user/program?\"",
1013 (char *) NULL);
1014 return TCL_ERROR;
1015 }
1016 if (argc == 3) {
1017 if (wmPtr->sizeHintsFlags & USPosition) {
1018 interp->result = "user";
1019 } else if (wmPtr->sizeHintsFlags & PPosition) {
1020 interp->result = "program";
1021 }
1022 return TCL_OK;
1023 }
1024 if (*argv[3] == '\0') {
1025 wmPtr->sizeHintsFlags &= ~(USPosition|PPosition);
1026 } else {
1027 c = argv[3][0];
1028 length = strlen(argv[3]);
1029 if ((c == 'u') && (strncmp(argv[3], "user", length) == 0)) {
1030 wmPtr->sizeHintsFlags &= ~PPosition;
1031 wmPtr->sizeHintsFlags |= USPosition;
1032 } else if ((c == 'p') && (strncmp(argv[3], "program", length) == 0)) {
1033 wmPtr->sizeHintsFlags &= ~USPosition;
1034 wmPtr->sizeHintsFlags |= PPosition;
1035 } else {
1036 Tcl_AppendResult(interp, "bad argument \"", argv[3],
1037 "\": must be program or user", (char *) NULL);
1038 return TCL_ERROR;
1039 }
1040 }
1041 wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
1042 goto updateGeom;
1043 } else if ((c == 'r') && (strncmp(argv[1], "raise", length) == 0)) {
1044 if (argc != 3) {
1045 Tcl_AppendResult(interp, "wrong # arguments: must be \"",
1046 argv[0], " raise window\"", (char *) NULL);
1047 return TCL_ERROR;
1048 }
1049 Tk_MakeWindowExist((Tk_Window) winPtr);
1050 XRaiseWindow(Tk_Display(winPtr), Tk_WindowId(winPtr));
1051 } else if ((c == 's') && (strncmp(argv[1], "sizefrom", length) == 0)) {
1052 if ((argc != 3) && (argc != 4)) {
1053 Tcl_AppendResult(interp, "wrong # arguments: must be \"",
1054 argv[0], " sizefrom window ?user|program?\"",
1055 (char *) NULL);
1056 return TCL_ERROR;
1057 }
1058 if (argc == 3) {
1059 if (wmPtr->sizeHintsFlags & USSize) {
1060 interp->result = "user";
1061 } else if (wmPtr->sizeHintsFlags & PSize) {
1062 interp->result = "program";
1063 }
1064 return TCL_OK;
1065 }
1066 if (*argv[3] == '\0') {
1067 wmPtr->sizeHintsFlags &= ~(USSize|PSize);
1068 } else {
1069 c = argv[3][0];
1070 length = strlen(argv[3]);
1071 if ((c == 'u') && (strncmp(argv[3], "user", length) == 0)) {
1072 wmPtr->sizeHintsFlags &= ~PSize;
1073 wmPtr->sizeHintsFlags |= USSize;
1074 } else if ((c == 'p')
1075 && (strncmp(argv[3], "program", length) == 0)) {
1076 wmPtr->sizeHintsFlags &= ~USSize;
1077 wmPtr->sizeHintsFlags |= PSize;
1078 } else {
1079 Tcl_AppendResult(interp, "bad argument \"", argv[3],
1080 "\": must be program or user", (char *) NULL);
1081 return TCL_ERROR;
1082 }
1083 }
1084 wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
1085 goto updateGeom;
1086 } else if ((c == 't') && (strncmp(argv[1], "title", length) == 0)
1087 && (length >= 2)) {
1088 if (argc > 4) {
1089 Tcl_AppendResult(interp, "wrong # arguments: must be \"",
1090 argv[0], " title window ?newTitle?\"", (char *) NULL);
1091 return TCL_ERROR;
1092 }
1093 if (argc == 3) {
1094 interp->result = (wmPtr->titleUid != NULL) ? wmPtr->titleUid
1095 : winPtr->nameUid;
1096 return TCL_OK;
1097 } else {
1098 wmPtr->titleUid = Tk_GetUid(argv[3]);
1099#ifndef X11R3
1100 if (!(wmPtr->flags & WM_NEVER_MAPPED)) {
1101 XTextProperty textProp;
1102
1103 if (XStringListToTextProperty(&wmPtr->titleUid, 1,
1104 &textProp) != 0) {
1105 XSetWMName(winPtr->display, winPtr->window, &textProp);
1106 XFree((char *) textProp.value);
1107 }
1108 }
1109#endif
1110 }
1111#ifndef X11R3
1112 } else if ((c == 't') && (strncmp(argv[1], "transient", length) == 0)
1113 && (length >= 2)) {
1114 Tk_Window master;
1115
1116 if ((argc != 3) && (argc != 4)) {
1117 Tcl_AppendResult(interp, "wrong # arguments: must be \"",
1118 argv[0], " transient window ?master?\"", (char *) NULL);
1119 return TCL_ERROR;
1120 }
1121 if (argc == 3) {
1122 if (wmPtr->master != None) {
1123 interp->result = wmPtr->masterWindowName;
1124 }
1125 return TCL_OK;
1126 }
1127 if (argv[3][0] == '\0') {
1128 wmPtr->master = None;
1129 wmPtr->masterWindowName = NULL;
1130 } else {
1131 master = Tk_NameToWindow(interp, argv[3], tkwin);
1132 if (master == NULL) {
1133 return TCL_ERROR;
1134 }
1135 Tk_MakeWindowExist(master);
1136 wmPtr->master = Tk_WindowId(master);
1137 wmPtr->masterWindowName = Tk_PathName(master);
1138 }
1139 if (!(wmPtr->flags & WM_NEVER_MAPPED)) {
1140 XSetTransientForHint(winPtr->display, winPtr->window,
1141 wmPtr->master);
1142 }
1143 } else if ((c == 'w') && (strncmp(argv[1], "withdraw", length) == 0)) {
1144 if (argc != 3) {
1145 Tcl_AppendResult(interp, "wrong # arguments: must be \"",
1146 argv[0], " withdraw window\"", (char *) NULL);
1147 return TCL_ERROR;
1148 }
1149 wmPtr->hints.initial_state = WithdrawnState;
1150 if (wmPtr->flags & WM_NEVER_MAPPED) {
1151 return TCL_OK;
1152 }
1153 if (XWithdrawWindow(winPtr->display, winPtr->window,
1154 winPtr->screenNum) == 0) {
1155 interp->result =
1156 "couldn't send withdraw message to window manager";
1157 return TCL_ERROR;
1158 }
1159 winPtr->flags &= ~TK_MAPPED;
1160 } else if ((c == 'p') && (strncmp(argv[1], "protocol", length) == 0)) {
1161 /*
1162 * handle various ICCCM WM_PROTOCOL attributes
1163 */
1164 if (argc < 4) {
1165 Tcl_AppendResult(interp, "wrong # arguments: must be \"",
1166 argv[0], " protocol window type..\"", (char *) NULL);
1167 return TCL_ERROR;
1168 }
1169 if (!strcmp(argv[3], "delete")) {
1170 return WmProtocolCmd(interp, &(wmPtr->deleteCmd), argc, argv);
1171 } else {
1172 Tcl_AppendResult(interp, argv[0],
1173 ": bad argument ", argv[3], " must be: ",
1174 "delete", (char *) NULL);
1175 return TCL_ERROR;
1176 }
1177#endif
1178 } else {
1179 Tcl_AppendResult(interp, "unknown or ambiguous option \"", argv[1],
1180 "\": must be aspect, deiconify, focusmodel, ",
1181 "fullscreen, geometry, grid, group, iconbitmap, ",
1182 "iconify, iconmask, iconname, iconposition, ",
1183 "iconwindow, maxsize, minsize, positionfrom, raise, ",
1184 "sizefrom, title, transient, withdraw, or protocol",
1185 (char *) NULL);
1186 return TCL_ERROR;
1187 }
1188 return TCL_OK;
1189
1190 updateGeom:
1191 if (!(wmPtr->flags & (WM_UPDATE_PENDING|WM_NEVER_MAPPED))) {
1192 Tk_DoWhenIdle(UpdateGeometryInfo, (ClientData) winPtr);
1193 wmPtr->flags |= WM_UPDATE_PENDING;
1194 }
1195 return TCL_OK;
1196}
1197\f
1198/*
1199 *----------------------------------------------------------------------
1200 *
1201 * Tk_SetGrid --
1202 *
1203 * This procedure is invoked by a widget when it wishes to set a grid
1204 * coordinate system that controls the size of a top-level window.
1205 * It provides a C interface equivalent to the "wm grid" command and
1206 * is usually asscoiated with the -setgrid option.
1207 *
1208 * Results:
1209 * None.
1210 *
1211 * Side effects:
1212 * Grid-related information will be passed to the window manager, so
1213 * that the top-level window associated with tkwin will resize on
1214 * even grid units.
1215 *
1216 *----------------------------------------------------------------------
1217 */
1218
1219void
1220Tk_SetGrid(tkwin, reqWidth, reqHeight, widthInc, heightInc)
1221 Tk_Window tkwin; /* Token for window. New window mgr info
1222 * will be posted for the top-level window
1223 * associated with this window. */
1224 int reqWidth; /* Width (in grid units) corresponding to
1225 * the requested geometry for tkwin. */
1226 int reqHeight; /* Height (in grid units) corresponding to
1227 * the requested geometry for tkwin. */
1228 int widthInc, heightInc; /* Pixel increments corresponding to a
1229 * change of one grid unit. */
1230{
1231 TkWindow *winPtr = (TkWindow *) tkwin;
1232 register WmInfo *wmPtr;
1233
1234 /*
1235 * Find the top-level window for tkwin, plus the window manager
1236 * information.
1237 */
1238
1239 while (!(winPtr->flags & TK_TOP_LEVEL)) {
1240 winPtr = winPtr->parentPtr;
1241 }
1242 wmPtr = winPtr->wmInfoPtr;
1243
1244 if ((wmPtr->reqGridWidth == reqWidth)
1245 && (wmPtr->reqGridHeight != reqHeight)
1246 && (wmPtr->widthInc != widthInc)
1247 && (wmPtr->heightInc != heightInc)
1248 && ((wmPtr->sizeHintsFlags & (PBaseSize|PResizeInc))
1249 == PBaseSize|PResizeInc)) {
1250 return;
1251 }
1252
1253 /*
1254 * If gridding was previously off, then forget about any window
1255 * size requests made by the user or via "wm geometry": these are
1256 * in pixel units and there's no easy way to translate them to
1257 * grid units since the new requested size of the top-level window in
1258 * pixels may not yet have been registered yet (it may filter up
1259 * the hierarchy in DoWhenIdle handlers).
1260 */
1261
1262 if (!(wmPtr->sizeHintsFlags & PBaseSize)) {
1263 wmPtr->width = -1;
1264 wmPtr->height = -1;
1265 }
1266
1267 /*
1268 * Set the new gridding information, and start the process of passing
1269 * all of this information to the window manager.
1270 */
1271
1272 wmPtr->reqGridWidth = reqWidth;
1273 wmPtr->reqGridHeight = reqHeight;
1274 wmPtr->widthInc = widthInc;
1275 wmPtr->heightInc = heightInc;
1276 wmPtr->sizeHintsFlags |= PBaseSize|PResizeInc;
1277 wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
1278 if (!(wmPtr->flags & (WM_UPDATE_PENDING|WM_NEVER_MAPPED))) {
1279 Tk_DoWhenIdle(UpdateGeometryInfo, (ClientData) winPtr);
1280 wmPtr->flags |= WM_UPDATE_PENDING;
1281 }
1282}
1283\f
1284/*
1285 *----------------------------------------------------------------------
1286 *
1287 * TopLevelEventProc --
1288 *
1289 * This procedure is invoked when a top-level (or other externally-
1290 * managed window) is restructured in any way.
1291 *
1292 * Results:
1293 * None.
1294 *
1295 * Side effects:
1296 * Tk's internal data structures for the window get modified to
1297 * reflect the structural change.
1298 *
1299 *----------------------------------------------------------------------
1300 */
1301
1302static void
1303TopLevelEventProc(clientData, eventPtr)
1304 ClientData clientData; /* Window for which event occurred. */
1305 XEvent *eventPtr; /* Event that just happened. */
1306{
1307 register TkWindow *winPtr = (TkWindow *) clientData;
1308
1309 if (eventPtr->type == DestroyNotify) {
1310 if (!(winPtr->flags & TK_ALREADY_DEAD)) {
1311 Tk_DestroyWindow((Tk_Window) winPtr);
1312 }
1313 } else if (eventPtr->type == ConfigureNotify) {
1314 register WmInfo *wmPtr = winPtr->wmInfoPtr;
1315 int diff, x, y;
1316
1317 /*
1318 * A top-level window has been reconfigured. Problem #1:
1319 * discard stale information. If the application has recently
1320 * tried to reconfigure itself, ignore all events until the
1321 * response to that reconfiguration arrives (the response is
1322 * assumed to be the first ConfigureNotify that arrives after
1323 * the server has seen the request; this suffers from potential
1324 * races with user actions, but it's the best I can think of
1325 * right now).
1326 */
1327
1328 diff = eventPtr->xconfigure.serial - wmPtr->configRequest;
1329 if (diff < 0) {
1330 return;
1331 }
1332
1333 /*
1334 * Problem #2: reparenting window managers. If the window
1335 * manager reparents a top-level window then the x and y
1336 * information that comes in events for the window is wrong:
1337 * it gives the location of the window inside its decorative
1338 * parent, rather than the location of the window in root
1339 * coordinates, which is what we want. Window managers
1340 * are supposed to send synthetic events with the correct
1341 * information, but ICCCM doesn't require them to do this
1342 * under all conditions, and the information provided doesn't
1343 * include everything we need here. So, the code below
1344 * maintains a bunch of information about the parent window.
1345 * If the window hasn't been reparented, we pretend that
1346 * there is a parent shrink-wrapped around the window.
1347 */
1348
1349 if (wmPtr->reparent == None) {
1350 noReparent:
1351 winPtr->changes.x = eventPtr->xconfigure.x;
1352 winPtr->changes.y = eventPtr->xconfigure.y;
1353 wmPtr->parentWidth = eventPtr->xconfigure.width
1354 + 2*eventPtr->xconfigure.border_width;
1355 wmPtr->parentHeight = eventPtr->xconfigure.height
1356 + 2*eventPtr->xconfigure.border_width;
1357 } else {
1358 unsigned int width, height, bd, dummy;
1359 Window dummy2;
1360 Status status;
1361 Tk_ErrorHandler handler;
1362
1363 handler = Tk_CreateErrorHandler(winPtr->display, BadDrawable, -1,
1364 -1, (Tk_ErrorProc *) NULL, (ClientData) NULL);
1365 status = XGetGeometry(winPtr->display, wmPtr->reparent,
1366 &dummy2, &x, &y, &width, &height, &bd, &dummy);
1367 Tk_DeleteErrorHandler(handler);
1368 if (status == 0) {
1369 /*
1370 * It appears that the reparented parent went away and
1371 * no-one told us. Reset the window to indicate that
1372 * it's not reparented, then handle it as a non-reparented
1373 * window.
1374 */
1375 wmPtr->reparent = None;
1376 wmPtr->flags &= ~WM_NESTED_REPARENT;
1377 wmPtr->xInParent = wmPtr->yInParent = 0;
1378 goto noReparent;
1379 }
1380 wmPtr->parentWidth = width + 2*bd;
1381 wmPtr->parentHeight = height + 2*bd;
1382 winPtr->changes.x = x;
1383 winPtr->changes.y = y;
1384 if (wmPtr->flags & WM_NESTED_REPARENT) {
1385 int xOffset, yOffset;
1386
1387 (void) XTranslateCoordinates(winPtr->display, winPtr->window,
1388 wmPtr->reparent, 0, 0, &xOffset, &yOffset, &dummy2);
1389 wmPtr->xInParent = xOffset + bd - winPtr->changes.border_width;
1390 wmPtr->yInParent = yOffset + bd - winPtr->changes.border_width;
1391 } else {
1392 if (!eventPtr->xconfigure.send_event) {
1393 wmPtr->xInParent = eventPtr->xconfigure.x + bd;
1394 wmPtr->yInParent = eventPtr->xconfigure.y + bd;
1395 }
1396 }
1397 winPtr->changes.x = x + wmPtr->xInParent;
1398 winPtr->changes.y = y + wmPtr->yInParent;
1399 }
1400
1401 /*
1402 * Problem #3: if the window size or location was changed
1403 * externally, update the geometry information in wmPtr to make
1404 * it look just as if the user had typed a "wm geometry" command
1405 * to make the change. There are many tricky situations to deal
1406 * with:
1407 * (a) the event is simply a reflection of an internal geometry
1408 * request from the window's widgets (must leave width and
1409 * height alone in this case).
1410 * (b) the window manager might respond to a size request from
1411 * us with a different size than requested (e.g. it might
1412 * have a minimum allowable window size). Because of this,
1413 * can't just compare new size with requested size to determine
1414 * whether this event is a reflection of an internal request
1415 * from within the application. Use WM_CONFIG_PENDING flag
1416 * instead.
1417 * (c) ConfigureNotify events also arise if the window has been
1418 * moved, even if its size hasn't changed. Must distinguish
1419 * between the user moving the window and the user resizing
1420 * the window.
1421 */
1422
1423 if (wmPtr->flags & WM_CONFIG_PENDING) {
1424 int diff;
1425 /*
1426 * Size change is just a reflection of something coming from
1427 * application.
1428 */
1429
1430 diff = eventPtr->xconfigure.serial - wmPtr->configRequest;
1431 if (diff >= 0) {
1432 if (wmPtr->flags & WM_CONFIG_AGAIN) {
1433 if (!(wmPtr->flags & WM_UPDATE_PENDING)) {
1434 Tk_DoWhenIdle(UpdateGeometryInfo, (ClientData) winPtr);
1435 wmPtr->flags |= WM_UPDATE_PENDING;
1436 }
1437 }
1438 wmPtr->flags &= ~(WM_CONFIG_PENDING|WM_CONFIG_AGAIN);
1439 }
1440 } else if ((winPtr->changes.width != eventPtr->xconfigure.width)
1441 || (winPtr->changes.height != eventPtr->xconfigure.height)) {
1442 wmPtr->configWidth = -1;
1443 wmPtr->configHeight = -1;
1444 if (IS_GRIDDED(wmPtr)) {
1445 wmPtr->width = wmPtr->reqGridWidth
1446 + (eventPtr->xconfigure.width
1447 - winPtr->reqWidth)/wmPtr->widthInc;
1448 if (wmPtr->width < 0) {
1449 wmPtr->width = 0;
1450 }
1451 wmPtr->height = wmPtr->reqGridHeight
1452 + (eventPtr->xconfigure.height
1453 - winPtr->reqHeight)/wmPtr->heightInc;
1454 if (wmPtr->height < 0) {
1455 wmPtr->height = 0;
1456 }
1457 } else if ((eventPtr->xconfigure.width != winPtr->changes.width)
1458 || (eventPtr->xconfigure.height
1459 != winPtr->changes.height)) {
1460 /*
1461 * The check above is needed so we don't think the user
1462 * requested a new size when all he/she did was to move
1463 * the window.
1464 */
1465
1466 wmPtr->width = eventPtr->xconfigure.width;
1467 wmPtr->height = eventPtr->xconfigure.height;
1468 }
1469 }
1470
1471 winPtr->changes.width = eventPtr->xconfigure.width;
1472 winPtr->changes.height = eventPtr->xconfigure.height;
1473 winPtr->changes.border_width = eventPtr->xconfigure.border_width;
1474 winPtr->changes.sibling = eventPtr->xconfigure.above;
1475 winPtr->changes.stack_mode = Above;
1476
1477 x = winPtr->changes.x - wmPtr->xInParent;
1478 if (wmPtr->flags & WM_NEGATIVE_X) {
1479 x = DisplayWidth(winPtr->display, winPtr->screenNum)
1480 - (x + wmPtr->parentWidth);
1481 }
1482 y = winPtr->changes.y - wmPtr->yInParent;
1483 if (wmPtr->flags & WM_NEGATIVE_Y) {
1484 y = DisplayHeight(winPtr->display, winPtr->screenNum)
1485 - (y + wmPtr->parentHeight);
1486 }
1487 if ((x != wmPtr->x) || (y != wmPtr->y)) {
1488 wmPtr->x = x;
1489 wmPtr->y = y;
1490 }
1491 } else if (eventPtr->type == MapNotify) {
1492 winPtr->flags |= TK_MAPPED;
1493 } else if (eventPtr->type == UnmapNotify) {
1494 winPtr->flags &= ~TK_MAPPED;
1495 } else if (eventPtr->type == ReparentNotify) {
1496 WmInfo *wmPtr = winPtr->wmInfoPtr;
1497 Window root, *children, dummy2, *virtualRootPtr;
1498 Atom virtualRootAtom, actualType;
1499 int actualFormat;
1500 unsigned long numItems, bytesAfter;
1501 unsigned int dummy;
1502
1503 /*
1504 * Locate the ancestor of this window that is just below the
1505 * root window for the screen (could be the window itself).
1506 * This code is a bit tricky because it allows for the
1507 * possibility of a virtual root window, which is identified
1508 * with a property named __SWM_VROOT.
1509 */
1510
1511 virtualRootAtom = Tk_InternAtom((Tk_Window) winPtr, "__SWM_VROOT");
1512 wmPtr->flags &= ~WM_NESTED_REPARENT;
1513 wmPtr->reparent = None;
1514 root = eventPtr->xreparent.parent;
1515 while (root != RootWindow(winPtr->display, winPtr->screenNum)) {
1516 Tk_ErrorHandler handler1, handler2;
1517 int status;
1518
1519 virtualRootPtr = NULL;
1520
1521 handler1 =
1522 Tk_CreateErrorHandler(winPtr->display, BadDrawable,
1523 -1, -1, (Tk_ErrorProc *) NULL,
1524 (ClientData) NULL);
1525 handler2 =
1526 Tk_CreateErrorHandler(winPtr->display, BadWindow,
1527 -1, -1, (Tk_ErrorProc *) NULL,
1528 (ClientData) NULL);
1529
1530 status = XGetWindowProperty(winPtr->display, root,
1531 virtualRootAtom,
1532 0, (long) 1, False, XA_WINDOW,
1533 &actualType, &actualFormat,
1534 &numItems, &bytesAfter,
1535 (unsigned char **) &virtualRootPtr);
1536
1537 Tk_DeleteErrorHandler(handler1);
1538 Tk_DeleteErrorHandler(handler2);
1539
1540 if (status == Success) {
1541 if (virtualRootPtr != NULL) {
1542 if (*virtualRootPtr != root) {
1543 panic("TopLevelEventProc confused over virtual root");
1544 }
1545 XFree((char *) virtualRootPtr);
1546 break;
1547 }
1548 }
1549 wmPtr->reparent = root;
1550 (void) XQueryTree(winPtr->display, root, &dummy2, &root,
1551 &children, &dummy);
1552 XFree((char *) children);
1553 }
1554
1555 /*
1556 * The ancestor just below the (virtual) root is in wmPtr->reparent
1557 * now, and the (virtual) root is in root.
1558 */
1559
1560
1561 if (eventPtr->xreparent.parent == root) {
1562 wmPtr->reparent = None;
1563 wmPtr->flags &= ~WM_NESTED_REPARENT;
1564 wmPtr->parentWidth = winPtr->changes.width
1565 + 2*winPtr->changes.border_width;
1566 wmPtr->parentHeight = winPtr->changes.height
1567 + 2*winPtr->changes.border_width;
1568 wmPtr->xInParent = wmPtr->yInParent = 0;
1569 winPtr->changes.x = eventPtr->xreparent.x;
1570 winPtr->changes.y = eventPtr->xreparent.y;
1571 } else {
1572 int x, y, xOffset, yOffset;
1573 unsigned int width, height, bd;
1574
1575 if (wmPtr->reparent != eventPtr->xreparent.parent) {
1576 wmPtr->flags |= WM_NESTED_REPARENT;
1577 } else {
1578 wmPtr->flags &= ~WM_NESTED_REPARENT;
1579 }
1580
1581 /*
1582 * Compute and save information about reparent and about
1583 * the window's position in reparent.
1584 */
1585
1586 (void) XGetGeometry(winPtr->display, wmPtr->reparent,
1587 &dummy2, &x, &y, &width, &height, &bd, &dummy);
1588 wmPtr->parentWidth = width + 2*bd;
1589 wmPtr->parentHeight = height + 2*bd;
1590 (void) XTranslateCoordinates(winPtr->display, winPtr->window,
1591 wmPtr->reparent, 0, 0, &xOffset, &yOffset, &dummy2);
1592 wmPtr->xInParent = xOffset + bd - winPtr->changes.border_width;
1593 wmPtr->yInParent = yOffset + bd - winPtr->changes.border_width;
1594 winPtr->changes.x = x + xOffset;
1595 winPtr->changes.y = y + yOffset;
1596 }
1597 } else if ((eventPtr->type == EnterNotify)
1598 || (eventPtr->type == LeaveNotify)) {
1599 TkFocusEventProc(winPtr, eventPtr);
1600 }
1601}
1602\f
1603/*
1604 *----------------------------------------------------------------------
1605 *
1606 * TopLevelReqProc --
1607 *
1608 * This procedure is invoked by the geometry manager whenever
1609 * the requested size for a top-level window is changed.
1610 *
1611 * Results:
1612 * None.
1613 *
1614 * Side effects:
1615 * Arrange for the window to be resized to satisfy the request
1616 * (this happens as a when-idle action).
1617 *
1618 *----------------------------------------------------------------------
1619 */
1620
1621 /* ARGSUSED */
1622static void
1623TopLevelReqProc(dummy, tkwin)
1624 ClientData dummy; /* Not used. */
1625 Tk_Window tkwin; /* Information about window. */
1626{
1627 TkWindow *winPtr = (TkWindow *) tkwin;
1628 WmInfo *wmPtr;
1629
1630 wmPtr = winPtr->wmInfoPtr;
1631 if ((wmPtr->prevReqWidth == winPtr->reqWidth)
1632 && (wmPtr->prevReqHeight == winPtr->reqHeight)) {
1633 return;
1634 }
1635 wmPtr->prevReqWidth = winPtr->reqWidth;
1636 wmPtr->prevReqHeight = winPtr->reqHeight;
1637 wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
1638 if (!(wmPtr->flags & (WM_UPDATE_PENDING|WM_NEVER_MAPPED))) {
1639 Tk_DoWhenIdle(UpdateGeometryInfo, (ClientData) winPtr);
1640 wmPtr->flags |= WM_UPDATE_PENDING;
1641 }
1642}
1643\f
1644/*
1645 *----------------------------------------------------------------------
1646 *
1647 * UpdateGeometryInfo --
1648 *
1649 * This procedure is invoked when a top-level window is first
1650 * mapped, and also as a when-idle procedure, to bring the
1651 * geometry and/or position of a top-level window back into
1652 * line with what has been requested by the user and/or widgets.
1653 *
1654 * Results:
1655 * None.
1656 *
1657 * Side effects:
1658 * The window's size and location may change, unless the WM prevents
1659 * that from happening.
1660 *
1661 *----------------------------------------------------------------------
1662 */
1663
1664static void
1665UpdateGeometryInfo(clientData)
1666 ClientData clientData; /* Pointer to the window's record. */
1667{
1668 register TkWindow *winPtr = (TkWindow *) clientData;
1669 register WmInfo *wmPtr = winPtr->wmInfoPtr;
1670 int x, y, width, height;
1671
1672 /*
1673 * It isn't safe to issue a new reconfigure request while there is
1674 * another reconfigure request outstanding. If this happens, skip
1675 * the second reconfigure operation but set a flag so it will get
1676 * done with the first one finishes.
1677 */
1678
1679 wmPtr->flags &= ~WM_UPDATE_PENDING;
1680 if (wmPtr->flags & WM_CONFIG_PENDING) {
1681 wmPtr->flags |= WM_CONFIG_AGAIN;
1682 return;
1683 }
1684
1685 /*
1686 * Compute the new size for the top-level window. See the
1687 * user documentation for details on this, but the size
1688 * requested depends on (a) the size requested internally
1689 * by the window's widgets, (b) the size requested by the
1690 * user in a "wm geometry" command or via wm-based interactive
1691 * resizing (if any), and (c) whether or not the window
1692 * gridded. Don't permit sizes <= 0 because this upsets
1693 * the X server.
1694 */
1695
1696 if (wmPtr->width == -1) {
1697 width = winPtr->reqWidth;
1698 height = winPtr->reqHeight;
1699 } else if (IS_GRIDDED(wmPtr)) {
1700 width = winPtr->reqWidth
1701 + (wmPtr->width - wmPtr->reqGridWidth)*wmPtr->widthInc;
1702 height = winPtr->reqHeight
1703 + (wmPtr->height - wmPtr->reqGridHeight)*wmPtr->heightInc;
1704 } else {
1705 width = wmPtr->width;
1706 height = wmPtr->height;
1707 }
1708 if (width <= 0) {
1709 width = 1;
1710 }
1711 if (height <= 0) {
1712 height = 1;
1713 }
1714
1715 /*
1716 * Compute the new position for the window. This is tricky, because
1717 * we need to include the border widths supplied by a reparented
1718 * parent in this calculation, but can't use the parent's current
1719 * overall size since that may change as a result of this code.
1720 */
1721
1722 if (wmPtr->flags & WM_NEGATIVE_X) {
1723 x = DisplayWidth(winPtr->display, winPtr->screenNum) - wmPtr->x
1724 - (width + (wmPtr->parentWidth - winPtr->changes.width))
1725 + wmPtr->xInParent;
1726 } else {
1727 x = wmPtr->x + wmPtr->xInParent;
1728 }
1729 if (wmPtr->flags & WM_NEGATIVE_Y) {
1730 y = DisplayHeight(winPtr->display, winPtr->screenNum) - wmPtr->y
1731 - (height + (wmPtr->parentHeight - winPtr->changes.height))
1732 + wmPtr->yInParent;
1733 } else {
1734 y = wmPtr->y + wmPtr->yInParent;
1735 }
1736
1737 /*
1738 * If the window's size is going to change and the window is
1739 * supposed to not be resizable by the user, then we have to
1740 * update the size hints. There may also be a size-hint-update
1741 * request pending from somewhere else, too.
1742 */
1743
1744 if (((width != winPtr->changes.width) || (width != winPtr->changes.width))
1745 && !IS_GRIDDED(wmPtr)
1746 && ((wmPtr->sizeHintsFlags & (PMinSize|PMaxSize)) == 0)) {
1747 wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
1748 }
1749 if (wmPtr->flags & WM_UPDATE_SIZE_HINTS) {
1750 UpdateSizeHints(winPtr);
1751 }
1752
1753 /*
1754 * If the geometry hasn't changed, be careful to use only a
1755 * resize operation. This is because of bugs in some window
1756 * managers (e.g. twm, as of 4/24/91) where they don't interpret
1757 * coordinates according to ICCCM.
1758 */
1759
1760 if ((x != winPtr->changes.x) || (y != winPtr->changes.y)) {
1761 wmPtr->configRequest = XNextRequest(winPtr->display);
1762 wmPtr->configWidth = width;
1763 wmPtr->configHeight = height;
1764 Tk_MoveResizeWindow((Tk_Window) winPtr, x, y, (unsigned) width,
1765 (unsigned) height);
1766 wmPtr->flags |= WM_CONFIG_PENDING;
1767 } else if ((width != wmPtr->configWidth)
1768 || (height != wmPtr->configHeight)) {
1769 wmPtr->configRequest = XNextRequest(winPtr->display);
1770 wmPtr->configWidth = width;
1771 wmPtr->configHeight = height;
1772 Tk_ResizeWindow((Tk_Window) winPtr, (unsigned) width,
1773 (unsigned) height);
1774 wmPtr->flags |= WM_CONFIG_PENDING;
1775 }
1776}
1777\f
1778/*
1779 *--------------------------------------------------------------
1780 *
1781 * UpdateSizeHints --
1782 *
1783 * This procedure is called to update the window manager's
1784 * size hints information from the information in a WmInfo
1785 * structure.
1786 *
1787 * Results:
1788 * None.
1789 *
1790 * Side effects:
1791 * Properties get changed for winPtr.
1792 *
1793 *--------------------------------------------------------------
1794 */
1795
1796static void
1797UpdateSizeHints(winPtr)
1798 TkWindow *winPtr;
1799{
1800 register WmInfo *wmPtr = winPtr->wmInfoPtr;
1801 XSizeHints *hintsPtr;
1802
1803 wmPtr->flags &= ~WM_UPDATE_SIZE_HINTS;
1804
1805#ifndef X11R3
1806 hintsPtr = XAllocSizeHints();
1807 if (hintsPtr == NULL) {
1808 return;
1809 }
1810
1811 /*
1812 * Compute the pixel-based sizes for the various fields in the
1813 * size hints structure, based on the grid-based sizes in
1814 * our structure.
1815 */
1816
1817 if (IS_GRIDDED(wmPtr)) {
1818 hintsPtr->base_width = winPtr->reqWidth
1819 - (wmPtr->reqGridWidth * wmPtr->widthInc);
1820 if (hintsPtr->base_width < 0) {
1821 hintsPtr->base_width = 0;
1822 }
1823 hintsPtr->base_height = winPtr->reqHeight
1824 - (wmPtr->reqGridHeight * wmPtr->heightInc);
1825 if (hintsPtr->base_height < 0) {
1826 hintsPtr->base_height = 0;
1827 }
1828 hintsPtr->min_width = hintsPtr->base_width
1829 + (wmPtr->minWidth * wmPtr->widthInc);
1830 hintsPtr->min_height = hintsPtr->base_height
1831 + (wmPtr->minHeight * wmPtr->heightInc);
1832 hintsPtr->max_width = hintsPtr->base_width
1833 + (wmPtr->maxWidth * wmPtr->widthInc);
1834 hintsPtr->max_height = hintsPtr->base_height
1835 + (wmPtr->maxHeight * wmPtr->heightInc);
1836 } else {
1837 hintsPtr->min_width = wmPtr->minWidth;
1838 hintsPtr->min_height = wmPtr->minHeight;
1839 hintsPtr->max_width = wmPtr->maxWidth;
1840 hintsPtr->max_height = wmPtr->maxHeight;
1841 hintsPtr->base_width = 0;
1842 hintsPtr->base_height = 0;
1843 }
1844 hintsPtr->width_inc = wmPtr->widthInc;
1845 hintsPtr->height_inc = wmPtr->heightInc;
1846 hintsPtr->min_aspect.x = wmPtr->minAspect.x;
1847 hintsPtr->min_aspect.y = wmPtr->minAspect.y;
1848 hintsPtr->max_aspect.x = wmPtr->maxAspect.x;
1849 hintsPtr->max_aspect.y = wmPtr->maxAspect.y;
1850 hintsPtr->win_gravity = wmPtr->gravity;
1851 hintsPtr->flags = wmPtr->sizeHintsFlags;
1852
1853 /*
1854 * If a window is non-gridded and no minimum or maximum size has
1855 * been specified, don't let the window be resized at all.
1856 */
1857
1858 if (!IS_GRIDDED(wmPtr)
1859 && ((wmPtr->sizeHintsFlags & (PMinSize|PMaxSize)) == 0)) {
1860 int width, height;
1861
1862 width = wmPtr->width;
1863 height = wmPtr->height;
1864 if (width < 0) {
1865 width = winPtr->reqWidth;
1866 height = winPtr->reqHeight;
1867 }
1868 hintsPtr->min_width = hintsPtr->max_width = width;
1869 hintsPtr->min_height = hintsPtr->max_height = height;
1870 hintsPtr->flags |= PMinSize|PMaxSize;
1871 }
1872
1873 /*
1874 * If min or max size isn't specified, fill in with extreme values
1875 * rather than leaving unspecified. Otherwise window manager may
1876 * do someting counter-intuitive like the last value ever specified.
1877 */
1878
1879 if (!(hintsPtr->flags & PMinSize)) {
1880 hintsPtr->min_width = hintsPtr->min_height = 0;
1881 hintsPtr->flags |= PMinSize;
1882 }
1883 if (!(hintsPtr->flags & PMaxSize)) {
1884 hintsPtr->max_width = hintsPtr->max_height = 1000000;
1885 hintsPtr->flags |= PMaxSize;
1886 }
1887
1888 XSetWMNormalHints(winPtr->display, winPtr->window, hintsPtr);
1889
1890 XFree((char *) hintsPtr);
1891#endif /* X11R3 */
1892}
1893\f
1894/*
1895 *--------------------------------------------------------------
1896 *
1897 * UpdateHints --
1898 *
1899 * This procedure is called to update the window manager's
1900 * hints information from the information in a WmInfo
1901 * structure.
1902 *
1903 * Results:
1904 * None.
1905 *
1906 * Side effects:
1907 * Properties get changed for winPtr.
1908 *
1909 *--------------------------------------------------------------
1910 */
1911
1912static void
1913UpdateHints(winPtr)
1914 TkWindow *winPtr;
1915{
1916 WmInfo *wmPtr = winPtr->wmInfoPtr;
1917
1918 if (wmPtr->flags & WM_NEVER_MAPPED) {
1919 return;
1920 }
1921 XSetWMHints(winPtr->display, winPtr->window, &wmPtr->hints);
1922}
1923\f
1924/*
1925 *--------------------------------------------------------------
1926 *
1927 * ParseGeometry --
1928 *
1929 * This procedure parses a geometry string and updates
1930 * information used to control the geometry of a top-level
1931 * window.
1932 *
1933 * Results:
1934 * A standard Tcl return value, plus an error message in
1935 * interp->result if an error occurs.
1936 *
1937 * Side effects:
1938 * The size and/or location of winPtr may change.
1939 *
1940 *--------------------------------------------------------------
1941 */
1942
1943static int
1944ParseGeometry(interp, string, winPtr)
1945 Tcl_Interp *interp; /* Used for error reporting. */
1946 char *string; /* String containing new geometry. Has the
1947 * standard form "=wxh+x+y". */
1948 TkWindow *winPtr; /* Pointer to top-level window whose
1949 * geometry is to be changed. */
1950{
1951 register WmInfo *wmPtr = winPtr->wmInfoPtr;
1952 int x, y, width, height, flags;
1953 char *end;
1954 register char *p = string;
1955
1956 /*
1957 * The leading "=" is optional.
1958 */
1959
1960 if (*p == '=') {
1961 p++;
1962 }
1963
1964 /*
1965 * Parse the width and height, if they are present. Don't
1966 * actually update any of the fields of wmPtr until we've
1967 * successfully parsed the entire geometry string.
1968 */
1969
1970 width = wmPtr->width;
1971 height = wmPtr->height;
1972 x = wmPtr->x;
1973 y = wmPtr->y;
1974 flags = wmPtr->flags;
1975 if (isdigit(*p)) {
1976 width = strtoul(p, &end, 10);
1977 p = end;
1978 if (*p != 'x') {
1979 goto error;
1980 }
1981 p++;
1982 if (!isdigit(*p)) {
1983 goto error;
1984 }
1985 height = strtoul(p, &end, 10);
1986 p = end;
1987 }
1988
1989 /*
1990 * Parse the X and Y coordinates, if they are present.
1991 */
1992
1993 if (*p != '\0') {
1994 flags &= ~(WM_NEGATIVE_X | WM_NEGATIVE_Y);
1995 if (*p == '-') {
1996 flags |= WM_NEGATIVE_X;
1997 } else if (*p != '+') {
1998 goto error;
1999 }
2000 x = strtol(p+1, &end, 10);
2001 p = end;
2002 if (*p == '-') {
2003 flags |= WM_NEGATIVE_Y;
2004 } else if (*p != '+') {
2005 goto error;
2006 }
2007 y = strtol(p+1, &end, 10);
2008 if (*end != '\0') {
2009 goto error;
2010 }
2011
2012 /*
2013 * Assume that the geometry information came from the user,
2014 * unless an explicit source has been specified. Otherwise
2015 * most window managers assume that the size hints were
2016 * program-specified and they ignore them.
2017 */
2018
2019 if ((wmPtr->sizeHintsFlags & (USPosition|PPosition)) == 0) {
2020 wmPtr->sizeHintsFlags |= USPosition;
2021 wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
2022 }
2023 }
2024
2025 /*
2026 * Everything was parsed OK. Update the fields of *wmPtr and
2027 * arrange for the appropriate information to be percolated out
2028 * to the window manager at the next idle moment.
2029 */
2030
2031 wmPtr->width = width;
2032 wmPtr->height = height;
2033 wmPtr->x = x;
2034 wmPtr->y = y;
2035 wmPtr->flags = flags;
2036
2037 if (!(wmPtr->flags & (WM_UPDATE_PENDING|WM_NEVER_MAPPED))) {
2038 Tk_DoWhenIdle(UpdateGeometryInfo, (ClientData) winPtr);
2039 wmPtr->flags |= WM_UPDATE_PENDING;
2040 }
2041 return TCL_OK;
2042
2043 error:
2044 Tcl_AppendResult(interp, "bad geometry specifier \"",
2045 string, "\"", (char *) NULL);
2046 return TCL_ERROR;
2047}
2048\f
2049/*
2050 *----------------------------------------------------------------------
2051 *
2052 * Tk_GetRootCoords --
2053 *
2054 * Given a token for a window, this procedure traces through the
2055 * window's lineage to find the root-window coordinates corresponding
2056 * to point (0,0) in the window.
2057 *
2058 * Results:
2059 * The locations pointed to by xPtr and yPtr are filled in with
2060 * the root coordinates of the (0,0) point in tkwin.
2061 *
2062 * Side effects:
2063 * None.
2064 *
2065 *----------------------------------------------------------------------
2066 */
2067
2068void
2069Tk_GetRootCoords(tkwin, xPtr, yPtr)
2070 Tk_Window tkwin; /* Token for window. */
2071 int *xPtr; /* Where to store x-displacement of (0,0). */
2072 int *yPtr; /* Where to store y-displacement of (0,0). */
2073{
2074 int x, y;
2075 register TkWindow *winPtr = (TkWindow *) tkwin;
2076
2077 /*
2078 * Search back through this window's parents all the way to a
2079 * top-level window, combining the offsets of each window within
2080 * its parent.
2081 */
2082
2083 x = y = 0;
2084 while (1) {
2085 x += winPtr->changes.x + winPtr->changes.border_width;
2086 y += winPtr->changes.y + winPtr->changes.border_width;
2087 if (winPtr->flags & TK_TOP_LEVEL) {
2088 break;
2089 }
2090 winPtr = winPtr->parentPtr;
2091 }
2092 *xPtr = x;
2093 *yPtr = y;
2094}
2095
2096
2097\f
2098/*
2099 *--------------------------------------------------------------
2100 *
2101 * TkWmSetWmProtocols --
2102 * Set the ICCCM WM_PROTOCOLS to be honored by this window.
2103 * Currently, it is just WM_DELETE_WINDOW.
2104 *
2105 * Results:
2106 * None.
2107 *
2108 * Side effects:
2109 * A window property may get updated.
2110 *
2111 *--------------------------------------------------------------
2112 */
2113
2114void
2115TkWmSetWmProtocols(winPtr)
2116 TkWindow *winPtr; /* Newly-created top-level window. */
2117{
2118 if (winPtr->wmInfoPtr->flags & WM_NEVER_MAPPED) {
2119 return;
2120 }
2121#ifndef X11R3
2122 else {
2123 /* assemble the WM_PROTOCOLS that we honor */
2124 int count = 0;
2125 Atom atomlist[8];
2126 atomlist[count++] = Tk_InternAtom((Tk_Window) winPtr,
2127 "WM_DELETE_WINDOW");
2128/*
2129 * other WM_PROTOCOLS go here -- e.g...
2130 * atomlist[count++] = Tk_InternAtom((Tk_Window) winPtr,
2131 * "WM_SAVE_YOURSELF");
2132 */
2133
2134 /*
2135 * assign the honor list to the window not all X11R4's have
2136 * XSetWmProtocols() so use XChangeProperty()
2137 */
2138
2139 /* XSetWmProtocols(winPtr->display, winPtr->window, atomlist, count); */
2140
2141 XChangeProperty(winPtr->display,
2142 winPtr->window,
2143 Tk_InternAtom((Tk_Window) winPtr, "WM_PROTOCOLS"),
2144 XA_ATOM, 32,
2145 PropModeReplace,
2146 (unsigned char *)atomlist,
2147 count);
2148
2149 }
2150#endif
2151
2152 return;
2153}
2154
2155\f
2156/*
2157 *----------------------------------------------------------------------
2158 *
2159 * TkWmProtocolEventProc --
2160 *
2161 * Handle a WM_PROTOCOL ICCCM event sent by the window manager to
2162 * top level window.
2163 *
2164 * The WM_PROTOCOL's currently handled are:
2165 *
2166 * WM_DELETE_PROTOCOL:
2167 *
2168 * Results: None
2169 *
2170 * Side effects:
2171 * for WM_DELETE_WINDOW:
2172 * - window may be deleted if specified earlier by a
2173 * wm tcl command
2174 * - a tcl command may be executed if sepcified earlier by a
2175 * wm tcl command
2176 *
2177 *
2178 */
2179void
2180TkWmProtocolEventProc(winPtr, eventPtr)
2181TkWindow *winPtr;
2182XEvent *eventPtr;
2183{
2184 if ((Atom)(eventPtr->xclient.data.l)[0] ==
2185 Tk_InternAtom((Tk_Window) winPtr, "WM_DELETE_WINDOW")) {
2186
2187 WmInfo *wmPtr = winPtr->wmInfoPtr;
2188
2189 if (wmPtr->deleteCmd) {
2190 if (*(wmPtr->deleteCmd) == '\0') {
2191 /* callback is empty, just delete the window */
2192 Tk_DestroyWindow((Tk_Window) winPtr);
2193 } else {
2194 /* there is a callback so run it */
2195 (void) Tcl_Eval(winPtr->mainPtr->interp,
2196 wmPtr->deleteCmd, 0, (char **)0);
2197 }
2198 } else {
2199 Tk_DestroyWindow((Tk_Window) winPtr);
2200 }
2201 }
2202 /*
2203 * else { .. other WM_<ETC> cases go here ... }
2204 */
2205 return;
2206}
2207\f
2208
2209/*
2210 *----------------------------------------------------------------------
2211 *
2212 * WmProtocolCmd
2213 *
2214 * implements
2215 *
2216 * wm protocol <window> delete [command_str]
2217 *
2218 * right now just delete is supported for OPTION
2219 *
2220 * Kind of artificial, But makes it easier to merge into new
2221 * versions of Stock Tk.
2222 */
2223int
2224WmProtocolCmd(interp, CmdPtr, argc, argv)
2225Tcl_Interp *interp;
2226char **CmdPtr;
2227int argc;
2228char **argv;
2229{
2230#define Cmd (*CmdPtr)
2231
2232 switch(argc) {
2233 case 4:
2234 /*
2235 * return current command
2236 */
2237 if (!Cmd || *Cmd == '\0') {
2238 return TCL_OK;
2239 } else {
2240 /*
2241 * chop off the <blank><window_name>
2242 * and return just the cmd
2243 */
2244 int x = strlen(Cmd) - strlen(argv[2]) - 1;
2245 char tmpc = Cmd[x];
2246 Cmd[x] = '\0';
2247 {
2248 /* maybe should just have them put the window in the cmd */
2249 Tcl_AppendResult(interp, Cmd, (char *)NULL);
2250 }
2251 /*
2252 * tack the blank and window name back on
2253 */
2254 Cmd[x] = tmpc;
2255 return TCL_OK;
2256 }
2257 case 5:
2258 /*
2259 * (re)set command
2260 */
2261 if (Cmd) {
2262 ckfree(Cmd);
2263 Cmd = (char *)NULL;
2264 }
2265 if (*argv[4] != '\0') {
2266 int x = strlen(argv[4]) + strlen(argv[2]) + 2;
2267 if (!(Cmd = ckalloc(x))) {
2268 perror("wm protocol:");
2269 } else {
2270 sprintf(Cmd, "%s %s", argv[4], argv[2]);
2271 }
2272 }
2273 return TCL_OK;
2274 default:
2275 Tcl_AppendResult(interp, "wrong # of arguments: must be \"",
2276 argv[0], " protocol window <attribute> [cmd]\"", (char *) NULL);
2277 return TCL_ERROR;
2278 }
2279
2280#undef Cmd
2281}
2282
2283\f
2284/*
2285 *----------------------------------------------------------------------
2286 *
2287 * Tk_CoordsToWindow --
2288 *
2289 * Given the root coordinates of a point, this procedure
2290 * returns the token for the top-most window covering that point,
2291 * if there exists such a window in this application.
2292 *
2293 * Results:
2294 * The return result is either a token for the window corresponding
2295 * to rootX and rootY, or else NULL to indicate that there is no such
2296 * window.
2297 *
2298 * Side effects:
2299 * None.
2300 *
2301 *----------------------------------------------------------------------
2302 */
2303
2304Tk_Window
2305Tk_CoordsToWindow(rootX, rootY, tkwin)
2306 int rootX, rootY; /* Coordinates of point in root window. */
2307 Tk_Window tkwin; /* Token for any window in application;
2308 * used to identify the application. */
2309{
2310 Window rootChild, dummy3, dummy4;
2311 int i, dummy1, dummy2;
2312 register WmInfo *wmPtr;
2313 register TkWindow *winPtr, *childPtr;
2314 TkWindow *nextPtr; /* Coordinates of highest child found so
2315 * far that contains point. */
2316 int x, y; /* Coordinates in winPtr. */
2317 int tmpx, tmpy, bd;
2318 Window *children; /* Children of winPtr, or NULL. */
2319 unsigned int numChildren; /* Size of children array. */
2320
2321 /*
2322 * Step 1: find the top-level window that contains the desired
2323 * coordinates.
2324 */
2325
2326 if (XTranslateCoordinates(Tk_Display(tkwin),
2327 RootWindowOfScreen(Tk_Screen(tkwin)),
2328 RootWindowOfScreen(Tk_Screen(tkwin)), rootX, rootY, &dummy1,
2329 &dummy2, &rootChild) == False) {
2330 panic("Tk_CoordsToWindow get False return from XTranslateCoordinates");
2331 }
2332 for (wmPtr = firstWmPtr; ; wmPtr = wmPtr->nextPtr) {
2333 if (wmPtr == NULL) {
2334 return NULL;
2335 }
2336 if ((wmPtr->reparent == rootChild) || ((wmPtr->reparent == None)
2337 && (wmPtr->winPtr->window == rootChild))) {
2338 break;
2339 }
2340 }
2341 winPtr = wmPtr->winPtr;
2342 if (winPtr->mainPtr != ((TkWindow *) tkwin)->mainPtr) {
2343 return NULL;
2344 }
2345
2346 /*
2347 * Step 2: work down through the hierarchy underneath this window.
2348 * At each level, scan through all the children to see if any contain
2349 * the point. If none do, then we're done. If one does, then do the
2350 * same thing on that child. If two or more do, then fetch enough
2351 * information from the window server to figure out which is on top,
2352 * and repeat on that child.
2353 */
2354
2355 x = rootX;
2356 y = rootY;
2357 while (1) {
2358 x -= winPtr->changes.x;
2359 y -= winPtr->changes.y;
2360 nextPtr = NULL;
2361 children = NULL;
2362 for (childPtr = winPtr->childList; childPtr != NULL;
2363 childPtr = childPtr->nextPtr) {
2364 if (!Tk_IsMapped(childPtr) || (childPtr->flags & TK_TOP_LEVEL)) {
2365 continue;
2366 }
2367 tmpx = x - childPtr->changes.x;
2368 tmpy = y - childPtr->changes.y;
2369 bd = childPtr->changes.border_width;
2370 if ((tmpx < -bd) || (tmpy < -bd)
2371 || (tmpx >= (childPtr->changes.width + bd))
2372 || (tmpy >= (childPtr->changes.height + bd))) {
2373 continue;
2374 }
2375 if (nextPtr == NULL) {
2376 nextPtr = childPtr;
2377 continue;
2378 }
2379
2380 /*
2381 * More than one child of same parent overlaps point. Must
2382 * figure out which is on top. Keep a cache of the stacking
2383 * order for winPtr to help with this, in case there are >2
2384 * children overlapping.
2385 */
2386
2387 if (children == NULL) {
2388 if (XQueryTree(winPtr->display, winPtr->window, &dummy3,
2389 &dummy4, &children, &numChildren) == 0) {
2390 panic("Tk_CoordsToWindow get error return from XQueryTree");
2391 }
2392 }
2393 for (i = 0; i < numChildren; i++) {
2394 if (children[i] == childPtr->window) {
2395 break;
2396 }
2397 if (children[i] == nextPtr->window) {
2398 nextPtr = childPtr;
2399 break;
2400 }
2401 }
2402 }
2403 if (children != NULL) {
2404 XFree((char *) children);
2405 }
2406 if (nextPtr == NULL) {
2407 break;
2408 }
2409 winPtr = nextPtr;
2410 }
2411 return (Tk_Window) winPtr;
2412}
2413
2414
Impressum, Datenschutz