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 
GetVirtualDrawableStub(Display * display,Drawable drawable)82 Drawable GetVirtualDrawableStub(Display *display, Drawable drawable) {
83     return drawable;
84 }
85 static GetVirtualDrawableFunc * GetVirtualDrawable = GetVirtualDrawableStub;
86 
Solaris_DGA_XineramaInit(Display * display)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 
getDevInfo(Dga_drawable dgadraw)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
mmap_dgaDev(SolarisDgaLibInfo * libInfo,Dga_drawable dgadraw)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
unmap_dgaDev(SolarisDgaLibInfo * pDevInfo)165 unmap_dgaDev(SolarisDgaLibInfo *pDevInfo)
166 {
167     DEBUG_PRINT(("winclose() called\n"));
168    (*pDevInfo->devInfo->function->winclose)(&(pDevInfo->winInfo));
169 }
170 
171 static jboolean
Solaris_DGA_Available(Display * display)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
Solaris_DGA_LibInit(JNIEnv * env,JDgaLibInfo * ppInfo)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
Solaris_DGA_GetLock(JNIEnv * env,Display * display,void ** dgaDev,Drawable drawable,JDgaSurfaceInfo * pSurface,jint lox,jint loy,jint hix,jint hiy)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
Solaris_DGA_ReleaseLock(JNIEnv * env,void * dgaDev,Drawable drawable)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
Solaris_DGA_XRequestSent(JNIEnv * env,void * dgaDev,Drawable drawable)504 Solaris_DGA_XRequestSent(JNIEnv *env, void *dgaDev, Drawable drawable)
505 {
506     needsSync = JNI_TRUE;
507 }
508 
509 static void
Solaris_DGA_LibDispose(JNIEnv * env)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
534