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