1 /*
   2  * Copyright (c) 1998, 2012, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 #if sparc
  27 
  28 /* #define DGA_DEBUG */
  29 
  30 #ifdef DGA_DEBUG
  31 #define DEBUG_PRINT(x)  printf x
  32 #else
  33 #define DEBUG_PRINT(x)
  34 #endif
  35 
  36 #include <dga/dga.h>
  37 #include <unistd.h>     /* ioctl */
  38 #include <stdlib.h>
  39 #include <sys/mman.h>   /* mmap */
  40 #include <sys/visual_io.h>
  41 #include <string.h>
  42 
  43 /* X11 */
  44 #include <X11/Xlib.h>
  45 
  46 #include "jni.h"
  47 #include "jvm_md.h"
  48 #include "jdga.h"
  49 #include "jdgadevice.h"
  50 
  51 #include <dlfcn.h>
  52 
  53 #define min(x, y)       ((x) < (y) ? (x) : (y))
  54 #define max(x, y)       ((x) > (y) ? (x) : (y))
  55 
  56 typedef struct _SolarisDgaLibInfo SolarisDgaLibInfo;
  57 
  58 struct _SolarisDgaLibInfo {
  59     /* The general (non-device specific) information */
  60     unsigned long       count;
  61     Drawable            drawable;
  62     Drawable            virtual_drawable;
  63 
  64     /* The device specific memory mapping information */
  65     SolarisJDgaDevInfo  *devInfo;
  66     SolarisJDgaWinInfo  winInfo;
  67 };
  68 
  69 typedef Bool IsXineramaOnFunc(Display *display);
  70 typedef Drawable GetVirtualDrawableFunc(Display *display, Drawable drawable);
  71 
  72 #define MAX_CACHED_INFO 16
  73 static SolarisDgaLibInfo cachedInfo[MAX_CACHED_INFO];
  74 static jboolean needsSync = JNI_FALSE;
  75 
  76 #define MAX_FB_TYPES 16
  77 static SolarisJDgaDevInfo devicesInfo[MAX_FB_TYPES];
  78 
  79 static IsXineramaOnFunc *IsXineramaOn = NULL;
  80 static GetVirtualDrawableFunc GetVirtualDrawableStub;
  81 
  82 Drawable GetVirtualDrawableStub(Display *display, Drawable drawable) {
  83     return drawable;
  84 }
  85 static GetVirtualDrawableFunc * GetVirtualDrawable = GetVirtualDrawableStub;
  86 
  87 static void Solaris_DGA_XineramaInit(Display *display) {
  88     void * handle = NULL;
  89     if (IsXineramaOn == NULL) {
  90         handle = dlopen(JNI_LIB_NAME("xinerama"), RTLD_NOW);
  91         if (handle != NULL) {
  92             void *sym = dlsym(handle, "IsXineramaOn");
  93             IsXineramaOn = (IsXineramaOnFunc *)sym;
  94             if (IsXineramaOn != 0 && (*IsXineramaOn)(display)) {
  95                 sym = dlsym(handle, "GetVirtualDrawable");
  96                 if (sym != 0) {
  97                     GetVirtualDrawable = (GetVirtualDrawableFunc *)sym;
  98                 }
  99             } else {
 100                 dlclose(handle);
 101             }
 102         }
 103     }
 104 }
 105 
 106 static SolarisJDgaDevInfo * getDevInfo(Dga_drawable dgadraw) {
 107     void *handle = 0;
 108     struct vis_identifier visid;
 109     int fd;
 110     char libName[64];
 111     int i;
 112     SolarisJDgaDevInfo *curDevInfo = devicesInfo;
 113 
 114     fd = dga_draw_devfd(dgadraw);
 115     if (ioctl(fd, VIS_GETIDENTIFIER, &visid) != 1) {
 116         /* check in the devices list */
 117         for (i = 0; (i < MAX_FB_TYPES) && (curDevInfo->visidName);
 118              i++, curDevInfo++) {
 119             if (strcmp(visid.name, curDevInfo->visidName) == 0) {
 120                 /* we already have such a device, return it */
 121                 return curDevInfo;
 122             }
 123         }
 124         if (i == MAX_FB_TYPES) {
 125             /* we're out of slots, return NULL */
 126             return NULL;
 127         }
 128 
 129         strcpy(libName, "libjdga");
 130         strcat(libName, visid.name);
 131         strcat(libName,".so");
 132         /* we use RTLD_NOW because of bug 4032715 */
 133         handle = dlopen(libName, RTLD_NOW);
 134         if (handle != 0) {
 135             JDgaStatus ret = JDGA_FAILED;
 136             void *sym = dlsym(handle, "SolarisJDgaDevOpen");
 137             if (sym != 0) {
 138                 curDevInfo->majorVersion = JDGALIB_MAJOR_VERSION;
 139                 curDevInfo->minorVersion = JDGALIB_MINOR_VERSION;
 140                 ret = (*(SolarisJDgaDevOpenFunc *)sym)(curDevInfo);
 141             }
 142             if (ret == JDGA_SUCCESS) {
 143                 curDevInfo->visidName = strdup(visid.name);
 144                 return curDevInfo;
 145             }
 146             dlclose(handle);
 147         }
 148     }
 149     return NULL;
 150 }
 151 static int
 152 mmap_dgaDev(SolarisDgaLibInfo *libInfo, Dga_drawable dgadraw)
 153 {
 154 
 155     if (!libInfo->devInfo) {
 156         libInfo->devInfo = getDevInfo(dgadraw);
 157         if (!libInfo->devInfo) {
 158             return JDGA_FAILED;
 159         }
 160     }
 161     return (*libInfo->devInfo->function->winopen)(&(libInfo->winInfo));
 162 }
 163 
 164 static void
 165 unmap_dgaDev(SolarisDgaLibInfo *pDevInfo)
 166 {
 167     DEBUG_PRINT(("winclose() called\n"));
 168    (*pDevInfo->devInfo->function->winclose)(&(pDevInfo->winInfo));
 169 }
 170 
 171 static jboolean
 172 Solaris_DGA_Available(Display *display)
 173 {
 174     Window root;
 175     int screen;
 176     Dga_drawable dgaDrawable;
 177     SolarisJDgaDevInfo * devinfo;
 178 
 179     /* return true if any screen supports DGA and we
 180      have a library for this type of framebuffer */
 181     for (screen = 0; screen < XScreenCount(display); screen++) {
 182         root = RootWindow(display, screen);
 183 
 184         dgaDrawable = XDgaGrabDrawable(display, root);
 185         if (dgaDrawable != 0) {
 186             devinfo = getDevInfo(dgaDrawable);
 187             XDgaUnGrabDrawable(dgaDrawable);
 188             if (devinfo != NULL) {
 189                 return JNI_TRUE;
 190             }
 191         }
 192     }
 193     return JNI_FALSE;
 194 }
 195 
 196 static JDgaLibInitFunc          Solaris_DGA_LibInit;
 197 static JDgaGetLockFunc          Solaris_DGA_GetLock;
 198 static JDgaReleaseLockFunc      Solaris_DGA_ReleaseLock;
 199 static JDgaXRequestSentFunc     Solaris_DGA_XRequestSent;
 200 static JDgaLibDisposeFunc       Solaris_DGA_LibDispose;
 201 static int firstInitDone = 0;
 202 
 203 #pragma weak JDgaLibInit = Solaris_DGA_LibInit
 204 
 205 static JDgaStatus
 206 Solaris_DGA_LibInit(JNIEnv *env, JDgaLibInfo *ppInfo)
 207 {
 208     /* Note: DGA_INIT can be called multiple times according to docs */
 209     DEBUG_PRINT(("DGA_INIT called\n"));
 210     DGA_INIT();
 211 
 212     if (!Solaris_DGA_Available(ppInfo->display)) {
 213         return JDGA_FAILED;
 214     }
 215     Solaris_DGA_XineramaInit(ppInfo->display);
 216 
 217     ppInfo->pGetLock = Solaris_DGA_GetLock;
 218     ppInfo->pReleaseLock = Solaris_DGA_ReleaseLock;
 219     ppInfo->pXRequestSent = Solaris_DGA_XRequestSent;
 220     ppInfo->pLibDispose = Solaris_DGA_LibDispose;
 221 
 222     return JDGA_SUCCESS;
 223 }
 224 
 225 static JDgaStatus
 226 Solaris_DGA_GetLock(JNIEnv *env, Display *display, void **dgaDev,
 227                         Drawable drawable, JDgaSurfaceInfo *pSurface,
 228                         jint lox, jint loy, jint hix, jint hiy)
 229 {
 230     SolarisDgaLibInfo *pDevInfo;
 231     SolarisDgaLibInfo *pCachedInfo = cachedInfo;
 232     int vis;
 233     int dlox, dloy, dhix, dhiy;
 234     int i;
 235     int type, site;
 236     unsigned long k;
 237     Drawable prev_virtual_drawable = 0;
 238     Dga_drawable dgaDrawable;
 239 
 240     if (*dgaDev) {
 241         if (((SolarisDgaLibInfo *)(*dgaDev))->drawable != drawable) {
 242             *dgaDev = 0;
 243         }
 244     }
 245 
 246     if (*dgaDev == 0) {
 247         pCachedInfo = cachedInfo;
 248         for (i = 0 ; (i < MAX_CACHED_INFO) && (pCachedInfo->drawable) ;
 249              i++, pCachedInfo++) {
 250             if (pCachedInfo->drawable == drawable) {
 251                 *dgaDev = pCachedInfo;
 252                 break;
 253             }
 254         }
 255         if (*dgaDev == 0) {
 256             if (i < MAX_CACHED_INFO) { /* slot can be used for new info */
 257                  *dgaDev = pCachedInfo;
 258             } else {
 259                 pCachedInfo = cachedInfo;
 260                 /* find the least used slot but does not handle an overflow of
 261                    the counter */
 262                 for (i = 0, k = 0xffffffff; i < MAX_CACHED_INFO ;
 263                      i++, pCachedInfo++) {
 264                     if (k > pCachedInfo->count) {
 265                         k = pCachedInfo->count;
 266                         *dgaDev = pCachedInfo;
 267                     }
 268                     pCachedInfo->count = 0; /* reset all counters */
 269                 }
 270                 pCachedInfo = *dgaDev;
 271                 if (pCachedInfo->winInfo.dgaDraw != 0) {
 272                     XDgaUnGrabDrawable(pCachedInfo->winInfo.dgaDraw);
 273                 }
 274                 pCachedInfo->winInfo.dgaDraw = 0;
 275                 /* the slot might be used for another device */
 276                 pCachedInfo->devInfo = 0;
 277             }
 278         }
 279     }
 280 
 281     pDevInfo = *dgaDev;
 282     pDevInfo->drawable = drawable;
 283 
 284     prev_virtual_drawable = pDevInfo->virtual_drawable;
 285     pDevInfo->virtual_drawable = GetVirtualDrawable(display, drawable);
 286     if (pDevInfo->virtual_drawable == NULL) {
 287         /* this usually means that the drawable is spanned across
 288            screens in xinerama mode - we can't handle this for now */
 289         return JDGA_FAILED;
 290     } else {
 291         /* check if the drawable has been moved to another screen
 292            since last time */
 293         if (pDevInfo->winInfo.dgaDraw != 0 &&
 294             pDevInfo->virtual_drawable != prev_virtual_drawable) {
 295             XDgaUnGrabDrawable(pDevInfo->winInfo.dgaDraw);
 296             pDevInfo->winInfo.dgaDraw = 0;
 297         }
 298     }
 299 
 300     pDevInfo->count++;
 301 
 302     if (pDevInfo->winInfo.dgaDraw == 0) {
 303         pDevInfo->winInfo.dgaDraw = XDgaGrabDrawable(display, pDevInfo->virtual_drawable);
 304         if (pDevInfo->winInfo.dgaDraw == 0) {
 305             DEBUG_PRINT(("DgaGrabDrawable failed for 0x%08x\n", drawable));
 306             return JDGA_UNAVAILABLE;
 307         }
 308         type = dga_draw_type(pDevInfo->winInfo.dgaDraw);
 309         if (type != DGA_DRAW_PIXMAP &&
 310             mmap_dgaDev(pDevInfo, pDevInfo->winInfo.dgaDraw) != JDGA_SUCCESS) {
 311             DEBUG_PRINT(("memory map failed for 0x%08x (depth = %d)\n",
 312                          drawable, dga_draw_depth(pDevInfo->winInfo.dgaDraw)));
 313             XDgaUnGrabDrawable(pDevInfo->winInfo.dgaDraw);
 314             pDevInfo->winInfo.dgaDraw = 0;
 315             return JDGA_UNAVAILABLE;
 316         }
 317     } else {
 318         type = dga_draw_type(pDevInfo->winInfo.dgaDraw);
 319     }
 320 
 321     if (needsSync) {
 322         XSync(display, False);
 323         needsSync = JNI_FALSE;
 324     }
 325 
 326     dgaDrawable = pDevInfo->winInfo.dgaDraw;
 327 
 328     DGA_DRAW_LOCK(dgaDrawable, -1);
 329 
 330     site = dga_draw_site(dgaDrawable);
 331     if (type == DGA_DRAW_PIXMAP) {
 332         if (site == DGA_SITE_SYSTEM) {
 333             pDevInfo->winInfo.mapDepth = dga_draw_depth(dgaDrawable);
 334             pDevInfo->winInfo.mapAddr = dga_draw_address(dgaDrawable);
 335             dga_draw_bbox(dgaDrawable, &dlox, &dloy, &dhix, &dhiy);
 336             pDevInfo->winInfo.mapWidth = dhix;
 337             pDevInfo->winInfo.mapHeight = dhiy;
 338             if (pDevInfo->winInfo.mapDepth == 8) {
 339                 pDevInfo->winInfo.mapLineStride = dga_draw_linebytes(dgaDrawable);
 340                 pDevInfo->winInfo.mapPixelStride = 1;
 341             } else {
 342                 pDevInfo->winInfo.mapLineStride = dga_draw_linebytes(dgaDrawable)/4;
 343                 pDevInfo->winInfo.mapPixelStride = 4;
 344             }
 345         } else {
 346             XDgaUnGrabDrawable(dgaDrawable);
 347             pDevInfo->winInfo.dgaDraw = 0;
 348             return JDGA_UNAVAILABLE;
 349         }
 350     } else {
 351         if (site == DGA_SITE_NULL) {
 352             DEBUG_PRINT(("zombie drawable = 0x%08x\n", dgaDrawable));
 353             DGA_DRAW_UNLOCK(dgaDrawable);
 354             unmap_dgaDev(pDevInfo);
 355             XDgaUnGrabDrawable(dgaDrawable);
 356             pDevInfo->winInfo.dgaDraw = 0;
 357             return JDGA_UNAVAILABLE;
 358         }
 359         dga_draw_bbox(dgaDrawable, &dlox, &dloy, &dhix, &dhiy);
 360     }
 361 
 362     /* get the screen address of the drawable */
 363     dhix += dlox;
 364     dhiy += dloy;
 365     DEBUG_PRINT(("window at (%d, %d) => (%d, %d)\n", dlox, dloy, dhix, dhiy));
 366     pSurface->window.lox = dlox;
 367     pSurface->window.loy = dloy;
 368     pSurface->window.hix = dhix;
 369     pSurface->window.hiy = dhiy;
 370 
 371             /* translate rendering coordinates relative to device bbox */
 372     lox += dlox;
 373     loy += dloy;
 374     hix += dlox;
 375     hiy += dloy;
 376     DEBUG_PRINT(("render at (%d, %d) => (%d, %d)\n", lox, loy, hix, hiy));
 377 
 378     vis = dga_draw_visibility(dgaDrawable);
 379     switch (vis) {
 380     case DGA_VIS_UNOBSCURED:
 381         pSurface->visible.lox = max(dlox, lox);
 382         pSurface->visible.loy = max(dloy, loy);
 383         pSurface->visible.hix = min(dhix, hix);
 384         pSurface->visible.hiy = min(dhiy, hiy);
 385         DEBUG_PRINT(("unobscured vis at (%d, %d) => (%d, %d)\n",
 386                      pSurface->visible.lox,
 387                      pSurface->visible.loy,
 388                      pSurface->visible.hix,
 389                      pSurface->visible.hiy));
 390         break;
 391     case DGA_VIS_PARTIALLY_OBSCURED: {
 392         /*
 393          * fix for #4305271
 394          * the dga_draw_clipinfo call returns the clipping bounds
 395          * in short ints, but use only full size ints for all comparisons.
 396          */
 397         short *ptr;
 398         int x0, y0, x1, y1;
 399         int cliplox, cliploy, cliphix, cliphiy;
 400 
 401         /*
 402          * iterate to find out whether the clipped blit draws to a
 403          * single clipping rectangle
 404          */
 405         cliplox = cliphix = lox;
 406         cliploy = cliphiy = loy;
 407         ptr = dga_draw_clipinfo(dgaDrawable);
 408         while (*ptr != DGA_Y_EOL) {
 409             y0 = *ptr++;
 410             y1 = *ptr++;
 411             DEBUG_PRINT(("DGA y range loy=%d hiy=%d\n", y0, y1));
 412             if (y0 < loy) {
 413                 y0 = loy;
 414             }
 415             if (y1 > hiy) {
 416                 y1 = hiy;
 417             }
 418             while (*ptr != DGA_X_EOL) {
 419                 x0 = *ptr++;
 420                 x1 = *ptr++;
 421                 DEBUG_PRINT(("  DGA x range lox=%d hix=%d\n", x0, x1));
 422                 if (x0 < lox) {
 423                     x0 = lox;
 424                 }
 425                 if (x1 > hix) {
 426                     x1 = hix;
 427                 }
 428                 if (x0 < x1 && y0 < y1) {
 429                     if (cliploy == cliphiy) {
 430                                 /* First rectangle intersection */
 431                         cliplox = x0;
 432                         cliploy = y0;
 433                         cliphix = x1;
 434                         cliphiy = y1;
 435                     } else {
 436                                 /* Can we merge this rect with previous? */
 437                         if (cliplox == x0 && cliphix == x1 &&
 438                             cliploy <= y1 && cliphiy >= y0)
 439                             {
 440                                 /* X ranges match, Y ranges touch */
 441                                 /* => absorb the Y ranges together */
 442                                 cliploy = min(cliploy, y0);
 443                                 cliphiy = max(cliphiy, y1);
 444                             } else if (cliploy == y0 && cliphiy == y1 &&
 445                                        cliplox <= x1 && cliphix >= x0)
 446                                 {
 447                                     /* Y ranges match, X ranges touch */
 448                                     /* => Absorb the X ranges together */
 449                                     cliplox = min(cliplox, x0);
 450                                     cliphix = max(cliphix, x1);
 451                                 } else {
 452                                     /* Assertion: any other combination */
 453                                     /* means non-rectangular intersect */
 454                                     DGA_DRAW_UNLOCK(dgaDrawable);
 455                                     return JDGA_FAILED;
 456                                 }
 457                     }
 458                 }
 459             }
 460             ptr++; /* advance past DGA_X_EOL */
 461         }
 462         DEBUG_PRINT(("DGA drawable fits\n"));
 463         pSurface->visible.lox = cliplox;
 464         pSurface->visible.loy = cliploy;
 465         pSurface->visible.hix = cliphix;
 466         pSurface->visible.hiy = cliphiy;
 467         break;
 468     }
 469     case DGA_VIS_FULLY_OBSCURED:
 470         pSurface->visible.lox =
 471             pSurface->visible.hix = lox;
 472         pSurface->visible.loy =
 473             pSurface->visible.hiy = loy;
 474         DEBUG_PRINT(("fully obscured vis\n"));
 475         break;
 476     default:
 477         DEBUG_PRINT(("unknown visibility = %d!\n", vis));
 478         DGA_DRAW_UNLOCK(dgaDrawable);
 479         return JDGA_FAILED;
 480     }
 481 
 482     pSurface->basePtr = pDevInfo->winInfo.mapAddr;
 483     pSurface->surfaceScan = pDevInfo->winInfo.mapLineStride;
 484     pSurface->surfaceWidth = pDevInfo->winInfo.mapWidth;
 485     pSurface->surfaceHeight = pDevInfo->winInfo.mapHeight;
 486     pSurface->surfaceDepth = pDevInfo->winInfo.mapDepth;
 487 
 488     return JDGA_SUCCESS;
 489 }
 490 
 491 static JDgaStatus
 492 Solaris_DGA_ReleaseLock(JNIEnv *env, void *dgaDev, Drawable drawable)
 493 {
 494     SolarisDgaLibInfo *pDevInfo = (SolarisDgaLibInfo *) dgaDev;
 495 
 496     if (pDevInfo != 0 && pDevInfo->drawable == drawable &&
 497         pDevInfo->winInfo.dgaDraw != 0) {
 498         DGA_DRAW_UNLOCK(pDevInfo->winInfo.dgaDraw);
 499     }
 500     return JDGA_SUCCESS;
 501 }
 502 
 503 static void
 504 Solaris_DGA_XRequestSent(JNIEnv *env, void *dgaDev, Drawable drawable)
 505 {
 506     needsSync = JNI_TRUE;
 507 }
 508 
 509 static void
 510 Solaris_DGA_LibDispose(JNIEnv *env)
 511 {
 512     SolarisDgaLibInfo *pCachedInfo = cachedInfo;
 513     SolarisJDgaDevInfo *curDevInfo = devicesInfo;
 514     int i;
 515 
 516     for (i = 0 ; (i < MAX_CACHED_INFO) && (pCachedInfo->drawable) ;
 517          i++, pCachedInfo++) {
 518         if (pCachedInfo->winInfo.dgaDraw != 0) {
 519             if (dga_draw_type(pCachedInfo->winInfo.dgaDraw) == DGA_DRAW_WINDOW &&
 520                 pCachedInfo->winInfo.mapDepth != 0) {
 521                 unmap_dgaDev(pCachedInfo);
 522             }
 523             XDgaUnGrabDrawable(pCachedInfo->winInfo.dgaDraw);
 524             pCachedInfo->winInfo.dgaDraw = 0;
 525         }
 526     }
 527     for (i = 0; (i < MAX_FB_TYPES) && (curDevInfo->visidName);
 528          i++, curDevInfo++) {
 529         curDevInfo->function->devclose(curDevInfo);
 530         free(curDevInfo->visidName);
 531     }
 532 }
 533 #endif