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