1 /*
   2  * Copyright �� 2008 Dennis Kasprzyk
   3  * Copyright �� 2007 Novell, Inc.
   4  *
   5  * Permission to use, copy, modify, distribute, and sell this software
   6  * and its documentation for any purpose is hereby granted without
   7  * fee, provided that the above copyright notice appear in all copies
   8  * and that both that copyright notice and this permission notice
   9  * appear in supporting documentation, and that the name of
  10  * Dennis Kasprzyk not be used in advertising or publicity pertaining to
  11  * distribution of the software without specific, written prior permission.
  12  * Dennis Kasprzyk makes no representations about the suitability of this
  13  * software for any purpose. It is provided "as is" without express or
  14  * implied warranty.
  15  *
  16  * DENNIS KASPRZYK DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
  17  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
  18  * NO EVENT SHALL DENNIS KASPRZYK BE LIABLE FOR ANY SPECIAL, INDIRECT OR
  19  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
  20  * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
  21  * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
  22  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  23  *
  24  * Authors: Dennis Kasprzyk <onestone@compiz-fusion.org>
  25  *          David Reveman <davidr@novell.com>
  26  */
  27 
  28 #ifdef HAVE_CONFIG_H
  29 #  include <config.h>
  30 #endif
  31 
  32 #include <sys/time.h>
  33 
  34 #include <X11/Xlib.h>
  35 #include <X11/Xatom.h>
  36 #include <X11/Xproto.h>
  37 #include <X11/extensions/Xcomposite.h>
  38 #include <X11/extensions/Xrandr.h>
  39 #include <X11/extensions/shape.h>
  40 
  41 #include "privates.h"
  42 
  43 #include <X11/extensions/shape.h>
  44 #include <X11/extensions/Xrandr.h>
  45 
  46 #include <core/timer.h>
  47 
  48 template class WrapableInterface<CompositeScreen, CompositeScreenInterface>;
  49 
  50 static const int FALLBACK_REFRESH_RATE = 60;   /* if all else fails */
  51 
  52 CompWindow *lastDamagedWindow = 0;
  53 
  54 void
  55 PrivateCompositeScreen::handleEvent (XEvent *event)
  56 {
  57     CompWindow      *w;
  58 
  59     switch (event->type) {
  60 
  61 	case CreateNotify:
  62 	    if (screen->root () == event->xcreatewindow.parent)
  63 	    {
  64 		/* The first time some client asks for the composite
  65 		 * overlay window, the X server creates it, which causes
  66 		 * an errorneous CreateNotify event.  We catch it and
  67 		 * ignore it. */
  68 		if (overlay == event->xcreatewindow.window)
  69 		    return;
  70 	    }
  71 	    break;
  72 	case PropertyNotify:
  73 	    if (event->xproperty.atom == Atoms::winOpacity)
  74 	    {
  75 		w = screen->findWindow (event->xproperty.window);
  76 		if (w)
  77 		    CompositeWindow::get (w)->updateOpacity ();
  78 	    }
  79 	    else if (event->xproperty.atom == Atoms::winBrightness)
  80 	    {
  81 		w = screen->findWindow (event->xproperty.window);
  82 		if (w)
  83 		    CompositeWindow::get (w)->updateBrightness ();
  84 	    }
  85 	    else if (event->xproperty.atom == Atoms::winSaturation)
  86 	    {
  87 		w = screen->findWindow (event->xproperty.window);
  88 		if (w)
  89 		    CompositeWindow::get (w)->updateSaturation ();
  90 	    }
  91 	    break;
  92 	default:
  93 	    if (shapeExtension &&
  94 		event->type == shapeEvent + ShapeNotify)
  95 	    {
  96 		w = screen->findWindow (((XShapeEvent *) event)->window);
  97 		if (w)
  98 		{
  99 		    if (w->mapNum ())
 100 		    {
 101 		        CompositeWindow::get (w)->addDamage ();
 102 		    }
 103 		}
 104 	    }
 105 	    else if (event->type == damageEvent + XDamageNotify)
 106 	    {
 107 		XDamageNotifyEvent *de = (XDamageNotifyEvent*)event;
 108 		damages[de->damage] = de->area;
 109 	    }
 110 	    break;
 111     }
 112 
 113     screen->handleEvent (event);
 114 
 115     switch (event->type) {
 116 	case Expose:
 117 	    handleExposeEvent (&event->xexpose);
 118 	break;
 119 	case ClientMessage:
 120 	    if (event->xclient.message_type == Atoms::winOpacity)
 121 	    {
 122 		w = screen->findWindow (event->xclient.window);
 123 		if (w && (w->type () & CompWindowTypeDesktopMask) == 0)
 124 		{
 125 		    unsigned short opacity = event->xclient.data.l[0] >> 16;
 126 
 127 		    screen->setWindowProp32 (w->id (),
 128 			Atoms::winOpacity, opacity);
 129 		}
 130 	    }
 131 	    else if (event->xclient.message_type ==
 132 		     Atoms::winBrightness)
 133 	    {
 134 		w = screen->findWindow (event->xclient.window);
 135 		if (w)
 136 		{
 137 		    unsigned short brightness = event->xclient.data.l[0] >> 16;
 138 
 139 		    screen->setWindowProp32 (w->id (),
 140 			Atoms::winBrightness, brightness);
 141 		}
 142 	    }
 143 	    else if (event->xclient.message_type ==
 144 		     Atoms::winSaturation)
 145 	    {
 146 		w = screen->findWindow (event->xclient.window);
 147 		if (w)
 148 		{
 149 		    unsigned short saturation = event->xclient.data.l[0] >> 16;
 150 
 151 		    screen->setWindowProp32 (w->id (),
 152 			Atoms::winSaturation, saturation);
 153 		}
 154 	    }
 155 	    break;
 156 	default:
 157 	    if (event->type == damageEvent + XDamageNotify)
 158 	    {
 159 		XDamageNotifyEvent *de = (XDamageNotifyEvent *) event;
 160 
 161 		if (lastDamagedWindow && de->drawable == lastDamagedWindow->id ())
 162 		{
 163 		    w = lastDamagedWindow;
 164 		}
 165 		else
 166 		{
 167 		    w = screen->findWindow (de->drawable);
 168 		    if (w)
 169 			lastDamagedWindow = w;
 170 		}
 171 
 172 		if (w)
 173 		    CompositeWindow::get (w)->processDamage (de);
 174 	    }
 175 	    else if (shapeExtension &&
 176 		     event->type == shapeEvent + ShapeNotify)
 177 	    {
 178 		w = screen->findWindow (((XShapeEvent *) event)->window);
 179 		if (w)
 180 		{
 181 		    if (w->mapNum ())
 182 		    {
 183 		        CompositeWindow::get (w)->addDamage ();
 184 		    }
 185 		}
 186 	    }
 187 	    else if (randrExtension &&
 188 		     event->type == randrEvent + RRScreenChangeNotify)
 189 	    {
 190 		XRRScreenChangeNotifyEvent *rre;
 191 
 192 		rre = (XRRScreenChangeNotifyEvent *) event;
 193 
 194 		if (screen->root () == rre->root)
 195 		    detectRefreshRate ();
 196 	    }
 197 	    break;
 198     }
 199 }
 200 
 201 int
 202 CompositeScreen::damageEvent ()
 203 {
 204     return priv->damageEvent;
 205 }
 206 
 207 
 208 template class PluginClassHandler<CompositeScreen, CompScreen, COMPIZ_COMPOSITE_ABI>;
 209 
 210 CompositeScreen::CompositeScreen (CompScreen *s) :
 211     PluginClassHandler<CompositeScreen, CompScreen, COMPIZ_COMPOSITE_ABI> (s),
 212     priv (new PrivateCompositeScreen (this))
 213 {
 214     if (!XQueryExtension (s->dpy (), COMPOSITE_NAME,
 215 			  &priv->compositeOpcode,
 216 			  &priv->compositeEvent,
 217 			  &priv->compositeError))
 218     {
 219 	compLogMessage ("core", CompLogLevelFatal,
 220 		        "No composite extension");
 221 	setFailed ();
 222 	return;
 223     }
 224 
 225     int	compositeMajor, compositeMinor;
 226 
 227     XCompositeQueryVersion (s->dpy (), &compositeMajor, &compositeMinor);
 228     if (compositeMajor == 0 && compositeMinor < 2)
 229     {
 230 	compLogMessage ("core", CompLogLevelFatal,
 231 		        "Old composite extension");
 232 	setFailed ();
 233 	return;
 234     }
 235 
 236     if (!XDamageQueryExtension (s->dpy (), &priv->damageEvent,
 237 				&priv->damageError))
 238     {
 239 	compLogMessage ("core", CompLogLevelFatal,
 240 		        "No damage extension");
 241 	setFailed ();
 242 	return;
 243     }
 244 
 245     if (!XFixesQueryExtension (s->dpy (), &priv->fixesEvent, &priv->fixesError))
 246     {
 247 	compLogMessage ("core", CompLogLevelFatal,
 248 		        "No fixes extension");
 249 	setFailed ();
 250 	return;
 251     }
 252 
 253     priv->shapeExtension = XShapeQueryExtension (s->dpy (), &priv->shapeEvent,
 254 						 &priv->shapeError);
 255     priv->randrExtension = XRRQueryExtension (s->dpy (), &priv->randrEvent,
 256 					      &priv->randrError);
 257 
 258     priv->makeOutputWindow ();
 259 
 260     priv->detectRefreshRate ();
 261 
 262     priv->slowAnimations = false;
 263 
 264     if (!priv->init ())
 265     {
 266         setFailed ();
 267     }
 268 
 269 }
 270 
 271 CompositeScreen::~CompositeScreen ()
 272 {
 273     priv->paintTimer.stop ();
 274     XCompositeReleaseOverlayWindow (screen->dpy (),
 275 				    screen->root ());
 276     delete priv;
 277 }
 278 
 279 
 280 PrivateCompositeScreen::PrivateCompositeScreen (CompositeScreen *cs) :
 281     cScreen (cs),
 282     compositeEvent (0),
 283     compositeError (0),
 284     compositeOpcode (0),
 285     damageEvent (0),
 286     damageError (0),
 287     fixesEvent (0),
 288     fixesError (0),
 289     fixesVersion (0),
 290     shapeExtension (false),
 291     shapeEvent (0),
 292     shapeError (0),
 293     randrExtension (false),
 294     randrEvent (0),
 295     randrError (0),
 296     damageMask (COMPOSITE_SCREEN_DAMAGE_ALL_MASK),
 297     overlay (None),
 298     output (None),
 299     exposeRects (),
 300     windowPaintOffset (0, 0),
 301     overlayWindowCount (0),
 302     outputShapeChanged (false),
 303     redrawTime (1000 / FALLBACK_REFRESH_RATE),
 304     optimalRedrawTime (1000 / FALLBACK_REFRESH_RATE),
 305     scheduled (false),
 306     painting (false),
 307     reschedule (false),
 308     slowAnimations (false),
 309     pHnd (NULL),
 310     FPSLimiterMode (CompositeFPSLimiterModeDefault),
 311     withDestroyedWindows (),
 312     cmSnAtom (0),
 313     newCmSnOwner (None)
 314 {
 315     gettimeofday (&lastRedraw, 0);
 316     // wrap outputChangeNotify
 317     ScreenInterface::setHandler (screen);
 318 
 319     optionSetSlowAnimationsKeyInitiate (CompositeScreen::toggleSlowAnimations);
 320 }
 321 
 322 PrivateCompositeScreen::~PrivateCompositeScreen ()
 323 {
 324     Display *dpy = screen->dpy ();
 325 
 326     if (cmSnAtom)
 327 	XSetSelectionOwner (dpy, cmSnAtom, None, CurrentTime);
 328 
 329     if (newCmSnOwner != None)
 330 	XDestroyWindow (dpy, newCmSnOwner);
 331 }
 332 
 333 bool
 334 PrivateCompositeScreen::init ()
 335 {
 336     Display              *dpy = screen->dpy ();
 337     Time                 cmSnTimestamp = 0;
 338     XEvent               event;
 339     XSetWindowAttributes attr;
 340     Window               currentCmSnOwner;
 341     char                 buf[128];
 342 
CID 12511 - SECURE_CODING
[VERY RISKY]. Using "sprintf" can cause a buffer overflow when done incorrectly.  Because sprintf() assumes an arbitrarily long string, callers must be careful not to overflow the actual space of the destination. Use snprintf() instead, or correct precision specifiers.
 343     sprintf (buf, "_NET_WM_CM_S%d", screen->screenNum ());
 344     cmSnAtom = XInternAtom (dpy, buf, 0);
 345 
 346     currentCmSnOwner = XGetSelectionOwner (dpy, cmSnAtom);
 347 
 348     if (currentCmSnOwner != None)
 349     {
 350 	if (!replaceCurrentWm)
 351 	{
 352 	    compLogMessage (
 353 		"composite", CompLogLevelError,
 354 		"Screen %d on display \"%s\" already has a compositing "
 355 		"manager (%x); try using the --replace option to replace "
 356 		"the current compositing manager.",
 357 		screen->screenNum (), DisplayString (dpy), currentCmSnOwner);
 358 
 359 	    return false;
 360 	}
 361     }
 362 
 363     attr.override_redirect = true;
 364     attr.event_mask        = PropertyChangeMask;
 365 
 366     newCmSnOwner =
 367 	XCreateWindow (dpy, screen->root (),
 368 		       -100, -100, 1, 1, 0,
 369 		       CopyFromParent, CopyFromParent,
 370 		       CopyFromParent,
 371 		       CWOverrideRedirect | CWEventMask,
 372 		       &attr);
 373 
 374     XChangeProperty (dpy, newCmSnOwner, Atoms::wmName, Atoms::utf8String, 8,
 375 		     PropModeReplace, (unsigned char *) PACKAGE,
 376 		     strlen (PACKAGE));
 377 
 378     XWindowEvent (dpy, newCmSnOwner, PropertyChangeMask, &event);
 379 
 380     cmSnTimestamp = event.xproperty.time;
 381 
 382     XSetSelectionOwner (dpy, cmSnAtom, newCmSnOwner, cmSnTimestamp);
 383 
 384     if (XGetSelectionOwner (dpy, cmSnAtom) != newCmSnOwner)
 385     {
 386 	compLogMessage ("core", CompLogLevelError,
 387 			"Could not acquire compositing manager "
 388 			"selection on screen %d display \"%s\"",
 389 			screen->screenNum (), DisplayString (dpy));
 390 
 391 	return false;
 392     }
 393 
 394     /* Send client message indicating that we are now the compositing manager */
 395     event.xclient.type         = ClientMessage;
 396     event.xclient.window       = screen->root ();
 397     event.xclient.message_type = Atoms::manager;
 398     event.xclient.format       = 32;
 399     event.xclient.data.l[0]    = cmSnTimestamp;
 400     event.xclient.data.l[1]    = cmSnAtom;
 401     event.xclient.data.l[2]    = 0;
 402     event.xclient.data.l[3]    = 0;
 403     event.xclient.data.l[4]    = 0;
 404 
 405     XSendEvent (dpy, screen->root (), FALSE, StructureNotifyMask, &event);
 406 
 407     return true;
 408 }
 409 
 410 
 411 bool
 412 CompositeScreen::registerPaintHandler (compiz::composite::PaintHandler *pHnd)
 413 {
 414     Display *dpy;
 415 
 416     WRAPABLE_HND_FUNCTN_RETURN (bool, registerPaintHandler, pHnd);
 417 
 418     dpy =  screen->dpy ();
 419 
 420     if (priv->pHnd)
 421 	return false;
 422 
 423     CompScreen::checkForError (dpy);
 424 
 425     XCompositeRedirectSubwindows (dpy, screen->root (),
 426 				  CompositeRedirectManual);
 427 
 428     priv->overlayWindowCount = 0;
 429 
 430     if (CompScreen::checkForError (dpy))
 431     {
 432 	compLogMessage ("composite", CompLogLevelError,
 433 			"Another composite manager is already "
 434 			"running on screen: %d", screen->screenNum ());
 435 
 436 	return false;
 437     }
 438 
 439     foreach (CompWindow *w, screen->windows ())
 440     {
 441 	CompositeWindow *cw = CompositeWindow::get (w);
 442 	cw->priv->overlayWindow = false;
 443 	cw->priv->redirected = true;
 444     }
 445 
 446     priv->pHnd = pHnd;
 447 
 448     priv->detectRefreshRate ();
 449 
 450     showOutputWindow ();
 451 
 452     return true;
 453 }
 454 
 455 void
 456 CompositeScreen::unregisterPaintHandler ()
 457 {
 458     Display *dpy;
 459 
 460     WRAPABLE_HND_FUNCTN (unregisterPaintHandler)
 461 
 462     dpy = screen->dpy ();
 463 
 464     foreach (CompWindow *w, screen->windows ())
 465     {
 466 	CompositeWindow *cw = CompositeWindow::get (w);
 467 	cw->priv->overlayWindow = false;
 468 	cw->priv->redirected = false;
 469 	cw->release ();
 470     }
 471 
 472     priv->overlayWindowCount = 0;
 473 
 474     XCompositeUnredirectSubwindows (dpy, screen->root (),
 475 				    CompositeRedirectManual);
 476 
 477     priv->pHnd = NULL;
 478     priv->paintTimer.stop ();
 479 
 480     priv->detectRefreshRate ();
 481 
 482     hideOutputWindow ();
 483 }
 484 
 485 bool
 486 CompositeScreen::compositingActive ()
 487 {
 488     if (priv->pHnd)
 489 	return priv->pHnd->compositingActive ();
 490 
 491     return false;
 492 }
 493 
 494 void
 495 CompositeScreen::damageScreen ()
 496 {
 497     bool alreadyDamaged = priv->damageMask & COMPOSITE_SCREEN_DAMAGE_ALL_MASK;
 498 
 499     priv->damageMask |= COMPOSITE_SCREEN_DAMAGE_ALL_MASK;
 500     priv->damageMask &= ~COMPOSITE_SCREEN_DAMAGE_REGION_MASK;
 501 
 502     priv->scheduleRepaint ();
 503 
 504     /*
 505      * Call through damageRegion since plugins listening for incoming damage
 506      * may need to know that the whole screen was redrawn
 507      */
 508 
 509     if (!alreadyDamaged)
 510 	damageRegion (CompRegion (0, 0, screen->width (), screen->height ()));
 511 }
 512 
 513 void
 514 CompositeScreen::damageRegion (const CompRegion &region)
 515 {
 516     WRAPABLE_HND_FUNCTN (damageRegion, region);
 517 
 518     if (priv->damageMask & COMPOSITE_SCREEN_DAMAGE_ALL_MASK)
 519 	return;
 520 
 521     priv->damage += region;
 522     priv->damageMask |= COMPOSITE_SCREEN_DAMAGE_REGION_MASK;
 523 
 524     /* if the number of damage rectangles grows two much between repaints,
 525        we have a lot of overhead just for doing the damage tracking -
 526        in order to make sure we're not having too much overhead, damage
 527        the whole screen if we have a lot of damage rects */
 528 
 529     if (priv->damage.numRects () > 100)
 530        damageScreen ();
 531     priv->scheduleRepaint ();
 532 }
 533 
 534 void
 535 CompositeScreen::damagePending ()
 536 {
 537     priv->damageMask |= COMPOSITE_SCREEN_DAMAGE_PENDING_MASK;
 538     priv->scheduleRepaint ();
 539 }
 540 
 541 unsigned int
 542 CompositeScreen::damageMask ()
 543 {
 544     return priv->damageMask;
 545 }
 546 
 547 void
 548 CompositeScreen::showOutputWindow ()
 549 {
 550     if (priv->pHnd)
 551     {
 552 	Display       *dpy = screen->dpy ();
 553 	XserverRegion region;
 554 
 555 	region = XFixesCreateRegion (dpy, NULL, 0);
 556 
 557 	XFixesSetWindowShapeRegion (dpy,
 558 				    priv->output,
 559 				    ShapeBounding,
 560 				    0, 0, 0);
 561 	XFixesSetWindowShapeRegion (dpy,
 562 				    priv->output,
 563 				    ShapeInput,
 564 				    0, 0, region);
 565 
 566 	XFixesDestroyRegion (dpy, region);
 567 
 568 	damageScreen ();
 569 
 570 	priv->outputShapeChanged = true;
 571     }
 572 }
 573 
 574 void
 575 CompositeScreen::hideOutputWindow ()
 576 {
 577     Display       *dpy = screen->dpy ();
 578     XserverRegion region;
 579 
 580     region = XFixesCreateRegion (dpy, NULL, 0);
 581 
 582     XFixesSetWindowShapeRegion (dpy,
 583 				priv->output,
 584 				ShapeBounding,
 585 				0, 0, region);
 586 
 587     XFixesDestroyRegion (dpy, region);
 588 }
 589 
 590 void
 591 CompositeScreen::updateOutputWindow ()
 592 {
 593     if (priv->pHnd)
 594     {
 595 	Display       *dpy = screen->dpy ();
 596 	XserverRegion region;
 597 	CompRegion    tmpRegion (screen->region ());
 598 
 599 	for (CompWindowList::reverse_iterator rit =
 600 	     screen->windows ().rbegin ();
 601 	     rit != screen->windows ().rend (); ++rit)
 602 	    if (CompositeWindow::get (*rit)->overlayWindow ())
 603 	    {
 604 		tmpRegion -= (*rit)->region ();
 605 	    }
 606 
 607 	XShapeCombineRegion (dpy, priv->output, ShapeBounding,
 608 			     0, 0, tmpRegion.handle (), ShapeSet);
 609 
 610 
 611 	region = XFixesCreateRegion (dpy, NULL, 0);
 612 
 613 	XFixesSetWindowShapeRegion (dpy,
 614 				    priv->output,
 615 				    ShapeInput,
 616 				    0, 0, region);
 617 
 618 	XFixesDestroyRegion (dpy, region);
 619 
 620 	priv->outputShapeChanged = true;
 621     }
 622 
 623 }
 624 
 625 bool
 626 CompositeScreen::outputWindowChanged () const
 627 {
 628     return priv->outputShapeChanged;
 629 }
 630 
 631 void
 632 PrivateCompositeScreen::makeOutputWindow ()
 633 {
 634     overlay = XCompositeGetOverlayWindow (screen->dpy (), screen->root ());
 635     output  = overlay;
 636 
 637     XSelectInput (screen->dpy (), output, ExposureMask);
 638 
 639     cScreen->hideOutputWindow ();
 640 }
 641 
 642 Window
 643 CompositeScreen::output ()
 644 {
 645     return priv->output;
 646 }
 647 
 648 Window
 649 CompositeScreen::overlay ()
 650 {
 651     return priv->overlay;
 652 }
 653 
 654 int &
 655 CompositeScreen::overlayWindowCount ()
 656 {
 657     return priv->overlayWindowCount;
 658 }
 659 
 660 void
 661 CompositeScreen::setWindowPaintOffset (int x, int y)
 662 {
 663     priv->windowPaintOffset = CompPoint (x, y);
 664 }
 665 
 666 CompPoint
 667 CompositeScreen::windowPaintOffset ()
 668 {
 669     return priv->windowPaintOffset;
 670 }
 671 
 672 void
 673 PrivateCompositeScreen::detectRefreshRate ()
 674 {
 675     const bool forceRefreshRate = (pHnd ? pHnd->requiredForcedRefreshRate () : false);
 676     const bool detect = optionGetDetectRefreshRate () && !forceRefreshRate;
 677 
 678     if (detect)
 679     {
 680 	CompString        name;
 681 	CompOption::Value value;
 682 
 683 	value.set ((int) 0);
 684 
 685 	if (randrExtension)
 686 	{
 687 	    XRRScreenConfiguration *config;
 688 
 689 	    config  = XRRGetScreenInfo (screen->dpy (),
 690 					screen->root ());
 691 	    value.set ((int) XRRConfigCurrentRate (config));
 692 
 693 	    XRRFreeScreenConfigInfo (config);
 694 	}
 695 
 696 	if (value.i () == 0)
 697 	    value.set ((int) FALLBACK_REFRESH_RATE);
 698 
 699 	mOptions[CompositeOptions::DetectRefreshRate].value ().set (false);
 700 	screen->setOptionForPlugin ("composite", "refresh_rate", value);
 701 	mOptions[CompositeOptions::DetectRefreshRate].value ().set (true);
 702 	optimalRedrawTime = redrawTime = 1000 / value.i ();
 703     }
 704     else
 705     {
 706 	redrawTime = 1000 / optionGetRefreshRate ();
 707 	optimalRedrawTime = redrawTime;
 708     }
 709 }
 710 
 711 CompositeFPSLimiterMode
 712 CompositeScreen::FPSLimiterMode ()
 713 {
 714     return priv->FPSLimiterMode;
 715 }
 716 
 717 void
 718 CompositeScreen::setFPSLimiterMode (CompositeFPSLimiterMode newMode)
 719 {
 720     priv->FPSLimiterMode = newMode;
 721 }
 722 
 723 void
 724 PrivateCompositeScreen::scheduleRepaint ()
 725 {
 726     if (painting)
 727     {
 728 	reschedule = true;
 729 	return;
 730     }
 731 
 732     if (scheduled)
 733 	return;
 734 
 735     scheduled = true;
 736 
 737     int delay;
 738     if (FPSLimiterMode == CompositeFPSLimiterModeVSyncLike ||
 739 	(pHnd && pHnd->hasVSync ()))
 740     {
 741 	delay = 1;
 742     }
 743     else
 744     {
 745 	struct timeval now;
 746 	gettimeofday (&now, 0);
 747 	int elapsed = compiz::core::timer::timeval_diff (&now, &lastRedraw);
 748 	if (elapsed < 0)
 749 	    elapsed = 0;
 750  	delay = elapsed < optimalRedrawTime ? optimalRedrawTime - elapsed : 1;
 751     }
 752 
 753     paintTimer.start
 754 	(boost::bind (&CompositeScreen::handlePaintTimeout, cScreen),
 755 	delay);
 756 }
 757 
 758 int
 759 CompositeScreen::redrawTime ()
 760 {
 761     return priv->redrawTime;
 762 }
 763 
 764 int
 765 CompositeScreen::optimalRedrawTime ()
 766 {
 767     return priv->optimalRedrawTime;
 768 }
 769 
 770 bool
 771 CompositeScreen::handlePaintTimeout ()
 772 {
 773     struct      timeval tv;
 774 
 775     priv->painting = true;
 776     priv->reschedule = false;
 777     gettimeofday (&tv, 0);
 778 
 779     if (priv->damageMask)
 780     {
 781 	int         timeDiff;
 782 
 783 	if (priv->pHnd)
 784 	    priv->pHnd->prepareDrawing ();
 785 
 786 	timeDiff = compiz::core::timer::timeval_diff (&tv, &priv->lastRedraw);
 787 
 788 	/* handle clock rollback */
 789 	if (timeDiff < 0)
 790 	    timeDiff = 0;
 791 	/*
 792 	 * Now that we use a "tickless" timing algorithm, timeDiff could be
 793 	 * very large if the screen is truely idle.
 794 	 * However plugins expect the old behaviour where timeDiff is rarely
 795 	 * larger than the frame rate (optimalRedrawTime).
 796 	 * So enforce this to keep animations timed correctly and smooth...
 797 	 */
 798 	if (timeDiff > 100)
 799 	    timeDiff = priv->optimalRedrawTime;
 800 
 801 	priv->redrawTime = timeDiff;
 802 	preparePaint (priv->slowAnimations ? 1 : timeDiff);
 803 
 804 	/* substract top most overlay window region */
 805 	if (priv->overlayWindowCount)
 806 	{
 807 	    for (CompWindowList::reverse_iterator rit =
 808 		 screen->windows ().rbegin ();
 809 		 rit != screen->windows ().rend (); ++rit)
 810 	    {
 811 		CompWindow *w = (*rit);
 812 
 813 		if (w->destroyed () || w->invisible ())
 814 		    continue;
 815 
 816 		if (!CompositeWindow::get (w)->redirected ())
 817 		    priv->damage -= w->region ();
 818 
 819 		break;
 820 	    }
 821 
 822 	    if (priv->damageMask & COMPOSITE_SCREEN_DAMAGE_ALL_MASK)
 823 	    {
 824 		priv->damageMask &= ~COMPOSITE_SCREEN_DAMAGE_ALL_MASK;
 825 		priv->damageMask |= COMPOSITE_SCREEN_DAMAGE_REGION_MASK;
 826 	    }
 827 	}
 828 
 829 	priv->tmpRegion = priv->damage & screen->region ();
 830 
 831 	if (priv->damageMask & COMPOSITE_SCREEN_DAMAGE_REGION_MASK)
 832 	{
 833 	    if (priv->tmpRegion == screen->region ())
 834 		damageScreen ();
 835 	}
 836 
 837 	Display *dpy = screen->dpy ();
 838 	std::map<Damage, XRectangle>::iterator d = priv->damages.begin ();
 839 	for (; d != priv->damages.end (); ++d)
 840 	{
 841 	    XserverRegion sub = XFixesCreateRegion (dpy, &d->second, 1);
 842 	    if (sub != None)
 843 	    {
 844 		XDamageSubtract (dpy, d->first, sub, None);
 845 		XFixesDestroyRegion (dpy, sub);
 846 	    }
 847 	}
 848 	XSync (dpy, False);
 849 	priv->damages.clear ();
 850 
 851 	priv->damage = CompRegion ();
 852 
 853 	int mask = priv->damageMask;
 854 	priv->damageMask = 0;
 855 
 856 	CompOutput::ptrList outputs (0);
 857 
 858 	if (priv->optionGetForceIndependentOutputPainting ()
 859 	    || !screen->hasOverlappingOutputs ())
 860 	{
 861 	    foreach (CompOutput &o, screen->outputDevs ())
 862 		outputs.push_back (&o);
 863 	}
 864 	else
 865 	    outputs.push_back (&screen->fullscreenOutput ());
 866 
 867 	paint (outputs, mask);
 868 
 869 
 870 	donePaint ();
 871 
 872 	priv->outputShapeChanged = false;
 873 
 874 	foreach (CompWindow *w, screen->windows ())
 875 	{
 876 	    if (w->destroyed ())
 877 	    {
 878 		CompositeWindow::get (w)->addDamage ();
 879 		break;
 880 	    }
 881 	}
 882     }
 883 
 884     priv->lastRedraw = tv;
 885     priv->painting = false;
 886     priv->scheduled = false;
 887     if (priv->reschedule)
 888 	priv->scheduleRepaint ();
 889 
 890     return false;
 891 }
 892 
 893 
 894 
 895 void
 896 CompositeScreen::preparePaint (int msSinceLastPaint)
 897     WRAPABLE_HND_FUNCTN (preparePaint, msSinceLastPaint)
 898 
 899 void
 900 CompositeScreen::donePaint ()
 901     WRAPABLE_HND_FUNCTN (donePaint)
 902 
 903 void
 904 CompositeScreen::paint (CompOutput::ptrList &outputs,
 905 		        unsigned int        mask)
 906 {
 907     WRAPABLE_HND_FUNCTN (paint, outputs, mask)
 908 
 909     if (priv->pHnd)
 910 	priv->pHnd->paintOutputs (outputs, mask, priv->tmpRegion);
 911 }
 912 
 913 const CompWindowList &
 914 CompositeScreen::getWindowPaintList ()
 915 {
 916     WRAPABLE_HND_FUNCTN_RETURN (const CompWindowList &, getWindowPaintList)
 917 
 918     /* Include destroyed windows */
 919     if (screen->destroyedWindows ().empty ())
 920 	return screen->windows ();
 921     else
 922     {
 923 	CompWindowList destroyedWindows = screen->destroyedWindows ();
 924 
 925 	priv->withDestroyedWindows.resize (0);
 926 
 927 	foreach (CompWindow *w, screen->windows ())
 928 	{
 929 	    foreach (CompWindow *dw, screen->destroyedWindows ())
 930 	    {
 931 		if (dw->next == w)
 932 		{
 933 		    priv->withDestroyedWindows.push_back (dw);
 934 		    destroyedWindows.remove (dw);
 935 		    break;
 936 		}
 937 	    }
 938 
 939 	    priv->withDestroyedWindows.push_back (w);
 940 	}
 941 
 942 	/* We need to put all the destroyed windows which didn't get
 943 	 * inserted in the paint list at the top of the stack since
 944 	 * w->next was probably either invalid or NULL */
 945 
 946 	foreach (CompWindow *dw, destroyedWindows)
 947 	    priv->withDestroyedWindows.push_back (dw);
 948 
 949 	return priv->withDestroyedWindows;
 950     }
 951 }
 952 
 953 void
 954 PrivateCompositeScreen::handleExposeEvent (XExposeEvent *event)
 955 {
 956     if (output == event->window)
 957 	return;
 958 
 959     exposeRects.push_back (CompRect (event->x,
 960 				     event->y,
 961 				     event->width,
 962 				     event->height));
 963 
 964     if (event->count == 0)
 965     {
 966 	CompRect rect;
 967 	foreach (CompRect rect, exposeRects)
 968 	{
 969 	    cScreen->damageRegion (CompRegion (rect));
 970 	}
 971 	exposeRects.clear ();
 972     }
 973 }
 974 
 975 void
 976 PrivateCompositeScreen::outputChangeNotify ()
 977 {
 978     screen->outputChangeNotify ();
 979     XMoveResizeWindow (screen->dpy (), overlay, 0, 0,
 980 		       screen->width (), screen->height ());
 981     cScreen->damageScreen ();
 982 }
 983 
 984 bool
 985 CompositeScreen::toggleSlowAnimations (CompAction         *action,
 986 				       CompAction::State  state,
 987 				       CompOption::Vector &options)
 988 {
 989     CompositeScreen *cs = CompositeScreen::get (screen);
 990     if (cs)
 991 	cs->priv->slowAnimations = !cs->priv->slowAnimations;
 992 
 993     return true;
 994 }
 995 
 996 
 997 void
 998 CompositeScreenInterface::preparePaint (int msSinceLastPaint)
 999     WRAPABLE_DEF (preparePaint, msSinceLastPaint)
1000 
1001 void
1002 CompositeScreenInterface::donePaint ()
1003     WRAPABLE_DEF (donePaint)
1004 
1005 void
1006 CompositeScreenInterface::paint (CompOutput::ptrList &outputs,
1007 				 unsigned int        mask)
1008     WRAPABLE_DEF (paint, outputs, mask)
1009 
1010 const CompWindowList &
1011 CompositeScreenInterface::getWindowPaintList ()
1012     WRAPABLE_DEF (getWindowPaintList)
1013 
1014 bool
1015 CompositeScreenInterface::registerPaintHandler (compiz::composite::PaintHandler *pHnd)
1016     WRAPABLE_DEF (registerPaintHandler, pHnd);
1017 
1018 void
1019 CompositeScreenInterface::unregisterPaintHandler ()
1020     WRAPABLE_DEF (unregisterPaintHandler);
1021 
1022 void
1023 CompositeScreenInterface::damageRegion (const CompRegion &r)
1024     WRAPABLE_DEF (damageRegion, r);
1025 
1026 const CompRegion &
1027 CompositeScreen::currentDamage () const
1028 {
1029     return priv->damage;
1030 }