xserver-multidpi/hw/xfree86/ramdac/xf86HWCurs.c
Michael Thayer c02f6a687c xfree86: Immediately handle failure to set HW cursor, v5
Based on v4 by Alexandre Courbot <acourbot@nvidia.com>

There is currently no reliable way to report failure to set a HW
cursor. Still such failures can happen if e.g. the MODE_CURSOR DRM
ioctl fails (which currently happens at least with modesetting on Tegra
for format incompatibility reasons).

As failures are currently handled by setting the HW cursor size to
(0,0), the fallback to SW cursor will not happen until the next time the
cursor changes and xf86CursorSetCursor() is called again. In the
meantime, the cursor will be invisible to the user.

This patch addresses that by adding _xf86CrtcFuncs::set_cursor_check and
_xf86CursorInfoRec::ShowCursorCheck hook variants that return booleans.
This allows to propagate errors up to xf86CursorSetCursor(), which can
then fall back to using the SW cursor immediately.

v5: Updated the patch to apply to current git HEAD, split up into two
patches (server and modesetting driver) and adjusted the code slightly
to match surrounding code.  I also removed the new exported function
ShowCursorCheck(), as instead just changing ShowCursor() to return Bool
should not affect its current callers.

Reviewed-by: Adam Jackson <ajax@redhat.com>
Signed-off-by: Michael Thayer <michael.thayer@oracle.com>
2017-02-08 12:04:42 -05:00

689 lines
19 KiB
C

#ifdef HAVE_XORG_CONFIG_H
#include <xorg-config.h>
#endif
#include <string.h>
#include "misc.h"
#include "xf86.h"
#include "xf86_OSproc.h"
#include <X11/X.h>
#include "scrnintstr.h"
#include "pixmapstr.h"
#include "windowstr.h"
#include "xf86str.h"
#include "cursorstr.h"
#include "mi.h"
#include "mipointer.h"
#include "randrstr.h"
#include "xf86CursorPriv.h"
#include "servermd.h"
static void
xf86RecolorCursor_locked(xf86CursorScreenPtr ScreenPriv, CursorPtr pCurs);
static CARD32
xf86ReverseBitOrder(CARD32 v)
{
return (((0x01010101 & v) << 7) | ((0x02020202 & v) << 5) |
((0x04040404 & v) << 3) | ((0x08080808 & v) << 1) |
((0x10101010 & v) >> 1) | ((0x20202020 & v) >> 3) |
((0x40404040 & v) >> 5) | ((0x80808080 & v) >> 7));
}
#if BITMAP_SCANLINE_PAD == 64
#if 1
/* Cursors might be only 32 wide. Give'em a chance */
#define SCANLINE CARD32
#define CUR_BITMAP_SCANLINE_PAD 32
#define CUR_LOG2_BITMAP_PAD 5
#define REVERSE_BIT_ORDER(w) xf86ReverseBitOrder(w)
#else
#define SCANLINE CARD64
#define CUR_BITMAP_SCANLINE_PAD BITMAP_SCANLINE_PAD
#define CUR_LOG2_BITMAP_PAD LOG2_BITMAP_PAD
#define REVERSE_BIT_ORDER(w) xf86CARD64ReverseBits(w)
static CARD64 xf86CARD64ReverseBits(CARD64 w);
static CARD64
xf86CARD64ReverseBits(CARD64 w)
{
unsigned char *p = (unsigned char *) &w;
p[0] = byte_reversed[p[0]];
p[1] = byte_reversed[p[1]];
p[2] = byte_reversed[p[2]];
p[3] = byte_reversed[p[3]];
p[4] = byte_reversed[p[4]];
p[5] = byte_reversed[p[5]];
p[6] = byte_reversed[p[6]];
p[7] = byte_reversed[p[7]];
return w;
}
#endif
#else
#define SCANLINE CARD32
#define CUR_BITMAP_SCANLINE_PAD BITMAP_SCANLINE_PAD
#define CUR_LOG2_BITMAP_PAD LOG2_BITMAP_PAD
#define REVERSE_BIT_ORDER(w) xf86ReverseBitOrder(w)
#endif /* BITMAP_SCANLINE_PAD == 64 */
static unsigned char *RealizeCursorInterleave0(xf86CursorInfoPtr, CursorPtr);
static unsigned char *RealizeCursorInterleave1(xf86CursorInfoPtr, CursorPtr);
static unsigned char *RealizeCursorInterleave8(xf86CursorInfoPtr, CursorPtr);
static unsigned char *RealizeCursorInterleave16(xf86CursorInfoPtr, CursorPtr);
static unsigned char *RealizeCursorInterleave32(xf86CursorInfoPtr, CursorPtr);
static unsigned char *RealizeCursorInterleave64(xf86CursorInfoPtr, CursorPtr);
Bool
xf86InitHardwareCursor(ScreenPtr pScreen, xf86CursorInfoPtr infoPtr)
{
if ((infoPtr->MaxWidth <= 0) || (infoPtr->MaxHeight <= 0))
return FALSE;
/* These are required for now */
if (!infoPtr->SetCursorPosition ||
!xf86DriverHasLoadCursorImage(infoPtr) ||
!infoPtr->HideCursor ||
!xf86DriverHasShowCursor(infoPtr) ||
!infoPtr->SetCursorColors)
return FALSE;
if (infoPtr->RealizeCursor) {
/* Don't overwrite a driver provided Realize Cursor function */
}
else if (HARDWARE_CURSOR_SOURCE_MASK_INTERLEAVE_1 & infoPtr->Flags) {
infoPtr->RealizeCursor = RealizeCursorInterleave1;
}
else if (HARDWARE_CURSOR_SOURCE_MASK_INTERLEAVE_8 & infoPtr->Flags) {
infoPtr->RealizeCursor = RealizeCursorInterleave8;
}
else if (HARDWARE_CURSOR_SOURCE_MASK_INTERLEAVE_16 & infoPtr->Flags) {
infoPtr->RealizeCursor = RealizeCursorInterleave16;
}
else if (HARDWARE_CURSOR_SOURCE_MASK_INTERLEAVE_32 & infoPtr->Flags) {
infoPtr->RealizeCursor = RealizeCursorInterleave32;
}
else if (HARDWARE_CURSOR_SOURCE_MASK_INTERLEAVE_64 & infoPtr->Flags) {
infoPtr->RealizeCursor = RealizeCursorInterleave64;
}
else { /* not interleaved */
infoPtr->RealizeCursor = RealizeCursorInterleave0;
}
infoPtr->pScrn = xf86ScreenToScrn(pScreen);
return TRUE;
}
static Bool
xf86ScreenCheckHWCursor(ScreenPtr pScreen, CursorPtr cursor, xf86CursorInfoPtr infoPtr)
{
return
(cursor->bits->argb && infoPtr->UseHWCursorARGB &&
infoPtr->UseHWCursorARGB(pScreen, cursor)) ||
(cursor->bits->argb == 0 &&
cursor->bits->height <= infoPtr->MaxHeight &&
cursor->bits->width <= infoPtr->MaxWidth &&
(!infoPtr->UseHWCursor || infoPtr->UseHWCursor(pScreen, cursor)));
}
Bool
xf86CheckHWCursor(ScreenPtr pScreen, CursorPtr cursor, xf86CursorInfoPtr infoPtr)
{
ScreenPtr pSlave;
Bool use_hw_cursor = TRUE;
input_lock();
if (!xf86ScreenCheckHWCursor(pScreen, cursor, infoPtr)) {
use_hw_cursor = FALSE;
goto unlock;
}
/* ask each driver consuming a pixmap if it can support HW cursor */
xorg_list_for_each_entry(pSlave, &pScreen->slave_list, slave_head) {
xf86CursorScreenPtr sPriv;
if (!RRHasScanoutPixmap(pSlave))
continue;
sPriv = dixLookupPrivate(&pSlave->devPrivates, xf86CursorScreenKey);
if (!sPriv) { /* NULL if Option "SWCursor", possibly other conditions */
use_hw_cursor = FALSE;
break;
}
/* FALSE if HWCursor not supported by slave */
if (!xf86ScreenCheckHWCursor(pSlave, cursor, sPriv->CursorInfoPtr)) {
use_hw_cursor = FALSE;
break;
}
}
unlock:
input_unlock();
return use_hw_cursor;
}
static Bool
xf86ScreenSetCursor(ScreenPtr pScreen, CursorPtr pCurs, int x, int y)
{
xf86CursorScreenPtr ScreenPriv =
(xf86CursorScreenPtr) dixLookupPrivate(&pScreen->devPrivates,
xf86CursorScreenKey);
xf86CursorInfoPtr infoPtr = ScreenPriv->CursorInfoPtr;
unsigned char *bits;
if (pCurs == NullCursor) {
(*infoPtr->HideCursor) (infoPtr->pScrn);
return TRUE;
}
/*
* Hot plugged GPU's do not have a CursorScreenKey, force sw cursor.
* This check can be removed once dix/privates.c gets relocation code for
* PRIVATE_CURSOR. Also see the related comment in AddGPUScreen().
*/
if (!_dixGetScreenPrivateKey(CursorScreenKey, pScreen))
return FALSE;
bits =
dixLookupScreenPrivate(&pCurs->devPrivates, CursorScreenKey, pScreen);
x -= infoPtr->pScrn->frameX0;
y -= infoPtr->pScrn->frameY0;
if (!pCurs->bits->argb || !xf86DriverHasLoadCursorARGB(infoPtr))
if (!bits) {
bits = (*infoPtr->RealizeCursor) (infoPtr, pCurs);
dixSetScreenPrivate(&pCurs->devPrivates, CursorScreenKey, pScreen,
bits);
}
if (!(infoPtr->Flags & HARDWARE_CURSOR_UPDATE_UNHIDDEN))
(*infoPtr->HideCursor) (infoPtr->pScrn);
if (pCurs->bits->argb && xf86DriverHasLoadCursorARGB(infoPtr)) {
if (!xf86DriverLoadCursorARGB (infoPtr, pCurs))
return FALSE;
} else
if (bits)
if (!xf86DriverLoadCursorImage (infoPtr, bits))
return FALSE;
xf86RecolorCursor_locked (ScreenPriv, pCurs);
(*infoPtr->SetCursorPosition) (infoPtr->pScrn, x, y);
return xf86DriverShowCursor(infoPtr);
}
Bool
xf86SetCursor(ScreenPtr pScreen, CursorPtr pCurs, int x, int y)
{
xf86CursorScreenPtr ScreenPriv =
(xf86CursorScreenPtr) dixLookupPrivate(&pScreen->devPrivates,
xf86CursorScreenKey);
ScreenPtr pSlave;
Bool ret = FALSE;
input_lock();
x -= ScreenPriv->HotX;
y -= ScreenPriv->HotY;
if (!xf86ScreenSetCursor(pScreen, pCurs, x, y))
goto out;
/* ask each slave driver to set the cursor. */
xorg_list_for_each_entry(pSlave, &pScreen->slave_list, slave_head) {
if (!RRHasScanoutPixmap(pSlave))
continue;
if (!xf86ScreenSetCursor(pSlave, pCurs, x, y)) {
/*
* hide the master (and successfully set slave) cursors,
* otherwise both the hw and sw cursor will show.
*/
xf86SetCursor(pScreen, NullCursor, x, y);
goto out;
}
}
ret = TRUE;
out:
input_unlock();
return ret;
}
void
xf86SetTransparentCursor(ScreenPtr pScreen)
{
xf86CursorScreenPtr ScreenPriv =
(xf86CursorScreenPtr) dixLookupPrivate(&pScreen->devPrivates,
xf86CursorScreenKey);
xf86CursorInfoPtr infoPtr = ScreenPriv->CursorInfoPtr;
input_lock();
if (!ScreenPriv->transparentData)
ScreenPriv->transparentData =
(*infoPtr->RealizeCursor) (infoPtr, NullCursor);
if (!(infoPtr->Flags & HARDWARE_CURSOR_UPDATE_UNHIDDEN))
(*infoPtr->HideCursor) (infoPtr->pScrn);
if (ScreenPriv->transparentData)
xf86DriverLoadCursorImage (infoPtr,
ScreenPriv->transparentData);
xf86DriverShowCursor(infoPtr);
input_unlock();
}
static void
xf86ScreenMoveCursor(ScreenPtr pScreen, int x, int y)
{
xf86CursorScreenPtr ScreenPriv =
(xf86CursorScreenPtr) dixLookupPrivate(&pScreen->devPrivates,
xf86CursorScreenKey);
xf86CursorInfoPtr infoPtr = ScreenPriv->CursorInfoPtr;
x -= infoPtr->pScrn->frameX0;
y -= infoPtr->pScrn->frameY0;
(*infoPtr->SetCursorPosition) (infoPtr->pScrn, x, y);
}
void
xf86MoveCursor(ScreenPtr pScreen, int x, int y)
{
xf86CursorScreenPtr ScreenPriv =
(xf86CursorScreenPtr) dixLookupPrivate(&pScreen->devPrivates,
xf86CursorScreenKey);
ScreenPtr pSlave;
input_lock();
x -= ScreenPriv->HotX;
y -= ScreenPriv->HotY;
xf86ScreenMoveCursor(pScreen, x, y);
/* ask each slave driver to move the cursor */
xorg_list_for_each_entry(pSlave, &pScreen->slave_list, slave_head) {
if (!RRHasScanoutPixmap(pSlave))
continue;
xf86ScreenMoveCursor(pSlave, x, y);
}
input_unlock();
}
static void
xf86RecolorCursor_locked(xf86CursorScreenPtr ScreenPriv, CursorPtr pCurs)
{
xf86CursorInfoPtr infoPtr = ScreenPriv->CursorInfoPtr;
/* recoloring isn't applicable to ARGB cursors and drivers
shouldn't have to ignore SetCursorColors requests */
if (pCurs->bits->argb)
return;
if (ScreenPriv->PalettedCursor) {
xColorItem sourceColor, maskColor;
ColormapPtr pmap = ScreenPriv->pInstalledMap;
if (!pmap)
return;
sourceColor.red = pCurs->foreRed;
sourceColor.green = pCurs->foreGreen;
sourceColor.blue = pCurs->foreBlue;
FakeAllocColor(pmap, &sourceColor);
maskColor.red = pCurs->backRed;
maskColor.green = pCurs->backGreen;
maskColor.blue = pCurs->backBlue;
FakeAllocColor(pmap, &maskColor);
FakeFreeColor(pmap, sourceColor.pixel);
FakeFreeColor(pmap, maskColor.pixel);
(*infoPtr->SetCursorColors) (infoPtr->pScrn,
maskColor.pixel, sourceColor.pixel);
}
else { /* Pass colors in 8-8-8 RGB format */
(*infoPtr->SetCursorColors) (infoPtr->pScrn,
(pCurs->backBlue >> 8) |
((pCurs->backGreen >> 8) << 8) |
((pCurs->backRed >> 8) << 16),
(pCurs->foreBlue >> 8) |
((pCurs->foreGreen >> 8) << 8) |
((pCurs->foreRed >> 8) << 16)
);
}
}
void
xf86RecolorCursor(ScreenPtr pScreen, CursorPtr pCurs, Bool displayed)
{
xf86CursorScreenPtr ScreenPriv =
(xf86CursorScreenPtr) dixLookupPrivate(&pScreen->devPrivates,
xf86CursorScreenKey);
input_lock();
xf86RecolorCursor_locked (ScreenPriv, pCurs);
input_unlock();
}
/* These functions assume that MaxWidth is a multiple of 32 */
static unsigned char *
RealizeCursorInterleave0(xf86CursorInfoPtr infoPtr, CursorPtr pCurs)
{
SCANLINE *SrcS, *SrcM, *DstS, *DstM;
SCANLINE *pSrc, *pMsk;
unsigned char *mem;
int size = (infoPtr->MaxWidth * infoPtr->MaxHeight) >> 2;
int SrcPitch, DstPitch, Pitch, y, x;
/* how many words are in the source or mask */
int words = size / (CUR_BITMAP_SCANLINE_PAD / 4);
if (!(mem = calloc(1, size)))
return NULL;
if (pCurs == NullCursor) {
if (infoPtr->Flags & HARDWARE_CURSOR_INVERT_MASK) {
DstM = (SCANLINE *) mem;
if (!(infoPtr->Flags & HARDWARE_CURSOR_SWAP_SOURCE_AND_MASK))
DstM += words;
memset(DstM, -1, words * sizeof(SCANLINE));
}
return mem;
}
/* SrcPitch == the number of scanlines wide the cursor image is */
SrcPitch = (pCurs->bits->width + (BITMAP_SCANLINE_PAD - 1)) >>
CUR_LOG2_BITMAP_PAD;
/* DstPitch is the width of the hw cursor in scanlines */
DstPitch = infoPtr->MaxWidth >> CUR_LOG2_BITMAP_PAD;
Pitch = SrcPitch < DstPitch ? SrcPitch : DstPitch;
SrcS = (SCANLINE *) pCurs->bits->source;
SrcM = (SCANLINE *) pCurs->bits->mask;
DstS = (SCANLINE *) mem;
DstM = DstS + words;
if (infoPtr->Flags & HARDWARE_CURSOR_SWAP_SOURCE_AND_MASK) {
SCANLINE *tmp;
tmp = DstS;
DstS = DstM;
DstM = tmp;
}
if (infoPtr->Flags & HARDWARE_CURSOR_AND_SOURCE_WITH_MASK) {
for (y = pCurs->bits->height, pSrc = DstS, pMsk = DstM;
y--;
pSrc += DstPitch, pMsk += DstPitch, SrcS += SrcPitch, SrcM +=
SrcPitch) {
for (x = 0; x < Pitch; x++) {
pSrc[x] = SrcS[x] & SrcM[x];
pMsk[x] = SrcM[x];
}
}
}
else {
for (y = pCurs->bits->height, pSrc = DstS, pMsk = DstM;
y--;
pSrc += DstPitch, pMsk += DstPitch, SrcS += SrcPitch, SrcM +=
SrcPitch) {
for (x = 0; x < Pitch; x++) {
pSrc[x] = SrcS[x];
pMsk[x] = SrcM[x];
}
}
}
if (infoPtr->Flags & HARDWARE_CURSOR_NIBBLE_SWAPPED) {
int count = size;
unsigned char *pntr1 = (unsigned char *) DstS;
unsigned char *pntr2 = (unsigned char *) DstM;
unsigned char a, b;
while (count) {
a = *pntr1;
b = *pntr2;
*pntr1 = ((a & 0xF0) >> 4) | ((a & 0x0F) << 4);
*pntr2 = ((b & 0xF0) >> 4) | ((b & 0x0F) << 4);
pntr1++;
pntr2++;
count -= 2;
}
}
/*
* Must be _after_ HARDWARE_CURSOR_AND_SOURCE_WITH_MASK to avoid wiping
* out entire source mask.
*/
if (infoPtr->Flags & HARDWARE_CURSOR_INVERT_MASK) {
int count = words;
SCANLINE *pntr = DstM;
while (count--) {
*pntr = ~(*pntr);
pntr++;
}
}
if (infoPtr->Flags & HARDWARE_CURSOR_BIT_ORDER_MSBFIRST) {
for (y = pCurs->bits->height, pSrc = DstS, pMsk = DstM;
y--; pSrc += DstPitch, pMsk += DstPitch) {
for (x = 0; x < Pitch; x++) {
pSrc[x] = REVERSE_BIT_ORDER(pSrc[x]);
pMsk[x] = REVERSE_BIT_ORDER(pMsk[x]);
}
}
}
return mem;
}
static unsigned char *
RealizeCursorInterleave1(xf86CursorInfoPtr infoPtr, CursorPtr pCurs)
{
unsigned char *DstS, *DstM;
unsigned char *pntr;
unsigned char *mem, *mem2;
int count;
int size = (infoPtr->MaxWidth * infoPtr->MaxHeight) >> 2;
/* Realize the cursor without interleaving */
if (!(mem2 = RealizeCursorInterleave0(infoPtr, pCurs)))
return NULL;
if (!(mem = calloc(1, size))) {
free(mem2);
return NULL;
}
/* 1 bit interleave */
DstS = mem2;
DstM = DstS + (size >> 1);
pntr = mem;
count = size;
while (count) {
*pntr++ = ((*DstS & 0x01)) | ((*DstM & 0x01) << 1) |
((*DstS & 0x02) << 1) | ((*DstM & 0x02) << 2) |
((*DstS & 0x04) << 2) | ((*DstM & 0x04) << 3) |
((*DstS & 0x08) << 3) | ((*DstM & 0x08) << 4);
*pntr++ = ((*DstS & 0x10) >> 4) | ((*DstM & 0x10) >> 3) |
((*DstS & 0x20) >> 3) | ((*DstM & 0x20) >> 2) |
((*DstS & 0x40) >> 2) | ((*DstM & 0x40) >> 1) |
((*DstS & 0x80) >> 1) | ((*DstM & 0x80));
DstS++;
DstM++;
count -= 2;
}
/* Free the uninterleaved cursor */
free(mem2);
return mem;
}
static unsigned char *
RealizeCursorInterleave8(xf86CursorInfoPtr infoPtr, CursorPtr pCurs)
{
unsigned char *DstS, *DstM;
unsigned char *pntr;
unsigned char *mem, *mem2;
int count;
int size = (infoPtr->MaxWidth * infoPtr->MaxHeight) >> 2;
/* Realize the cursor without interleaving */
if (!(mem2 = RealizeCursorInterleave0(infoPtr, pCurs)))
return NULL;
if (!(mem = calloc(1, size))) {
free(mem2);
return NULL;
}
/* 8 bit interleave */
DstS = mem2;
DstM = DstS + (size >> 1);
pntr = mem;
count = size;
while (count) {
*pntr++ = *DstS++;
*pntr++ = *DstM++;
count -= 2;
}
/* Free the uninterleaved cursor */
free(mem2);
return mem;
}
static unsigned char *
RealizeCursorInterleave16(xf86CursorInfoPtr infoPtr, CursorPtr pCurs)
{
unsigned short *DstS, *DstM;
unsigned short *pntr;
unsigned char *mem, *mem2;
int count;
int size = (infoPtr->MaxWidth * infoPtr->MaxHeight) >> 2;
/* Realize the cursor without interleaving */
if (!(mem2 = RealizeCursorInterleave0(infoPtr, pCurs)))
return NULL;
if (!(mem = calloc(1, size))) {
free(mem2);
return NULL;
}
/* 16 bit interleave */
DstS = (void *) mem2;
DstM = DstS + (size >> 2);
pntr = (void *) mem;
count = (size >> 1);
while (count) {
*pntr++ = *DstS++;
*pntr++ = *DstM++;
count -= 2;
}
/* Free the uninterleaved cursor */
free(mem2);
return mem;
}
static unsigned char *
RealizeCursorInterleave32(xf86CursorInfoPtr infoPtr, CursorPtr pCurs)
{
CARD32 *DstS, *DstM;
CARD32 *pntr;
unsigned char *mem, *mem2;
int count;
int size = (infoPtr->MaxWidth * infoPtr->MaxHeight) >> 2;
/* Realize the cursor without interleaving */
if (!(mem2 = RealizeCursorInterleave0(infoPtr, pCurs)))
return NULL;
if (!(mem = calloc(1, size))) {
free(mem2);
return NULL;
}
/* 32 bit interleave */
DstS = (void *) mem2;
DstM = DstS + (size >> 3);
pntr = (void *) mem;
count = (size >> 2);
while (count) {
*pntr++ = *DstS++;
*pntr++ = *DstM++;
count -= 2;
}
/* Free the uninterleaved cursor */
free(mem2);
return mem;
}
static unsigned char *
RealizeCursorInterleave64(xf86CursorInfoPtr infoPtr, CursorPtr pCurs)
{
CARD32 *DstS, *DstM;
CARD32 *pntr;
unsigned char *mem, *mem2;
int count;
int size = (infoPtr->MaxWidth * infoPtr->MaxHeight) >> 2;
/* Realize the cursor without interleaving */
if (!(mem2 = RealizeCursorInterleave0(infoPtr, pCurs)))
return NULL;
if (!(mem = calloc(1, size))) {
free(mem2);
return NULL;
}
/* 64 bit interleave */
DstS = (void *) mem2;
DstM = DstS + (size >> 3);
pntr = (void *) mem;
count = (size >> 2);
while (count) {
*pntr++ = *DstS++;
*pntr++ = *DstS++;
*pntr++ = *DstM++;
*pntr++ = *DstM++;
count -= 4;
}
/* Free the uninterleaved cursor */
free(mem2);
return mem;
}