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