win_iconify.c revision 0bbfda8a
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 */ 28static void MosaicFade(TwmWindow *tmp_win, Window blanket); 29static void ZoomInWindow(TwmWindow *tmp_win, Window blanket); 30static void ZoomOutWindow(TwmWindow *tmp_win, Window blanket); 31static void FadeWindow(TwmWindow *tmp_win, Window blanket); 32static void SweepWindow(TwmWindow *tmp_win, Window blanket); 33 34/* De/iconify utils */ 35static void Zoom(Window wf, Window wt); 36static void ReMapOne(TwmWindow *t, TwmWindow *leader); 37static void waitamoment(float timeout); 38 39 40 41/* 42 * The main routines for iconifying... 43 */ 44void 45Iconify(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 */ 166void 167DeIconify(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 t = GetTwmWindow(tmp_win->group); 183 if(t && t->icon_on && t->icon && t->icon->w) { 184 Zoom(t->icon->w, tmp_win->frame); 185 } 186 } 187 } 188 189 ReMapOne(tmp_win, t); 190 191 if(isicon && 192 (Scr->WarpCursor || 193 LookInList(Scr->WarpCursorL, tmp_win->name, &tmp_win->class))) { 194 WarpToWindow(tmp_win, false); 195 } 196 197 /* now de-iconify any window group transients */ 198 ReMapTransients(tmp_win); 199 200 if(! Scr->WindowMask && Scr->DeIconifyFunction.func != 0) { 201 char *action; 202 XEvent event; 203 204 action = Scr->DeIconifyFunction.item ? 205 Scr->DeIconifyFunction.item->action : NULL; 206 ExecuteFunction(Scr->DeIconifyFunction.func, action, 207 (Window) 0, tmp_win, &event, C_ROOT, false); 208 } 209 XSync(dpy, 0); 210} 211 212 213 214/* 215 * Animations for popping windows around. 216 */ 217static void 218MosaicFade(TwmWindow *tmp_win, Window blanket) 219{ 220 int srect; 221 int i, j, nrects; 222 Pixmap mask; 223 GC gc; 224 XGCValues gcv; 225 XRectangle *rectangles; 226 int width = tmp_win->frame_width; 227 int height = tmp_win->frame_height; 228 229 srect = (width < height) ? (width / 20) : (height / 20); 230 mask = XCreatePixmap(dpy, blanket, width, height, 1); 231 232 gcv.foreground = 1; 233 gc = XCreateGC(dpy, mask, GCForeground, &gcv); 234 XFillRectangle(dpy, mask, gc, 0, 0, width, height); 235 gcv.function = GXclear; 236 XChangeGC(dpy, gc, GCFunction, &gcv); 237 238 nrects = ((width * height) / (srect * srect)) / 10; 239 rectangles = calloc(nrects, sizeof(XRectangle)); 240 for(j = 0; j < nrects; j++) { 241 rectangles [j].width = srect; 242 rectangles [j].height = srect; 243 } 244 for(i = 0; i < 10; i++) { 245 for(j = 0; j < nrects; j++) { 246 rectangles [j].x = ((lrand48() % width) / srect) * srect; 247 rectangles [j].y = ((lrand48() % height) / srect) * srect; 248 } 249 XFillRectangles(dpy, mask, gc, rectangles, nrects); 250 XShapeCombineMask(dpy, blanket, ShapeBounding, 0, 0, mask, ShapeSet); 251 XFlush(dpy); 252 waitamoment(0.020); 253 } 254 XFreePixmap(dpy, mask); 255 XFreeGC(dpy, gc); 256 free(rectangles); 257} 258 259 260static void 261ZoomInWindow(TwmWindow *tmp_win, Window blanket) 262{ 263 Pixmap mask; 264 GC gc, gcn; 265 XGCValues gcv; 266 267 int i, nsteps = 20; 268 int w = tmp_win->frame_width; 269 int h = tmp_win->frame_height; 270 int step = (MAX(w, h)) / (2.0 * nsteps); 271 272 mask = XCreatePixmap(dpy, blanket, w, h, 1); 273 gcv.foreground = 1; 274 gc = XCreateGC(dpy, mask, GCForeground, &gcv); 275 gcv.function = GXclear; 276 gcn = XCreateGC(dpy, mask, GCForeground | GCFunction, &gcv); 277 278 for(i = 0; i < nsteps; i++) { 279 XFillRectangle(dpy, mask, gcn, 0, 0, w, h); 280 XFillArc(dpy, mask, gc, (w / 2) - ((nsteps - i) * step), 281 (h / 2) - ((nsteps - i) * step), 282 2 * (nsteps - i) * step, 283 2 * (nsteps - i) * step, 284 0, 360 * 64); 285 XShapeCombineMask(dpy, blanket, ShapeBounding, 0, 0, mask, ShapeSet); 286 XFlush(dpy); 287 waitamoment(0.020); 288 } 289} 290 291 292static void 293ZoomOutWindow(TwmWindow *tmp_win, Window blanket) 294{ 295 Pixmap mask; 296 GC gc; 297 XGCValues gcv; 298 299 int i, nsteps = 20; 300 int w = tmp_win->frame_width; 301 int h = tmp_win->frame_height; 302 int step = (MAX(w, h)) / (2.0 * nsteps); 303 304 mask = XCreatePixmap(dpy, blanket, w, h, 1); 305 gcv.foreground = 1; 306 gc = XCreateGC(dpy, mask, GCForeground, &gcv); 307 XFillRectangle(dpy, mask, gc, 0, 0, w, h); 308 gcv.function = GXclear; 309 XChangeGC(dpy, gc, GCFunction, &gcv); 310 311 for(i = 0; i < nsteps; i++) { 312 XFillArc(dpy, mask, gc, (w / 2) - (i * step), 313 (h / 2) - (i * step), 314 2 * i * step, 315 2 * i * step, 316 0, 360 * 64); 317 XShapeCombineMask(dpy, blanket, ShapeBounding, 0, 0, mask, ShapeSet); 318 XFlush(dpy); 319 waitamoment(0.020); 320 } 321} 322 323 324void 325FadeWindow(TwmWindow *tmp_win, Window blanket) 326{ 327 Pixmap mask, stipple; 328 GC gc; 329 XGCValues gcv; 330 static unsigned char stipple_bits[] = { 0x0F, 0x0F, 331 0xF0, 0xF0, 332 0x0F, 0x0F, 333 0xF0, 0xF0, 334 0x0F, 0x0F, 335 0xF0, 0xF0, 336 0x0F, 0x0F, 337 0xF0, 0xF0, 338 }; 339 int w = tmp_win->frame_width; 340 int h = tmp_win->frame_height; 341 342 stipple = XCreateBitmapFromData(dpy, blanket, (char *)stipple_bits, 8, 8); 343 mask = XCreatePixmap(dpy, blanket, w, h, 1); 344 gcv.background = 0; 345 gcv.foreground = 1; 346 gcv.stipple = stipple; 347 gcv.fill_style = FillOpaqueStippled; 348 gc = XCreateGC(dpy, mask, GCBackground | GCForeground | GCFillStyle | GCStipple, 349 &gcv); 350 XFillRectangle(dpy, mask, gc, 0, 0, w, h); 351 352 XShapeCombineMask(dpy, blanket, ShapeBounding, 0, 0, mask, ShapeSet); 353 XFlush(dpy); 354 waitamoment(0.10); 355 XFreePixmap(dpy, stipple); 356 XFlush(dpy); 357} 358 359 360static void 361SweepWindow(TwmWindow *tmp_win, Window blanket) 362{ 363 float step = 0.0; 364 int i, nsteps = 20; 365 int dir = 0, dist = tmp_win->frame_x, dist1; 366 367 dist1 = tmp_win->frame_y; 368 if(dist1 < dist) { 369 dir = 1; 370 dist = dist1; 371 } 372 dist1 = tmp_win->vs->w - (tmp_win->frame_x + tmp_win->frame_width); 373 if(dist1 < dist) { 374 dir = 2; 375 dist = dist1; 376 } 377 dist1 = tmp_win->vs->h - (tmp_win->frame_y + tmp_win->frame_height); 378 if(dist1 < dist) { 379 dir = 3; 380 dist = dist1; 381 } 382 383 switch(dir) { 384 case 0: 385 step = tmp_win->frame_x + tmp_win->frame_width; 386 break; 387 case 1: 388 step = tmp_win->frame_y + tmp_win->frame_height; 389 break; 390 case 2: 391 step = tmp_win->vs->w - tmp_win->frame_x; 392 break; 393 case 3: 394 step = tmp_win->vs->h - tmp_win->frame_y; 395 break; 396 } 397 step /= (float) nsteps; 398 step /= (float) nsteps; 399 for(i = 0; i < 20; i++) { 400 int x = tmp_win->frame_x; 401 int y = tmp_win->frame_y; 402 switch(dir) { 403 case 0: 404 x -= i * i * step; 405 break; 406 case 1: 407 y -= i * i * step; 408 break; 409 case 2: 410 x += i * i * step; 411 break; 412 case 3: 413 y += i * i * step; 414 break; 415 } 416 XMoveWindow(dpy, blanket, x, y); 417 XFlush(dpy); 418 waitamoment(0.020); 419 } 420} 421 422 423 424/* 425 * Utils used by various bits above 426 */ 427 428/*********************************************************************** 429 * 430 * Procedure: 431 * Zoom - zoom in or out of an icon 432 * 433 * Inputs: 434 * wf - window to zoom from 435 * wt - window to zoom to 436 * 437 *********************************************************************** 438 */ 439static void 440Zoom(Window wf, Window wt) 441{ 442 int fx, fy, tx, ty; /* from, to */ 443 unsigned int fw, fh, tw, th; /* from, to */ 444 long dx, dy, dw, dh; 445 long z; 446 int j; 447 448 if((Scr->IconifyStyle != ICONIFY_NORMAL) || !Scr->DoZoom 449 || Scr->ZoomCount < 1) { 450 return; 451 } 452 453 if(wf == None || wt == None) { 454 return; 455 } 456 457 XGetGeometry(dpy, wf, &JunkRoot, &fx, &fy, &fw, &fh, &JunkBW, &JunkDepth); 458 XGetGeometry(dpy, wt, &JunkRoot, &tx, &ty, &tw, &th, &JunkBW, &JunkDepth); 459 460 dx = (long) tx - (long) fx; /* going from -> to */ 461 dy = (long) ty - (long) fy; /* going from -> to */ 462 dw = (long) tw - (long) fw; /* going from -> to */ 463 dh = (long) th - (long) fh; /* going from -> to */ 464 z = (long)(Scr->ZoomCount + 1); 465 466 for(j = 0; j < 2; j++) { 467 long i; 468 469 XDrawRectangle(dpy, Scr->Root, Scr->DrawGC, fx, fy, fw, fh); 470 for(i = 1; i < z; i++) { 471 int x = fx + (int)((dx * i) / z); 472 int y = fy + (int)((dy * i) / z); 473 unsigned width = (unsigned)(((long) fw) + (dw * i) / z); 474 unsigned height = (unsigned)(((long) fh) + (dh * i) / z); 475 476 XDrawRectangle(dpy, Scr->Root, Scr->DrawGC, 477 x, y, width, height); 478 } 479 XDrawRectangle(dpy, Scr->Root, Scr->DrawGC, tx, ty, tw, th); 480 } 481} 482 483 484static void 485ReMapOne(TwmWindow *t, TwmWindow *leader) 486{ 487 if(t->icon_on) { 488 Zoom(t->icon->w, t->frame); 489 } 490 else if(leader->icon) { 491 Zoom(leader->icon->w, t->frame); 492 } 493 494 if(!t->squeezed) { 495 XMapWindow(dpy, t->w); 496 } 497 t->mapped = true; 498 if(false && Scr->Root != Scr->CaptiveRoot) { /* XXX dubious test */ 499 ReparentWindow(dpy, t, WinWin, Scr->Root, t->frame_x, t->frame_y); 500 } 501 if(!Scr->NoRaiseDeicon) { 502 OtpRaise(t, WinWin); 503 } 504 XMapWindow(dpy, t->frame); 505 SetMapStateProp(t, NormalState); 506 507 if(t->icon && t->icon->w) { 508 XUnmapWindow(dpy, t->icon->w); 509 IconDown(t); 510 if(Scr->ShrinkIconTitles) { 511 t->icon->title_shrunk = true; 512 } 513 } 514 if(t->iconmanagerlist) { 515 WList *wl; 516 517 for(wl = t->iconmanagerlist; wl != NULL; wl = wl->nextv) { 518 XUnmapWindow(dpy, wl->icon); 519 } 520 } 521 t->isicon = false; 522 t->icon_on = false; 523 WMapDeIconify(t); 524} 525 526 527/* 528 * Mostly internal util of iconification, but squeezing code needs it 529 * too. 530 */ 531void 532ReMapTransients(TwmWindow *tmp_win) 533{ 534 TwmWindow *t; 535 536 /* find t such that it is a transient or group member window */ 537 for(t = Scr->FirstWindow; t != NULL; t = t->next) { 538 if(t != tmp_win && 539 ((t->istransient && t->transientfor == tmp_win->w) || 540 (t->group == tmp_win->w && t->isicon))) { 541 ReMapOne(t, tmp_win); 542 } 543 } 544} 545 546 547/* 548 * Ditto previous note about squeezing. 549 */ 550void 551UnmapTransients(TwmWindow *tmp_win, bool iconify, long eventMask) 552{ 553 TwmWindow *t; 554 555 for(t = Scr->FirstWindow; t != NULL; t = t->next) { 556 if(t != tmp_win && 557 ((t->istransient && t->transientfor == tmp_win->w) || 558 t->group == tmp_win->w)) { 559 if(iconify) { 560 if(t->icon_on) { 561 Zoom(t->icon->w, tmp_win->icon->w); 562 } 563 else if(tmp_win->icon) { 564 Zoom(t->frame, tmp_win->icon->w); 565 } 566 } 567 568 /* 569 * Prevent the receipt of an UnmapNotify, since that would 570 * cause a transition to the Withdrawn state. 571 */ 572 t->mapped = false; 573 574 /* 575 * Note that here, we're setting masks relative to what we 576 * were passed, which is that of the window these are 577 * transient for, rather than relative to these windows' 578 * current masks. I believe in practice it's the same thing, 579 * and it saves getting attributes on each for masking. 580 * Still, a little odd... 581 */ 582 mask_out_event_mask(t->w, StructureNotifyMask, eventMask); 583 XUnmapWindow(dpy, t->w); 584 XUnmapWindow(dpy, t->frame); 585 restore_mask(t->w, eventMask); 586 587 if(t->icon && t->icon->w) { 588 XUnmapWindow(dpy, t->icon->w); 589 } 590 SetMapStateProp(t, IconicState); 591 if(t == Scr->Focus) { 592 SetFocus(NULL, EventTime); 593 if(! Scr->ClickToFocus) { 594 Scr->FocusRoot = true; 595 } 596 } 597 if(t->iconmanagerlist) { 598 XMapWindow(dpy, t->iconmanagerlist->icon); 599 } 600 t->isicon = true; 601 t->icon_on = false; 602 WMapIconify(t); 603 } 604 } 605} 606 607 608static void 609waitamoment(float timeout) 610{ 611 struct timeval timeoutstruct; 612 int usec = timeout * 1000000; 613 timeoutstruct.tv_usec = usec % (unsigned long) 1000000; 614 timeoutstruct.tv_sec = usec / (unsigned long) 1000000; 615 select(0, NULL, NULL, NULL, &timeoutstruct); 616} 617