502 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			502 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C
		
	
	
	
| /*
 | |
|  * Copyright © 2006 Keith Packard
 | |
|  * Copyright © 2011 Aaron Plattner
 | |
|  *
 | |
|  * Permission to use, copy, modify, distribute, and sell this software and its
 | |
|  * documentation for any purpose is hereby granted without fee, provided that
 | |
|  * the above copyright notice appear in all copies and that both that copyright
 | |
|  * notice and this permission notice appear in supporting documentation, and
 | |
|  * that the name of the copyright holders not be used in advertising or
 | |
|  * publicity pertaining to distribution of the software without specific,
 | |
|  * written prior permission.  The copyright holders make no representations
 | |
|  * about the suitability of this software for any purpose.  It is provided "as
 | |
|  * is" without express or implied warranty.
 | |
|  *
 | |
|  * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 | |
|  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
 | |
|  * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
 | |
|  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
 | |
|  * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
 | |
|  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
 | |
|  * OF THIS SOFTWARE.
 | |
|  */
 | |
| 
 | |
| #ifdef HAVE_XORG_CONFIG_H
 | |
| #include <xorg-config.h>
 | |
| #endif
 | |
| 
 | |
| #include <stddef.h>
 | |
| #include <string.h>
 | |
| #include <stdio.h>
 | |
| #include <X11/Xatom.h>
 | |
| #include <X11/extensions/render.h>
 | |
| #include <X11/extensions/dpmsconst.h>
 | |
| 
 | |
| #include "dix/dix_priv.h"
 | |
| #include "mi/mi_priv.h"
 | |
| 
 | |
| #include "xf86.h"
 | |
| #include "xf86DDC.h"
 | |
| #include "windowstr.h"
 | |
| #include "xf86Crtc.h"
 | |
| #include "xf86Modes.h"
 | |
| #include "xf86RandR12.h"
 | |
| 
 | |
| void
 | |
| xf86RotateCrtcRedisplay(xf86CrtcPtr crtc, PixmapPtr dst_pixmap,
 | |
|                         DrawableRec *src_drawable, RegionPtr region,
 | |
|                         Bool transform_src)
 | |
| {
 | |
|     ScrnInfoPtr scrn = crtc->scrn;
 | |
|     ScreenPtr screen = scrn->pScreen;
 | |
|     PictFormatPtr format = PictureWindowFormat(screen->root);
 | |
|     int error;
 | |
|     PicturePtr src, dst;
 | |
|     int n = RegionNumRects(region);
 | |
|     BoxPtr b = RegionRects(region);
 | |
|     XID include_inferiors = IncludeInferiors;
 | |
| 
 | |
|     if (crtc->driverIsPerformingTransform & XF86DriverTransformOutput)
 | |
|         return;
 | |
| 
 | |
|     src = CreatePicture(None,
 | |
|                         src_drawable,
 | |
|                         format,
 | |
|                         CPSubwindowMode,
 | |
|                         &include_inferiors, serverClient, &error);
 | |
|     if (!src)
 | |
|         return;
 | |
| 
 | |
|     dst = CreatePicture(None,
 | |
|                         &dst_pixmap->drawable,
 | |
|                         format, 0L, NULL, serverClient, &error);
 | |
|     if (!dst)
 | |
|         return;
 | |
| 
 | |
|     if (transform_src) {
 | |
|         error = SetPictureTransform(src, &crtc->crtc_to_framebuffer);
 | |
|         if (error)
 | |
|             return;
 | |
|     }
 | |
|     if (crtc->transform_in_use && crtc->filter)
 | |
|         SetPicturePictFilter(src, crtc->filter, crtc->params, crtc->nparams);
 | |
| 
 | |
|     if (crtc->shadowClear) {
 | |
|         CompositePicture(PictOpSrc,
 | |
|                          src, NULL, dst,
 | |
|                          0, 0, 0, 0, 0, 0,
 | |
|                          crtc->mode.HDisplay, crtc->mode.VDisplay);
 | |
|         crtc->shadowClear = FALSE;
 | |
|     }
 | |
|     else {
 | |
|         while (n--) {
 | |
|             BoxRec dst_box;
 | |
| 
 | |
|             dst_box = *b;
 | |
|             dst_box.x1 -= crtc->filter_width >> 1;
 | |
|             dst_box.x2 += crtc->filter_width >> 1;
 | |
|             dst_box.y1 -= crtc->filter_height >> 1;
 | |
|             dst_box.y2 += crtc->filter_height >> 1;
 | |
|             pixman_f_transform_bounds(&crtc->f_framebuffer_to_crtc, &dst_box);
 | |
|             CompositePicture(PictOpSrc,
 | |
|                              src, NULL, dst,
 | |
|                              dst_box.x1, dst_box.y1, 0, 0, dst_box.x1,
 | |
|                              dst_box.y1, dst_box.x2 - dst_box.x1,
 | |
|                              dst_box.y2 - dst_box.y1);
 | |
|             b++;
 | |
|         }
 | |
|     }
 | |
|     FreePicture(src, None);
 | |
|     FreePicture(dst, None);
 | |
| }
 | |
| 
 | |
| static void
 | |
| xf86CrtcDamageShadow(xf86CrtcPtr crtc)
 | |
| {
 | |
|     ScrnInfoPtr pScrn = crtc->scrn;
 | |
|     BoxRec damage_box;
 | |
|     RegionRec damage_region;
 | |
|     ScreenPtr pScreen = xf86ScrnToScreen(pScrn);
 | |
| 
 | |
|     damage_box.x1 = 0;
 | |
|     damage_box.x2 = crtc->mode.HDisplay;
 | |
|     damage_box.y1 = 0;
 | |
|     damage_box.y2 = crtc->mode.VDisplay;
 | |
|     if (!pixman_transform_bounds(&crtc->crtc_to_framebuffer, &damage_box)) {
 | |
|         damage_box.x1 = 0;
 | |
|         damage_box.y1 = 0;
 | |
|         damage_box.x2 = pScreen->width;
 | |
|         damage_box.y2 = pScreen->height;
 | |
|     }
 | |
|     if (damage_box.x1 < 0)
 | |
|         damage_box.x1 = 0;
 | |
|     if (damage_box.y1 < 0)
 | |
|         damage_box.y1 = 0;
 | |
|     if (damage_box.x2 > pScreen->width)
 | |
|         damage_box.x2 = pScreen->width;
 | |
|     if (damage_box.y2 > pScreen->height)
 | |
|         damage_box.y2 = pScreen->height;
 | |
|     RegionInit(&damage_region, &damage_box, 1);
 | |
|     DamageDamageRegion(&(*pScreen->GetScreenPixmap) (pScreen)->drawable,
 | |
|                        &damage_region);
 | |
|     RegionUninit(&damage_region);
 | |
|     crtc->shadowClear = TRUE;
 | |
| }
 | |
| 
 | |
| static void
 | |
| xf86RotatePrepare(ScreenPtr pScreen)
 | |
| {
 | |
|     ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
 | |
|     xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
 | |
|     int c;
 | |
| 
 | |
|     for (c = 0; c < xf86_config->num_crtc; c++) {
 | |
|         xf86CrtcPtr crtc = xf86_config->crtc[c];
 | |
| 
 | |
|         if (crtc->rotatedData && !crtc->rotatedPixmap) {
 | |
|             crtc->rotatedPixmap = crtc->funcs->shadow_create(crtc,
 | |
|                                                              crtc->rotatedData,
 | |
|                                                              crtc->mode.
 | |
|                                                              HDisplay,
 | |
|                                                              crtc->mode.
 | |
|                                                              VDisplay);
 | |
|             if (!xf86_config->rotation_damage_registered) {
 | |
|                 /* Hook damage to screen pixmap */
 | |
|                 DamageRegister(&pScreen->root->drawable,
 | |
|                                xf86_config->rotation_damage);
 | |
|                 xf86_config->rotation_damage_registered = TRUE;
 | |
|                 EnableLimitedSchedulingLatency();
 | |
|             }
 | |
| 
 | |
|             xf86CrtcDamageShadow(crtc);
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| static Bool
 | |
| xf86RotateRedisplay(ScreenPtr pScreen)
 | |
| {
 | |
|     ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
 | |
|     xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
 | |
|     DamagePtr damage = xf86_config->rotation_damage;
 | |
|     RegionPtr region;
 | |
| 
 | |
|     if (!damage || !pScreen->root)
 | |
|         return FALSE;
 | |
|     xf86RotatePrepare(pScreen);
 | |
|     region = DamageRegion(damage);
 | |
|     if (RegionNotEmpty(region)) {
 | |
|         int c;
 | |
|         SourceValidateProcPtr SourceValidate;
 | |
| 
 | |
|         /*
 | |
|          * SourceValidate is used by the software cursor code
 | |
|          * to pull the cursor off of the screen when reading
 | |
|          * bits from the frame buffer. Bypassing this function
 | |
|          * leaves the software cursor in place
 | |
|          */
 | |
|         SourceValidate = pScreen->SourceValidate;
 | |
|         pScreen->SourceValidate = miSourceValidate;
 | |
| 
 | |
|         for (c = 0; c < xf86_config->num_crtc; c++) {
 | |
|             xf86CrtcPtr crtc = xf86_config->crtc[c];
 | |
| 
 | |
|             if (crtc->transform_in_use && crtc->enabled) {
 | |
|                 RegionRec crtc_damage;
 | |
| 
 | |
|                 /* compute portion of damage that overlaps crtc */
 | |
|                 RegionInit(&crtc_damage, &crtc->bounds, 1);
 | |
|                 RegionIntersect(&crtc_damage, &crtc_damage, region);
 | |
| 
 | |
|                 /* update damaged region */
 | |
|                 if (RegionNotEmpty(&crtc_damage))
 | |
|                     xf86RotateCrtcRedisplay(crtc, crtc->rotatedPixmap,
 | |
|                                             &pScreen->root->drawable,
 | |
|                                             &crtc_damage, TRUE);
 | |
| 
 | |
|                 RegionUninit(&crtc_damage);
 | |
|             }
 | |
|         }
 | |
|         pScreen->SourceValidate = SourceValidate;
 | |
|         DamageEmpty(damage);
 | |
|     }
 | |
|     return TRUE;
 | |
| }
 | |
| 
 | |
| static void
 | |
| xf86RotateBlockHandler(ScreenPtr pScreen, void *pTimeout)
 | |
| {
 | |
|     ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
 | |
|     xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
 | |
| 
 | |
|     /* Unwrap before redisplay in case the software
 | |
|      * cursor layer wants to add its block handler to the
 | |
|      * chain
 | |
|      */
 | |
|     pScreen->BlockHandler = xf86_config->BlockHandler;
 | |
| 
 | |
|     xf86RotateRedisplay(pScreen);
 | |
| 
 | |
|     (*pScreen->BlockHandler) (pScreen, pTimeout);
 | |
| 
 | |
|     /* Re-wrap if we still need this hook */
 | |
|     if (xf86_config->rotation_damage != NULL) {
 | |
|         xf86_config->BlockHandler = pScreen->BlockHandler;
 | |
|         pScreen->BlockHandler = xf86RotateBlockHandler;
 | |
|     } else
 | |
|         xf86_config->BlockHandler = NULL;
 | |
| }
 | |
| 
 | |
| void
 | |
| xf86RotateDestroy(xf86CrtcPtr crtc)
 | |
| {
 | |
|     ScrnInfoPtr pScrn = crtc->scrn;
 | |
|     xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
 | |
|     int c;
 | |
| 
 | |
|     /* Free memory from rotation */
 | |
|     if (crtc->rotatedPixmap || crtc->rotatedData) {
 | |
|         crtc->funcs->shadow_destroy(crtc, crtc->rotatedPixmap,
 | |
|                                     crtc->rotatedData);
 | |
|         crtc->rotatedPixmap = NULL;
 | |
|         crtc->rotatedData = NULL;
 | |
|     }
 | |
| 
 | |
|     for (c = 0; c < xf86_config->num_crtc; c++)
 | |
|         if (xf86_config->crtc[c]->rotatedData)
 | |
|             return;
 | |
| 
 | |
|     /*
 | |
|      * Clean up damage structures when no crtcs are rotated
 | |
|      */
 | |
|     if (xf86_config->rotation_damage) {
 | |
|         /* Free damage structure */
 | |
|         if (xf86_config->rotation_damage_registered) {
 | |
|             xf86_config->rotation_damage_registered = FALSE;
 | |
|             DisableLimitedSchedulingLatency();
 | |
|         }
 | |
|         DamageDestroy(xf86_config->rotation_damage);
 | |
|         xf86_config->rotation_damage = NULL;
 | |
|     }
 | |
| }
 | |
| 
 | |
| void
 | |
| xf86RotateFreeShadow(ScrnInfoPtr pScrn)
 | |
| {
 | |
|     xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(pScrn);
 | |
|     int c;
 | |
| 
 | |
|     for (c = 0; c < config->num_crtc; c++) {
 | |
|         xf86CrtcPtr crtc = config->crtc[c];
 | |
| 
 | |
|         if (crtc->rotatedPixmap || crtc->rotatedData) {
 | |
|             crtc->funcs->shadow_destroy(crtc, crtc->rotatedPixmap,
 | |
|                                         crtc->rotatedData);
 | |
|             crtc->rotatedPixmap = NULL;
 | |
|             crtc->rotatedData = NULL;
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| void
 | |
| xf86RotateCloseScreen(ScreenPtr screen)
 | |
| {
 | |
|     ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
 | |
|     xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(scrn);
 | |
|     int c;
 | |
| 
 | |
|     /* This has already been destroyed when the root window was destroyed */
 | |
|     xf86_config->rotation_damage = NULL;
 | |
|     for (c = 0; c < xf86_config->num_crtc; c++)
 | |
|         xf86RotateDestroy(xf86_config->crtc[c]);
 | |
| }
 | |
| 
 | |
| static Bool
 | |
| xf86CrtcFitsScreen(xf86CrtcPtr crtc, struct pixman_f_transform *crtc_to_fb)
 | |
| {
 | |
|     ScrnInfoPtr pScrn = crtc->scrn;
 | |
|     BoxRec b;
 | |
| 
 | |
|     /* When called before PreInit, the driver is
 | |
|      * presumably doing load detect
 | |
|      */
 | |
|     if (pScrn->is_gpu) {
 | |
| 	ScreenPtr pScreen = xf86ScrnToScreen(pScrn);
 | |
| 	if (pScreen->current_primary)
 | |
| 	    pScrn = xf86ScreenToScrn(pScreen->current_primary);
 | |
|     }
 | |
| 
 | |
|     if (pScrn->virtualX == 0 || pScrn->virtualY == 0)
 | |
|         return TRUE;
 | |
| 
 | |
|     b.x1 = 0;
 | |
|     b.y1 = 0;
 | |
|     b.x2 = crtc->mode.HDisplay;
 | |
|     b.y2 = crtc->mode.VDisplay;
 | |
|     if (crtc_to_fb)
 | |
|         pixman_f_transform_bounds(crtc_to_fb, &b);
 | |
|     else {
 | |
|         b.x1 += crtc->x;
 | |
|         b.y1 += crtc->y;
 | |
|         b.x2 += crtc->x;
 | |
|         b.y2 += crtc->y;
 | |
|     }
 | |
| 
 | |
|     return (0 <= b.x1 && b.x2 <= pScrn->virtualX &&
 | |
|             0 <= b.y1 && b.y2 <= pScrn->virtualY);
 | |
| }
 | |
| 
 | |
| Bool
 | |
| xf86CrtcRotate(xf86CrtcPtr crtc)
 | |
| {
 | |
|     ScrnInfoPtr pScrn = crtc->scrn;
 | |
|     xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
 | |
|     ScreenPtr pScreen = xf86ScrnToScreen(pScrn);
 | |
|     PictTransform crtc_to_fb;
 | |
|     struct pixman_f_transform f_crtc_to_fb, f_fb_to_crtc;
 | |
|     xFixed *new_params = NULL;
 | |
|     int new_nparams = 0;
 | |
|     PictFilterPtr new_filter = NULL;
 | |
|     int new_width = 0;
 | |
|     int new_height = 0;
 | |
|     RRTransformPtr transform = NULL;
 | |
|     Bool damage = FALSE;
 | |
| 
 | |
|     if (pScreen->isGPU)
 | |
|         return TRUE;
 | |
|     if (crtc->transformPresent)
 | |
|         transform = &crtc->transform;
 | |
| 
 | |
|     if (!RRTransformCompute(crtc->x, crtc->y,
 | |
|                             crtc->mode.HDisplay, crtc->mode.VDisplay,
 | |
|                             crtc->rotation,
 | |
|                             transform,
 | |
|                             &crtc_to_fb,
 | |
|                             &f_crtc_to_fb,
 | |
|                             &f_fb_to_crtc) &&
 | |
|         xf86CrtcFitsScreen(crtc, &f_crtc_to_fb)) {
 | |
|         /*
 | |
|          * If the untranslated transformation is the identity,
 | |
|          * disable the shadow buffer
 | |
|          */
 | |
|         xf86RotateDestroy(crtc);
 | |
|         crtc->transform_in_use = FALSE;
 | |
|         free(new_params);
 | |
|         new_params = NULL;
 | |
|         new_nparams = 0;
 | |
|         new_filter = NULL;
 | |
|         new_width = 0;
 | |
|         new_height = 0;
 | |
|     }
 | |
|     else {
 | |
|         if (crtc->driverIsPerformingTransform & XF86DriverTransformOutput) {
 | |
|             xf86RotateDestroy(crtc);
 | |
|         }
 | |
|         else {
 | |
|             /*
 | |
|              * these are the size of the shadow pixmap, which
 | |
|              * matches the mode, not the pre-rotated copy in the
 | |
|              * frame buffer
 | |
|              */
 | |
|             int width = crtc->mode.HDisplay;
 | |
|             int height = crtc->mode.VDisplay;
 | |
|             void *shadowData = crtc->rotatedData;
 | |
|             PixmapPtr shadow = crtc->rotatedPixmap;
 | |
|             int old_width = shadow ? shadow->drawable.width : 0;
 | |
|             int old_height = shadow ? shadow->drawable.height : 0;
 | |
| 
 | |
|             /* Allocate memory for rotation */
 | |
|             if (old_width != width || old_height != height) {
 | |
|                 if (shadow || shadowData) {
 | |
|                     crtc->funcs->shadow_destroy(crtc, shadow, shadowData);
 | |
|                     crtc->rotatedPixmap = NULL;
 | |
|                     crtc->rotatedData = NULL;
 | |
|                 }
 | |
|                 shadowData = crtc->funcs->shadow_allocate(crtc, width, height);
 | |
|                 if (!shadowData)
 | |
|                     goto bail1;
 | |
|                 crtc->rotatedData = shadowData;
 | |
|                 /* shadow will be damaged in xf86RotatePrepare */
 | |
|             }
 | |
|             else {
 | |
|                 /* mark shadowed area as damaged so it will be repainted */
 | |
|                 damage = TRUE;
 | |
|             }
 | |
| 
 | |
|             if (!xf86_config->rotation_damage) {
 | |
|                 /* Create damage structure */
 | |
|                 xf86_config->rotation_damage = DamageCreate(NULL, NULL,
 | |
|                                                             DamageReportNone,
 | |
|                                                             TRUE, pScreen,
 | |
|                                                             pScreen);
 | |
|                 if (!xf86_config->rotation_damage)
 | |
|                     goto bail2;
 | |
| 
 | |
|                 /* Wrap block handler */
 | |
|                 if (!xf86_config->BlockHandler) {
 | |
|                     xf86_config->BlockHandler = pScreen->BlockHandler;
 | |
|                     pScreen->BlockHandler = xf86RotateBlockHandler;
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             if (0) {
 | |
|  bail2:
 | |
|                 if (shadow || shadowData) {
 | |
|                     crtc->funcs->shadow_destroy(crtc, shadow, shadowData);
 | |
|                     crtc->rotatedPixmap = NULL;
 | |
|                     crtc->rotatedData = NULL;
 | |
|                 }
 | |
|  bail1:
 | |
|                 if (old_width && old_height)
 | |
|                     crtc->rotatedPixmap =
 | |
|                         crtc->funcs->shadow_create(crtc, NULL, old_width,
 | |
|                                                    old_height);
 | |
|                 return FALSE;
 | |
|             }
 | |
|         }
 | |
| #ifdef RANDR_12_INTERFACE
 | |
|         if (transform) {
 | |
|             if (transform->nparams) {
 | |
|                 new_params = malloc(transform->nparams * sizeof(xFixed));
 | |
|                 if (new_params) {
 | |
|                     memcpy(new_params, transform->params,
 | |
|                            transform->nparams * sizeof(xFixed));
 | |
|                     new_nparams = transform->nparams;
 | |
|                     new_filter = transform->filter;
 | |
|                 }
 | |
|             }
 | |
|             else
 | |
|                 new_filter = transform->filter;
 | |
|             if (new_filter) {
 | |
|                 new_width = new_filter->width;
 | |
|                 new_height = new_filter->height;
 | |
|             }
 | |
|         }
 | |
| #endif
 | |
|         crtc->transform_in_use = TRUE;
 | |
|     }
 | |
|     crtc->crtc_to_framebuffer = crtc_to_fb;
 | |
|     crtc->f_crtc_to_framebuffer = f_crtc_to_fb;
 | |
|     crtc->f_framebuffer_to_crtc = f_fb_to_crtc;
 | |
|     free(crtc->params);
 | |
|     crtc->params = new_params;
 | |
|     crtc->nparams = new_nparams;
 | |
|     crtc->filter = new_filter;
 | |
|     crtc->filter_width = new_width;
 | |
|     crtc->filter_height = new_height;
 | |
|     crtc->bounds.x1 = 0;
 | |
|     crtc->bounds.x2 = crtc->mode.HDisplay;
 | |
|     crtc->bounds.y1 = 0;
 | |
|     crtc->bounds.y2 = crtc->mode.VDisplay;
 | |
|     pixman_f_transform_bounds(&f_crtc_to_fb, &crtc->bounds);
 | |
| 
 | |
|     if (damage)
 | |
|         xf86CrtcDamageShadow(crtc);
 | |
|     else if (crtc->rotatedData && !crtc->rotatedPixmap)
 | |
|         /* Make sure the new rotate buffer has valid transformed contents */
 | |
|         xf86RotateRedisplay(pScreen);
 | |
| 
 | |
|     /* All done */
 | |
|     return TRUE;
 | |
| }
 |