xf86Rotate.c revision 1b5d61b8
1/* 2 * Copyright © 2006 Keith Packard 3 * Copyright © 2011 Aaron Plattner 4 * 5 * Permission to use, copy, modify, distribute, and sell this software and its 6 * documentation for any purpose is hereby granted without fee, provided that 7 * the above copyright notice appear in all copies and that both that copyright 8 * notice and this permission notice appear in supporting documentation, and 9 * that the name of the copyright holders not be used in advertising or 10 * publicity pertaining to distribution of the software without specific, 11 * written prior permission. The copyright holders make no representations 12 * about the suitability of this software for any purpose. It is provided "as 13 * is" without express or implied warranty. 14 * 15 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, 16 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO 17 * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR 18 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, 19 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 20 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 21 * OF THIS SOFTWARE. 22 */ 23 24#ifdef HAVE_XORG_CONFIG_H 25#include <xorg-config.h> 26#endif 27 28#include <stddef.h> 29#include <string.h> 30#include <stdio.h> 31 32#include "xf86.h" 33#include "xf86DDC.h" 34#include "windowstr.h" 35#include "xf86Crtc.h" 36#include "xf86Modes.h" 37#include "xf86RandR12.h" 38#include "X11/extensions/render.h" 39#include "X11/extensions/dpmsconst.h" 40#include "X11/Xatom.h" 41 42static void 43xf86RotateCrtcRedisplay(xf86CrtcPtr crtc, RegionPtr region) 44{ 45 ScrnInfoPtr scrn = crtc->scrn; 46 ScreenPtr screen = scrn->pScreen; 47 WindowPtr root = screen->root; 48 PixmapPtr dst_pixmap = crtc->rotatedPixmap; 49 PictFormatPtr format = PictureWindowFormat(screen->root); 50 int error; 51 PicturePtr src, dst; 52 int n = RegionNumRects(region); 53 BoxPtr b = RegionRects(region); 54 XID include_inferiors = IncludeInferiors; 55 56 if (crtc->driverIsPerformingTransform & XF86DriverTransformOutput) 57 return; 58 59 src = CreatePicture(None, 60 &root->drawable, 61 format, 62 CPSubwindowMode, 63 &include_inferiors, serverClient, &error); 64 if (!src) 65 return; 66 67 dst = CreatePicture(None, 68 &dst_pixmap->drawable, 69 format, 0L, NULL, serverClient, &error); 70 if (!dst) 71 return; 72 73 error = SetPictureTransform(src, &crtc->crtc_to_framebuffer); 74 if (error) 75 return; 76 if (crtc->transform_in_use && crtc->filter) 77 SetPicturePictFilter(src, crtc->filter, crtc->params, crtc->nparams); 78 79 if (crtc->shadowClear) { 80 CompositePicture(PictOpSrc, 81 src, NULL, dst, 82 0, 0, 0, 0, 0, 0, 83 crtc->mode.HDisplay, crtc->mode.VDisplay); 84 crtc->shadowClear = FALSE; 85 } 86 else { 87 while (n--) { 88 BoxRec dst_box; 89 90 dst_box = *b; 91 dst_box.x1 -= crtc->filter_width >> 1; 92 dst_box.x2 += crtc->filter_width >> 1; 93 dst_box.y1 -= crtc->filter_height >> 1; 94 dst_box.y2 += crtc->filter_height >> 1; 95 pixman_f_transform_bounds(&crtc->f_framebuffer_to_crtc, &dst_box); 96 CompositePicture(PictOpSrc, 97 src, NULL, dst, 98 dst_box.x1, dst_box.y1, 0, 0, dst_box.x1, 99 dst_box.y1, dst_box.x2 - dst_box.x1, 100 dst_box.y2 - dst_box.y1); 101 b++; 102 } 103 } 104 FreePicture(src, None); 105 FreePicture(dst, None); 106} 107 108static void 109xf86CrtcDamageShadow(xf86CrtcPtr crtc) 110{ 111 ScrnInfoPtr pScrn = crtc->scrn; 112 BoxRec damage_box; 113 RegionRec damage_region; 114 ScreenPtr pScreen = xf86ScrnToScreen(pScrn); 115 116 damage_box.x1 = 0; 117 damage_box.x2 = crtc->mode.HDisplay; 118 damage_box.y1 = 0; 119 damage_box.y2 = crtc->mode.VDisplay; 120 if (!pixman_transform_bounds(&crtc->crtc_to_framebuffer, &damage_box)) { 121 damage_box.x1 = 0; 122 damage_box.y1 = 0; 123 damage_box.x2 = pScreen->width; 124 damage_box.y2 = pScreen->height; 125 } 126 if (damage_box.x1 < 0) 127 damage_box.x1 = 0; 128 if (damage_box.y1 < 0) 129 damage_box.y1 = 0; 130 if (damage_box.x2 > pScreen->width) 131 damage_box.x2 = pScreen->width; 132 if (damage_box.y2 > pScreen->height) 133 damage_box.y2 = pScreen->height; 134 RegionInit(&damage_region, &damage_box, 1); 135 DamageDamageRegion(&(*pScreen->GetScreenPixmap) (pScreen)->drawable, 136 &damage_region); 137 RegionUninit(&damage_region); 138 crtc->shadowClear = TRUE; 139} 140 141static void 142xf86RotatePrepare(ScreenPtr pScreen) 143{ 144 ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen); 145 xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn); 146 int c; 147 148 for (c = 0; c < xf86_config->num_crtc; c++) { 149 xf86CrtcPtr crtc = xf86_config->crtc[c]; 150 151 if (crtc->rotatedData && !crtc->rotatedPixmap) { 152 crtc->rotatedPixmap = crtc->funcs->shadow_create(crtc, 153 crtc->rotatedData, 154 crtc->mode. 155 HDisplay, 156 crtc->mode. 157 VDisplay); 158 if (!xf86_config->rotation_damage_registered) { 159 /* Hook damage to screen pixmap */ 160 DamageRegister(&pScreen->root->drawable, 161 xf86_config->rotation_damage); 162 xf86_config->rotation_damage_registered = TRUE; 163 EnableLimitedSchedulingLatency(); 164 } 165 166 xf86CrtcDamageShadow(crtc); 167 } 168 } 169} 170 171static Bool 172xf86RotateRedisplay(ScreenPtr pScreen) 173{ 174 ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen); 175 xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn); 176 DamagePtr damage = xf86_config->rotation_damage; 177 RegionPtr region; 178 179 if (!damage) 180 return FALSE; 181 xf86RotatePrepare(pScreen); 182 region = DamageRegion(damage); 183 if (RegionNotEmpty(region)) { 184 int c; 185 SourceValidateProcPtr SourceValidate; 186 187 /* 188 * SourceValidate is used by the software cursor code 189 * to pull the cursor off of the screen when reading 190 * bits from the frame buffer. Bypassing this function 191 * leaves the software cursor in place 192 */ 193 SourceValidate = pScreen->SourceValidate; 194 pScreen->SourceValidate = NULL; 195 196 for (c = 0; c < xf86_config->num_crtc; c++) { 197 xf86CrtcPtr crtc = xf86_config->crtc[c]; 198 199 if (crtc->transform_in_use && crtc->enabled) { 200 RegionRec crtc_damage; 201 202 /* compute portion of damage that overlaps crtc */ 203 RegionInit(&crtc_damage, &crtc->bounds, 1); 204 RegionIntersect(&crtc_damage, &crtc_damage, region); 205 206 /* update damaged region */ 207 if (RegionNotEmpty(&crtc_damage)) 208 xf86RotateCrtcRedisplay(crtc, &crtc_damage); 209 210 RegionUninit(&crtc_damage); 211 } 212 } 213 pScreen->SourceValidate = SourceValidate; 214 DamageEmpty(damage); 215 } 216 return TRUE; 217} 218 219static void 220xf86RotateBlockHandler(ScreenPtr pScreen, void *pTimeout) 221{ 222 ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen); 223 xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn); 224 225 /* Unwrap before redisplay in case the software 226 * cursor layer wants to add its block handler to the 227 * chain 228 */ 229 pScreen->BlockHandler = xf86_config->BlockHandler; 230 231 xf86RotateRedisplay(pScreen); 232 233 (*pScreen->BlockHandler) (pScreen, pTimeout); 234 235 /* Re-wrap if we still need this hook */ 236 if (xf86_config->rotation_damage != NULL) { 237 xf86_config->BlockHandler = pScreen->BlockHandler; 238 pScreen->BlockHandler = xf86RotateBlockHandler; 239 } else 240 xf86_config->BlockHandler = NULL; 241} 242 243void 244xf86RotateDestroy(xf86CrtcPtr crtc) 245{ 246 ScrnInfoPtr pScrn = crtc->scrn; 247 xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn); 248 int c; 249 250 /* Free memory from rotation */ 251 if (crtc->rotatedPixmap || crtc->rotatedData) { 252 crtc->funcs->shadow_destroy(crtc, crtc->rotatedPixmap, 253 crtc->rotatedData); 254 crtc->rotatedPixmap = NULL; 255 crtc->rotatedData = NULL; 256 } 257 258 for (c = 0; c < xf86_config->num_crtc; c++) 259 if (xf86_config->crtc[c]->rotatedData) 260 return; 261 262 /* 263 * Clean up damage structures when no crtcs are rotated 264 */ 265 if (xf86_config->rotation_damage) { 266 /* Free damage structure */ 267 if (xf86_config->rotation_damage_registered) { 268 xf86_config->rotation_damage_registered = FALSE; 269 DisableLimitedSchedulingLatency(); 270 } 271 DamageDestroy(xf86_config->rotation_damage); 272 xf86_config->rotation_damage = NULL; 273 } 274} 275 276void 277xf86RotateFreeShadow(ScrnInfoPtr pScrn) 278{ 279 xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(pScrn); 280 int c; 281 282 for (c = 0; c < config->num_crtc; c++) { 283 xf86CrtcPtr crtc = config->crtc[c]; 284 285 if (crtc->rotatedPixmap || crtc->rotatedData) { 286 crtc->funcs->shadow_destroy(crtc, crtc->rotatedPixmap, 287 crtc->rotatedData); 288 crtc->rotatedPixmap = NULL; 289 crtc->rotatedData = NULL; 290 } 291 } 292} 293 294void 295xf86RotateCloseScreen(ScreenPtr screen) 296{ 297 ScrnInfoPtr scrn = xf86ScreenToScrn(screen); 298 xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(scrn); 299 int c; 300 301 /* This has already been destroyed when the root window was destroyed */ 302 xf86_config->rotation_damage = NULL; 303 for (c = 0; c < xf86_config->num_crtc; c++) 304 xf86RotateDestroy(xf86_config->crtc[c]); 305} 306 307static Bool 308xf86CrtcFitsScreen(xf86CrtcPtr crtc, struct pict_f_transform *crtc_to_fb) 309{ 310 ScrnInfoPtr pScrn = crtc->scrn; 311 BoxRec b; 312 313 /* When called before PreInit, the driver is 314 * presumably doing load detect 315 */ 316 if (pScrn->is_gpu) { 317 ScreenPtr pScreen = xf86ScrnToScreen(pScrn); 318 if (pScreen->current_master) 319 pScrn = xf86ScreenToScrn(pScreen->current_master); 320 } 321 322 if (pScrn->virtualX == 0 || pScrn->virtualY == 0) 323 return TRUE; 324 325 b.x1 = 0; 326 b.y1 = 0; 327 b.x2 = crtc->mode.HDisplay; 328 b.y2 = crtc->mode.VDisplay; 329 if (crtc_to_fb) 330 pixman_f_transform_bounds(crtc_to_fb, &b); 331 else { 332 b.x1 += crtc->x; 333 b.y1 += crtc->y; 334 b.x2 += crtc->x; 335 b.y2 += crtc->y; 336 } 337 338 return (0 <= b.x1 && b.x2 <= pScrn->virtualX && 339 0 <= b.y1 && b.y2 <= pScrn->virtualY); 340} 341 342Bool 343xf86CrtcRotate(xf86CrtcPtr crtc) 344{ 345 ScrnInfoPtr pScrn = crtc->scrn; 346 xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn); 347 ScreenPtr pScreen = xf86ScrnToScreen(pScrn); 348 PictTransform crtc_to_fb; 349 struct pict_f_transform f_crtc_to_fb, f_fb_to_crtc; 350 xFixed *new_params = NULL; 351 int new_nparams = 0; 352 PictFilterPtr new_filter = NULL; 353 int new_width = 0; 354 int new_height = 0; 355 RRTransformPtr transform = NULL; 356 Bool damage = FALSE; 357 358 if (pScreen->isGPU) 359 return TRUE; 360 if (crtc->transformPresent) 361 transform = &crtc->transform; 362 363 if (!RRTransformCompute(crtc->x, crtc->y, 364 crtc->mode.HDisplay, crtc->mode.VDisplay, 365 crtc->rotation, 366 transform, 367 &crtc_to_fb, 368 &f_crtc_to_fb, 369 &f_fb_to_crtc) && 370 xf86CrtcFitsScreen(crtc, &f_crtc_to_fb)) { 371 /* 372 * If the untranslated transformation is the identity, 373 * disable the shadow buffer 374 */ 375 xf86RotateDestroy(crtc); 376 crtc->transform_in_use = FALSE; 377 free(new_params); 378 new_params = NULL; 379 new_nparams = 0; 380 new_filter = NULL; 381 new_width = 0; 382 new_height = 0; 383 } 384 else { 385 if (crtc->driverIsPerformingTransform & XF86DriverTransformOutput) { 386 xf86RotateDestroy(crtc); 387 } 388 else { 389 /* 390 * these are the size of the shadow pixmap, which 391 * matches the mode, not the pre-rotated copy in the 392 * frame buffer 393 */ 394 int width = crtc->mode.HDisplay; 395 int height = crtc->mode.VDisplay; 396 void *shadowData = crtc->rotatedData; 397 PixmapPtr shadow = crtc->rotatedPixmap; 398 int old_width = shadow ? shadow->drawable.width : 0; 399 int old_height = shadow ? shadow->drawable.height : 0; 400 401 /* Allocate memory for rotation */ 402 if (old_width != width || old_height != height) { 403 if (shadow || shadowData) { 404 crtc->funcs->shadow_destroy(crtc, shadow, shadowData); 405 crtc->rotatedPixmap = NULL; 406 crtc->rotatedData = NULL; 407 } 408 shadowData = crtc->funcs->shadow_allocate(crtc, width, height); 409 if (!shadowData) 410 goto bail1; 411 crtc->rotatedData = shadowData; 412 /* shadow will be damaged in xf86RotatePrepare */ 413 } 414 else { 415 /* mark shadowed area as damaged so it will be repainted */ 416 damage = TRUE; 417 } 418 419 if (!xf86_config->rotation_damage) { 420 /* Create damage structure */ 421 xf86_config->rotation_damage = DamageCreate(NULL, NULL, 422 DamageReportNone, 423 TRUE, pScreen, 424 pScreen); 425 if (!xf86_config->rotation_damage) 426 goto bail2; 427 428 /* Wrap block handler */ 429 if (!xf86_config->BlockHandler) { 430 xf86_config->BlockHandler = pScreen->BlockHandler; 431 pScreen->BlockHandler = xf86RotateBlockHandler; 432 } 433 } 434 435 if (0) { 436 bail2: 437 if (shadow || shadowData) { 438 crtc->funcs->shadow_destroy(crtc, shadow, shadowData); 439 crtc->rotatedPixmap = NULL; 440 crtc->rotatedData = NULL; 441 } 442 bail1: 443 if (old_width && old_height) 444 crtc->rotatedPixmap = 445 crtc->funcs->shadow_create(crtc, NULL, old_width, 446 old_height); 447 return FALSE; 448 } 449 } 450#ifdef RANDR_12_INTERFACE 451 if (transform) { 452 if (transform->nparams) { 453 new_params = malloc(transform->nparams * sizeof(xFixed)); 454 if (new_params) { 455 memcpy(new_params, transform->params, 456 transform->nparams * sizeof(xFixed)); 457 new_nparams = transform->nparams; 458 new_filter = transform->filter; 459 } 460 } 461 else 462 new_filter = transform->filter; 463 if (new_filter) { 464 new_width = new_filter->width; 465 new_height = new_filter->height; 466 } 467 } 468#endif 469 crtc->transform_in_use = TRUE; 470 } 471 crtc->crtc_to_framebuffer = crtc_to_fb; 472 crtc->f_crtc_to_framebuffer = f_crtc_to_fb; 473 crtc->f_framebuffer_to_crtc = f_fb_to_crtc; 474 free(crtc->params); 475 crtc->params = new_params; 476 crtc->nparams = new_nparams; 477 crtc->filter = new_filter; 478 crtc->filter_width = new_width; 479 crtc->filter_height = new_height; 480 crtc->bounds.x1 = 0; 481 crtc->bounds.x2 = crtc->mode.HDisplay; 482 crtc->bounds.y1 = 0; 483 crtc->bounds.y2 = crtc->mode.VDisplay; 484 pixman_f_transform_bounds(&f_crtc_to_fb, &crtc->bounds); 485 486 if (damage) 487 xf86CrtcDamageShadow(crtc); 488 489 /* All done */ 490 return TRUE; 491} 492