/*
* Copyright (c) 1998, 2001, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#if sparc
/* #define DGA_DEBUG */
#ifdef DGA_DEBUG
#define DEBUG_PRINT(x) printf x
#else
#define DEBUG_PRINT(x)
#endif
#include <dga/dga.h>
#include <unistd.h> /* ioctl */
#include <stdlib.h>
#include <sys/mman.h> /* mmap */
#include <sys/visual_io.h>
#include <string.h>
/* X11 */
#include <X11/Xlib.h>
#include "jni.h"
#include "jvm_md.h"
#include "jdga.h"
#include "jdgadevice.h"
#include <dlfcn.h>
#define min(x, y) ((x) < (y) ? (x) : (y))
#define max(x, y) ((x) > (y) ? (x) : (y))
typedef struct _SolarisDgaLibInfo SolarisDgaLibInfo;
struct _SolarisDgaLibInfo {
/* The general (non-device specific) information */
unsigned long count;
Drawable drawable;
Drawable virtual_drawable;
/* The device specific memory mapping information */
SolarisJDgaDevInfo *devInfo;
SolarisJDgaWinInfo winInfo;
};
typedef Bool IsXineramaOnFunc(Display *display);
typedef Drawable GetVirtualDrawableFunc(Display *display, Drawable drawable);
#define MAX_CACHED_INFO 16
static SolarisDgaLibInfo cachedInfo[MAX_CACHED_INFO];
static jboolean needsSync = JNI_FALSE;
#define MAX_FB_TYPES 16
static SolarisJDgaDevInfo devicesInfo[MAX_FB_TYPES];
static IsXineramaOnFunc *IsXineramaOn = NULL;
static GetVirtualDrawableFunc GetVirtualDrawableStub;
Drawable GetVirtualDrawableStub(Display *display, Drawable drawable) {
return drawable;
}
static GetVirtualDrawableFunc * GetVirtualDrawable = GetVirtualDrawableStub;
static void Solaris_DGA_XineramaInit(Display *display) {
void * handle = NULL;
if (IsXineramaOn == NULL) {
handle = dlopen(JNI_LIB_NAME("xinerama"), RTLD_NOW);
if (handle != NULL) {
void *sym = dlsym(handle, "IsXineramaOn");
IsXineramaOn = (IsXineramaOnFunc *)sym;
if (IsXineramaOn != 0 && (*IsXineramaOn)(display)) {
sym = dlsym(handle, "GetVirtualDrawable");
if (sym != 0) {
GetVirtualDrawable = (GetVirtualDrawableFunc *)sym;
}
} else {
dlclose(handle);
}
}
}
}
static SolarisJDgaDevInfo * getDevInfo(Dga_drawable dgadraw) {
void *handle = 0;
struct vis_identifier visid;
int fd;
char libName[64];
int i;
SolarisJDgaDevInfo *curDevInfo = devicesInfo;
fd = dga_draw_devfd(dgadraw);
if (ioctl(fd, VIS_GETIDENTIFIER, &visid) != 1) {
/* check in the devices list */
for (i = 0; (i < MAX_FB_TYPES) && (curDevInfo->visidName);
i++, curDevInfo++) {
if (strcmp(visid.name, curDevInfo->visidName) == 0) {
/* we already have such a device, return it */
return curDevInfo;
}
}
if (i == MAX_FB_TYPES) {
/* we're out of slots, return NULL */
return NULL;
}
strcpy(libName, "libjdga");
strcat(libName, visid.name);
strcat(libName,".so");
/* we use RTLD_NOW because of bug 4032715 */
handle = dlopen(libName, RTLD_NOW);
if (handle != 0) {
JDgaStatus ret = JDGA_FAILED;
void *sym = dlsym(handle, "SolarisJDgaDevOpen");
if (sym != 0) {
curDevInfo->majorVersion = JDGALIB_MAJOR_VERSION;
curDevInfo->minorVersion = JDGALIB_MINOR_VERSION;
ret = (*(SolarisJDgaDevOpenFunc *)sym)(curDevInfo);
}
if (ret == JDGA_SUCCESS) {
curDevInfo->visidName = strdup(visid.name);
return curDevInfo;
}
dlclose(handle);
}
}
return NULL;
}
static int
mmap_dgaDev(SolarisDgaLibInfo *libInfo, Dga_drawable dgadraw)
{
if (!libInfo->devInfo) {
libInfo->devInfo = getDevInfo(dgadraw);
if (!libInfo->devInfo) {
return JDGA_FAILED;
}
}
return (*libInfo->devInfo->function->winopen)(&(libInfo->winInfo));
}
static void
unmap_dgaDev(SolarisDgaLibInfo *pDevInfo)
{
DEBUG_PRINT(("winclose() called\n"));
(*pDevInfo->devInfo->function->winclose)(&(pDevInfo->winInfo));
}
static jboolean
Solaris_DGA_Available(Display *display)
{
Window root;
int screen;
Dga_drawable dgaDrawable;
SolarisJDgaDevInfo * devinfo;
/* return true if any screen supports DGA and we
have a library for this type of framebuffer */
for (screen = 0; screen < XScreenCount(display); screen++) {
root = RootWindow(display, screen);
dgaDrawable = XDgaGrabDrawable(display, root);
if (dgaDrawable != 0) {
devinfo = getDevInfo(dgaDrawable);
XDgaUnGrabDrawable(dgaDrawable);
if (devinfo != NULL) {
return JNI_TRUE;
}
}
}
return JNI_FALSE;
}
static JDgaLibInitFunc Solaris_DGA_LibInit;
static JDgaGetLockFunc Solaris_DGA_GetLock;
static JDgaReleaseLockFunc Solaris_DGA_ReleaseLock;
static JDgaXRequestSentFunc Solaris_DGA_XRequestSent;
static JDgaLibDisposeFunc Solaris_DGA_LibDispose;
static int firstInitDone = 0;
#pragma weak JDgaLibInit = Solaris_DGA_LibInit
static JDgaStatus
Solaris_DGA_LibInit(JNIEnv *env, JDgaLibInfo *ppInfo)
{
/* Note: DGA_INIT can be called multiple times according to docs */
DEBUG_PRINT(("DGA_INIT called\n"));
DGA_INIT();
if (!Solaris_DGA_Available(ppInfo->display)) {
return JDGA_FAILED;
}
Solaris_DGA_XineramaInit(ppInfo->display);
ppInfo->pGetLock = Solaris_DGA_GetLock;
ppInfo->pReleaseLock = Solaris_DGA_ReleaseLock;
ppInfo->pXRequestSent = Solaris_DGA_XRequestSent;
ppInfo->pLibDispose = Solaris_DGA_LibDispose;
return JDGA_SUCCESS;
}
static JDgaStatus
Solaris_DGA_GetLock(JNIEnv *env, Display *display, void **dgaDev,
Drawable drawable, JDgaSurfaceInfo *pSurface,
jint lox, jint loy, jint hix, jint hiy)
{
SolarisDgaLibInfo *pDevInfo;
SolarisDgaLibInfo *pCachedInfo = cachedInfo;
int vis;
int dlox, dloy, dhix, dhiy;
int i;
int type, site;
unsigned long k;
Drawable prev_virtual_drawable = 0;
Dga_drawable dgaDrawable;
if (*dgaDev) {
if (((SolarisDgaLibInfo *)(*dgaDev))->drawable != drawable) {
*dgaDev = 0;
}
}
if (*dgaDev == 0) {
pCachedInfo = cachedInfo;
for (i = 0 ; (i < MAX_CACHED_INFO) && (pCachedInfo->drawable) ;
i++, pCachedInfo++) {
if (pCachedInfo->drawable == drawable) {
*dgaDev = pCachedInfo;
break;
}
}
if (*dgaDev == 0) {
if (i < MAX_CACHED_INFO) { /* slot can be used for new info */
*dgaDev = pCachedInfo;
} else {
pCachedInfo = cachedInfo;
/* find the least used slot but does not handle an overflow of
the counter */
for (i = 0, k = 0xffffffff; i < MAX_CACHED_INFO ;
i++, pCachedInfo++) {
if (k > pCachedInfo->count) {
k = pCachedInfo->count;
*dgaDev = pCachedInfo;
}
pCachedInfo->count = 0; /* reset all counters */
}
pCachedInfo = *dgaDev;
if (pCachedInfo->winInfo.dgaDraw != 0) {
XDgaUnGrabDrawable(pCachedInfo->winInfo.dgaDraw);
}
pCachedInfo->winInfo.dgaDraw = 0;
/* the slot might be used for another device */
pCachedInfo->devInfo = 0;
}
}
}
pDevInfo = *dgaDev;
pDevInfo->drawable = drawable;
prev_virtual_drawable = pDevInfo->virtual_drawable;
pDevInfo->virtual_drawable = GetVirtualDrawable(display, drawable);
if (pDevInfo->virtual_drawable == NULL) {
/* this usually means that the drawable is spanned across
screens in xinerama mode - we can't handle this for now */
return JDGA_FAILED;
} else {
/* check if the drawable has been moved to another screen
since last time */
if (pDevInfo->winInfo.dgaDraw != 0 &&
pDevInfo->virtual_drawable != prev_virtual_drawable) {
XDgaUnGrabDrawable(pDevInfo->winInfo.dgaDraw);
pDevInfo->winInfo.dgaDraw = 0;
}
}
pDevInfo->count++;
if (pDevInfo->winInfo.dgaDraw == 0) {
pDevInfo->winInfo.dgaDraw = XDgaGrabDrawable(display, pDevInfo->virtual_drawable);
if (pDevInfo->winInfo.dgaDraw == 0) {
DEBUG_PRINT(("DgaGrabDrawable failed for 0x%08x\n", drawable));
return JDGA_UNAVAILABLE;
}
type = dga_draw_type(pDevInfo->winInfo.dgaDraw);
if (type != DGA_DRAW_PIXMAP &&
mmap_dgaDev(pDevInfo, pDevInfo->winInfo.dgaDraw) != JDGA_SUCCESS) {
DEBUG_PRINT(("memory map failed for 0x%08x (depth = %d)\n",
drawable, dga_draw_depth(pDevInfo->winInfo.dgaDraw)));
XDgaUnGrabDrawable(pDevInfo->winInfo.dgaDraw);
pDevInfo->winInfo.dgaDraw = 0;
return JDGA_UNAVAILABLE;
}
} else {
type = dga_draw_type(pDevInfo->winInfo.dgaDraw);
}
if (needsSync) {
XSync(display, False);
needsSync = JNI_FALSE;
}
dgaDrawable = pDevInfo->winInfo.dgaDraw;
DGA_DRAW_LOCK(dgaDrawable, -1);
site = dga_draw_site(dgaDrawable);
if (type == DGA_DRAW_PIXMAP) {
if (site == DGA_SITE_SYSTEM) {
pDevInfo->winInfo.mapDepth = dga_draw_depth(dgaDrawable);
pDevInfo->winInfo.mapAddr = dga_draw_address(dgaDrawable);
dga_draw_bbox(dgaDrawable, &dlox, &dloy, &dhix, &dhiy);
pDevInfo->winInfo.mapWidth = dhix;
pDevInfo->winInfo.mapHeight = dhiy;
if (pDevInfo->winInfo.mapDepth == 8) {
pDevInfo->winInfo.mapLineStride = dga_draw_linebytes(dgaDrawable);
pDevInfo->winInfo.mapPixelStride = 1;
} else {
pDevInfo->winInfo.mapLineStride = dga_draw_linebytes(dgaDrawable)/4;
pDevInfo->winInfo.mapPixelStride = 4;
}
} else {
XDgaUnGrabDrawable(dgaDrawable);
pDevInfo->winInfo.dgaDraw = 0;
return JDGA_UNAVAILABLE;
}
} else {
if (site == DGA_SITE_NULL) {
DEBUG_PRINT(("zombie drawable = 0x%08x\n", dgaDrawable));
DGA_DRAW_UNLOCK(dgaDrawable);
unmap_dgaDev(pDevInfo);
XDgaUnGrabDrawable(dgaDrawable);
pDevInfo->winInfo.dgaDraw = 0;
return JDGA_UNAVAILABLE;
}
dga_draw_bbox(dgaDrawable, &dlox, &dloy, &dhix, &dhiy);
}
/* get the screen address of the drawable */
dhix += dlox;
dhiy += dloy;
DEBUG_PRINT(("window at (%d, %d) => (%d, %d)\n", dlox, dloy, dhix, dhiy));
pSurface->window.lox = dlox;
pSurface->window.loy = dloy;
pSurface->window.hix = dhix;
pSurface->window.hiy = dhiy;
/* translate rendering coordinates relative to device bbox */
lox += dlox;
loy += dloy;
hix += dlox;
hiy += dloy;
DEBUG_PRINT(("render at (%d, %d) => (%d, %d)\n", lox, loy, hix, hiy));
vis = dga_draw_visibility(dgaDrawable);
switch (vis) {
case DGA_VIS_UNOBSCURED:
pSurface->visible.lox = max(dlox, lox);
pSurface->visible.loy = max(dloy, loy);
pSurface->visible.hix = min(dhix, hix);
pSurface->visible.hiy = min(dhiy, hiy);
DEBUG_PRINT(("unobscured vis at (%d, %d) => (%d, %d)\n",
pSurface->visible.lox,
pSurface->visible.loy,
pSurface->visible.hix,
pSurface->visible.hiy));
break;
case DGA_VIS_PARTIALLY_OBSCURED: {
/*
* fix for #4305271
* the dga_draw_clipinfo call returns the clipping bounds
* in short ints, but use only full size ints for all comparisons.
*/
short *ptr;
int x0, y0, x1, y1;
int cliplox, cliploy, cliphix, cliphiy;
/*
* iterate to find out whether the clipped blit draws to a
* single clipping rectangle
*/
cliplox = cliphix = lox;
cliploy = cliphiy = loy;
ptr = dga_draw_clipinfo(dgaDrawable);
while (*ptr != DGA_Y_EOL) {
y0 = *ptr++;
y1 = *ptr++;
DEBUG_PRINT(("DGA y range loy=%d hiy=%d\n", y0, y1));
if (y0 < loy) {
y0 = loy;
}
if (y1 > hiy) {
y1 = hiy;
}
while (*ptr != DGA_X_EOL) {
x0 = *ptr++;
x1 = *ptr++;
DEBUG_PRINT((" DGA x range lox=%d hix=%d\n", x0, x1));
if (x0 < lox) {
x0 = lox;
}
if (x1 > hix) {
x1 = hix;
}
if (x0 < x1 && y0 < y1) {
if (cliploy == cliphiy) {
/* First rectangle intersection */
cliplox = x0;
cliploy = y0;
cliphix = x1;
cliphiy = y1;
} else {
/* Can we merge this rect with previous? */
if (cliplox == x0 && cliphix == x1 &&
cliploy <= y1 && cliphiy >= y0)
{
/* X ranges match, Y ranges touch */
/* => absorb the Y ranges together */
cliploy = min(cliploy, y0);
cliphiy = max(cliphiy, y1);
} else if (cliploy == y0 && cliphiy == y1 &&
cliplox <= x1 && cliphix >= x0)
{
/* Y ranges match, X ranges touch */
/* => Absorb the X ranges together */
cliplox = min(cliplox, x0);
cliphix = max(cliphix, x1);
} else {
/* Assertion: any other combination */
/* means non-rectangular intersect */
DGA_DRAW_UNLOCK(dgaDrawable);
return JDGA_FAILED;
}
}
}
}
ptr++; /* advance past DGA_X_EOL */
}
DEBUG_PRINT(("DGA drawable fits\n"));
pSurface->visible.lox = cliplox;
pSurface->visible.loy = cliploy;
pSurface->visible.hix = cliphix;
pSurface->visible.hiy = cliphiy;
break;
}
case DGA_VIS_FULLY_OBSCURED:
pSurface->visible.lox =
pSurface->visible.hix = lox;
pSurface->visible.loy =
pSurface->visible.hiy = loy;
DEBUG_PRINT(("fully obscured vis\n"));
break;
default:
DEBUG_PRINT(("unknown visibility = %d!\n", vis));
DGA_DRAW_UNLOCK(dgaDrawable);
return JDGA_FAILED;
}
pSurface->basePtr = pDevInfo->winInfo.mapAddr;
pSurface->surfaceScan = pDevInfo->winInfo.mapLineStride;
pSurface->surfaceWidth = pDevInfo->winInfo.mapWidth;
pSurface->surfaceHeight = pDevInfo->winInfo.mapHeight;
pSurface->surfaceDepth = pDevInfo->winInfo.mapDepth;
return JDGA_SUCCESS;
}
static JDgaStatus
Solaris_DGA_ReleaseLock(JNIEnv *env, void *dgaDev, Drawable drawable)
{
SolarisDgaLibInfo *pDevInfo = (SolarisDgaLibInfo *) dgaDev;
if (pDevInfo != 0 && pDevInfo->drawable == drawable &&
pDevInfo->winInfo.dgaDraw != 0) {
DGA_DRAW_UNLOCK(pDevInfo->winInfo.dgaDraw);
}
return JDGA_SUCCESS;
}
static void
Solaris_DGA_XRequestSent(JNIEnv *env, void *dgaDev, Drawable drawable)
{
needsSync = JNI_TRUE;
}
static void
Solaris_DGA_LibDispose(JNIEnv *env)
{
SolarisDgaLibInfo *pCachedInfo = cachedInfo;
SolarisJDgaDevInfo *curDevInfo = devicesInfo;
int i;
for (i = 0 ; (i < MAX_CACHED_INFO) && (pCachedInfo->drawable) ;
i++, pCachedInfo++) {
if (pCachedInfo->winInfo.dgaDraw != 0) {
if (dga_draw_type(pCachedInfo->winInfo.dgaDraw) == DGA_DRAW_WINDOW &&
pCachedInfo->winInfo.mapDepth != 0) {
unmap_dgaDev(pCachedInfo);
}
XDgaUnGrabDrawable(pCachedInfo->winInfo.dgaDraw);
pCachedInfo->winInfo.dgaDraw = 0;
}
}
for (i = 0; (i < MAX_FB_TYPES) && (curDevInfo->visidName);
i++, curDevInfo++) {
curDevInfo->function->devclose(curDevInfo);
free(curDevInfo->visidName);
}
}
#endif