Home | History | Annotate | Line # | Download | only in dist
      1 /*
      2  * Window de/iconification handling
      3  */
      4 
      5 #include "ctwm.h"
      6 
      7 #include <stdlib.h>
      8 #include <sys/time.h>
      9 
     10 #include <X11/extensions/shape.h>
     11 
     12 #include "events.h"
     13 #include "functions.h"
     14 #include "iconmgr.h"
     15 #include "icons.h"
     16 #include "list.h"
     17 #include "otp.h"
     18 #include "screen.h"
     19 #include "util.h"
     20 #include "vscreen.h"
     21 #include "win_iconify.h"
     22 #include "win_ops.h"
     23 #include "win_utils.h"
     24 #include "workspace_manager.h"
     25 
     26 
     27 /* Animations */
     28 static void MosaicFade(TwmWindow *tmp_win, Window blanket);
     29 static void ZoomInWindow(TwmWindow *tmp_win, Window blanket);
     30 static void ZoomOutWindow(TwmWindow *tmp_win, Window blanket);
     31 static void FadeWindow(TwmWindow *tmp_win, Window blanket);
     32 static void SweepWindow(TwmWindow *tmp_win, Window blanket);
     33 
     34 /* De/iconify utils */
     35 static void Zoom(Window wf, Window wt);
     36 static void ReMapOne(TwmWindow *t, TwmWindow *leader);
     37 static void waitamoment(float timeout);
     38 
     39 
     40 
     41 /*
     42  * The main routines for iconifying...
     43  */
     44 void
     45 Iconify(TwmWindow *tmp_win, int def_x, int def_y)
     46 {
     47 	TwmWindow *t;
     48 	bool iconify;
     49 	long eventMask;
     50 	WList *wl;
     51 	Window leader = (Window) - 1;
     52 	Window blanket = (Window) - 1;
     53 
     54 	iconify = (!tmp_win->iconify_by_unmapping);
     55 	t = NULL;
     56 	if(tmp_win->istransient) {
     57 		leader = tmp_win->transientfor;
     58 		t = GetTwmWindow(leader);
     59 	}
     60 	else if((leader = tmp_win->group) != 0 && leader != tmp_win->w) {
     61 		t = GetTwmWindow(leader);
     62 	}
     63 	if(t && t->icon_on) {
     64 		iconify = false;
     65 	}
     66 	if(iconify) {
     67 		if(!tmp_win->icon || !tmp_win->icon->w) {
     68 			CreateIconWindow(tmp_win, def_x, def_y);
     69 		}
     70 		else {
     71 			IconUp(tmp_win);
     72 		}
     73 		if(visible(tmp_win)) {
     74 			OtpRaise(tmp_win, IconWin);
     75 			XMapWindow(dpy, tmp_win->icon->w);
     76 		}
     77 	}
     78 	if(tmp_win->iconmanagerlist) {
     79 		for(wl = tmp_win->iconmanagerlist; wl != NULL; wl = wl->nextv) {
     80 			XMapWindow(dpy, wl->icon);
     81 		}
     82 	}
     83 
     84 	/* Don't mask anything yet, just get the current for various uses */
     85 	eventMask = mask_out_event(tmp_win->w, 0);
     86 
     87 	/* iconify transients and window group first */
     88 	UnmapTransients(tmp_win, iconify, eventMask);
     89 
     90 	if(iconify) {
     91 		Zoom(tmp_win->frame, tmp_win->icon->w);
     92 	}
     93 
     94 	/*
     95 	 * Prevent the receipt of an UnmapNotify, since that would
     96 	 * cause a transition to the Withdrawn state.
     97 	 */
     98 	tmp_win->mapped = false;
     99 
    100 	if((Scr->IconifyStyle != ICONIFY_NORMAL) && !Scr->WindowMask) {
    101 		XWindowAttributes winattrs;
    102 		XSetWindowAttributes attr;
    103 
    104 		XGetWindowAttributes(dpy, tmp_win->frame, &winattrs);
    105 		blanket = XCreateWindow(dpy, Scr->Root, winattrs.x, winattrs.y,
    106 		                        winattrs.width, winattrs.height, 0,
    107 		                        CopyFromParent, CopyFromParent,
    108 		                        CopyFromParent, None, &attr);
    109 		XMapWindow(dpy, blanket);
    110 	}
    111 
    112 	mask_out_event_mask(tmp_win->w, StructureNotifyMask, eventMask);
    113 	XUnmapWindow(dpy, tmp_win->w);
    114 	XUnmapWindow(dpy, tmp_win->frame);
    115 	restore_mask(tmp_win->w, eventMask);
    116 
    117 	SetMapStateProp(tmp_win, IconicState);
    118 
    119 	if((Scr->IconifyStyle != ICONIFY_NORMAL) && !Scr->WindowMask) {
    120 		switch(Scr->IconifyStyle) {
    121 			case ICONIFY_MOSAIC:
    122 				MosaicFade(tmp_win, blanket);
    123 				break;
    124 			case ICONIFY_ZOOMIN:
    125 				ZoomInWindow(tmp_win, blanket);
    126 				break;
    127 			case ICONIFY_ZOOMOUT:
    128 				ZoomOutWindow(tmp_win, blanket);
    129 				break;
    130 			case ICONIFY_FADE:
    131 				FadeWindow(tmp_win, blanket);
    132 				break;
    133 			case ICONIFY_SWEEP:
    134 				SweepWindow(tmp_win, blanket);
    135 				break;
    136 			case ICONIFY_NORMAL:
    137 				/* Placate insufficiently smart clang warning */
    138 				break;
    139 		}
    140 		XDestroyWindow(dpy, blanket);
    141 	}
    142 	if(tmp_win == Scr->Focus) {
    143 		SetFocus(NULL, EventTime);
    144 		if(! Scr->ClickToFocus) {
    145 			Scr->FocusRoot = true;
    146 		}
    147 	}
    148 	tmp_win->isicon = true;
    149 	tmp_win->icon_on = iconify;
    150 	WMapIconify(tmp_win);
    151 	if(! Scr->WindowMask && Scr->IconifyFunction.func != 0) {
    152 		char *action;
    153 		XEvent event;
    154 
    155 		action = Scr->IconifyFunction.item ? Scr->IconifyFunction.item->action : NULL;
    156 		ExecuteFunction(Scr->IconifyFunction.func, action,
    157 		                (Window) 0, tmp_win, &event, C_ROOT, false);
    158 	}
    159 	XSync(dpy, 0);
    160 }
    161 
    162 
    163 /*
    164  * ... and its complement
    165  */
    166 void
    167 DeIconify(TwmWindow *tmp_win)
    168 {
    169 	TwmWindow *t = tmp_win;
    170 	bool isicon = false;
    171 
    172 	/* de-iconify the main window */
    173 	if(Scr->WindowMask) {
    174 		XRaiseWindow(dpy, Scr->WindowMask);
    175 	}
    176 	if(tmp_win->isicon) {
    177 		isicon = true;
    178 		if(tmp_win->icon_on && tmp_win->icon && tmp_win->icon->w) {
    179 			Zoom(tmp_win->icon->w, tmp_win->frame);
    180 		}
    181 		else if(tmp_win->group != (Window) 0) {
    182 			TwmWindow *tmpt = GetTwmWindow(tmp_win->group);
    183 			if(tmpt) {
    184 				t = tmpt;
    185 				if(t->icon_on && t->icon && t->icon->w) {
    186 					Zoom(t->icon->w, tmp_win->frame);
    187 				}
    188 			}
    189 		}
    190 	}
    191 
    192 	ReMapOne(tmp_win, t);
    193 
    194 	if(isicon &&
    195 	                (Scr->WarpCursor ||
    196 	                 LookInList(Scr->WarpCursorL, tmp_win->name, &tmp_win->class))) {
    197 		WarpToWindow(tmp_win, false);
    198 	}
    199 
    200 	/* now de-iconify any window group transients */
    201 	ReMapTransients(tmp_win);
    202 
    203 	if(! Scr->WindowMask && Scr->DeIconifyFunction.func != 0) {
    204 		char *action;
    205 		XEvent event;
    206 
    207 		action = Scr->DeIconifyFunction.item ?
    208 		         Scr->DeIconifyFunction.item->action : NULL;
    209 		ExecuteFunction(Scr->DeIconifyFunction.func, action,
    210 		                (Window) 0, tmp_win, &event, C_ROOT, false);
    211 	}
    212 	XSync(dpy, 0);
    213 }
    214 
    215 
    216 
    217 /*
    218  * Animations for popping windows around.
    219  */
    220 static void
    221 MosaicFade(TwmWindow *tmp_win, Window blanket)
    222 {
    223 	int         srect;
    224 	int         i, j, nrects;
    225 	Pixmap      mask;
    226 	GC          gc;
    227 	XGCValues   gcv;
    228 	XRectangle *rectangles;
    229 	int  width = tmp_win->frame_width;
    230 	int height = tmp_win->frame_height;
    231 
    232 	srect = (width < height) ? (width / 20) : (height / 20);
    233 	mask  = XCreatePixmap(dpy, blanket, width, height, 1);
    234 
    235 	gcv.foreground = 1;
    236 	gc = XCreateGC(dpy, mask, GCForeground, &gcv);
    237 	XFillRectangle(dpy, mask, gc, 0, 0, width, height);
    238 	gcv.function = GXclear;
    239 	XChangeGC(dpy, gc, GCFunction, &gcv);
    240 
    241 	nrects = ((width * height) / (srect * srect)) / 10;
    242 	rectangles = calloc(nrects, sizeof(XRectangle));
    243 	for(j = 0; j < nrects; j++) {
    244 		rectangles [j].width  = srect;
    245 		rectangles [j].height = srect;
    246 	}
    247 	for(i = 0; i < 10; i++) {
    248 		for(j = 0; j < nrects; j++) {
    249 			rectangles [j].x = ((lrand48() %  width) / srect) * srect;
    250 			rectangles [j].y = ((lrand48() % height) / srect) * srect;
    251 		}
    252 		XFillRectangles(dpy, mask, gc, rectangles, nrects);
    253 		XShapeCombineMask(dpy, blanket, ShapeBounding, 0, 0, mask, ShapeSet);
    254 		XFlush(dpy);
    255 		waitamoment(0.020);
    256 	}
    257 	XFreePixmap(dpy, mask);
    258 	XFreeGC(dpy, gc);
    259 	free(rectangles);
    260 }
    261 
    262 
    263 static void
    264 ZoomInWindow(TwmWindow *tmp_win, Window blanket)
    265 {
    266 	Pixmap        mask;
    267 	GC            gc, gcn;
    268 	XGCValues     gcv;
    269 
    270 	int i, nsteps = 20;
    271 	int w = tmp_win->frame_width;
    272 	int h = tmp_win->frame_height;
    273 	int step = (MAX(w, h)) / (2.0 * nsteps);
    274 
    275 	mask = XCreatePixmap(dpy, blanket, w, h, 1);
    276 	gcv.foreground = 1;
    277 	gc  = XCreateGC(dpy, mask, GCForeground, &gcv);
    278 	gcv.function = GXclear;
    279 	gcn = XCreateGC(dpy, mask, GCForeground | GCFunction, &gcv);
    280 
    281 	for(i = 0; i < nsteps; i++) {
    282 		XFillRectangle(dpy, mask, gcn, 0, 0, w, h);
    283 		XFillArc(dpy, mask, gc, (w / 2) - ((nsteps - i) * step),
    284 		         (h / 2) - ((nsteps - i) * step),
    285 		         2 * (nsteps - i) * step,
    286 		         2 * (nsteps - i) * step,
    287 		         0, 360 * 64);
    288 		XShapeCombineMask(dpy, blanket, ShapeBounding, 0, 0, mask, ShapeSet);
    289 		XFlush(dpy);
    290 		waitamoment(0.020);
    291 	}
    292 }
    293 
    294 
    295 static void
    296 ZoomOutWindow(TwmWindow *tmp_win, Window blanket)
    297 {
    298 	Pixmap        mask;
    299 	GC            gc;
    300 	XGCValues     gcv;
    301 
    302 	int i, nsteps = 20;
    303 	int w = tmp_win->frame_width;
    304 	int h = tmp_win->frame_height;
    305 	int step = (MAX(w, h)) / (2.0 * nsteps);
    306 
    307 	mask  = XCreatePixmap(dpy, blanket, w, h, 1);
    308 	gcv.foreground = 1;
    309 	gc = XCreateGC(dpy, mask, GCForeground, &gcv);
    310 	XFillRectangle(dpy, mask, gc, 0, 0, w, h);
    311 	gcv.function = GXclear;
    312 	XChangeGC(dpy, gc, GCFunction, &gcv);
    313 
    314 	for(i = 0; i < nsteps; i++) {
    315 		XFillArc(dpy, mask, gc, (w / 2) - (i * step),
    316 		         (h / 2) - (i * step),
    317 		         2 * i * step,
    318 		         2 * i * step,
    319 		         0, 360 * 64);
    320 		XShapeCombineMask(dpy, blanket, ShapeBounding, 0, 0, mask, ShapeSet);
    321 		XFlush(dpy);
    322 		waitamoment(0.020);
    323 	}
    324 }
    325 
    326 
    327 void
    328 FadeWindow(TwmWindow *tmp_win, Window blanket)
    329 {
    330 	Pixmap        mask, stipple;
    331 	GC            gc;
    332 	XGCValues     gcv;
    333 	static unsigned char stipple_bits[] = { 0x0F, 0x0F,
    334 	                                        0xF0, 0xF0,
    335 	                                        0x0F, 0x0F,
    336 	                                        0xF0, 0xF0,
    337 	                                        0x0F, 0x0F,
    338 	                                        0xF0, 0xF0,
    339 	                                        0x0F, 0x0F,
    340 	                                        0xF0, 0xF0,
    341 	                                      };
    342 	int w = tmp_win->frame_width;
    343 	int h = tmp_win->frame_height;
    344 
    345 	stipple = XCreateBitmapFromData(dpy, blanket, (char *)stipple_bits, 8, 8);
    346 	mask    = XCreatePixmap(dpy, blanket, w, h, 1);
    347 	gcv.background = 0;
    348 	gcv.foreground = 1;
    349 	gcv.stipple    = stipple;
    350 	gcv.fill_style = FillOpaqueStippled;
    351 	gc = XCreateGC(dpy, mask, GCBackground | GCForeground | GCFillStyle | GCStipple,
    352 	               &gcv);
    353 	XFillRectangle(dpy, mask, gc, 0, 0, w, h);
    354 
    355 	XShapeCombineMask(dpy, blanket, ShapeBounding, 0, 0, mask, ShapeSet);
    356 	XFlush(dpy);
    357 	waitamoment(0.10);
    358 	XFreePixmap(dpy, stipple);
    359 	XFlush(dpy);
    360 }
    361 
    362 
    363 static void
    364 SweepWindow(TwmWindow *tmp_win, Window blanket)
    365 {
    366 	float step = 0.0;
    367 	int i, nsteps = 20;
    368 	int dir = 0, dist = tmp_win->frame_x, dist1;
    369 
    370 	dist1 = tmp_win->frame_y;
    371 	if(dist1 < dist) {
    372 		dir = 1;
    373 		dist = dist1;
    374 	}
    375 	dist1 = tmp_win->vs->w - (tmp_win->frame_x + tmp_win->frame_width);
    376 	if(dist1 < dist) {
    377 		dir = 2;
    378 		dist = dist1;
    379 	}
    380 	dist1 = tmp_win->vs->h - (tmp_win->frame_y + tmp_win->frame_height);
    381 	if(dist1 < dist) {
    382 		dir = 3;
    383 		dist = dist1;
    384 		ALLOW_DEAD_STORE(dist);
    385 	}
    386 
    387 	switch(dir) {
    388 		case 0:
    389 			step = tmp_win->frame_x + tmp_win->frame_width;
    390 			break;
    391 		case 1:
    392 			step = tmp_win->frame_y + tmp_win->frame_height;
    393 			break;
    394 		case 2:
    395 			step = tmp_win->vs->w - tmp_win->frame_x;
    396 			break;
    397 		case 3:
    398 			step = tmp_win->vs->h - tmp_win->frame_y;
    399 			break;
    400 	}
    401 	step /= (float) nsteps;
    402 	step /= (float) nsteps;
    403 	for(i = 0; i < 20; i++) {
    404 		int x = tmp_win->frame_x;
    405 		int y = tmp_win->frame_y;
    406 		switch(dir) {
    407 			case 0:
    408 				x -= i * i * step;
    409 				break;
    410 			case 1:
    411 				y -= i * i * step;
    412 				break;
    413 			case 2:
    414 				x += i * i * step;
    415 				break;
    416 			case 3:
    417 				y += i * i * step;
    418 				break;
    419 		}
    420 		XMoveWindow(dpy, blanket, x, y);
    421 		XFlush(dpy);
    422 		waitamoment(0.020);
    423 	}
    424 }
    425 
    426 
    427 
    428 /*
    429  * Utils used by various bits above
    430  */
    431 
    432 /***********************************************************************
    433  *
    434  *  Procedure:
    435  *      Zoom - zoom in or out of an icon
    436  *
    437  *  Inputs:
    438  *      wf      - window to zoom from
    439  *      wt      - window to zoom to
    440  *
    441  ***********************************************************************
    442  */
    443 static void
    444 Zoom(Window wf, Window wt)
    445 {
    446 	int fx, fy, tx, ty;                 /* from, to */
    447 	unsigned int fw, fh, tw, th;        /* from, to */
    448 	long dx, dy, dw, dh;
    449 	long z;
    450 	int j;
    451 
    452 	if((Scr->IconifyStyle != ICONIFY_NORMAL) || !Scr->DoZoom
    453 	                || Scr->ZoomCount < 1) {
    454 		return;
    455 	}
    456 
    457 	if(wf == None || wt == None) {
    458 		return;
    459 	}
    460 
    461 	XGetGeometry(dpy, wf, &JunkRoot, &fx, &fy, &fw, &fh, &JunkBW, &JunkDepth);
    462 	XGetGeometry(dpy, wt, &JunkRoot, &tx, &ty, &tw, &th, &JunkBW, &JunkDepth);
    463 
    464 	dx = (long) tx - (long) fx; /* going from -> to */
    465 	dy = (long) ty - (long) fy; /* going from -> to */
    466 	dw = (long) tw - (long) fw; /* going from -> to */
    467 	dh = (long) th - (long) fh; /* going from -> to */
    468 	z = (long)(Scr->ZoomCount + 1);
    469 
    470 	for(j = 0; j < 2; j++) {
    471 		long i;
    472 
    473 		XDrawRectangle(dpy, Scr->Root, Scr->DrawGC, fx, fy, fw, fh);
    474 		for(i = 1; i < z; i++) {
    475 			int x = fx + (int)((dx * i) / z);
    476 			int y = fy + (int)((dy * i) / z);
    477 			unsigned width = (unsigned)(((long) fw) + (dw * i) / z);
    478 			unsigned height = (unsigned)(((long) fh) + (dh * i) / z);
    479 
    480 			XDrawRectangle(dpy, Scr->Root, Scr->DrawGC,
    481 			               x, y, width, height);
    482 		}
    483 		XDrawRectangle(dpy, Scr->Root, Scr->DrawGC, tx, ty, tw, th);
    484 	}
    485 }
    486 
    487 
    488 static void
    489 ReMapOne(TwmWindow *t, TwmWindow *leader)
    490 {
    491 	if(t->icon_on) {
    492 		Zoom(t->icon->w, t->frame);
    493 	}
    494 	else if(leader->icon) {
    495 		Zoom(leader->icon->w, t->frame);
    496 	}
    497 
    498 	if(!t->squeezed) {
    499 		XMapWindow(dpy, t->w);
    500 	}
    501 	t->mapped = true;
    502 #ifdef CAPTIVE
    503 	if(false && Scr->Root != Scr->CaptiveRoot) {        /* XXX dubious test */
    504 		ReparentWindow(dpy, t, WinWin, Scr->Root, t->frame_x, t->frame_y);
    505 	}
    506 #endif
    507 	if(!Scr->NoRaiseDeicon) {
    508 		OtpRaise(t, WinWin);
    509 	}
    510 	XMapWindow(dpy, t->frame);
    511 	SetMapStateProp(t, NormalState);
    512 
    513 	if(t->icon && t->icon->w) {
    514 		XUnmapWindow(dpy, t->icon->w);
    515 		IconDown(t);
    516 		if(Scr->ShrinkIconTitles) {
    517 			t->icon->title_shrunk = true;
    518 		}
    519 	}
    520 	if(t->iconmanagerlist) {
    521 		WList *wl;
    522 
    523 		for(wl = t->iconmanagerlist; wl != NULL; wl = wl->nextv) {
    524 			XUnmapWindow(dpy, wl->icon);
    525 		}
    526 	}
    527 	t->isicon = false;
    528 	t->icon_on = false;
    529 	WMapDeIconify(t);
    530 }
    531 
    532 
    533 /*
    534  * Mostly internal util of iconification, but squeezing code needs it
    535  * too.
    536  */
    537 void
    538 ReMapTransients(TwmWindow *tmp_win)
    539 {
    540 	TwmWindow *t;
    541 
    542 	/* find t such that it is a transient or group member window */
    543 	for(t = Scr->FirstWindow; t != NULL; t = t->next) {
    544 		if(t != tmp_win &&
    545 		                ((t->istransient && t->transientfor == tmp_win->w) ||
    546 		                 (t->group == tmp_win->w && t->isicon))) {
    547 			ReMapOne(t, tmp_win);
    548 		}
    549 	}
    550 }
    551 
    552 
    553 /*
    554  * Ditto previous note about squeezing.
    555  */
    556 void
    557 UnmapTransients(TwmWindow *tmp_win, bool iconify, long eventMask)
    558 {
    559 	TwmWindow *t;
    560 
    561 	for(t = Scr->FirstWindow; t != NULL; t = t->next) {
    562 		if(t != tmp_win &&
    563 		                ((t->istransient && t->transientfor == tmp_win->w) ||
    564 		                 t->group == tmp_win->w)) {
    565 			if(iconify) {
    566 				if(t->icon_on) {
    567 					Zoom(t->icon->w, tmp_win->icon->w);
    568 				}
    569 				else if(tmp_win->icon) {
    570 					Zoom(t->frame, tmp_win->icon->w);
    571 				}
    572 			}
    573 
    574 			/*
    575 			 * Prevent the receipt of an UnmapNotify, since that would
    576 			 * cause a transition to the Withdrawn state.
    577 			 */
    578 			t->mapped = false;
    579 
    580 			/*
    581 			 * Note that here, we're setting masks relative to what we
    582 			 * were passed, which is that of the window these are
    583 			 * transient for, rather than relative to these windows'
    584 			 * current masks.  I believe in practice it's the same thing,
    585 			 * and it saves getting attributes on each for masking.
    586 			 * Still, a little odd...
    587 			 */
    588 			mask_out_event_mask(t->w, StructureNotifyMask, eventMask);
    589 			XUnmapWindow(dpy, t->w);
    590 			XUnmapWindow(dpy, t->frame);
    591 			restore_mask(t->w, eventMask);
    592 
    593 			if(t->icon && t->icon->w) {
    594 				XUnmapWindow(dpy, t->icon->w);
    595 			}
    596 			SetMapStateProp(t, IconicState);
    597 			if(t == Scr->Focus) {
    598 				SetFocus(NULL, EventTime);
    599 				if(! Scr->ClickToFocus) {
    600 					Scr->FocusRoot = true;
    601 				}
    602 			}
    603 			if(t->iconmanagerlist) {
    604 				XMapWindow(dpy, t->iconmanagerlist->icon);
    605 			}
    606 			t->isicon = true;
    607 			t->icon_on = false;
    608 			WMapIconify(t);
    609 		}
    610 	}
    611 }
    612 
    613 
    614 static void
    615 waitamoment(float timeout)
    616 {
    617 	struct timeval timeoutstruct;
    618 	int usec = timeout * 1000000;
    619 	timeoutstruct.tv_usec = usec % (unsigned long) 1000000;
    620 	timeoutstruct.tv_sec  = usec / (unsigned long) 1000000;
    621 	select(0, NULL, NULL, NULL, &timeoutstruct);
    622 }
    623