xserver-multidpi/hw/xfree86/modes/xf86Rotate.c
Keith Packard e4507825bf Enable startup-time rotation; change rotation pixmap creation API.
Add monitor "Rotate" option taking one of "normal", "left", "inverted" or
"right". However, because initial mode selection is made before the screen
is completely initialized, we cannot create the shadow pixmap object at this
point. Pend the shadow pixmap creation until the block handler.

Note that this code is not completely functional yet.
2007-02-17 15:16:20 -08:00

405 lines
11 KiB
C

/*
* Copyright © 2006 Keith Packard
*
* 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>
#else
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#endif
#include <stddef.h>
#include <string.h>
#include <stdio.h>
#include "xf86.h"
#include "xf86DDC.h"
#include "fb.h"
#include "windowstr.h"
#include "xf86Crtc.h"
#include "xf86Modes.h"
#include "xf86RandR12.h"
#include "X11/extensions/render.h"
#define DPMS_SERVER
#include "X11/extensions/dpms.h"
#include "X11/Xatom.h"
/* borrowed from composite extension, move to Render and publish? */
static VisualPtr
compGetWindowVisual (WindowPtr pWin)
{
ScreenPtr pScreen = pWin->drawable.pScreen;
VisualID vid = wVisual (pWin);
int i;
for (i = 0; i < pScreen->numVisuals; i++)
if (pScreen->visuals[i].vid == vid)
return &pScreen->visuals[i];
return 0;
}
static PictFormatPtr
compWindowFormat (WindowPtr pWin)
{
ScreenPtr pScreen = pWin->drawable.pScreen;
return PictureMatchVisual (pScreen, pWin->drawable.depth,
compGetWindowVisual (pWin));
}
static void
xf86RotateBox (BoxPtr dst, BoxPtr src, Rotation rotation,
int dest_width, int dest_height)
{
switch (rotation & 0xf) {
default:
case RR_Rotate_0:
*dst = *src;
break;
case RR_Rotate_90:
dst->x1 = src->y1;
dst->y1 = dest_height - src->x2;
dst->x2 = src->y2;
dst->y2 = dest_height - src->x1;
break;
case RR_Rotate_180:
dst->x1 = dest_width - src->x2;
dst->y1 = dest_height - src->y2;
dst->x2 = dest_width - src->x1;
dst->y2 = dest_height - src->y1;
break;
case RR_Rotate_270:
dst->x1 = dest_width - src->y2;
dst->y1 = src->x1;
dst->y2 = src->x2;
dst->x2 = dest_width - src->y1;
break;
}
if (rotation & RR_Reflect_X) {
int x1 = dst->x1;
dst->x1 = dest_width - dst->x2;
dst->x2 = dest_width - x1;
}
if (rotation & RR_Reflect_Y) {
int y1 = dst->y1;
dst->y1 = dest_height - dst->y2;
dst->y2 = dest_height - y1;
}
}
static void
xf86RotateCrtcRedisplay (xf86CrtcPtr crtc, RegionPtr region)
{
ScrnInfoPtr scrn = crtc->scrn;
ScreenPtr screen = scrn->pScreen;
WindowPtr root = WindowTable[screen->myNum];
PixmapPtr dst_pixmap = crtc->rotatedPixmap;
PictFormatPtr format = compWindowFormat (WindowTable[screen->myNum]);
int error;
PicturePtr src, dst;
PictTransform transform;
int n = REGION_NUM_RECTS(region);
BoxPtr b = REGION_RECTS(region);
XID include_inferiors = IncludeInferiors;
src = CreatePicture (None,
&root->drawable,
format,
CPSubwindowMode,
&include_inferiors,
serverClient,
&error);
if (!src) {
ErrorF("couldn't create src pict\n");
return;
}
dst = CreatePicture (None,
&dst_pixmap->drawable,
format,
0L,
NULL,
serverClient,
&error);
if (!dst) {
ErrorF("couldn't create src pict\n");
return;
}
memset (&transform, '\0', sizeof (transform));
transform.matrix[2][2] = IntToxFixed(1);
transform.matrix[0][2] = IntToxFixed(crtc->x);
transform.matrix[1][2] = IntToxFixed(crtc->y);
switch (crtc->rotation & 0xf) {
default:
case RR_Rotate_0:
transform.matrix[0][0] = IntToxFixed(1);
transform.matrix[1][1] = IntToxFixed(1);
break;
case RR_Rotate_90:
transform.matrix[0][1] = IntToxFixed(-1);
transform.matrix[1][0] = IntToxFixed(1);
transform.matrix[0][2] += IntToxFixed(crtc->mode.VDisplay);
break;
case RR_Rotate_180:
transform.matrix[0][0] = IntToxFixed(-1);
transform.matrix[1][1] = IntToxFixed(-1);
transform.matrix[0][2] += IntToxFixed(crtc->mode.HDisplay);
transform.matrix[1][2] += IntToxFixed(crtc->mode.VDisplay);
break;
case RR_Rotate_270:
transform.matrix[0][1] = IntToxFixed(1);
transform.matrix[1][0] = IntToxFixed(-1);
transform.matrix[1][2] += IntToxFixed(crtc->mode.HDisplay);
break;
}
/* handle reflection */
if (crtc->rotation & RR_Reflect_X)
{
/* XXX figure this out */
}
if (crtc->rotation & RR_Reflect_Y)
{
/* XXX figure this out too */
}
error = SetPictureTransform (src, &transform);
if (error) {
ErrorF("Couldn't set transform\n");
return;
}
while (n--)
{
BoxRec dst_box;
xf86RotateBox (&dst_box, b, crtc->rotation,
crtc->mode.HDisplay, crtc->mode.VDisplay);
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
xf86RotatePrepare (ScreenPtr pScreen)
{
ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
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)
{
BoxRec damage_box;
RegionRec damage_region;
crtc->rotatedPixmap = crtc->funcs->shadow_create (crtc,
crtc->rotatedData,
crtc->mode.HDisplay,
crtc->mode.VDisplay);
/* Hook damage to screen pixmap */
DamageRegister (&(*pScreen->GetScreenPixmap)(pScreen)->drawable,
xf86_config->rotationDamage);
damage_box.x1 = 0;
damage_box.y1 = 0;
damage_box.x2 = xf86ModeWidth (&crtc->mode, crtc->rotation);
damage_box.y2 = xf86ModeHeight (&crtc->mode, crtc->rotation);
REGION_INIT (pScreen, &damage_region, &damage_box, 1);
DamageDamageRegion (&(*pScreen->GetScreenPixmap)(pScreen)->drawable,
&damage_region);
REGION_UNINIT (pScreen, &damage_region);
}
}
}
static void
xf86RotateRedisplay(ScreenPtr pScreen)
{
ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
DamagePtr damage = xf86_config->rotationDamage;
RegionPtr region;
if (!damage)
return;
xf86RotatePrepare (pScreen);
region = DamageRegion(damage);
if (REGION_NOTEMPTY(pScreen, region))
{
int c;
for (c = 0; c < xf86_config->num_crtc; c++)
{
xf86CrtcPtr crtc = xf86_config->crtc[c];
if (crtc->rotation != RR_Rotate_0)
{
BoxRec box;
RegionRec crtc_damage;
/* compute portion of damage that overlaps crtc */
box.x1 = crtc->x;
box.x2 = crtc->x + xf86ModeWidth (&crtc->mode, crtc->rotation);
box.y1 = crtc->y;
box.y2 = crtc->y + xf86ModeHeight (&crtc->mode, crtc->rotation);
REGION_INIT(pScreen, &crtc_damage, &box, 1);
REGION_INTERSECT (pScreen, &crtc_damage, &crtc_damage, region);
/* update damaged region */
if (REGION_NOTEMPTY(pScreen, &crtc_damage))
xf86RotateCrtcRedisplay (crtc, &crtc_damage);
REGION_UNINIT (pScreen, &crtc_damage);
}
}
DamageEmpty(damage);
}
}
static void
xf86RotateBlockHandler(pointer data, OSTimePtr pTimeout, pointer pRead)
{
ScreenPtr pScreen = (ScreenPtr) data;
xf86RotateRedisplay(pScreen);
}
static void
xf86RotateWakeupHandler(pointer data, int i, pointer LastSelectMask)
{
}
Bool
xf86CrtcRotate (xf86CrtcPtr crtc, DisplayModePtr mode, Rotation rotation)
{
ScrnInfoPtr pScrn = crtc->scrn;
xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
ScreenPtr pScreen = pScrn->pScreen;
if (rotation == RR_Rotate_0)
{
/* Free memory from rotation */
if (crtc->rotatedPixmap || crtc->rotatedData)
{
crtc->funcs->shadow_destroy (crtc, crtc->rotatedPixmap, crtc->rotatedData);
crtc->rotatedPixmap = NULL;
crtc->rotatedData = NULL;
}
if (xf86_config->rotationDamage)
{
/* Free damage structure */
DamageUnregister (&(*pScreen->GetScreenPixmap)(pScreen)->drawable,
xf86_config->rotationDamage);
DamageDestroy (xf86_config->rotationDamage);
xf86_config->rotationDamage = NULL;
/* Free block/wakeup handler */
RemoveBlockAndWakeupHandlers (xf86RotateBlockHandler,
xf86RotateWakeupHandler,
(pointer) pScreen);
}
}
else
{
/*
* these are the size of the shadow pixmap, which
* matches the mode, not the pre-rotated copy in the
* frame buffer
*/
int width = mode->HDisplay;
int height = 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;
}
if (!xf86_config->rotationDamage)
{
/* Create damage structure */
xf86_config->rotationDamage = DamageCreate (NULL, NULL,
DamageReportNone,
TRUE, pScreen, pScreen);
if (!xf86_config->rotationDamage)
goto bail2;
/* Assign block/wakeup handler */
if (!RegisterBlockAndWakeupHandlers (xf86RotateBlockHandler,
xf86RotateWakeupHandler,
(pointer) pScreen))
{
goto bail3;
}
}
if (0)
{
bail3:
DamageDestroy (xf86_config->rotationDamage);
xf86_config->rotationDamage = NULL;
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;
}
}
/* All done */
return TRUE;
}