add_window.c revision f66df612
1/*****************************************************************************/
2/*
3
4Copyright 1989,1998  The Open Group
5
6Permission to use, copy, modify, distribute, and sell this software and its
7documentation for any purpose is hereby granted without fee, provided that
8the above copyright notice appear in all copies and that both that
9copyright notice and this permission notice appear in supporting
10documentation.
11
12The above copyright notice and this permission notice shall be included in
13all copies or substantial portions of the Software.
14
15THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
18OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
19AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
22Except as contained in this notice, the name of The Open Group shall not be
23used in advertising or otherwise to promote the sale, use or other dealings
24in this Software without prior written authorization from The Open Group.
25
26*/
27/**       Copyright 1988 by Evans & Sutherland Computer Corporation,        **/
28/**                          Salt Lake City, Utah                           **/
29/**                        Cambridge, Massachusetts                         **/
30/**                                                                         **/
31/**                           All Rights Reserved                           **/
32/**                                                                         **/
33/**    Permission to use, copy, modify, and distribute this software and    **/
34/**    its documentation  for  any  purpose  and  without  fee is hereby    **/
35/**    granted, provided that the above copyright notice appear  in  all    **/
36/**    copies and that both  that  copyright  notice  and  this  permis-    **/
37/**    sion  notice appear in supporting  documentation,  and  that  the    **/
38/**    name of Evans & Sutherland not be used in advertising    **/
39/**    in publicity pertaining to distribution of the  software  without    **/
40/**    specific, written prior permission.                                  **/
41/**                                                                         **/
42/**    EVANS & SUTHERLAND DISCLAIMs ALL WARRANTIES WITH REGARD    **/
43/**    TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES  OF  MERCHANT-    **/
44/**    ABILITY  AND  FITNESS,  IN  NO  EVENT SHALL EVANS & SUTHERLAND    **/
45/**    BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL  DAM-    **/
46/**    AGES OR  ANY DAMAGES WHATSOEVER  RESULTING FROM LOSS OF USE, DATA    **/
47/**    OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER    **/
48/**    TORTIOUS ACTION, ARISING OUT OF OR IN  CONNECTION  WITH  THE  USE    **/
49/**    OR PERFORMANCE OF THIS SOFTWARE.                                     **/
50/*****************************************************************************/
51
52/**********************************************************************
53 *
54 * Add a new window, put the titlebar and other stuff around
55 * the window
56 *
57 * 31-Mar-88 Tom LaStrange        Initial Version.
58 *
59 **********************************************************************/
60
61#include <stdio.h>
62#include "twm.h"
63#include <X11/Xatom.h>
64#include "util.h"
65#include "resize.h"
66#include "parse.h"
67#include "list.h"
68#include "events.h"
69#include "menus.h"
70#include "screen.h"
71#include "iconmgr.h"
72#include "session.h"
73#include "add_window.h"
74
75#define short_lookup (short)(long)(void*)
76
77#define gray_width 2
78#define gray_height 2
79static char gray_bits[] = {
80    0x02, 0x01
81};
82
83int AddingX;
84int AddingY;
85int AddingW;
86int AddingH;
87
88static int PlaceX = 50;
89static int PlaceY = 50;
90
91static void do_add_binding(int button, int context, int modifier, int func);
92static Window CreateHighlightWindow(TwmWindow *tmp_win);
93static void CreateWindowTitlebarButtons(TwmWindow *tmp_win);
94
95char NoName[] = "Untitled";     /* name if no name is specified */
96
97/**
98 * map gravity to (x,y) offset signs for adding to x and y when window is
99 * mapped to get proper placement.
100 *
101 *  \param tmp    window from which to get gravity
102 *  \param xp,yp  return values
103 *
104 */
105void
106GetGravityOffsets(TwmWindow *tmp, int *xp, int *yp)
107{
108    /* *INDENT-OFF* */
109    static struct _gravity_offset {
110        int x, y;
111    } gravity_offsets[11] = {
112        {  0,  0 },                     /* ForgetGravity */
113        { -1, -1 },                     /* NorthWestGravity */
114        {  0, -1 },                     /* NorthGravity */
115        {  1, -1 },                     /* NorthEastGravity */
116        { -1,  0 },                     /* WestGravity */
117        {  0,  0 },                     /* CenterGravity */
118        {  1,  0 },                     /* EastGravity */
119        { -1,  1 },                     /* SouthWestGravity */
120        {  0,  1 },                     /* SouthGravity */
121        {  1,  1 },                     /* SouthEastGravity */
122        {  0,  0 },                     /* StaticGravity */
123    };
124    /* *INDENT-ON* */
125    register int g = ((tmp->hints.flags & PWinGravity)
126                      ? tmp->hints.win_gravity : NorthWestGravity);
127
128    if (g < ForgetGravity || g > StaticGravity) {
129        *xp = *yp = 0;
130    }
131    else {
132        *xp = gravity_offsets[g].x;
133        *yp = gravity_offsets[g].y;
134    }
135}
136
137/**
138 * add a new window to the twm list.
139 *
140 *  \return  pointer to the TwmWindow structure
141 *
142 *      \param w      the window id of the window to add
143 *      \param iconm  flag to tell if this is an icon manager window
144 *      \param iconp  pointer to icon manager struct
145 */
146TwmWindow *
147AddWindow(Window w, int iconm, IconMgr *iconp)
148{
149    TwmWindow *tmp_win;         /* new twm window structure */
150    XEvent event;
151    unsigned long valuemask;    /* mask for create windows */
152    XSetWindowAttributes attributes;    /* attributes for create windows */
153    int ask_user;               /* don't know where to put the window */
154    int gravx, gravy;           /* gravity signs for positioning */
155    int namelen;
156    int bw2;
157    short saved_x, saved_y, restore_icon_x, restore_icon_y;
158    unsigned short saved_width, saved_height;
159    Bool restore_iconified = 0;
160    Bool restore_icon_info_present = 0;
161    int restoredFromPrevSession;
162    Bool width_ever_changed_by_user;
163    Bool height_ever_changed_by_user;
164    char *name;
165
166#ifdef DEBUG
167    fprintf(stderr, "AddWindow: w = 0x%lx\n", (unsigned long) w);
168#endif
169
170    /* allocate space for the twm window */
171    tmp_win = calloc(1, sizeof(TwmWindow));
172    if (tmp_win == NULL) {
173        twmWarning("Unable to allocate memory to manage window ID %lx.", w);
174        return NULL;
175    }
176    tmp_win->w = w;
177    tmp_win->zoomed = ZOOM_NONE;
178    tmp_win->iconmgr = (short) iconm;
179    tmp_win->iconmgrp = iconp;
180    tmp_win->cmaps.number_cwins = 0;
181
182    XSelectInput(dpy, tmp_win->w, PropertyChangeMask);
183
184    XGetWindowAttributes(dpy, tmp_win->w, &tmp_win->attr);
185
186    if (!I18N_FetchName(dpy, tmp_win->w, &name))
187        name = NULL;
188    tmp_win->class = NoClass;
189    XGetClassHint(dpy, tmp_win->w, &tmp_win->class);
190    FetchWmProtocols(tmp_win);
191    FetchWmColormapWindows(tmp_win);
192
193    if (name == NULL)
194        tmp_win->name = strdup(NoName);
195    else {
196        tmp_win->name = strdup(name);
197        free(name);
198    }
199
200    if (GetWindowConfig(tmp_win,
201                        &saved_x, &saved_y, &saved_width, &saved_height,
202                        &restore_iconified, &restore_icon_info_present,
203                        &restore_icon_x, &restore_icon_y,
204                        &width_ever_changed_by_user,
205                        &height_ever_changed_by_user)) {
206        tmp_win->attr.x = saved_x;
207        tmp_win->attr.y = saved_y;
208
209        tmp_win->widthEverChangedByUser = width_ever_changed_by_user;
210        tmp_win->heightEverChangedByUser = height_ever_changed_by_user;
211
212        if (width_ever_changed_by_user)
213            tmp_win->attr.width = saved_width;
214
215        if (height_ever_changed_by_user)
216            tmp_win->attr.height = saved_height;
217
218        restoredFromPrevSession = 1;
219    }
220    else {
221        tmp_win->widthEverChangedByUser = False;
222        tmp_win->heightEverChangedByUser = False;
223
224        restoredFromPrevSession = 0;
225    }
226
227    /*
228     * do initial clip; should look at window gravity
229     */
230    if (tmp_win->attr.width > Scr->MaxWindowWidth)
231        tmp_win->attr.width = Scr->MaxWindowWidth;
232    if (tmp_win->attr.height > Scr->MaxWindowHeight)
233        tmp_win->attr.height = Scr->MaxWindowHeight;
234
235    tmp_win->wmhints = XGetWMHints(dpy, tmp_win->w);
236
237    if (tmp_win->wmhints) {
238        if (restore_iconified) {
239            tmp_win->wmhints->initial_state = IconicState;
240            tmp_win->wmhints->flags |= StateHint;
241        }
242
243        if (restore_icon_info_present) {
244            tmp_win->wmhints->icon_x = restore_icon_x;
245            tmp_win->wmhints->icon_y = restore_icon_y;
246            tmp_win->wmhints->flags |= IconPositionHint;
247        }
248    }
249
250    if (tmp_win->wmhints && (tmp_win->wmhints->flags & WindowGroupHint))
251        tmp_win->group = tmp_win->wmhints->window_group;
252    else
253        tmp_win->group = tmp_win->w /* NULL */ ;
254
255    /*
256     * The July 27, 1988 draft of the ICCCM ignores the size and position
257     * fields in the WM_NORMAL_HINTS property.
258     */
259
260    tmp_win->transient = (short) Transient(tmp_win->w, &tmp_win->transientfor);
261
262    tmp_win->nameChanged = 0;
263    if (tmp_win->class.res_name == NULL)
264        tmp_win->class.res_name = NoName;
265    if (tmp_win->class.res_class == NULL)
266        tmp_win->class.res_class = NoName;
267
268    tmp_win->full_name = strdup(tmp_win->name);
269    namelen = (int) strlen(tmp_win->name);
270
271    tmp_win->highlight = Scr->Highlight &&
272        (!short_lookup LookInList(Scr->NoHighlight, tmp_win->full_name,
273                                  &tmp_win->class));
274
275    tmp_win->stackmode = Scr->StackMode &&
276        (!short_lookup LookInList(Scr->NoStackModeL, tmp_win->full_name,
277                                  &tmp_win->class));
278
279    tmp_win->titlehighlight = Scr->TitleHighlight &&
280        (!short_lookup LookInList(Scr->NoTitleHighlight, tmp_win->full_name,
281                                  &tmp_win->class));
282
283    tmp_win->auto_raise = short_lookup LookInList(Scr->AutoRaise,
284                                                  tmp_win->full_name,
285                                                  &tmp_win->class);
286    if (tmp_win->auto_raise)
287        Scr->NumAutoRaises++;
288    if (Scr->IconifyByUnmapping) {
289        tmp_win->iconify_by_unmapping = iconm ? FALSE :
290            !short_lookup LookInList(Scr->DontIconify, tmp_win->full_name,
291                                     &tmp_win->class);
292    }
293    else {
294        tmp_win->iconify_by_unmapping = Scr->IconifyByUnmapping;
295    }
296    tmp_win->iconify_by_unmapping |=
297        short_lookup LookInList(Scr->IconifyByUn, tmp_win->full_name,
298                                &tmp_win->class);
299
300    if (LookInList(Scr->WindowRingL, tmp_win->full_name, &tmp_win->class)) {
301        if (Scr->Ring) {
302            tmp_win->ring.next = Scr->Ring->ring.next;
303            if (Scr->Ring->ring.next->ring.prev)
304                Scr->Ring->ring.next->ring.prev = tmp_win;
305            Scr->Ring->ring.next = tmp_win;
306            tmp_win->ring.prev = Scr->Ring;
307        }
308        else {
309            tmp_win->ring.next = tmp_win->ring.prev = Scr->Ring = tmp_win;
310        }
311    }
312    else
313        tmp_win->ring.next = tmp_win->ring.prev = NULL;
314    tmp_win->ring.cursor_valid = False;
315
316    tmp_win->squeeze_info = NULL;
317    /*
318     * get the squeeze information; note that this does not have to be freed
319     * since it is coming from the screen list
320     */
321    if (HasShape) {
322        if (!LookInList(Scr->DontSqueezeTitleL, tmp_win->full_name,
323                        &tmp_win->class)) {
324            tmp_win->squeeze_info = (SqueezeInfo *)
325                LookInList(Scr->SqueezeTitleL, tmp_win->full_name,
326                           &tmp_win->class);
327            if (!tmp_win->squeeze_info) {
328                static SqueezeInfo default_squeeze = { J_LEFT, 0, 0 };
329                if (Scr->SqueezeTitle)
330                    tmp_win->squeeze_info = &default_squeeze;
331            }
332        }
333    }
334
335    tmp_win->old_bw = tmp_win->attr.border_width;
336
337    if (Scr->ClientBorderWidth) {
338        tmp_win->frame_bw = tmp_win->old_bw;
339    }
340    else {
341        tmp_win->frame_bw = Scr->BorderWidth;
342    }
343    bw2 = tmp_win->frame_bw * 2;
344
345    tmp_win->title_height = Scr->TitleHeight + tmp_win->frame_bw;
346    if (Scr->NoTitlebar)
347        tmp_win->title_height = 0;
348    if (LookInList(Scr->MakeTitle, tmp_win->full_name, &tmp_win->class))
349        tmp_win->title_height = Scr->TitleHeight + tmp_win->frame_bw;
350    if (LookInList(Scr->NoTitle, tmp_win->full_name, &tmp_win->class))
351        tmp_win->title_height = 0;
352
353    /* if it is a transient window, don't put a title on it */
354    if (tmp_win->transient && !Scr->DecorateTransients)
355        tmp_win->title_height = 0;
356
357    if (LookInList(Scr->StartIconified, tmp_win->full_name, &tmp_win->class)) {
358        if (!tmp_win->wmhints) {
359            tmp_win->wmhints = malloc(sizeof(XWMHints));
360            tmp_win->wmhints->flags = 0;
361        }
362        tmp_win->wmhints->initial_state = IconicState;
363        tmp_win->wmhints->flags |= StateHint;
364    }
365
366    GetWindowSizeHints(tmp_win);
367
368    if (restoredFromPrevSession) {
369        /*
370         * When restoring window positions from the previous session,
371         * we always use NorthWest gravity.
372         */
373
374        gravx = gravy = -1;
375    }
376    else {
377        GetGravityOffsets(tmp_win, &gravx, &gravy);
378    }
379
380    /*
381     * Don't bother user if:
382     *
383     *     o  the window is a transient, or
384     *
385     *     o  a USPosition was requested, or
386     *
387     *     o  a PPosition was requested and UsePPosition is ON or
388     *        NON_ZERO if the window is at other than (0,0)
389     */
390    ask_user = TRUE;
391    if (tmp_win->transient ||
392        (tmp_win->hints.flags & USPosition) ||
393        ((tmp_win->hints.flags & PPosition) && Scr->UsePPosition &&
394         (Scr->UsePPosition == PPOS_ON ||
395          tmp_win->attr.x != 0 || tmp_win->attr.y != 0)))
396        ask_user = FALSE;
397
398    /*
399     * do any prompting for position
400     */
401    if (HandlingEvents && ask_user && !restoredFromPrevSession) {
402        if (Scr->RandomPlacement) {     /* just stick it somewhere */
403            /* Avoid putting the new window off-screen */
404            if ((PlaceX + tmp_win->attr.width) > Scr->MyDisplayWidth) {
405                PlaceX = Scr->MyDisplayWidth - tmp_win->attr.width;
406                if (PlaceX < 0)
407                    PlaceX = 0;
408                if (PlaceX > 50)
409                    PlaceX = 50;
410            }
411            if ((PlaceY + tmp_win->attr.height) > Scr->MyDisplayHeight) {
412                PlaceY = Scr->MyDisplayHeight - tmp_win->attr.height;
413                if (PlaceY < 0)
414                    PlaceY = 0;
415                if (PlaceY > 50)
416                    PlaceY = 50;
417            }
418
419            tmp_win->attr.x = PlaceX;
420            tmp_win->attr.y = PlaceY;
421            PlaceX += 30;
422            PlaceY += 30;
423        }
424        else {                  /* else prompt */
425            if (!(tmp_win->wmhints && tmp_win->wmhints->flags & StateHint &&
426                  tmp_win->wmhints->initial_state == IconicState)) {
427                Bool firsttime = True;
428                int height, width;
429
430                /* better wait until all the mouse buttons have been
431                 * released.
432                 */
433                while (TRUE) {
434                    int stat;
435
436                    XUngrabServer(dpy);
437                    XSync(dpy, 0);
438                    XGrabServer(dpy);
439
440                    JunkMask = 0;
441                    if (!XQueryPointer(dpy, Scr->Root, &JunkRoot,
442                                       &JunkChild, &JunkX, &JunkY,
443                                       &AddingX, &AddingY, &JunkMask))
444                        JunkMask = 0;
445
446                    JunkMask &= (Button1Mask | Button2Mask | Button3Mask |
447                                 Button4Mask | Button5Mask);
448
449                    /*
450                     * watch out for changing screens
451                     */
452                    if (firsttime) {
453                        if (JunkRoot != Scr->Root) {
454                            register int scrnum;
455
456                            for (scrnum = 0; scrnum < NumScreens; scrnum++) {
457                                if (JunkRoot == RootWindow(dpy, scrnum))
458                                    break;
459                            }
460
461                            if (scrnum != NumScreens)
462                                PreviousScreen = scrnum;
463                        }
464                        firsttime = False;
465                    }
466
467                    /*
468                     * wait for buttons to come up; yuck
469                     */
470                    if (JunkMask != 0)
471                        continue;
472
473                    /*
474                     * this will cause a warp to the indicated root
475                     */
476                    stat = XGrabPointer(dpy, Scr->Root, False,
477                                        ButtonPressMask | ButtonReleaseMask |
478                                        PointerMotionMask |
479                                        PointerMotionHintMask, GrabModeAsync,
480                                        GrabModeAsync, Scr->Root,
481                                        UpperLeftCursor, CurrentTime);
482
483                    if (stat == GrabSuccess)
484                        break;
485                }
486
487                width = (SIZE_HINDENT + MyFont_TextWidth(&Scr->SizeFont,
488                                                         tmp_win->name,
489                                                         namelen));
490                height = Scr->SizeFont.height + SIZE_VINDENT * 2;
491
492                XResizeWindow(dpy, Scr->SizeWindow,
493                              (unsigned) (width + SIZE_HINDENT),
494                              (unsigned) height);
495                XMapRaised(dpy, Scr->SizeWindow);
496                InstallRootColormap();
497
498                MyFont_ChangeGC(Scr->DefaultC.fore, Scr->DefaultC.back,
499                                &Scr->SizeFont);
500                MyFont_DrawImageString(dpy, Scr->SizeWindow, &Scr->SizeFont,
501                                       Scr->NormalGC,
502                                       SIZE_HINDENT,
503                                       SIZE_VINDENT + Scr->SizeFont.ascent,
504                                       tmp_win->name, namelen);
505
506                AddingW = tmp_win->attr.width + bw2;
507                AddingH = tmp_win->attr.height + tmp_win->title_height + bw2;
508
509                if (Scr->DontMoveOff) {
510                    /*
511                     * Make sure the initial outline comes up on the screen.
512                     */
513                    if (AddingX < 0)
514                        AddingX = 0;
515                    if (AddingX > Scr->MyDisplayWidth - AddingW)
516                        AddingX = Scr->MyDisplayWidth - AddingW;
517
518                    if (AddingY < 0)
519                        AddingY = 0;
520                    if (AddingY > Scr->MyDisplayHeight - AddingH)
521                        AddingY = Scr->MyDisplayHeight - AddingH;
522                }
523
524                MoveOutline(Scr->Root, AddingX, AddingY, AddingW, AddingH,
525                            tmp_win->frame_bw, tmp_win->title_height);
526
527                while (TRUE) {
528                    XMaskEvent(dpy, ButtonPressMask | PointerMotionMask,
529                               &event);
530
531                    if (Event.type == MotionNotify) {
532                        /* discard any extra motion events before a release */
533                        while (XCheckMaskEvent(dpy,
534                                               ButtonMotionMask |
535                                               ButtonPressMask, &Event))
536                            if (Event.type == ButtonPress)
537                                break;
538                    }
539
540                    if (event.type == ButtonPress) {
541                        AddingX = event.xbutton.x_root;
542                        AddingY = event.xbutton.y_root;
543
544                        /* DontMoveOff prohibits user form off-screen placement */
545                        if (Scr->DontMoveOff) {
546                            int AddingR, AddingB;
547
548                            AddingR = AddingX + AddingW;
549                            AddingB = AddingY + AddingH;
550
551                            if (AddingX < 0)
552                                AddingX = 0;
553                            if (AddingR > Scr->MyDisplayWidth)
554                                AddingX = Scr->MyDisplayWidth - AddingW;
555
556                            if (AddingY < 0)
557                                AddingY = 0;
558                            if (AddingB > Scr->MyDisplayHeight)
559                                AddingY = Scr->MyDisplayHeight - AddingH;
560
561                        }
562                        break;
563                    }
564
565                    if (event.type != MotionNotify) {
566                        continue;
567                    }
568
569                    XQueryPointer(dpy, Scr->Root, &JunkRoot, &JunkChild,
570                                  &JunkX, &JunkY, &AddingX, &AddingY,
571                                  &JunkMask);
572
573                    if (Scr->DontMoveOff) {
574                        int AddingR, AddingB;
575
576                        AddingR = AddingX + AddingW;
577                        AddingB = AddingY + AddingH;
578
579                        if (AddingX < 0)
580                            AddingX = 0;
581                        if (AddingR > Scr->MyDisplayWidth)
582                            AddingX = Scr->MyDisplayWidth - AddingW;
583
584                        if (AddingY < 0)
585                            AddingY = 0;
586                        if (AddingB > Scr->MyDisplayHeight)
587                            AddingY = Scr->MyDisplayHeight - AddingH;
588                    }
589
590                    MoveOutline(Scr->Root, AddingX, AddingY, AddingW, AddingH,
591                                tmp_win->frame_bw, tmp_win->title_height);
592
593                }
594
595                if (event.xbutton.button == Button2) {
596                    int lastx, lasty;
597
598                    Scr->SizeStringOffset = width +
599                        MyFont_TextWidth(&Scr->SizeFont, ": ", 2);
600                    XResizeWindow(dpy, Scr->SizeWindow,
601                                  (unsigned) (Scr->SizeStringOffset +
602                                              Scr->SizeStringWidth),
603                                  (unsigned) height);
604                    MyFont_DrawImageString(dpy, Scr->SizeWindow, &Scr->SizeFont,
605                                           Scr->NormalGC, width,
606                                           SIZE_VINDENT + Scr->SizeFont.ascent,
607                                           ": ", 2);
608                    if (0 /*Scr->AutoRelativeResize */ ) {
609                        int dx = (tmp_win->attr.width / 4);
610                        int dy = (tmp_win->attr.height / 4);
611
612#define HALF_AVE_CURSOR_SIZE 8  /* so that it is visible */
613                        if (dx < HALF_AVE_CURSOR_SIZE)
614                            dx = HALF_AVE_CURSOR_SIZE;
615                        if (dy < HALF_AVE_CURSOR_SIZE)
616                            dy = HALF_AVE_CURSOR_SIZE;
617#undef HALF_AVE_CURSOR_SIZE
618                        dx += (tmp_win->frame_bw + 1);
619                        dy += (bw2 + tmp_win->title_height + 1);
620                        if (AddingX + dx >= Scr->MyDisplayWidth)
621                            dx = Scr->MyDisplayWidth - AddingX - 1;
622                        if (AddingY + dy >= Scr->MyDisplayHeight)
623                            dy = Scr->MyDisplayHeight - AddingY - 1;
624                        if (dx > 0 && dy > 0)
625                            XWarpPointer(dpy, None, None, 0, 0, 0, 0, dx, dy);
626                    }
627                    else {
628                        XWarpPointer(dpy, None, Scr->Root, 0, 0, 0, 0,
629                                     AddingX + AddingW / 2,
630                                     AddingY + AddingH / 2);
631                    }
632                    AddStartResize(tmp_win, AddingX, AddingY, AddingW, AddingH);
633
634                    lastx = -10000;
635                    lasty = -10000;
636                    while (TRUE) {
637                        XMaskEvent(dpy,
638                                   ButtonReleaseMask | ButtonMotionMask,
639                                   &event);
640
641                        if (Event.type == MotionNotify) {
642                            /* discard any extra motion events before a release */
643                            while (XCheckMaskEvent(dpy,
644                                                   ButtonMotionMask |
645                                                   ButtonReleaseMask, &Event))
646                                if (Event.type == ButtonRelease)
647                                    break;
648                        }
649
650                        if (event.type == ButtonRelease) {
651                            AddEndResize(tmp_win);
652                            break;
653                        }
654
655                        if (event.type != MotionNotify) {
656                            continue;
657                        }
658
659                        /*
660                         * XXX - if we are going to do a loop, we ought to consider
661                         * using multiple GXxor lines so that we don't need to
662                         * grab the server.
663                         */
664                        XQueryPointer(dpy, Scr->Root, &JunkRoot, &JunkChild,
665                                      &JunkX, &JunkY, &AddingX, &AddingY,
666                                      &JunkMask);
667
668                        if (lastx != AddingX || lasty != AddingY) {
669                            DoResize(AddingX, AddingY, tmp_win);
670
671                            lastx = AddingX;
672                            lasty = AddingY;
673                        }
674
675                    }
676                }
677                else if (event.xbutton.button == Button3) {
678                    int maxw = Scr->MyDisplayWidth - AddingX;
679                    int maxh = Scr->MyDisplayHeight - AddingY;
680
681                    /*
682                     * Make window go to bottom of screen, and clip to right edge.
683                     * This is useful when popping up large windows and fixed
684                     * column text windows.
685                     */
686                    if (AddingW > maxw)
687                        AddingW = maxw;
688                    AddingH = maxh;
689
690                    AddingW -= bw2;
691                    AddingH -= bw2;
692                    ConstrainSize(tmp_win, &AddingW, &AddingH); /* w/o borders */
693                    AddingW += bw2;
694                    AddingH += bw2;
695                }
696                else {
697                    XMaskEvent(dpy, ButtonReleaseMask, &event);
698                }
699
700                MoveOutline(Scr->Root, 0, 0, 0, 0, 0, 0);
701                XUnmapWindow(dpy, Scr->SizeWindow);
702                UninstallRootColormap();
703                XUngrabPointer(dpy, CurrentTime);
704
705                tmp_win->attr.x = AddingX;
706                tmp_win->attr.y = AddingY + tmp_win->title_height;
707                tmp_win->attr.width = AddingW - bw2;
708                tmp_win->attr.height = AddingH - tmp_win->title_height - bw2;
709
710                XUngrabServer(dpy);
711            }
712        }
713    }
714    else {                      /* put it where asked, mod title bar */
715        /* if the gravity is towards the top, move it by the title height */
716        if (gravy < 0)
717            tmp_win->attr.y -= gravy * tmp_win->title_height;
718    }
719
720#ifdef DEBUG
721    fprintf(stderr, "  position window  %d, %d  %dx%d\n",
722            tmp_win->attr.x,
723            tmp_win->attr.y, tmp_win->attr.width, tmp_win->attr.height);
724#endif
725
726    if (!Scr->ClientBorderWidth) {      /* need to adjust for twm borders */
727        int delta = tmp_win->attr.border_width - tmp_win->frame_bw;
728
729        tmp_win->attr.x += gravx * delta;
730        tmp_win->attr.y += gravy * delta;
731    }
732
733    tmp_win->title_width = tmp_win->attr.width;
734
735    if (tmp_win->old_bw)
736        XSetWindowBorderWidth(dpy, tmp_win->w, 0);
737
738    tmp_win->name_width = MyFont_TextWidth(&Scr->TitleBarFont, tmp_win->name,
739                                           namelen);
740
741    if (!I18N_GetIconName(dpy, tmp_win->w, &name)) {
742        tmp_win->icon_name = strdup(tmp_win->name);
743    }
744    else {
745        if (name == NULL) {
746            tmp_win->icon_name = strdup(tmp_win->name);
747        }
748        else {
749            tmp_win->icon_name = strdup(name);
750            free(name);
751        }
752    }
753
754    tmp_win->iconified = FALSE;
755    tmp_win->icon = FALSE;
756    tmp_win->icon_on = FALSE;
757
758    XGrabServer(dpy);
759
760    /*
761     * Make sure the client window still exists.  We don't want to leave an
762     * orphan frame window if it doesn't.  Since we now have the server
763     * grabbed, the window can't disappear later without having been
764     * reparented, so we'll get a DestroyNotify for it.  We won't have
765     * gotten one for anything up to here, however.
766     */
767    if (XGetGeometry(dpy, tmp_win->w, &JunkRoot, &JunkX, &JunkY,
768                     &JunkWidth, &JunkHeight, &JunkBW, &JunkDepth) == 0) {
769        free(tmp_win);
770        XUngrabServer(dpy);
771        return (NULL);
772    }
773
774    /* add the window into the twm list */
775    tmp_win->next = Scr->TwmRoot.next;
776    if (Scr->TwmRoot.next != NULL)
777        Scr->TwmRoot.next->prev = tmp_win;
778    tmp_win->prev = &Scr->TwmRoot;
779    Scr->TwmRoot.next = tmp_win;
780
781    /* get all the colors for the window */
782
783    tmp_win->border = Scr->BorderColor;
784    tmp_win->icon_border = Scr->IconBorderColor;
785    tmp_win->border_tile.fore = Scr->BorderTileC.fore;
786    tmp_win->border_tile.back = Scr->BorderTileC.back;
787    tmp_win->title.fore = Scr->TitleC.fore;
788    tmp_win->title.back = Scr->TitleC.back;
789    tmp_win->iconc.fore = Scr->IconC.fore;
790    tmp_win->iconc.back = Scr->IconC.back;
791
792    GetColorFromList(Scr->BorderColorL, tmp_win->full_name, &tmp_win->class,
793                     &tmp_win->border);
794    GetColorFromList(Scr->IconBorderColorL, tmp_win->full_name, &tmp_win->class,
795                     &tmp_win->icon_border);
796    GetColorFromList(Scr->BorderTileForegroundL, tmp_win->full_name,
797                     &tmp_win->class, &tmp_win->border_tile.fore);
798    GetColorFromList(Scr->BorderTileBackgroundL, tmp_win->full_name,
799                     &tmp_win->class, &tmp_win->border_tile.back);
800    GetColorFromList(Scr->TitleForegroundL, tmp_win->full_name, &tmp_win->class,
801                     &tmp_win->title.fore);
802    GetColorFromList(Scr->TitleBackgroundL, tmp_win->full_name, &tmp_win->class,
803                     &tmp_win->title.back);
804    GetColorFromList(Scr->IconForegroundL, tmp_win->full_name, &tmp_win->class,
805                     &tmp_win->iconc.fore);
806    GetColorFromList(Scr->IconBackgroundL, tmp_win->full_name, &tmp_win->class,
807                     &tmp_win->iconc.back);
808
809    /* create windows */
810
811    tmp_win->frame_x = tmp_win->attr.x + tmp_win->old_bw - tmp_win->frame_bw;
812    tmp_win->frame_y = tmp_win->attr.y - tmp_win->title_height +
813        tmp_win->old_bw - tmp_win->frame_bw;
814    tmp_win->frame_width = tmp_win->attr.width;
815    tmp_win->frame_height = tmp_win->attr.height + tmp_win->title_height;
816
817    valuemask = CWBackPixmap | CWBorderPixel | CWCursor | CWEventMask;
818    attributes.background_pixmap = None;
819    attributes.border_pixel = tmp_win->border;
820    attributes.cursor = Scr->FrameCursor;
821    attributes.event_mask = (SubstructureRedirectMask |
822                             ButtonPressMask | ButtonReleaseMask |
823                             EnterWindowMask | LeaveWindowMask);
824    if (tmp_win->attr.save_under) {
825        attributes.save_under = True;
826        valuemask |= CWSaveUnder;
827    }
828
829    tmp_win->frame = XCreateWindow(dpy, Scr->Root, tmp_win->frame_x,
830                                   tmp_win->frame_y,
831                                   (unsigned int) tmp_win->frame_width,
832                                   (unsigned int) tmp_win->frame_height,
833                                   (unsigned int) tmp_win->frame_bw,
834                                   Scr->d_depth,
835                                   (unsigned int) CopyFromParent,
836                                   Scr->d_visual, valuemask, &attributes);
837
838    if (tmp_win->title_height) {
839        valuemask = (CWEventMask | CWBorderPixel | CWBackPixel);
840        attributes.event_mask = (KeyPressMask | ButtonPressMask |
841                                 ButtonReleaseMask | ExposureMask);
842        attributes.border_pixel = tmp_win->border;
843        attributes.background_pixel = tmp_win->title.back;
844        tmp_win->title_w = XCreateWindow(dpy, tmp_win->frame,
845                                         -tmp_win->frame_bw,
846                                         -tmp_win->frame_bw,
847                                         (unsigned int) tmp_win->attr.width,
848                                         (unsigned int) Scr->TitleHeight,
849                                         (unsigned int) tmp_win->frame_bw,
850                                         Scr->d_depth,
851                                         (unsigned int) CopyFromParent,
852                                         Scr->d_visual, valuemask, &attributes);
853    }
854    else {
855        tmp_win->title_w = 0;
856        tmp_win->squeeze_info = NULL;
857    }
858
859    if (tmp_win->highlight) {
860        tmp_win->gray = XCreatePixmapFromBitmapData(dpy, Scr->Root,
861                                                    gray_bits, gray_width,
862                                                    gray_height,
863                                                    tmp_win->border_tile.fore,
864                                                    tmp_win->border_tile.back,
865                                                    (unsigned) Scr->d_depth);
866
867        SetBorder(tmp_win, False);
868    }
869    else
870        tmp_win->gray = None;
871
872    if (tmp_win->title_w) {
873        CreateWindowTitlebarButtons(tmp_win);
874        ComputeTitleLocation(tmp_win);
875        XMoveWindow(dpy, tmp_win->title_w, tmp_win->title_x, tmp_win->title_y);
876        XDefineCursor(dpy, tmp_win->title_w, Scr->TitleCursor);
877    }
878
879    valuemask = (CWEventMask | CWDontPropagate);
880    attributes.event_mask = (StructureNotifyMask | PropertyChangeMask |
881                             ColormapChangeMask | VisibilityChangeMask |
882                             EnterWindowMask | LeaveWindowMask);
883    attributes.do_not_propagate_mask = ButtonPressMask | ButtonReleaseMask;
884    XChangeWindowAttributes(dpy, tmp_win->w, valuemask, &attributes);
885
886    if (HasShape)
887        XShapeSelectInput(dpy, tmp_win->w, ShapeNotifyMask);
888
889    if (tmp_win->title_w) {
890        XMapWindow(dpy, tmp_win->title_w);
891    }
892
893    if (HasShape) {
894        int xws, yws, xbs, ybs;
895        unsigned wws, hws, wbs, hbs;
896        int boundingShaped, clipShaped;
897
898        XShapeSelectInput(dpy, tmp_win->w, ShapeNotifyMask);
899        XShapeQueryExtents(dpy, tmp_win->w,
900                           &boundingShaped, &xws, &yws, &wws, &hws,
901                           &clipShaped, &xbs, &ybs, &wbs, &hbs);
902        tmp_win->wShaped = (short) boundingShaped;
903    }
904
905    if (!tmp_win->iconmgr)
906        XAddToSaveSet(dpy, tmp_win->w);
907
908    XReparentWindow(dpy, tmp_win->w, tmp_win->frame, 0, tmp_win->title_height);
909    /*
910     * Reparenting generates an UnmapNotify event, followed by a MapNotify.
911     * Set the map state to FALSE to prevent a transition back to
912     * WithdrawnState in HandleUnmapNotify.  Map state gets set correctly
913     * again in HandleMapNotify.
914     */
915    tmp_win->mapped = FALSE;
916
917    SetupFrame(tmp_win, tmp_win->frame_x, tmp_win->frame_y,
918               tmp_win->frame_width, tmp_win->frame_height, -1, True);
919
920    /* wait until the window is iconified and the icon window is mapped
921     * before creating the icon window
922     */
923    tmp_win->icon_w = (Window) 0;
924
925    if (!tmp_win->iconmgr) {
926        GrabButtons(tmp_win);
927        GrabKeys(tmp_win);
928    }
929
930    (void) AddIconManager(tmp_win);
931
932    XSaveContext(dpy, tmp_win->w, TwmContext, (XPointer) tmp_win);
933    XSaveContext(dpy, tmp_win->w, ScreenContext, (XPointer) Scr);
934    XSaveContext(dpy, tmp_win->frame, TwmContext, (XPointer) tmp_win);
935    XSaveContext(dpy, tmp_win->frame, ScreenContext, (XPointer) Scr);
936    if (tmp_win->title_height) {
937        int i;
938        int nb = Scr->TBInfo.nleft + Scr->TBInfo.nright;
939
940        XSaveContext(dpy, tmp_win->title_w, TwmContext, (XPointer) tmp_win);
941        XSaveContext(dpy, tmp_win->title_w, ScreenContext, (XPointer) Scr);
942        for (i = 0; i < nb; i++) {
943            XSaveContext(dpy, tmp_win->titlebuttons[i].window, TwmContext,
944                         (XPointer) tmp_win);
945            XSaveContext(dpy, tmp_win->titlebuttons[i].window, ScreenContext,
946                         (XPointer) Scr);
947        }
948        if (tmp_win->hilite_w) {
949            XSaveContext(dpy, tmp_win->hilite_w, TwmContext,
950                         (XPointer) tmp_win);
951            XSaveContext(dpy, tmp_win->hilite_w, ScreenContext, (XPointer) Scr);
952        }
953    }
954
955    XUngrabServer(dpy);
956
957    /* if we were in the middle of a menu activated function, regrab
958     * the pointer
959     */
960    if (RootFunction)
961        ReGrab();
962
963    return (tmp_win);
964}
965
966/**
967 * checks to see if we should really put a twm frame on the window
968 *
969 *  \return TRUE  - go ahead and place the window
970 *  \return FALSE - don't frame the window
971 *      \param w the window to check
972 */
973int
974MappedNotOverride(Window w)
975{
976    XWindowAttributes wa;
977
978    XGetWindowAttributes(dpy, w, &wa);
979    return ((wa.map_state != IsUnmapped) && (wa.override_redirect != True));
980}
981
982/**
983 * attach default bindings so that naive users don't get messed up if they
984 * provide a minimal twmrc.
985 */
986static void
987do_add_binding(int button, int context, int modifier, int func)
988{
989    MouseButton *mb = &Scr->Mouse[button][context][modifier];
990
991    if (mb->func)
992        return;                 /* already defined */
993
994    mb->func = func;
995    mb->item = NULL;
996}
997
998void
999AddDefaultBindings(void)
1000{
1001    /*
1002     * The bindings are stored in Scr->Mouse, indexed by
1003     * Mouse[button_number][C_context][modifier].
1004     */
1005
1006#define NoModifierMask 0
1007
1008    do_add_binding(Button1, C_TITLE, NoModifierMask, F_MOVE);
1009    do_add_binding(Button1, C_ICON, NoModifierMask, F_ICONIFY);
1010    do_add_binding(Button1, C_ICONMGR, NoModifierMask, F_ICONIFY);
1011
1012    do_add_binding(Button2, C_TITLE, NoModifierMask, F_RAISELOWER);
1013    do_add_binding(Button2, C_ICON, NoModifierMask, F_ICONIFY);
1014    do_add_binding(Button2, C_ICONMGR, NoModifierMask, F_ICONIFY);
1015
1016#undef NoModifierMask
1017}
1018
1019/**
1020 * grab needed buttons for the window
1021 *
1022 *  \param[in] tmp_win the twm window structure to use
1023 */
1024void
1025GrabButtons(TwmWindow *tmp_win)
1026{
1027    int i, j;
1028
1029    for (i = 0; i < MAX_BUTTONS + 1; i++) {
1030        for (j = 0; j < MOD_SIZE; j++) {
1031            if (Scr->Mouse[i][C_WINDOW][j].func != 0) {
1032                /* twm used to do this grab on the application main window,
1033                 * tmp_win->w . This was not ICCCM complient and was changed.
1034                 */
1035                XGrabButton(dpy, (unsigned) i, (unsigned) j, tmp_win->frame,
1036                            True, ButtonPressMask | ButtonReleaseMask,
1037                            GrabModeAsync, GrabModeAsync, None,
1038                            Scr->FrameCursor);
1039            }
1040        }
1041    }
1042}
1043
1044/**
1045 * grab needed keys for the window
1046 *
1047 *  \param[in] tmp_win the twm window structure to use
1048 */
1049void
1050GrabKeys(TwmWindow *tmp_win)
1051{
1052    FuncKey *tmp;
1053    IconMgr *p;
1054
1055    for (tmp = Scr->FuncKeyRoot.next; tmp != NULL; tmp = tmp->next) {
1056        switch (tmp->cont) {
1057        case C_WINDOW:
1058            XGrabKey(dpy, tmp->keycode, (unsigned) tmp->mods, tmp_win->w, True,
1059                     GrabModeAsync, GrabModeAsync);
1060            break;
1061
1062        case C_ICON:
1063            if (tmp_win->icon_w)
1064                XGrabKey(dpy, tmp->keycode, (unsigned) tmp->mods,
1065                         tmp_win->icon_w, True, GrabModeAsync, GrabModeAsync);
1066            /* FALLTHRU */
1067
1068        case C_TITLE:
1069            if (tmp_win->title_w)
1070                XGrabKey(dpy, tmp->keycode, (unsigned) tmp->mods,
1071                         tmp_win->title_w, True, GrabModeAsync, GrabModeAsync);
1072            break;
1073
1074        case C_NAME:
1075            XGrabKey(dpy, tmp->keycode, (unsigned) tmp->mods, tmp_win->w, True,
1076                     GrabModeAsync, GrabModeAsync);
1077            if (tmp_win->icon_w)
1078                XGrabKey(dpy, tmp->keycode, (unsigned) tmp->mods,
1079                         tmp_win->icon_w, True, GrabModeAsync, GrabModeAsync);
1080            if (tmp_win->title_w)
1081                XGrabKey(dpy, tmp->keycode, (unsigned) tmp->mods,
1082                         tmp_win->title_w, True, GrabModeAsync, GrabModeAsync);
1083            break;
1084            /*
1085               case C_ROOT:
1086               XGrabKey(dpy, tmp->keycode, tmp->mods, Scr->Root, True,
1087               GrabModeAsync, GrabModeAsync);
1088               break;
1089             */
1090        }
1091    }
1092    for (tmp = Scr->FuncKeyRoot.next; tmp != NULL; tmp = tmp->next) {
1093        if (tmp->cont == C_ICONMGR && !Scr->NoIconManagers) {
1094            for (p = &Scr->iconmgr; p != NULL; p = p->next) {
1095                XUngrabKey(dpy, tmp->keycode, (unsigned) tmp->mods,
1096                           p->twm_win->w);
1097            }
1098        }
1099    }
1100}
1101
1102static Window
1103CreateHighlightWindow(TwmWindow *tmp_win)
1104{
1105    XSetWindowAttributes attributes;    /* attributes for create windows */
1106    Pixmap pm = None;
1107    GC gc;
1108    XGCValues gcv;
1109    unsigned long valuemask;
1110    int h = (Scr->TitleHeight - 2 * Scr->FramePadding);
1111    Window w;
1112
1113    /*
1114     * If a special highlight pixmap was given, use that.  Otherwise,
1115     * use a nice, even gray pattern.  The old horizontal lines look really
1116     * awful on interlaced monitors (as well as resembling other looks a
1117     * little bit too closely), but can be used by putting
1118     *
1119     *                 Pixmaps { TitleHighlight "hline2" }
1120     *
1121     * (or whatever the horizontal line bitmap is named) in the startup
1122     * file.  If all else fails, use the foreground color to look like a
1123     * solid line.
1124     */
1125    if (!Scr->hilitePm) {
1126        Scr->hilitePm = XCreateBitmapFromData(dpy, tmp_win->title_w,
1127                                              gray_bits, gray_width,
1128                                              gray_height);
1129        Scr->hilite_pm_width = gray_width;
1130        Scr->hilite_pm_height = gray_height;
1131    }
1132    if (Scr->hilitePm) {
1133        pm = XCreatePixmap(dpy, tmp_win->title_w,
1134                           (unsigned) Scr->hilite_pm_width,
1135                           (unsigned) Scr->hilite_pm_height,
1136                           (unsigned) Scr->d_depth);
1137        gcv.foreground = tmp_win->title.fore;
1138        gcv.background = tmp_win->title.back;
1139        gcv.graphics_exposures = False;
1140        gc = XCreateGC(dpy, pm,
1141                       (GCForeground | GCBackground | GCGraphicsExposures),
1142                       &gcv);
1143        if (gc) {
1144            XCopyPlane(dpy, Scr->hilitePm, pm, gc, 0, 0,
1145                       (unsigned) Scr->hilite_pm_width,
1146                       (unsigned) Scr->hilite_pm_height, 0, 0, 1);
1147            XFreeGC(dpy, gc);
1148        }
1149        else {
1150            XFreePixmap(dpy, pm);
1151            pm = None;
1152        }
1153    }
1154    if (pm) {
1155        valuemask = CWBackPixmap;
1156        attributes.background_pixmap = pm;
1157    }
1158    else {
1159        valuemask = CWBackPixel;
1160        attributes.background_pixel = tmp_win->title.fore;
1161    }
1162
1163    w = XCreateWindow(dpy, tmp_win->title_w, 0, Scr->FramePadding,
1164                      (unsigned int) Scr->TBInfo.width, (unsigned int) h,
1165                      (unsigned int) 0,
1166                      Scr->d_depth, (unsigned int) CopyFromParent,
1167                      Scr->d_visual, valuemask, &attributes);
1168    if (pm)
1169        XFreePixmap(dpy, pm);
1170    return w;
1171}
1172
1173void
1174ComputeCommonTitleOffsets(void)
1175{
1176    int buttonwidth = (Scr->TBInfo.width + Scr->TBInfo.pad);
1177
1178    Scr->TBInfo.leftx = Scr->TBInfo.rightoff = Scr->FramePadding;
1179    if (Scr->TBInfo.nleft > 0)
1180        Scr->TBInfo.leftx += Scr->ButtonIndent;
1181    Scr->TBInfo.titlex = (Scr->TBInfo.leftx +
1182                          (Scr->TBInfo.nleft * buttonwidth) -
1183                          Scr->TBInfo.pad + Scr->TitlePadding);
1184    if (Scr->TBInfo.nright > 0)
1185        Scr->TBInfo.rightoff += (Scr->ButtonIndent +
1186                                 ((Scr->TBInfo.nright * buttonwidth) -
1187                                  Scr->TBInfo.pad));
1188    return;
1189}
1190
1191void
1192ComputeWindowTitleOffsets(TwmWindow *tmp_win, int width, Bool squeeze)
1193{
1194    tmp_win->highlightx = (Scr->TBInfo.titlex + tmp_win->name_width);
1195    if (tmp_win->hilite_w || Scr->TBInfo.nright > 0)
1196        tmp_win->highlightx += Scr->TitlePadding;
1197    tmp_win->rightx = width - Scr->TBInfo.rightoff;
1198    if (squeeze && tmp_win->squeeze_info) {
1199        int rx = (tmp_win->highlightx +
1200                  (tmp_win->hilite_w
1201                   ? Scr->TBInfo.width * 2 : 0) +
1202                  (Scr->TBInfo.nright > 0 ? Scr->TitlePadding : 0) +
1203                  Scr->FramePadding);
1204        if (rx < tmp_win->rightx)
1205            tmp_win->rightx = rx;
1206    }
1207    return;
1208}
1209
1210/**
1211 * calculate the position of the title window.  We need to take the frame_bw
1212 * into account since we want (0,0) of the title window to line up with (0,0)
1213 * of the frame window.
1214 */
1215void
1216ComputeTitleLocation(register TwmWindow *tmp)
1217{
1218    tmp->title_x = -tmp->frame_bw;
1219    tmp->title_y = -tmp->frame_bw;
1220
1221    if (tmp->squeeze_info) {
1222        register SqueezeInfo *si = tmp->squeeze_info;
1223        int basex;
1224        int maxwidth = tmp->frame_width;
1225        int tw = tmp->title_width;
1226
1227        /*
1228         * figure label base from squeeze info (justification fraction)
1229         */
1230        if (si->denom == 0) {   /* num is pixel based */
1231            if ((basex = si->num) == 0) {       /* look for special cases */
1232                switch (si->justify) {
1233                case J_RIGHT:
1234                    basex = maxwidth;
1235                    break;
1236                case J_CENTER:
1237                    basex = maxwidth / 2;
1238                    break;
1239                }
1240            }
1241        }
1242        else {                  /* num/denom is fraction */
1243            basex = ((si->num * maxwidth) / si->denom);
1244            if (si->num < 0)
1245                basex += maxwidth;
1246        }
1247
1248        /*
1249         * adjust for left (nop), center, right justify and clip
1250         */
1251        switch (si->justify) {
1252        case J_CENTER:
1253            basex -= tw / 2;
1254            break;
1255        case J_RIGHT:
1256            basex -= tw - 1;
1257            break;
1258        }
1259        if (basex > maxwidth - tw + 1)
1260            basex = maxwidth - tw + 1;
1261        if (basex < 0)
1262            basex = 0;
1263
1264        tmp->title_x = basex - tmp->frame_bw;
1265    }
1266}
1267
1268static void
1269CreateWindowTitlebarButtons(TwmWindow *tmp_win)
1270{
1271    unsigned long valuemask;    /* mask for create windows */
1272    XSetWindowAttributes attributes;    /* attributes for create windows */
1273    int leftx, rightx, y;
1274    TitleButton *tb;
1275    int nb;
1276
1277    if (tmp_win->title_height == 0) {
1278        tmp_win->hilite_w = 0;
1279        return;
1280    }
1281
1282    /*
1283     * create the title bar windows; let the event handler deal with painting
1284     * so that we don't have to spend two pixmaps (or deal with hashing)
1285     */
1286    ComputeWindowTitleOffsets(tmp_win, tmp_win->attr.width, False);
1287
1288    leftx = y = Scr->TBInfo.leftx;
1289    rightx = tmp_win->rightx;
1290
1291    attributes.win_gravity = NorthWestGravity;
1292    attributes.background_pixel = tmp_win->title.back;
1293    attributes.border_pixel = tmp_win->title.fore;
1294    attributes.event_mask = (ButtonPressMask | ButtonReleaseMask |
1295                             ExposureMask);
1296    attributes.cursor = Scr->ButtonCursor;
1297    valuemask = (CWWinGravity | CWBackPixel | CWBorderPixel | CWEventMask |
1298                 CWCursor);
1299
1300    tmp_win->titlebuttons = NULL;
1301    nb = Scr->TBInfo.nleft + Scr->TBInfo.nright;
1302    if (nb > 0) {
1303        tmp_win->titlebuttons = malloc((size_t) nb * sizeof(TBWindow));
1304        if (!tmp_win->titlebuttons) {
1305            twmWarning("unable to allocate %d titlebuttons", nb);
1306        }
1307        else {
1308            TBWindow *tbw;
1309            int boxwidth = (Scr->TBInfo.width + Scr->TBInfo.pad);
1310            unsigned h =
1311                (unsigned) (Scr->TBInfo.width - Scr->TBInfo.border * 2);
1312
1313            for (tb = Scr->TBInfo.head, tbw = tmp_win->titlebuttons; tb;
1314                 tb = tb->next, tbw++) {
1315                int x;
1316
1317                if (tb->rightside) {
1318                    x = rightx;
1319                    rightx += boxwidth;
1320                    attributes.win_gravity = NorthEastGravity;
1321                }
1322                else {
1323                    x = leftx;
1324                    leftx += boxwidth;
1325                    attributes.win_gravity = NorthWestGravity;
1326                }
1327                tbw->window = XCreateWindow(dpy, tmp_win->title_w, x, y, h, h,
1328                                            (unsigned int) Scr->TBInfo.border,
1329                                            0, (unsigned int) CopyFromParent,
1330                                            (Visual *) CopyFromParent,
1331                                            valuemask, &attributes);
1332                tbw->info = tb;
1333            }
1334        }
1335    }
1336
1337    tmp_win->hilite_w = (tmp_win->titlehighlight
1338                         ? CreateHighlightWindow(tmp_win) : None);
1339
1340    XMapSubwindows(dpy, tmp_win->title_w);
1341    if (tmp_win->hilite_w)
1342        XUnmapWindow(dpy, tmp_win->hilite_w);
1343    return;
1344}
1345
1346void
1347SetHighlightPixmap(char *filename)
1348{
1349    Pixmap pm = GetBitmap(filename);
1350
1351    if (pm) {
1352        if (Scr->hilitePm) {
1353            XFreePixmap(dpy, Scr->hilitePm);
1354        }
1355        Scr->hilitePm = pm;
1356        Scr->hilite_pm_width = (int) JunkWidth;
1357        Scr->hilite_pm_height = (int) JunkHeight;
1358    }
1359}
1360
1361void
1362FetchWmProtocols(TwmWindow *tmp)
1363{
1364    unsigned long flags = 0L;
1365    Atom *protocols = NULL;
1366    int n;
1367
1368    if (XGetWMProtocols(dpy, tmp->w, &protocols, &n)) {
1369        register int i;
1370        register Atom *ap;
1371
1372        for (i = 0, ap = protocols; i < n; i++, ap++) {
1373            if (*ap == _XA_WM_TAKE_FOCUS)
1374                flags |= DoesWmTakeFocus;
1375            if (*ap == _XA_WM_SAVE_YOURSELF)
1376                flags |= DoesWmSaveYourself;
1377            if (*ap == _XA_WM_DELETE_WINDOW)
1378                flags |= DoesWmDeleteWindow;
1379        }
1380        if (protocols)
1381            XFree(protocols);
1382    }
1383    tmp->protocols = flags;
1384}
1385
1386TwmColormap *
1387CreateTwmColormap(Colormap c)
1388{
1389    TwmColormap *cmap;
1390
1391    cmap = malloc(sizeof(TwmColormap));
1392    if (!cmap || XSaveContext(dpy, c, ColormapContext, (XPointer) cmap)) {
1393        if (cmap)
1394            free(cmap);
1395        return (NULL);
1396    }
1397    cmap->c = c;
1398    cmap->state = 0;
1399    cmap->install_req = 0;
1400    cmap->w = None;
1401    cmap->refcnt = 1;
1402    return (cmap);
1403}
1404
1405ColormapWindow *
1406CreateColormapWindow(Window w, Bool creating_parent, Bool property_window)
1407{
1408    ColormapWindow *cwin;
1409    TwmColormap *cmap;
1410    XWindowAttributes attributes;
1411
1412    cwin = malloc(sizeof(ColormapWindow));
1413    if (cwin) {
1414        if (!XGetWindowAttributes(dpy, w, &attributes) ||
1415            XSaveContext(dpy, w, ColormapContext, (XPointer) cwin)) {
1416            free(cwin);
1417            return (NULL);
1418        }
1419
1420        if (XFindContext(dpy, attributes.colormap, ColormapContext,
1421                         (XPointer *) &cwin->colormap) == XCNOENT) {
1422            cwin->colormap = cmap = CreateTwmColormap(attributes.colormap);
1423            if (!cmap) {
1424                XDeleteContext(dpy, w, ColormapContext);
1425                free(cwin);
1426                return (NULL);
1427            }
1428        }
1429        else {
1430            cwin->colormap->refcnt++;
1431        }
1432
1433        cwin->w = w;
1434        /*
1435         * Assume that windows in colormap list are
1436         * obscured if we are creating the parent window.
1437         * Otherwise, we assume they are unobscured.
1438         */
1439        cwin->visibility = creating_parent ?
1440            VisibilityPartiallyObscured : VisibilityUnobscured;
1441        cwin->refcnt = 1;
1442
1443        /*
1444         * If this is a ColormapWindow property window and we
1445         * are not monitoring ColormapNotify or VisibilityNotify
1446         * events, we need to.
1447         */
1448        if (property_window &&
1449            (attributes.your_event_mask &
1450             (ColormapChangeMask | VisibilityChangeMask)) !=
1451            (ColormapChangeMask | VisibilityChangeMask)) {
1452            XSelectInput(dpy, w, attributes.your_event_mask |
1453                         (ColormapChangeMask | VisibilityChangeMask));
1454        }
1455    }
1456
1457    return (cwin);
1458}
1459
1460void
1461FetchWmColormapWindows(TwmWindow *tmp)
1462{
1463    register int i, j;
1464    Window *cmap_windows = NULL;
1465    Bool can_free_cmap_windows = False;
1466    int number_cmap_windows = 0;
1467    ColormapWindow **cwins = NULL;
1468    int previously_installed;
1469
1470    number_cmap_windows = 0;
1471
1472    /* SUPPRESS 560 */
1473    if ((previously_installed =
1474         (Scr->cmapInfo.cmaps == &tmp->cmaps && tmp->cmaps.number_cwins))) {
1475        cwins = tmp->cmaps.cwins;
1476        for (i = 0; i < tmp->cmaps.number_cwins; i++)
1477            cwins[i]->colormap->state = 0;
1478    }
1479
1480    if (XGetWMColormapWindows(dpy, tmp->w, &cmap_windows,
1481                              &number_cmap_windows) &&
1482        number_cmap_windows > 0) {
1483
1484        can_free_cmap_windows = False;
1485        /*
1486         * check if the top level is in the list, add to front if not
1487         */
1488        for (i = 0; i < number_cmap_windows; i++) {
1489            if (cmap_windows[i] == tmp->w)
1490                break;
1491        }
1492        if (i == number_cmap_windows) { /* not in list */
1493            Window *new_cmap_windows =
1494                malloc(sizeof(Window) * (size_t) (number_cmap_windows + 1));
1495
1496            if (!new_cmap_windows) {
1497                twmWarning
1498                    ("unable to allocate %d element colormap window array",
1499                     number_cmap_windows + 1);
1500                goto done;
1501            }
1502            new_cmap_windows[0] = tmp->w;       /* add to front */
1503            for (i = 0; i < number_cmap_windows; i++) { /* append rest */
1504                new_cmap_windows[i + 1] = cmap_windows[i];
1505            }
1506            XFree(cmap_windows);
1507            can_free_cmap_windows = True;       /* do not use XFree any more */
1508            cmap_windows = new_cmap_windows;
1509            number_cmap_windows++;
1510        }
1511
1512        cwins = malloc(sizeof(ColormapWindow *) * (size_t) number_cmap_windows);
1513        if (cwins) {
1514            for (i = 0; i < number_cmap_windows; i++) {
1515
1516                /*
1517                 * Copy any existing entries into new list.
1518                 */
1519                for (j = 0; j < tmp->cmaps.number_cwins; j++) {
1520                    if (tmp->cmaps.cwins[j]->w == cmap_windows[i]) {
1521                        cwins[i] = tmp->cmaps.cwins[j];
1522                        cwins[i]->refcnt++;
1523                        break;
1524                    }
1525                }
1526
1527                /*
1528                 * If the colormap window is not being pointed by
1529                 * some other applications colormap window list,
1530                 * create a new entry.
1531                 */
1532                if (j == tmp->cmaps.number_cwins) {
1533                    if (XFindContext(dpy, cmap_windows[i], ColormapContext,
1534                                     (XPointer *) &cwins[i]) == XCNOENT) {
1535                        if ((cwins[i] =
1536                             CreateColormapWindow(cmap_windows[i],
1537                                                  (tmp->cmaps.number_cwins == 0
1538                                                   ? True
1539                                                   : False), True)) == NULL) {
1540                            int k;
1541
1542                            for (k = i + 1; k < number_cmap_windows; k++)
1543                                cmap_windows[k - 1] = cmap_windows[k];
1544                            i--;
1545                            number_cmap_windows--;
1546                        }
1547                    }
1548                    else
1549                        cwins[i]->refcnt++;
1550                }
1551            }
1552        }
1553    }
1554
1555    /* No else here, in case we bailed out of clause above.
1556     */
1557    if (number_cmap_windows == 0) {
1558
1559        number_cmap_windows = 1;
1560
1561        cwins = malloc(sizeof(ColormapWindow *));
1562        if (XFindContext(dpy, tmp->w, ColormapContext, (XPointer *) &cwins[0])
1563            == XCNOENT) {
1564            cwins[0] =
1565                CreateColormapWindow(tmp->w,
1566                                     (Bool) tmp->cmaps.number_cwins == 0,
1567                                     False);
1568            if (cwins[0] == NULL)
1569                number_cmap_windows = 0;
1570        }
1571        else
1572            cwins[0]->refcnt++;
1573    }
1574
1575    if (tmp->cmaps.number_cwins)
1576        free_cwins(tmp);
1577
1578    tmp->cmaps.cwins = cwins;
1579    tmp->cmaps.number_cwins = number_cmap_windows;
1580    if (number_cmap_windows > 1)
1581        tmp->cmaps.scoreboard =
1582            calloc(1, ColormapsScoreboardLength(&tmp->cmaps));
1583
1584    if (previously_installed)
1585        InstallWindowColormaps(PropertyNotify, (TwmWindow *) NULL);
1586
1587 done:
1588    if (cmap_windows) {
1589        if (can_free_cmap_windows)
1590            free(cmap_windows);
1591        else
1592            XFree(cmap_windows);
1593    }
1594
1595    return;
1596}
1597
1598void
1599GetWindowSizeHints(TwmWindow *tmp)
1600{
1601    long supplied = 0;
1602
1603    if (!XGetWMNormalHints(dpy, tmp->w, &tmp->hints, &supplied))
1604        tmp->hints.flags = 0;
1605
1606    if (tmp->hints.flags & PResizeInc) {
1607        if (tmp->hints.width_inc == 0)
1608            tmp->hints.width_inc = 1;
1609        if (tmp->hints.height_inc == 0)
1610            tmp->hints.height_inc = 1;
1611    }
1612
1613    if (!(supplied & PWinGravity) && (tmp->hints.flags & USPosition)) {
1614        static int gravs[] = { SouthEastGravity, SouthWestGravity,
1615            NorthEastGravity, NorthWestGravity
1616        };
1617        int right = tmp->attr.x + tmp->attr.width + 2 * tmp->old_bw;
1618        int bottom = tmp->attr.y + tmp->attr.height + 2 * tmp->old_bw;
1619
1620        tmp->hints.win_gravity =
1621            gravs[((Scr->MyDisplayHeight - bottom <
1622                    tmp->title_height) ? 0 : 2) | ((Scr->MyDisplayWidth -
1623                                                    right <
1624                                                    tmp->title_height) ? 0 :
1625                                                   1)];
1626        tmp->hints.flags |= PWinGravity;
1627    }
1628}
1629