xserver-multidpi/hw/kdrive/ephyr/ephyr.c
Eric Anholt d695579848 If fakexa is enabled, create a larger buffer in the Ximage, but keep the
same width/height for front-buffer drawing. The fakexa code then uses
    this extra space for offscreen pixmaps. Note that this tones down the
    absurdity of fakexa's offscreen pixmap alignment requirements (odd
    alignment is too weird, so stick with "24", which is still strange but
    exists out there). It also fixes a couple of bugs in the fakexa
    implementation revealed by using offscreen pixmaps.
2006-03-10 21:36:24 +00:00

884 lines
18 KiB
C

/*
* Xephyr - A kdrive X server thats runs in a host X window.
* Authored by Matthew Allum <mallum@openedhand.com>
*
* Copyright © 2004 Nokia
*
* 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 Nokia not be used in
* advertising or publicity pertaining to distribution of the software without
* specific, written prior permission. Nokia makes no
* representations about the suitability of this software for any purpose. It
* is provided "as is" without express or implied warranty.
*
* NOKIA DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
* EVENT SHALL NOKIA 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.
*/
/* TODO:
*
* o Support multiple screens, shouldn't be hard just alot of rejigging.
*/
#ifdef HAVE_CONFIG_H
#include <kdrive-config.h>
#endif
#include "ephyr.h"
#include "inputstr.h"
extern int KdTsPhyScreen;
extern DeviceIntPtr pKdKeyboard;
static int mouseState = 0;
Bool
ephyrInitialize (KdCardInfo *card, EphyrPriv *priv)
{
OsSignal(SIGUSR1, hostx_handle_signal);
priv->base = 0;
priv->bytes_per_line = 0;
return TRUE;
}
Bool
ephyrCardInit (KdCardInfo *card)
{
EphyrPriv *priv;
priv = (EphyrPriv *) xalloc (sizeof (EphyrPriv));
if (!priv)
return FALSE;
if (!ephyrInitialize (card, priv))
{
xfree (priv);
return FALSE;
}
card->driver = priv;
return TRUE;
}
Bool
ephyrScreenInitialize (KdScreenInfo *screen, EphyrScrPriv *scrpriv)
{
int width = 640, height = 480;
if (hostx_want_screen_size(&width, &height)
|| !screen->width || !screen->height)
{
screen->width = width;
screen->height = height;
}
if (screen->fb[0].depth && screen->fb[0].depth != hostx_get_depth())
{
if (screen->fb[0].depth < hostx_get_depth()
&& (screen->fb[0].depth == 24 || screen->fb[0].depth == 16
|| screen->fb[0].depth == 8))
{
hostx_set_server_depth(screen->fb[0].depth);
}
else
ErrorF("\nXephyr: requested screen depth not supported, setting to match hosts.\n");
}
screen->fb[0].depth = hostx_get_server_depth();
screen->rate = 72;
if (screen->fb[0].depth <= 8)
{
screen->fb[0].visuals = ((1 << StaticGray) |
(1 << GrayScale) |
(1 << StaticColor) |
(1 << PseudoColor) |
(1 << TrueColor) |
(1 << DirectColor));
screen->fb[0].redMask = 0x00;
screen->fb[0].greenMask = 0x00;
screen->fb[0].blueMask = 0x00;
screen->fb[0].depth = 8;
screen->fb[0].bitsPerPixel = 8;
}
else
{
screen->fb[0].visuals = (1 << TrueColor);
if (screen->fb[0].depth <= 15)
{
screen->fb[0].depth = 15;
screen->fb[0].bitsPerPixel = 16;
hostx_get_visual_masks (&screen->fb[0].redMask,
&screen->fb[0].greenMask,
&screen->fb[0].blueMask);
}
else if (screen->fb[0].depth <= 16)
{
screen->fb[0].depth = 16;
screen->fb[0].bitsPerPixel = 16;
hostx_get_visual_masks (&screen->fb[0].redMask,
&screen->fb[0].greenMask,
&screen->fb[0].blueMask);
}
else
{
screen->fb[0].depth = 24;
screen->fb[0].bitsPerPixel = 32;
hostx_get_visual_masks (&screen->fb[0].redMask,
&screen->fb[0].greenMask,
&screen->fb[0].blueMask);
}
}
scrpriv->randr = screen->randr;
return ephyrMapFramebuffer (screen);
}
Bool
ephyrScreenInit (KdScreenInfo *screen)
{
EphyrScrPriv *scrpriv;
scrpriv = xalloc (sizeof (EphyrScrPriv));
if (!scrpriv)
return FALSE;
memset (scrpriv, 0, sizeof (EphyrScrPriv));
screen->driver = scrpriv;
if (!ephyrScreenInitialize (screen, scrpriv))
{
screen->driver = 0;
xfree (scrpriv);
return FALSE;
}
return TRUE;
}
void*
ephyrWindowLinear (ScreenPtr pScreen,
CARD32 row,
CARD32 offset,
int mode,
CARD32 *size,
void *closure)
{
KdScreenPriv(pScreen);
EphyrPriv *priv = pScreenPriv->card->driver;
if (!pScreenPriv->enabled)
{
return 0;
}
*size = priv->bytes_per_line;
return priv->base + row * priv->bytes_per_line + offset;
}
Bool
ephyrMapFramebuffer (KdScreenInfo *screen)
{
EphyrScrPriv *scrpriv = screen->driver;
EphyrPriv *priv = screen->card->driver;
KdMouseMatrix m;
int buffer_height;
EPHYR_DBG(" screen->width: %d, screen->height: %d",
screen->width, screen->height);
KdComputeMouseMatrix (&m, scrpriv->randr, screen->width, screen->height);
KdSetMouseMatrix (&m);
priv->bytes_per_line = ((screen->width * screen->fb[0].bitsPerPixel + 31) >> 5) << 2;
/* point the framebuffer to the data in an XImage */
/* If fakexa is enabled, allocate a larger buffer so that fakexa has space to
* put offscreen pixmaps.
*/
if (ephyrFuncs.initAccel == NULL)
buffer_height = screen->height;
else
buffer_height = 3 * screen->height;
priv->base = hostx_screen_init (screen->width, screen->height, buffer_height);
screen->memory_base = (CARD8 *) (priv->base);
screen->memory_size = priv->bytes_per_line * buffer_height;
screen->off_screen_base = priv->bytes_per_line * screen->height;
if ((scrpriv->randr & RR_Rotate_0) && !(scrpriv->randr & RR_Reflect_All))
{
scrpriv->shadow = FALSE;
screen->fb[0].byteStride = priv->bytes_per_line;
screen->fb[0].pixelStride = screen->width;
screen->fb[0].frameBuffer = (CARD8 *) (priv->base);
}
else
{
/* Rotated/Reflected so we need to use shadow fb */
scrpriv->shadow = TRUE;
EPHYR_DBG("allocing shadow");
KdShadowFbAlloc (screen, 0,
scrpriv->randr & (RR_Rotate_90|RR_Rotate_270));
}
return TRUE;
}
void
ephyrSetScreenSizes (ScreenPtr pScreen)
{
KdScreenPriv(pScreen);
KdScreenInfo *screen = pScreenPriv->screen;
EphyrScrPriv *scrpriv = screen->driver;
if (scrpriv->randr & (RR_Rotate_0|RR_Rotate_180))
{
pScreen->width = screen->width;
pScreen->height = screen->height;
pScreen->mmWidth = screen->width_mm;
pScreen->mmHeight = screen->height_mm;
}
else
{
pScreen->width = screen->height;
pScreen->height = screen->width;
pScreen->mmWidth = screen->height_mm;
pScreen->mmHeight = screen->width_mm;
}
}
Bool
ephyrUnmapFramebuffer (KdScreenInfo *screen)
{
EphyrScrPriv *scrpriv = screen->driver;
if (scrpriv->shadow)
KdShadowFbFree (screen, 0);
/* Note, priv->base will get freed when XImage recreated */
return TRUE;
}
void
ephyrShadowUpdate (ScreenPtr pScreen, shadowBufPtr pBuf)
{
KdScreenPriv(pScreen);
KdScreenInfo *screen = pScreenPriv->screen;
EPHYR_DBG("slow paint");
/* FIXME: Slow Rotated/Reflected updates could be much
* much faster efficiently updating via tranforming
* pBuf->pDamage regions
*/
shadowUpdateRotatePacked(pScreen, pBuf);
hostx_paint_rect(0,0,0,0, screen->width, screen->height);
}
static void
ephyrInternalDamageRedisplay (ScreenPtr pScreen)
{
KdScreenPriv(pScreen);
KdScreenInfo *screen = pScreenPriv->screen;
EphyrScrPriv *scrpriv = screen->driver;
RegionPtr pRegion;
if (!scrpriv || !scrpriv->pDamage)
return;
pRegion = DamageRegion (scrpriv->pDamage);
if (REGION_NOTEMPTY (pScreen, pRegion))
{
int nbox;
BoxPtr pbox;
nbox = REGION_NUM_RECTS (pRegion);
pbox = REGION_RECTS (pRegion);
while (nbox--)
{
hostx_paint_rect(pbox->x1, pbox->y1,
pbox->x1, pbox->y1,
pbox->x2 - pbox->x1,
pbox->y2 - pbox->y1);
pbox++;
}
DamageEmpty (scrpriv->pDamage);
}
}
static void
ephyrInternalDamageBlockHandler (pointer data,
OSTimePtr pTimeout,
pointer pRead)
{
ScreenPtr pScreen = (ScreenPtr) data;
ephyrInternalDamageRedisplay (pScreen);
}
static void
ephyrInternalDamageWakeupHandler (pointer data, int i, pointer LastSelectMask)
{
/* FIXME: Not needed ? */
}
Bool
ephyrSetInternalDamage (ScreenPtr pScreen)
{
KdScreenPriv(pScreen);
KdScreenInfo *screen = pScreenPriv->screen;
EphyrScrPriv *scrpriv = screen->driver;
PixmapPtr pPixmap = NULL;
scrpriv->pDamage = DamageCreate ((DamageReportFunc) 0,
(DamageDestroyFunc) 0,
DamageReportNone,
TRUE,
pScreen,
pScreen);
if (!RegisterBlockAndWakeupHandlers (ephyrInternalDamageBlockHandler,
ephyrInternalDamageWakeupHandler,
(pointer) pScreen))
return FALSE;
pPixmap = (*pScreen->GetScreenPixmap) (pScreen);
DamageRegister (&pPixmap->drawable, scrpriv->pDamage);
return TRUE;
}
void
ephyrUnsetInternalDamage (ScreenPtr pScreen)
{
KdScreenPriv(pScreen);
KdScreenInfo *screen = pScreenPriv->screen;
EphyrScrPriv *scrpriv = screen->driver;
PixmapPtr pPixmap = NULL;
pPixmap = (*pScreen->GetScreenPixmap) (pScreen);
DamageUnregister (&pPixmap->drawable, scrpriv->pDamage);
RemoveBlockAndWakeupHandlers (ephyrInternalDamageBlockHandler,
ephyrInternalDamageWakeupHandler,
(pointer) pScreen);
}
#ifdef RANDR
Bool
ephyrRandRGetInfo (ScreenPtr pScreen, Rotation *rotations)
{
KdScreenPriv(pScreen);
KdScreenInfo *screen = pScreenPriv->screen;
EphyrScrPriv *scrpriv = screen->driver;
RRScreenSizePtr pSize;
Rotation randr;
int n = 0;
EPHYR_DBG("mark");
struct { int width, height; } sizes[] =
{
{ 1600, 1200 },
{ 1400, 1050 },
{ 1280, 960 },
{ 1280, 1024 },
{ 1152, 864 },
{ 1024, 768 },
{ 832, 624 },
{ 800, 600 },
{ 720, 400 },
{ 480, 640 },
{ 640, 480 },
{ 640, 400 },
{ 320, 240 },
{ 240, 320 },
{ 160, 160 },
{ 0, 0 }
};
*rotations = RR_Rotate_All|RR_Reflect_All;
if (!hostx_want_preexisting_window()
&& !hostx_want_fullscreen()) /* only if no -parent switch */
{
while (sizes[n].width != 0 && sizes[n].height != 0)
{
RRRegisterSize (pScreen,
sizes[n].width,
sizes[n].height,
(sizes[n].width * screen->width_mm)/screen->width,
(sizes[n].height *screen->height_mm)/screen->height
);
n++;
}
}
pSize = RRRegisterSize (pScreen,
screen->width,
screen->height,
screen->width_mm,
screen->height_mm);
randr = KdSubRotation (scrpriv->randr, screen->randr);
RRSetCurrentConfig (pScreen, randr, 0, pSize);
return TRUE;
}
Bool
ephyrRandRSetConfig (ScreenPtr pScreen,
Rotation randr,
int rate,
RRScreenSizePtr pSize)
{
KdScreenPriv(pScreen);
KdScreenInfo *screen = pScreenPriv->screen;
EphyrScrPriv *scrpriv = screen->driver;
Bool wasEnabled = pScreenPriv->enabled;
EphyrScrPriv oldscr;
int oldwidth, oldheight, oldmmwidth, oldmmheight;
Bool oldshadow;
int newwidth, newheight;
if (screen->randr & (RR_Rotate_0|RR_Rotate_180))
{
newwidth = pSize->width;
newheight = pSize->height;
}
else
{
newwidth = pSize->height;
newheight = pSize->width;
}
if (wasEnabled)
KdDisableScreen (pScreen);
oldscr = *scrpriv;
oldwidth = screen->width;
oldheight = screen->height;
oldmmwidth = pScreen->mmWidth;
oldmmheight = pScreen->mmHeight;
oldshadow = scrpriv->shadow;
/*
* Set new configuration
*/
scrpriv->randr = KdAddRotation (screen->randr, randr);
KdOffscreenSwapOut (screen->pScreen);
ephyrUnmapFramebuffer (screen);
screen->width = newwidth;
screen->height = newheight;
if (!ephyrMapFramebuffer (screen))
goto bail4;
/* FIXME below should go in own call */
if (oldshadow)
KdShadowUnset (screen->pScreen);
else
ephyrUnsetInternalDamage(screen->pScreen);
if (scrpriv->shadow)
{
if (!KdShadowSet (screen->pScreen,
scrpriv->randr,
ephyrShadowUpdate,
ephyrWindowLinear))
goto bail4;
}
else
{
/* Without shadow fb ( non rotated ) we need
* to use damage to efficiently update display
* via signal regions what to copy from 'fb'.
*/
if (!ephyrSetInternalDamage(screen->pScreen))
goto bail4;
}
ephyrSetScreenSizes (screen->pScreen);
/*
* Set frame buffer mapping
*/
(*pScreen->ModifyPixmapHeader) (fbGetScreenPixmap (pScreen),
pScreen->width,
pScreen->height,
screen->fb[0].depth,
screen->fb[0].bitsPerPixel,
screen->fb[0].byteStride,
screen->fb[0].frameBuffer);
/* set the subpixel order */
KdSetSubpixelOrder (pScreen, scrpriv->randr);
if (wasEnabled)
KdEnableScreen (pScreen);
return TRUE;
bail4:
EPHYR_DBG("bailed");
ephyrUnmapFramebuffer (screen);
*scrpriv = oldscr;
(void) ephyrMapFramebuffer (screen);
pScreen->width = oldwidth;
pScreen->height = oldheight;
pScreen->mmWidth = oldmmwidth;
pScreen->mmHeight = oldmmheight;
if (wasEnabled)
KdEnableScreen (pScreen);
return FALSE;
}
Bool
ephyrRandRInit (ScreenPtr pScreen)
{
rrScrPrivPtr pScrPriv;
if (!RRScreenInit (pScreen))
{
return FALSE;
}
pScrPriv = rrGetScrPriv(pScreen);
pScrPriv->rrGetInfo = ephyrRandRGetInfo;
pScrPriv->rrSetConfig = ephyrRandRSetConfig;
return TRUE;
}
#endif
Bool
ephyrCreateColormap (ColormapPtr pmap)
{
return fbInitializeColormap (pmap);
}
Bool
ephyrInitScreen (ScreenPtr pScreen)
{
pScreen->CreateColormap = ephyrCreateColormap;
return TRUE;
}
Bool
ephyrFinishInitScreen (ScreenPtr pScreen)
{
/* FIXME: Calling this even if not using shadow.
* Seems harmless enough. But may be safer elsewhere.
*/
if (!shadowSetup (pScreen))
return FALSE;
#ifdef RANDR
if (!ephyrRandRInit (pScreen))
return FALSE;
#endif
return TRUE;
}
Bool
ephyrCreateResources (ScreenPtr pScreen)
{
KdScreenPriv(pScreen);
KdScreenInfo *screen = pScreenPriv->screen;
EphyrScrPriv *scrpriv = screen->driver;
EPHYR_DBG("mark");
if (scrpriv->shadow)
return KdShadowSet (pScreen,
scrpriv->randr,
ephyrShadowUpdate,
ephyrWindowLinear);
else
return ephyrSetInternalDamage(pScreen);
}
void
ephyrPreserve (KdCardInfo *card)
{
}
Bool
ephyrEnable (ScreenPtr pScreen)
{
return TRUE;
}
Bool
ephyrDPMS (ScreenPtr pScreen, int mode)
{
return TRUE;
}
void
ephyrDisable (ScreenPtr pScreen)
{
}
void
ephyrRestore (KdCardInfo *card)
{
}
void
ephyrScreenFini (KdScreenInfo *screen)
{
}
/*
* Port of Mark McLoughlin's Xnest fix for focus in + modifier bug.
* See https://bugs.freedesktop.org/show_bug.cgi?id=3030
*/
void
ephyrUpdateModifierState(unsigned int state)
{
DeviceIntPtr pkeydev;
KeyClassPtr keyc;
int i;
CARD8 mask;
pkeydev = (DeviceIntPtr)LookupKeyboardDevice();
if (!pkeydev)
return;
keyc = pkeydev->key;
state = state & 0xff;
if (keyc->state == state)
return;
for (i = 0, mask = 1; i < 8; i++, mask <<= 1)
{
int key;
/* Modifier is down, but shouldn't be */
if ((keyc->state & mask) && !(state & mask))
{
int count = keyc->modifierKeyCount[i];
for (key = 0; key < MAP_LENGTH; key++)
if (keyc->modifierMap[key] & mask)
{
int bit;
BYTE *kptr;
kptr = &keyc->down[key >> 3];
bit = 1 << (key & 7);
if (*kptr & bit)
KdEnqueueKeyboardEvent(key, TRUE); /* release */
if (--count == 0)
break;
}
}
/* Modifier shoud be down, but isn't */
if (!(keyc->state & mask) && (state & mask))
for (key = 0; key < MAP_LENGTH; key++)
if (keyc->modifierMap[key] & mask)
{
KdEnqueueKeyboardEvent(key, FALSE); /* press */
break;
}
}
}
void
ephyrPoll(void)
{
EphyrHostXEvent ev;
while (hostx_get_event(&ev))
{
switch (ev.type)
{
case EPHYR_EV_MOUSE_MOTION:
KdEnqueueMouseEvent(kdMouseInfo, mouseState,
ev.data.mouse_motion.x,
ev.data.mouse_motion.y);
break;
case EPHYR_EV_MOUSE_PRESS:
ephyrUpdateModifierState(ev.key_state);
mouseState |= ev.data.mouse_down.button_num;
KdEnqueueMouseEvent(kdMouseInfo, mouseState|KD_MOUSE_DELTA, 0, 0);
break;
case EPHYR_EV_MOUSE_RELEASE:
ephyrUpdateModifierState(ev.key_state);
mouseState &= ~ev.data.mouse_up.button_num;
KdEnqueueMouseEvent(kdMouseInfo, mouseState|KD_MOUSE_DELTA, 0, 0);
break;
case EPHYR_EV_KEY_PRESS:
ephyrUpdateModifierState(ev.key_state);
KdEnqueueKeyboardEvent (ev.data.key_down.scancode, FALSE);
break;
case EPHYR_EV_KEY_RELEASE:
ephyrUpdateModifierState(ev.key_state);
KdEnqueueKeyboardEvent (ev.data.key_up.scancode, TRUE);
break;
default:
break;
}
}
}
void
ephyrCardFini (KdCardInfo *card)
{
EphyrPriv *priv = card->driver;
xfree (priv);
}
void
ephyrGetColors (ScreenPtr pScreen, int fb, int n, xColorItem *pdefs)
{
/* XXX Not sure if this is right */
EPHYR_DBG("mark");
while (n--)
{
pdefs->red = 0;
pdefs->green = 0;
pdefs->blue = 0;
pdefs++;
}
}
void
ephyrPutColors (ScreenPtr pScreen, int fb, int n, xColorItem *pdefs)
{
int min, max, p;
/* XXX Not sure if this is right */
min = 256;
max = 0;
while (n--)
{
p = pdefs->pixel;
if (p < min)
min = p;
if (p > max)
max = p;
hostx_set_cmap_entry(p,
pdefs->red >> 8,
pdefs->green >> 8,
pdefs->blue >> 8);
pdefs++;
}
}
/* Mouse calls */
static Bool
MouseInit (void)
{
return TRUE;
}
static void
MouseFini (void)
{
;
}
KdMouseFuncs EphyrMouseFuncs = {
MouseInit,
MouseFini,
};
/* Keyboard */
static void
EphyrKeyboardLoad (void)
{
EPHYR_DBG("mark");
hostx_load_keymap();
}
static int
EphyrKeyboardInit (void)
{
return 0;
}
static void
EphyrKeyboardFini (void)
{
}
static void
EphyrKeyboardLeds (int leds)
{
}
static void
EphyrKeyboardBell (int volume, int frequency, int duration)
{
}
KdKeyboardFuncs EphyrKeyboardFuncs = {
EphyrKeyboardLoad,
EphyrKeyboardInit,
EphyrKeyboardLeds,
EphyrKeyboardBell,
EphyrKeyboardFini,
0,
};