xf86Rotate.c revision b1d344b3
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		EnableLimitedSchedulingLatency();
202	    }
203
204	    xf86CrtcDamageShadow (crtc);
205	}
206    }
207}
208
209static Bool
210xf86RotateRedisplay(ScreenPtr pScreen)
211{
212    ScrnInfoPtr		pScrn = xf86Screens[pScreen->myNum];
213    xf86CrtcConfigPtr   xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
214    DamagePtr		damage = xf86_config->rotation_damage;
215    RegionPtr		region;
216
217    if (!damage)
218	return FALSE;
219    xf86RotatePrepare (pScreen);
220    region = DamageRegion(damage);
221    if (REGION_NOTEMPTY(pScreen, region))
222    {
223	int			c;
224	SourceValidateProcPtr	SourceValidate;
225
226	/*
227	 * SourceValidate is used by the software cursor code
228	 * to pull the cursor off of the screen when reading
229	 * bits from the frame buffer. Bypassing this function
230	 * leaves the software cursor in place
231	 */
232	SourceValidate = pScreen->SourceValidate;
233	pScreen->SourceValidate = NULL;
234
235	for (c = 0; c < xf86_config->num_crtc; c++)
236	{
237	    xf86CrtcPtr	    crtc = xf86_config->crtc[c];
238
239	    if (crtc->transform_in_use && crtc->enabled)
240	    {
241		RegionRec   crtc_damage;
242
243		/* compute portion of damage that overlaps crtc */
244		REGION_INIT(pScreen, &crtc_damage, &crtc->bounds, 1);
245		REGION_INTERSECT (pScreen, &crtc_damage, &crtc_damage, region);
246
247		/* update damaged region */
248		if (REGION_NOTEMPTY(pScreen, &crtc_damage))
249    		    xf86RotateCrtcRedisplay (crtc, &crtc_damage);
250
251		REGION_UNINIT (pScreen, &crtc_damage);
252	    }
253	}
254	pScreen->SourceValidate = SourceValidate;
255	DamageEmpty(damage);
256    }
257    return TRUE;
258}
259
260static void
261xf86RotateBlockHandler(int screenNum, pointer blockData,
262		       pointer pTimeout, pointer pReadmask)
263{
264    ScreenPtr		pScreen = screenInfo.screens[screenNum];
265    ScrnInfoPtr		pScrn = xf86Screens[screenNum];
266    xf86CrtcConfigPtr   xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
267    Bool		rotation_active;
268
269    rotation_active = xf86RotateRedisplay(pScreen);
270    pScreen->BlockHandler = xf86_config->BlockHandler;
271    (*pScreen->BlockHandler) (screenNum, blockData, pTimeout, pReadmask);
272    if (rotation_active) {
273	/* Re-wrap if rotation is still happening */
274	xf86_config->BlockHandler = pScreen->BlockHandler;
275	pScreen->BlockHandler = xf86RotateBlockHandler;
276    } else {
277	xf86_config->BlockHandler = NULL;
278    }
279}
280
281void
282xf86RotateDestroy (xf86CrtcPtr crtc)
283{
284    ScrnInfoPtr		pScrn = crtc->scrn;
285    ScreenPtr		pScreen = pScrn->pScreen;
286    xf86CrtcConfigPtr   xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
287    int			c;
288
289    /* Free memory from rotation */
290    if (crtc->rotatedPixmap || crtc->rotatedData)
291    {
292	crtc->funcs->shadow_destroy (crtc, crtc->rotatedPixmap, crtc->rotatedData);
293	crtc->rotatedPixmap = NULL;
294	crtc->rotatedData = NULL;
295    }
296
297    for (c = 0; c < xf86_config->num_crtc; c++)
298	if (xf86_config->crtc[c]->transform_in_use)
299	    return;
300
301    /*
302     * Clean up damage structures when no crtcs are rotated
303     */
304    if (xf86_config->rotation_damage)
305    {
306	/* Free damage structure */
307	if (xf86_config->rotation_damage_registered)
308	{
309	    DamageUnregister (&(*pScreen->GetScreenPixmap)(pScreen)->drawable,
310			      xf86_config->rotation_damage);
311	    xf86_config->rotation_damage_registered = FALSE;
312	    DisableLimitedSchedulingLatency();
313	}
314	DamageDestroy (xf86_config->rotation_damage);
315	xf86_config->rotation_damage = NULL;
316    }
317}
318
319_X_EXPORT void
320xf86RotateFreeShadow(ScrnInfoPtr pScrn)
321{
322    xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(pScrn);
323    int c;
324
325   for (c = 0; c < config->num_crtc; c++) {
326       xf86CrtcPtr crtc = config->crtc[c];
327
328       if (crtc->rotatedPixmap || crtc->rotatedData) {
329	   crtc->funcs->shadow_destroy(crtc, crtc->rotatedPixmap,
330				crtc->rotatedData);
331	   crtc->rotatedPixmap = NULL;
332	   crtc->rotatedData = NULL;
333       }
334   }
335}
336
337_X_EXPORT void
338xf86RotateCloseScreen (ScreenPtr screen)
339{
340    ScrnInfoPtr		scrn = xf86Screens[screen->myNum];
341    xf86CrtcConfigPtr   xf86_config = XF86_CRTC_CONFIG_PTR(scrn);
342    int			c;
343
344    for (c = 0; c < xf86_config->num_crtc; c++)
345	xf86RotateDestroy (xf86_config->crtc[c]);
346}
347
348static Bool
349xf86CrtcFitsScreen (xf86CrtcPtr crtc, struct pict_f_transform *crtc_to_fb)
350{
351    ScrnInfoPtr		pScrn = crtc->scrn;
352    BoxRec		b;
353
354    /* When called before PreInit, the driver is
355     * presumably doing load detect
356     */
357    if (pScrn->virtualX == 0 || pScrn->virtualY == 0)
358	return TRUE;
359
360    b.x1 = 0;
361    b.y1 = 0;
362    b.x2 = crtc->mode.HDisplay;
363    b.y2 = crtc->mode.VDisplay;
364    if (crtc_to_fb)
365	pixman_f_transform_bounds (crtc_to_fb, &b);
366    else {
367	b.x1 += crtc->x;
368	b.y1 += crtc->y;
369	b.x2 += crtc->x;
370	b.y2 += crtc->y;
371    }
372
373    return (0 <= b.x1 && b.x2 <= pScrn->virtualX &&
374	    0 <= b.y1 && b.y2 <= pScrn->virtualY);
375}
376
377_X_EXPORT Bool
378xf86CrtcRotate (xf86CrtcPtr crtc)
379{
380    ScrnInfoPtr		pScrn = crtc->scrn;
381    xf86CrtcConfigPtr   xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
382    /* if this is called during ScreenInit() we don't have pScrn->pScreen yet */
383    ScreenPtr		pScreen = screenInfo.screens[pScrn->scrnIndex];
384    PictTransform	crtc_to_fb;
385    struct pict_f_transform f_crtc_to_fb, f_fb_to_crtc;
386    xFixed		*new_params = NULL;
387    int			new_nparams = 0;
388    PictFilterPtr	new_filter = NULL;
389    int			new_width = 0;
390    int			new_height = 0;
391    RRTransformPtr	transform = NULL;
392    Bool		damage = FALSE;
393
394    if (crtc->transformPresent)
395	transform = &crtc->transform;
396
397    if (!RRTransformCompute (crtc->x, crtc->y,
398			     crtc->mode.HDisplay, crtc->mode.VDisplay,
399			     crtc->rotation,
400			     transform,
401
402			     &crtc_to_fb,
403			     &f_crtc_to_fb,
404			     &f_fb_to_crtc) &&
405	xf86CrtcFitsScreen (crtc, &f_crtc_to_fb))
406    {
407	/*
408	 * If the untranslated transformation is the identity,
409	 * disable the shadow buffer
410	 */
411	xf86RotateDestroy (crtc);
412	crtc->transform_in_use = FALSE;
413	if (new_params)
414	    xfree (new_params);
415	new_params = NULL;
416	new_nparams = 0;
417	new_filter = NULL;
418	new_width = 0;
419	new_height = 0;
420    }
421    else
422    {
423	/*
424	 * these are the size of the shadow pixmap, which
425	 * matches the mode, not the pre-rotated copy in the
426	 * frame buffer
427	 */
428	int	    width = crtc->mode.HDisplay;
429	int	    height = crtc->mode.VDisplay;
430	void	    *shadowData = crtc->rotatedData;
431	PixmapPtr   shadow = crtc->rotatedPixmap;
432	int	    old_width = shadow ? shadow->drawable.width : 0;
433	int	    old_height = shadow ? shadow->drawable.height : 0;
434
435	/* Allocate memory for rotation */
436	if (old_width != width || old_height != height)
437	{
438	    if (shadow || shadowData)
439	    {
440		crtc->funcs->shadow_destroy (crtc, shadow, shadowData);
441		crtc->rotatedPixmap = NULL;
442		crtc->rotatedData = NULL;
443	    }
444	    shadowData = crtc->funcs->shadow_allocate (crtc, width, height);
445	    if (!shadowData)
446		goto bail1;
447	    crtc->rotatedData = shadowData;
448	    /* shadow will be damaged in xf86RotatePrepare */
449	}
450	else
451	{
452	    /* mark shadowed area as damaged so it will be repainted */
453	    damage = TRUE;
454	}
455
456	if (!xf86_config->rotation_damage)
457	{
458	    /* Create damage structure */
459	    xf86_config->rotation_damage = DamageCreate (NULL, NULL,
460						DamageReportNone,
461						TRUE, pScreen, pScreen);
462	    if (!xf86_config->rotation_damage)
463		goto bail2;
464
465	    /* Wrap block handler */
466	    if (!xf86_config->BlockHandler) {
467		xf86_config->BlockHandler = pScreen->BlockHandler;
468		pScreen->BlockHandler = xf86RotateBlockHandler;
469	    }
470	}
471#ifdef RANDR_12_INTERFACE
472	if (transform)
473	{
474	    if (transform->nparams) {
475		new_params = xalloc (transform->nparams * sizeof (xFixed));
476		if (new_params) {
477		    memcpy (new_params, transform->params,
478			    transform->nparams * sizeof (xFixed));
479		    new_nparams = transform->nparams;
480		    new_filter = transform->filter;
481		}
482	    } else
483		new_filter = transform->filter;
484	    if (new_filter)
485	    {
486		new_width = new_filter->width;
487		new_height = new_filter->height;
488	    }
489	}
490#endif
491
492	if (0)
493	{
494    bail2:
495	    if (shadow || shadowData)
496	    {
497		crtc->funcs->shadow_destroy (crtc, shadow, shadowData);
498		crtc->rotatedPixmap = NULL;
499		crtc->rotatedData = NULL;
500	    }
501    bail1:
502	    if (old_width && old_height)
503		crtc->rotatedPixmap = crtc->funcs->shadow_create (crtc,
504								  NULL,
505								  old_width,
506								  old_height);
507	    return FALSE;
508	}
509	crtc->transform_in_use = TRUE;
510    }
511    crtc->crtc_to_framebuffer = crtc_to_fb;
512    crtc->f_crtc_to_framebuffer = f_crtc_to_fb;
513    crtc->f_framebuffer_to_crtc = f_fb_to_crtc;
514    if (crtc->params)
515	xfree (crtc->params);
516    crtc->params = new_params;
517    crtc->nparams = new_nparams;
518    crtc->filter = new_filter;
519    crtc->filter_width = new_width;
520    crtc->filter_height = new_height;
521    crtc->bounds.x1 = 0;
522    crtc->bounds.x2 = crtc->mode.HDisplay;
523    crtc->bounds.y1 = 0;
524    crtc->bounds.y2 = crtc->mode.VDisplay;
525    pixman_f_transform_bounds (&f_crtc_to_fb, &crtc->bounds);
526
527    if (damage)
528        xf86CrtcDamageShadow (crtc);
529
530    /* All done */
531    return TRUE;
532}
533