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