1/*
2 * Copyright (c) 2003, 2015, 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#include <jlong.h>
27
28#include "sun_java2d_opengl_GLXSurfaceData.h"
29
30#include "OGLRenderQueue.h"
31#include "GLXGraphicsConfig.h"
32#include "GLXSurfaceData.h"
33#include "awt_Component.h"
34#include "awt_GraphicsEnv.h"
35
36/**
37 * The methods in this file implement the native windowing system specific
38 * layer (GLX) for the OpenGL-based Java 2D pipeline.
39 */
40
41#ifndef HEADLESS
42
43extern LockFunc       OGLSD_Lock;
44extern GetRasInfoFunc OGLSD_GetRasInfo;
45extern UnlockFunc     OGLSD_Unlock;
46extern DisposeFunc    OGLSD_Dispose;
47
48extern void
49    OGLSD_SetNativeDimensions(JNIEnv *env, OGLSDOps *oglsdo, jint w, jint h);
50
51jboolean surfaceCreationFailed = JNI_FALSE;
52
53#endif /* !HEADLESS */
54
55JNIEXPORT void JNICALL
56Java_sun_java2d_opengl_GLXSurfaceData_initOps(JNIEnv *env, jobject glxsd,
57                                              jobject peer, jlong aData)
58{
59#ifndef HEADLESS
60    GLXSDOps *glxsdo = (GLXSDOps *)malloc(sizeof(GLXSDOps));
61
62    if (glxsdo == NULL) {
63        JNU_ThrowOutOfMemoryError(env, "creating native GLX ops");
64        return;
65    }
66
67    OGLSDOps *oglsdo = (OGLSDOps *)SurfaceData_InitOps(env, glxsd,
68                                                       sizeof(OGLSDOps));
69    if (oglsdo == NULL) {
70        free(glxsdo);
71        JNU_ThrowOutOfMemoryError(env, "Initialization of SurfaceData failed.");
72        return;
73    }
74
75    J2dTraceLn(J2D_TRACE_INFO, "GLXSurfaceData_initOps");
76
77    oglsdo->privOps = glxsdo;
78
79    oglsdo->sdOps.Lock       = OGLSD_Lock;
80    oglsdo->sdOps.GetRasInfo = OGLSD_GetRasInfo;
81    oglsdo->sdOps.Unlock     = OGLSD_Unlock;
82    oglsdo->sdOps.Dispose    = OGLSD_Dispose;
83
84    oglsdo->drawableType = OGLSD_UNDEFINED;
85    oglsdo->activeBuffer = GL_FRONT;
86    oglsdo->needsInit = JNI_TRUE;
87
88    if (peer != NULL) {
89        glxsdo->window = JNU_CallMethodByName(env, NULL, peer,
90                                              "getContentWindow", "()J").j;
91    } else {
92        glxsdo->window = 0;
93    }
94    glxsdo->configData = (AwtGraphicsConfigDataPtr)jlong_to_ptr(aData);
95    if (glxsdo->configData == NULL) {
96        free(glxsdo);
97        JNU_ThrowNullPointerException(env,
98                                 "Native GraphicsConfig data block missing");
99        return;
100    }
101
102    if (glxsdo->configData->glxInfo == NULL) {
103        free(glxsdo);
104        JNU_ThrowNullPointerException(env, "GLXGraphicsConfigInfo missing");
105        return;
106    }
107#endif /* HEADLESS */
108}
109
110#ifndef HEADLESS
111
112/**
113 * This function disposes of any native windowing system resources associated
114 * with this surface.
115 */
116void
117OGLSD_DestroyOGLSurface(JNIEnv *env, OGLSDOps *oglsdo)
118{
119    J2dTraceLn(J2D_TRACE_INFO, "OGLSD_DestroyOGLSurface");
120    // X Window is free'd later by AWT code...
121}
122
123/**
124 * Makes the given context current to its associated "scratch" surface.  If
125 * the operation is successful, this method will return JNI_TRUE; otherwise,
126 * returns JNI_FALSE.
127 */
128static jboolean
129GLXSD_MakeCurrentToScratch(JNIEnv *env, OGLContext *oglc)
130{
131    GLXCtxInfo *ctxInfo;
132
133    J2dTraceLn(J2D_TRACE_INFO, "GLXSD_MakeCurrentToScratch");
134
135    if (oglc == NULL) {
136        J2dRlsTraceLn(J2D_TRACE_ERROR,
137                      "GLXSD_MakeCurrentToScratch: context is null");
138        return JNI_FALSE;
139    }
140
141    ctxInfo = (GLXCtxInfo *)oglc->ctxInfo;
142    if (!j2d_glXMakeContextCurrent(awt_display,
143                                   ctxInfo->scratchSurface,
144                                   ctxInfo->scratchSurface,
145                                   ctxInfo->context))
146    {
147        J2dRlsTraceLn(J2D_TRACE_ERROR,
148                      "GLXSD_MakeCurrentToScratch: could not make current");
149        return JNI_FALSE;
150    }
151
152    return JNI_TRUE;
153}
154
155/**
156 * Returns a pointer (as a jlong) to the native GLXGraphicsConfigInfo
157 * associated with the given OGLSDOps.  This method can be called from
158 * shared code to retrieve the native GraphicsConfig data in a platform-
159 * independent manner.
160 */
161jlong
162OGLSD_GetNativeConfigInfo(OGLSDOps *oglsdo)
163{
164    GLXSDOps *glxsdo;
165
166    if (oglsdo == NULL) {
167        J2dRlsTraceLn(J2D_TRACE_ERROR,
168                      "OGLSD_GetNativeConfigInfo: ops are null");
169        return 0L;
170    }
171
172    glxsdo = (GLXSDOps *)oglsdo->privOps;
173    if (glxsdo == NULL) {
174        J2dRlsTraceLn(J2D_TRACE_ERROR,
175                      "OGLSD_GetNativeConfigInfo: glx ops are null");
176        return 0L;
177    }
178
179    if (glxsdo->configData == NULL) {
180        J2dRlsTraceLn(J2D_TRACE_ERROR,
181                      "OGLSD_GetNativeConfigInfo: config data is null");
182        return 0L;
183    }
184
185    return ptr_to_jlong(glxsdo->configData->glxInfo);
186}
187
188/**
189 * Makes the given GraphicsConfig's context current to its associated
190 * "scratch" surface.  If there is a problem making the context current,
191 * this method will return NULL; otherwise, returns a pointer to the
192 * OGLContext that is associated with the given GraphicsConfig.
193 */
194OGLContext *
195OGLSD_SetScratchSurface(JNIEnv *env, jlong pConfigInfo)
196{
197    GLXGraphicsConfigInfo *glxInfo =
198        (GLXGraphicsConfigInfo *)jlong_to_ptr(pConfigInfo);
199    OGLContext *oglc;
200
201    J2dTraceLn(J2D_TRACE_INFO, "OGLSD_SetScratchContext");
202
203    if (glxInfo == NULL) {
204        J2dRlsTraceLn(J2D_TRACE_ERROR,
205                      "OGLSD_SetScratchContext: glx config info is null");
206        return NULL;
207    }
208
209    oglc = glxInfo->context;
210    if (!GLXSD_MakeCurrentToScratch(env, oglc)) {
211        return NULL;
212    }
213
214    if (OGLC_IS_CAP_PRESENT(oglc, CAPS_EXT_FBOBJECT)) {
215        // the GL_EXT_framebuffer_object extension is present, so this call
216        // will ensure that we are bound to the scratch pbuffer (and not
217        // some other framebuffer object)
218        j2d_glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
219    }
220
221    return oglc;
222}
223
224/**
225 * Makes a context current to the given source and destination
226 * surfaces.  If there is a problem making the context current, this method
227 * will return NULL; otherwise, returns a pointer to the OGLContext that is
228 * associated with the destination surface.
229 */
230OGLContext *
231OGLSD_MakeOGLContextCurrent(JNIEnv *env, OGLSDOps *srcOps, OGLSDOps *dstOps)
232{
233    GLXSDOps *dstGLXOps = (GLXSDOps *)dstOps->privOps;
234    OGLContext *oglc;
235
236    J2dTraceLn(J2D_TRACE_INFO, "OGLSD_MakeOGLContextCurrent");
237
238    oglc = dstGLXOps->configData->glxInfo->context;
239    if (oglc == NULL) {
240        J2dRlsTraceLn(J2D_TRACE_ERROR,
241                      "OGLSD_MakeOGLContextCurrent: context is null");
242        return NULL;
243    }
244
245    if (dstOps->drawableType == OGLSD_FBOBJECT) {
246        OGLContext *currentContext = OGLRenderQueue_GetCurrentContext();
247
248        // first make sure we have a current context (if the context isn't
249        // already current to some drawable, we will make it current to
250        // its scratch surface)
251        if (oglc != currentContext) {
252            if (!GLXSD_MakeCurrentToScratch(env, oglc)) {
253                return NULL;
254            }
255        }
256
257        // now bind to the fbobject associated with the destination surface;
258        // this means that all rendering will go into the fbobject destination
259        // (note that we unbind the currently bound texture first; this is
260        // recommended procedure when binding an fbobject)
261        j2d_glBindTexture(dstOps->textureTarget, 0);
262        j2d_glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, dstOps->fbobjectID);
263    } else {
264        GLXSDOps *srcGLXOps = (GLXSDOps *)srcOps->privOps;
265        GLXCtxInfo *ctxinfo = (GLXCtxInfo *)oglc->ctxInfo;
266
267        // make the context current
268        if (!j2d_glXMakeContextCurrent(awt_display,
269                                       dstGLXOps->drawable,
270                                       srcGLXOps->drawable,
271                                       ctxinfo->context))
272        {
273            J2dRlsTraceLn(J2D_TRACE_ERROR,
274                "OGLSD_MakeOGLContextCurrent: could not make current");
275            return NULL;
276        }
277
278        if (OGLC_IS_CAP_PRESENT(oglc, CAPS_EXT_FBOBJECT)) {
279            // the GL_EXT_framebuffer_object extension is present, so we
280            // must bind to the default (windowing system provided)
281            // framebuffer
282            j2d_glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
283        }
284    }
285
286    return oglc;
287}
288
289/**
290 * This function initializes a native window surface and caches the window
291 * bounds in the given OGLSDOps.  Returns JNI_TRUE if the operation was
292 * successful; JNI_FALSE otherwise.
293 */
294jboolean
295OGLSD_InitOGLWindow(JNIEnv *env, OGLSDOps *oglsdo)
296{
297    GLXSDOps *glxsdo;
298    Window window;
299    XWindowAttributes attr;
300
301    J2dTraceLn(J2D_TRACE_INFO, "OGLSD_InitOGLWindow");
302
303    if (oglsdo == NULL) {
304        J2dRlsTraceLn(J2D_TRACE_ERROR,
305                      "OGLSD_InitOGLWindow: ops are null");
306        return JNI_FALSE;
307    }
308
309    glxsdo = (GLXSDOps *)oglsdo->privOps;
310    if (glxsdo == NULL) {
311        J2dRlsTraceLn(J2D_TRACE_ERROR,
312                      "OGLSD_InitOGLWindow: glx ops are null");
313        return JNI_FALSE;
314    }
315
316    window = glxsdo->window;
317    if (window == 0) {
318        J2dRlsTraceLn(J2D_TRACE_ERROR,
319                      "OGLSD_InitOGLWindow: window is invalid");
320        return JNI_FALSE;
321    }
322
323    XGetWindowAttributes(awt_display, window, &attr);
324    oglsdo->width = attr.width;
325    oglsdo->height = attr.height;
326
327    oglsdo->drawableType = OGLSD_WINDOW;
328    oglsdo->isOpaque = JNI_TRUE;
329    oglsdo->xOffset = 0;
330    oglsdo->yOffset = 0;
331    glxsdo->drawable = window;
332    glxsdo->xdrawable = window;
333
334    J2dTraceLn2(J2D_TRACE_VERBOSE, "  created window: w=%d h=%d",
335                oglsdo->width, oglsdo->height);
336
337    return JNI_TRUE;
338}
339
340static int
341GLXSD_BadAllocXErrHandler(Display *display, XErrorEvent *xerr)
342{
343    if (xerr->error_code == BadAlloc) {
344        surfaceCreationFailed = JNI_TRUE;
345    }
346    return 0;
347}
348
349void
350OGLSD_SwapBuffers(JNIEnv *env, jlong window)
351{
352    J2dTraceLn(J2D_TRACE_INFO, "OGLSD_SwapBuffers");
353
354    if (window == 0L) {
355        J2dRlsTraceLn(J2D_TRACE_ERROR,
356                      "OGLSD_SwapBuffers: window is null");
357        return;
358    }
359
360    j2d_glXSwapBuffers(awt_display, (Window)window);
361}
362
363// needed by Mac OS X port, no-op on other platforms
364void
365OGLSD_Flush(JNIEnv *env)
366{
367}
368
369#endif /* !HEADLESS */
370