10bbfda8aSnia/*
20bbfda8aSnia * Various operations done on windows.
30bbfda8aSnia */
40bbfda8aSnia
50bbfda8aSnia#include "ctwm.h"
60bbfda8aSnia
70bbfda8aSnia#include <stdio.h>
80bbfda8aSnia
90bbfda8aSnia#include "animate.h"
100bbfda8aSnia#include "colormaps.h"
110bbfda8aSnia#include "drawing.h"
120bbfda8aSnia#include "events.h"
130bbfda8aSnia#include "iconmgr.h"
140bbfda8aSnia#include "image.h"
150bbfda8aSnia#include "otp.h"
160bbfda8aSnia#include "screen.h"
170bbfda8aSnia#include "win_decorations.h"
180bbfda8aSnia#include "win_iconify.h"
190bbfda8aSnia#include "win_ops.h"
200bbfda8aSnia#include "win_utils.h"
210bbfda8aSnia
220bbfda8aSnia
230bbfda8aSnia/*
240bbfda8aSnia * Update the visuals of a window (e.g., its own decorations and its
250bbfda8aSnia * representation in the icon manager) for having/losing focus.
260bbfda8aSnia *
270bbfda8aSnia * Formerly in util.c
280bbfda8aSnia */
290bbfda8aSniavoid
300bbfda8aSniaSetFocusVisualAttributes(TwmWindow *tmp_win, bool focus)
310bbfda8aSnia{
320bbfda8aSnia	if(! tmp_win) {
330bbfda8aSnia		return;
340bbfda8aSnia	}
350bbfda8aSnia
360bbfda8aSnia	if(focus == tmp_win->hasfocusvisible) {
370bbfda8aSnia		return;
380bbfda8aSnia	}
390bbfda8aSnia	if(tmp_win->highlight) {
400bbfda8aSnia		if(Scr->use3Dborders) {
410bbfda8aSnia			PaintBorders(tmp_win, focus);
420bbfda8aSnia		}
430bbfda8aSnia		else {
440bbfda8aSnia			if(focus) {
450bbfda8aSnia				XSetWindowBorder(dpy, tmp_win->frame, tmp_win->borderC.back);
460bbfda8aSnia				if(tmp_win->title_w) {
470bbfda8aSnia					XSetWindowBorder(dpy, tmp_win->title_w, tmp_win->borderC.back);
480bbfda8aSnia				}
490bbfda8aSnia			}
500bbfda8aSnia			else {
510bbfda8aSnia				/*
520bbfda8aSnia				 * XXX It seems possible this could be replaced by a
530bbfda8aSnia				 * single global 'gray' pixmap; I don't think it actually
540bbfda8aSnia				 * varies per window, and I don't see any obvious reason
550bbfda8aSnia				 * it can't be reused, so we may be able to save an
560bbfda8aSnia				 * allocation for each window by doing so...
570bbfda8aSnia				 */
580bbfda8aSnia				XSetWindowBorderPixmap(dpy, tmp_win->frame, tmp_win->gray);
590bbfda8aSnia				if(tmp_win->title_w) {
600bbfda8aSnia					XSetWindowBorderPixmap(dpy, tmp_win->title_w, tmp_win->gray);
610bbfda8aSnia				}
620bbfda8aSnia			}
630bbfda8aSnia		}
640bbfda8aSnia	}
650bbfda8aSnia
660bbfda8aSnia	if(focus) {
670bbfda8aSnia		bool hil = false;
680bbfda8aSnia
690bbfda8aSnia		if(tmp_win->lolite_wl) {
700bbfda8aSnia			XUnmapWindow(dpy, tmp_win->lolite_wl);
710bbfda8aSnia		}
720bbfda8aSnia		if(tmp_win->lolite_wr) {
730bbfda8aSnia			XUnmapWindow(dpy, tmp_win->lolite_wr);
740bbfda8aSnia		}
750bbfda8aSnia		if(tmp_win->hilite_wl) {
760bbfda8aSnia			XMapWindow(dpy, tmp_win->hilite_wl);
770bbfda8aSnia			hil = true;
780bbfda8aSnia		}
790bbfda8aSnia		if(tmp_win->hilite_wr) {
800bbfda8aSnia			XMapWindow(dpy, tmp_win->hilite_wr);
810bbfda8aSnia			hil = true;
820bbfda8aSnia		}
830bbfda8aSnia		if(hil && tmp_win->HiliteImage && tmp_win->HiliteImage->next) {
840bbfda8aSnia			MaybeAnimate = true;
850bbfda8aSnia		}
860bbfda8aSnia		if(tmp_win->iconmanagerlist) {
870bbfda8aSnia			ActiveIconManager(tmp_win->iconmanagerlist);
880bbfda8aSnia		}
890bbfda8aSnia	}
900bbfda8aSnia	else {
910bbfda8aSnia		if(tmp_win->hilite_wl) {
920bbfda8aSnia			XUnmapWindow(dpy, tmp_win->hilite_wl);
930bbfda8aSnia		}
940bbfda8aSnia		if(tmp_win->hilite_wr) {
950bbfda8aSnia			XUnmapWindow(dpy, tmp_win->hilite_wr);
960bbfda8aSnia		}
970bbfda8aSnia		if(tmp_win->lolite_wl) {
980bbfda8aSnia			XMapWindow(dpy, tmp_win->lolite_wl);
990bbfda8aSnia		}
1000bbfda8aSnia		if(tmp_win->lolite_wr) {
1010bbfda8aSnia			XMapWindow(dpy, tmp_win->lolite_wr);
1020bbfda8aSnia		}
1030bbfda8aSnia		if(tmp_win->iconmanagerlist) {
1040bbfda8aSnia			NotActiveIconManager(tmp_win->iconmanagerlist);
1050bbfda8aSnia		}
1060bbfda8aSnia	}
1070bbfda8aSnia	if(Scr->use3Dtitles && Scr->SunkFocusWindowTitle && tmp_win->title_height) {
1080bbfda8aSnia		ButtonState bs;
1090bbfda8aSnia
1100bbfda8aSnia		bs = focus ? on : off;
1110bbfda8aSnia		Draw3DBorder(tmp_win->title_w, Scr->TBInfo.titlex, 0,
1120bbfda8aSnia		             tmp_win->title_width - Scr->TBInfo.titlex -
1130bbfda8aSnia		             Scr->TBInfo.rightoff - Scr->TitlePadding,
1140bbfda8aSnia		             Scr->TitleHeight, Scr->TitleShadowDepth,
1150bbfda8aSnia		             tmp_win->title, bs, false, false);
1160bbfda8aSnia	}
1170bbfda8aSnia	tmp_win->hasfocusvisible = focus;
1180bbfda8aSnia}
1190bbfda8aSnia
1200bbfda8aSnia
1210bbfda8aSnia/*
1220bbfda8aSnia * Shift the focus to a given window, and do whatever subsidiary ops that
1230bbfda8aSnia * entails.
1240bbfda8aSnia *
1250bbfda8aSnia * Formerly in util.c
1260bbfda8aSnia */
1270bbfda8aSniavoid
1280bbfda8aSniaSetFocus(TwmWindow *tmp_win, Time tim)
1290bbfda8aSnia{
1300bbfda8aSnia	Window w = (tmp_win ? tmp_win->w : PointerRoot);
1310bbfda8aSnia	bool f_iconmgr = false;
1320bbfda8aSnia
1330bbfda8aSnia	if(Scr->Focus && (Scr->Focus->isiconmgr)) {
1340bbfda8aSnia		f_iconmgr = true;
1350bbfda8aSnia	}
1360bbfda8aSnia	if(Scr->SloppyFocus && (w == PointerRoot) && (!f_iconmgr)) {
1370bbfda8aSnia		return;
1380bbfda8aSnia	}
1390bbfda8aSnia
1400bbfda8aSnia	XSetInputFocus(dpy, w, RevertToPointerRoot, tim);
1410bbfda8aSnia#ifdef EWMH
1420bbfda8aSnia	EwmhSet_NET_ACTIVE_WINDOW(w);
1430bbfda8aSnia#endif
1440bbfda8aSnia	if(Scr->Focus == tmp_win) {
1450bbfda8aSnia		return;
1460bbfda8aSnia	}
1470bbfda8aSnia
1480bbfda8aSnia	if(Scr->Focus) {
1490bbfda8aSnia		if(Scr->Focus->AutoSqueeze && !Scr->Focus->squeezed) {
1500bbfda8aSnia			AutoSqueeze(Scr->Focus);
1510bbfda8aSnia		}
1520bbfda8aSnia		SetFocusVisualAttributes(Scr->Focus, false);
1530bbfda8aSnia#ifdef EWMH
1540bbfda8aSnia		// Priority may change when focus does
1550bbfda8aSnia		if(OtpIsFocusDependent(Scr->Focus)) {
1560bbfda8aSnia			OtpUnfocusWindow(Scr->Focus);
1570bbfda8aSnia			// That Scr->Focus = NULL's internally for us, but we don't
1580bbfda8aSnia			// care, since we're about to reset it if we need to.
1590bbfda8aSnia		}
1600bbfda8aSnia#endif
1610bbfda8aSnia	}
1620bbfda8aSnia
1630bbfda8aSnia	if(tmp_win) {
1640bbfda8aSnia		if(tmp_win->AutoSqueeze && tmp_win->squeezed) {
1650bbfda8aSnia			AutoSqueeze(tmp_win);
1660bbfda8aSnia		}
1670bbfda8aSnia		SetFocusVisualAttributes(tmp_win, true);
1680bbfda8aSnia#ifdef EWMH
1690bbfda8aSnia		// Priority may change when focus does
1700bbfda8aSnia		if(OtpIsFocusDependent(tmp_win)) {
1710bbfda8aSnia			OtpFocusWindow(tmp_win);
1720bbfda8aSnia			// Pre-sets Scr->Focus
1730bbfda8aSnia		}
1740bbfda8aSnia#endif
1750bbfda8aSnia	}
1760bbfda8aSnia
1770bbfda8aSnia	// in the EWMH cases, this was already done.
1780bbfda8aSnia	Scr->Focus = tmp_win;
1790bbfda8aSnia
1800bbfda8aSnia	return;
1810bbfda8aSnia}
1820bbfda8aSnia
1830bbfda8aSnia
1840bbfda8aSnia/*
1850bbfda8aSnia * Move the focus straight to the root, with associated cleanup.
1860bbfda8aSnia *
1870bbfda8aSnia * Formerly in menus.c
1880bbfda8aSnia */
1890bbfda8aSniavoid FocusOnRoot(void)
1900bbfda8aSnia{
1910bbfda8aSnia	SetFocus(NULL, EventTime);
1920bbfda8aSnia	InstallColormaps(0, &Scr->RootColormaps);
1930bbfda8aSnia	if(! Scr->ClickToFocus) {
1940bbfda8aSnia		Scr->FocusRoot = true;
1950bbfda8aSnia	}
1960bbfda8aSnia}
1970bbfda8aSnia
1980bbfda8aSnia
1990bbfda8aSnia/*
2000bbfda8aSnia * Handle doing squeezing bits for AutoSqueeze{} windows.
2010bbfda8aSnia *
2020bbfda8aSnia * Formerly in menus.c
2030bbfda8aSnia */
2040bbfda8aSniavoid
2050bbfda8aSniaAutoSqueeze(TwmWindow *tmp_win)
2060bbfda8aSnia{
2070bbfda8aSnia	if(tmp_win->isiconmgr) {
2080bbfda8aSnia		return;
2090bbfda8aSnia	}
2100bbfda8aSnia	if(Scr->RaiseWhenAutoUnSqueeze && tmp_win->squeezed) {
2110bbfda8aSnia		OtpRaise(tmp_win, WinWin);
2120bbfda8aSnia	}
2130bbfda8aSnia	Squeeze(tmp_win);
2140bbfda8aSnia}
2150bbfda8aSnia
2160bbfda8aSnia
2170bbfda8aSnia/*
2180bbfda8aSnia * Toggle a window's squeezed state.
2190bbfda8aSnia *
2200bbfda8aSnia * Formerly in menus.c
2210bbfda8aSnia */
2220bbfda8aSniavoid
2230bbfda8aSniaSqueeze(TwmWindow *tmp_win)
2240bbfda8aSnia{
2250bbfda8aSnia	long fx, fy, savex, savey;
2260bbfda8aSnia	int  neww, newh;
2270bbfda8aSnia	bool south;
2280bbfda8aSnia	int  grav = ((tmp_win->hints.flags & PWinGravity)
2290bbfda8aSnia	             ? tmp_win->hints.win_gravity : NorthWestGravity);
2300bbfda8aSnia	long eventMask;
2310bbfda8aSnia	if(tmp_win->squeezed) {
232b18c2d1eSnia		tmp_win->squeezed = false;
2330bbfda8aSnia#ifdef EWMH
2340bbfda8aSnia		EwmhSet_NET_WM_STATE(tmp_win, EWMH_STATE_SHADED);
2350bbfda8aSnia#endif /* EWMH */
2360bbfda8aSnia		if(!tmp_win->isicon) {
2370bbfda8aSnia			XMapWindow(dpy, tmp_win->w);
2380bbfda8aSnia		}
2390bbfda8aSnia		SetupWindow(tmp_win, tmp_win->actual_frame_x, tmp_win->actual_frame_y,
2400bbfda8aSnia		            tmp_win->actual_frame_width, tmp_win->actual_frame_height, -1);
2410bbfda8aSnia		ReMapTransients(tmp_win);
2420bbfda8aSnia		return;
2430bbfda8aSnia	}
2440bbfda8aSnia
2450bbfda8aSnia	newh = tmp_win->title_height + 2 * tmp_win->frame_bw3D;
2460bbfda8aSnia	if(newh < 3) {
2470bbfda8aSnia		XBell(dpy, 0);
2480bbfda8aSnia		return;
2490bbfda8aSnia	}
2500bbfda8aSnia	switch(grav) {
2510bbfda8aSnia		case SouthWestGravity :
2520bbfda8aSnia		case SouthGravity :
2530bbfda8aSnia		case SouthEastGravity :
2540bbfda8aSnia			south = true;
2550bbfda8aSnia			break;
2560bbfda8aSnia		default :
2570bbfda8aSnia			south = false;
2580bbfda8aSnia			break;
2590bbfda8aSnia	}
2600bbfda8aSnia	if(tmp_win->title_height && !tmp_win->AlwaysSqueezeToGravity) {
2610bbfda8aSnia		south = false;
2620bbfda8aSnia	}
2630bbfda8aSnia
2640bbfda8aSnia	tmp_win->squeezed = true;
2650bbfda8aSnia	tmp_win->actual_frame_width  = tmp_win->frame_width;
2660bbfda8aSnia	tmp_win->actual_frame_height = tmp_win->frame_height;
2670bbfda8aSnia	savex = fx = tmp_win->frame_x;
2680bbfda8aSnia	savey = fy = tmp_win->frame_y;
2690bbfda8aSnia	neww  = tmp_win->actual_frame_width;
2700bbfda8aSnia	if(south) {
2710bbfda8aSnia		fy += tmp_win->frame_height - newh;
2720bbfda8aSnia	}
2730bbfda8aSnia	if(tmp_win->squeeze_info) {
274b18c2d1eSnia		fx  += tmp_win->title_x + tmp_win->frame_bw - tmp_win->frame_bw3D;
275b18c2d1eSnia		neww = tmp_win->title_width + 2 * tmp_win->frame_bw3D;
2760bbfda8aSnia	}
2770bbfda8aSnia
2780bbfda8aSnia	eventMask = mask_out_event(tmp_win->w, StructureNotifyMask);
2790bbfda8aSnia#ifdef EWMH
2800bbfda8aSnia	EwmhSet_NET_WM_STATE(tmp_win, EWMH_STATE_SHADED);
2810bbfda8aSnia#endif /* EWMH */
2820bbfda8aSnia	XUnmapWindow(dpy, tmp_win->w);
2830bbfda8aSnia	restore_mask(tmp_win->w, eventMask);
2840bbfda8aSnia
2850bbfda8aSnia	if(fx + neww >= Scr->rootw - Scr->BorderRight) {
2860bbfda8aSnia		fx = Scr->rootw - Scr->BorderRight - neww;
2870bbfda8aSnia	}
2880bbfda8aSnia	if(fy + newh >= Scr->rooth - Scr->BorderBottom) {
2890bbfda8aSnia		fy = Scr->rooth - Scr->BorderBottom - newh;
2900bbfda8aSnia	}
2910bbfda8aSnia	SetupWindow(tmp_win, fx, fy, neww, newh, -1);
2920bbfda8aSnia	tmp_win->actual_frame_x = savex;
2930bbfda8aSnia	tmp_win->actual_frame_y = savey;
2940bbfda8aSnia
2950bbfda8aSnia	/* Now make the group members disappear */
2960bbfda8aSnia	UnmapTransients(tmp_win, false, eventMask);
2970bbfda8aSnia}
2980bbfda8aSnia
2990bbfda8aSnia
3000bbfda8aSnia/***********************************************************************
3010bbfda8aSnia *
3020bbfda8aSnia *  Procedure:
3030bbfda8aSnia *      MoveOutline - move a window outline
3040bbfda8aSnia *
3050bbfda8aSnia *  Inputs:
3060bbfda8aSnia *      root        - the window we are outlining
3070bbfda8aSnia *      x           - upper left x coordinate
3080bbfda8aSnia *      y           - upper left y coordinate
3090bbfda8aSnia *      width       - the width of the rectangle
3100bbfda8aSnia *      height      - the height of the rectangle
3110bbfda8aSnia *      bw          - the border width of the frame
3120bbfda8aSnia *      th          - title height
3130bbfda8aSnia *
3140bbfda8aSnia ***********************************************************************
3150bbfda8aSnia */
3160bbfda8aSniavoid
3170bbfda8aSniaMoveOutline(Window root, int x, int y, int width, int height, int bw, int th)
3180bbfda8aSnia{
3190bbfda8aSnia	static int  lastx = 0;
3200bbfda8aSnia	static int  lasty = 0;
3210bbfda8aSnia	static int  lastWidth = 0;
3220bbfda8aSnia	static int  lastHeight = 0;
3230bbfda8aSnia	static int  lastBW = 0;
3240bbfda8aSnia	static int  lastTH = 0;
3250bbfda8aSnia	int         xl, xr, yt, yb, xinnerl, xinnerr, yinnert, yinnerb;
3260bbfda8aSnia	int         xthird, ythird;
3270bbfda8aSnia	XSegment    outline[18];
3280bbfda8aSnia	XSegment   *r;
3290bbfda8aSnia
3300bbfda8aSnia	if(x == lastx && y == lasty && width == lastWidth && height == lastHeight
3310bbfda8aSnia	                && lastBW == bw && th == lastTH) {
3320bbfda8aSnia		return;
3330bbfda8aSnia	}
3340bbfda8aSnia
3350bbfda8aSnia	r = outline;
3360bbfda8aSnia
3370bbfda8aSnia#define DRAWIT() \
3380bbfda8aSnia    if (lastWidth || lastHeight)                        \
3390bbfda8aSnia    {                                                   \
3400bbfda8aSnia        xl = lastx;                                     \
3410bbfda8aSnia        xr = lastx + lastWidth - 1;                     \
3420bbfda8aSnia        yt = lasty;                                     \
3430bbfda8aSnia        yb = lasty + lastHeight - 1;                    \
3440bbfda8aSnia        xinnerl = xl + lastBW;                          \
3450bbfda8aSnia        xinnerr = xr - lastBW;                          \
3460bbfda8aSnia        yinnert = yt + lastTH + lastBW;                 \
3470bbfda8aSnia        yinnerb = yb - lastBW;                          \
3480bbfda8aSnia        xthird = (xinnerr - xinnerl) / 3;               \
3490bbfda8aSnia        ythird = (yinnerb - yinnert) / 3;               \
3500bbfda8aSnia                                                        \
3510bbfda8aSnia        r->x1 = xl;                                     \
3520bbfda8aSnia        r->y1 = yt;                                     \
3530bbfda8aSnia        r->x2 = xr;                                     \
3540bbfda8aSnia        r->y2 = yt;                                     \
3550bbfda8aSnia        r++;                                            \
3560bbfda8aSnia                                                        \
3570bbfda8aSnia        r->x1 = xl;                                     \
3580bbfda8aSnia        r->y1 = yb;                                     \
3590bbfda8aSnia        r->x2 = xr;                                     \
3600bbfda8aSnia        r->y2 = yb;                                     \
3610bbfda8aSnia        r++;                                            \
3620bbfda8aSnia                                                        \
3630bbfda8aSnia        r->x1 = xl;                                     \
3640bbfda8aSnia        r->y1 = yt;                                     \
3650bbfda8aSnia        r->x2 = xl;                                     \
3660bbfda8aSnia        r->y2 = yb;                                     \
3670bbfda8aSnia        r++;                                            \
3680bbfda8aSnia                                                        \
3690bbfda8aSnia        r->x1 = xr;                                     \
3700bbfda8aSnia        r->y1 = yt;                                     \
3710bbfda8aSnia        r->x2 = xr;                                     \
3720bbfda8aSnia        r->y2 = yb;                                     \
3730bbfda8aSnia        r++;                                            \
3740bbfda8aSnia                                                        \
3750bbfda8aSnia        r->x1 = xinnerl + xthird;                       \
3760bbfda8aSnia        r->y1 = yinnert;                                \
3770bbfda8aSnia        r->x2 = r->x1;                                  \
3780bbfda8aSnia        r->y2 = yinnerb;                                \
3790bbfda8aSnia        r++;                                            \
3800bbfda8aSnia                                                        \
3810bbfda8aSnia        r->x1 = xinnerl + (2 * xthird);                 \
3820bbfda8aSnia        r->y1 = yinnert;                                \
3830bbfda8aSnia        r->x2 = r->x1;                                  \
3840bbfda8aSnia        r->y2 = yinnerb;                                \
3850bbfda8aSnia        r++;                                            \
3860bbfda8aSnia                                                        \
3870bbfda8aSnia        r->x1 = xinnerl;                                \
3880bbfda8aSnia        r->y1 = yinnert + ythird;                       \
3890bbfda8aSnia        r->x2 = xinnerr;                                \
3900bbfda8aSnia        r->y2 = r->y1;                                  \
3910bbfda8aSnia        r++;                                            \
3920bbfda8aSnia                                                        \
3930bbfda8aSnia        r->x1 = xinnerl;                                \
3940bbfda8aSnia        r->y1 = yinnert + (2 * ythird);                 \
3950bbfda8aSnia        r->x2 = xinnerr;                                \
3960bbfda8aSnia        r->y2 = r->y1;                                  \
3970bbfda8aSnia        r++;                                            \
3980bbfda8aSnia                                                        \
3990bbfda8aSnia        if (lastTH != 0) {                              \
4000bbfda8aSnia            r->x1 = xl;                                 \
4010bbfda8aSnia            r->y1 = yt + lastTH;                        \
4020bbfda8aSnia            r->x2 = xr;                                 \
4030bbfda8aSnia            r->y2 = r->y1;                              \
4040bbfda8aSnia            r++;                                        \
4050bbfda8aSnia        }                                               \
4060bbfda8aSnia    }
4070bbfda8aSnia
4080bbfda8aSnia	/* undraw the old one, if any */
4090bbfda8aSnia	DRAWIT();
4100bbfda8aSnia
4110bbfda8aSnia	lastx = x;
4120bbfda8aSnia	lasty = y;
4130bbfda8aSnia	lastWidth = width;
4140bbfda8aSnia	lastHeight = height;
4150bbfda8aSnia	lastBW = bw;
4160bbfda8aSnia	lastTH = th;
4170bbfda8aSnia
4180bbfda8aSnia	/* draw the new one, if any */
4190bbfda8aSnia	DRAWIT();
4200bbfda8aSnia
4210bbfda8aSnia#undef DRAWIT
4220bbfda8aSnia
4230bbfda8aSnia
4240bbfda8aSnia	if(r != outline) {
4250bbfda8aSnia		XDrawSegments(dpy, root, Scr->DrawGC, outline, r - outline);
4260bbfda8aSnia	}
4270bbfda8aSnia}
428