diff --git a/src/native/linux/org_lwjgl_opengl_Display.c b/src/native/linux/org_lwjgl_opengl_Display.c new file mode 100644 index 00000000..34cef0b6 --- /dev/null +++ b/src/native/linux/org_lwjgl_opengl_Display.c @@ -0,0 +1,791 @@ +/* + * Copyright (c) 2002-2004 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * $Id$ + * + * Linux specific display functions. + * + * @author elias_naur + * @version $Revision$ + */ + +#include +#include +#include +#include +#include +#include +#include +#include "common_tools.h" +#include "extgl.h" +#include "extgl_glx.h" +#include "Window.h" +#include "display.h" +#include "org_lwjgl_opengl_LinuxDisplay.h" + +#define USEGLX13 extgl_Extensions.GLX13 +#define ERR_MSG_SIZE 1024 + +typedef struct { + unsigned long flags; + unsigned long functions; + unsigned long decorations; + long input_mode; + unsigned long status; +} MotifWmHints; + +#define MWM_HINTS_DECORATIONS (1L << 1) + +typedef enum {FULLSCREEN_LEGACY, FULLSCREEN_NETWM, WINDOWED} window_mode; + +static GLXContext context = NULL; // OpenGL rendering context +static GLXFBConfig *configs = NULL; +static GLXWindow glx_window; +static XVisualInfo *vis_info = NULL; + +static Atom delete_atom; +static Colormap cmap; +static Window current_win; +static window_mode current_window_mode; +static int current_height; +static int current_width; + +static bool input_released; + +static bool dirty; +static bool vsync_enabled; +static bool minimized; +static bool focused; +static bool closerequested; +static bool grab; + +static int current_screen; +static Display *display_connection = NULL; +static int display_connection_usage = 0; +static bool async_x_error; +static char error_message[ERR_MSG_SIZE]; +static Atom warp_atom; + +GLXFBConfig getCurrentGLXFBConfig(void) { + return configs[0]; +} + +GLXContext getCurrentGLXContext(void) { + return context; +} + +int getCurrentScreen(void) { + return current_screen; +} + +bool checkXError(JNIEnv *env) { + XSync(getDisplay(), False); + if (async_x_error) { + async_x_error = false; + if (env != NULL) + throwException(env, error_message); + else + printfDebug(error_message); + return false; + } else + return true; +} + +static int errorHandler(Display *disp, XErrorEvent *error) { + char err_msg_buffer[ERR_MSG_SIZE]; + XGetErrorText(disp, error->error_code, err_msg_buffer, ERR_MSG_SIZE); + err_msg_buffer[ERR_MSG_SIZE - 1] = '\0'; + snprintf(error_message, ERR_MSG_SIZE, "X Error - serial: %d, error_code: %s, request_code: %d, minor_code: %d", (int)error->serial, err_msg_buffer, (int)error->request_code, (int)error->minor_code); + error_message[ERR_MSG_SIZE - 1] = '\0'; + async_x_error = true; + return 0; +} + + +Display *getDisplay(void) { + return display_connection; +} + +Display *incDisplay(JNIEnv *env) { + if (display_connection_usage == 0) { + async_x_error = false; + XSetErrorHandler(errorHandler); + display_connection = XOpenDisplay(NULL); + if (display_connection == NULL) { + if (env != NULL) + throwException(env, "Could not open X display connection"); + else + printfDebug("Could not open X display connection\n"); + return NULL; + } + warp_atom = XInternAtom(display_connection, "_LWJGL_WARP", False); + } + async_x_error = false; + display_connection_usage++; + return display_connection; +} + +Atom getWarpAtom(void) { + return warp_atom; +} + +void decDisplay(void) { + display_connection_usage--; + if (display_connection_usage == 0) { + XCloseDisplay(display_connection); + display_connection = NULL; + } +} + +static void waitMapped(Window win) { + XEvent event; + do { + XMaskEvent(getDisplay(), StructureNotifyMask, &event); + } while ((event.type != MapNotify) || (event.xmap.event != win)); +} + +static void updateInputGrab(void) { + updatePointerGrab(); + updateKeyboardGrab(); +} + +static void setRepeatMode(int mode) { + XKeyboardControl repeat_mode; + repeat_mode.auto_repeat_mode = mode; + Display *disp = XOpenDisplay(NULL); + if (disp == NULL) { + printfDebug("Could not open display to set repeat mode\n"); + return; + } + XChangeKeyboardControl(disp, KBAutoRepeatMode, &repeat_mode); + XCloseDisplay(disp); +} + +static void setDecorations(int dec) { + Atom motif_hints_atom = XInternAtom(getDisplay(), "_MOTIF_WM_HINTS", False); + MotifWmHints motif_hints; + motif_hints.flags = MWM_HINTS_DECORATIONS; + motif_hints.decorations = dec; + XChangeProperty (getDisplay(), getCurrentWindow(), motif_hints_atom, motif_hints_atom, 32, PropModeReplace, (unsigned char *)&motif_hints, sizeof(MotifWmHints)/sizeof(long)); +} + +bool releaseInput(void) { + if (isLegacyFullscreen() || input_released) + return false; + input_released = true; + setRepeatMode(AutoRepeatModeDefault); + updateInputGrab(); + if (current_window_mode == FULLSCREEN_NETWM) { + XIconifyWindow(getDisplay(), getCurrentWindow(), getCurrentScreen()); + resetDisplayMode(getCurrentScreen(), true); + } + return true; +} + +static void acquireInput(void) { + if (isLegacyFullscreen() || !input_released) + return; + input_released = false; + setRepeatMode(AutoRepeatModeOff); + updateInputGrab(); + if (current_window_mode == FULLSCREEN_NETWM) { + temporaryRestoreMode(getCurrentScreen()); + } +} + +bool isLegacyFullscreen(void) { + return current_window_mode == FULLSCREEN_LEGACY; +} + +bool shouldGrab(void) { + return !input_released && grab; +} + +void setGrab(bool new_grab) { + if (new_grab != grab) { + grab = new_grab; + updateInputGrab(); + } +} + +static void checkInput(void) { + Window win; + int revert_mode; + XGetInputFocus(getDisplay(), &win, &revert_mode); + if (win == current_win) { + acquireInput(); + focused = true; + } +} + +void handleMessages(void) { + XEvent event; + Window win; + int revert_mode; + while (XPending(getDisplay()) > 0) { + XNextEvent(getDisplay(), &event); + if (XFilterEvent(&event, None) == True) + continue; + switch (event.type) { + case ClientMessage: + if (event.xclient.message_type == warp_atom) { + handleWarpEvent(&(event.xclient)); + } else if ((event.xclient.format == 32) && ((Atom)event.xclient.data.l[0] == delete_atom)) + closerequested = true; + break; + case FocusOut: + XGetInputFocus(getDisplay(), &win, &revert_mode); + if (win != current_win) { + releaseInput(); + focused = false; + } + break; + case FocusIn: + checkInput(); + break; + case MapNotify: + dirty = true; + minimized = false; + break; + case UnmapNotify: + dirty = true; + minimized = true; + break; + case Expose: + dirty = true; + break; + case ButtonPress: + handleButtonPress(&(event.xbutton)); + break; + case ButtonRelease: + handleButtonRelease(&(event.xbutton)); + break; + case MotionNotify: + handlePointerMotion(&(event.xmotion)); + break; + case KeyPress: + case KeyRelease: + handleKeyEvent(&(event.xkey)); + break; + } + } +} + +static void setWindowTitle(const char *title) { + XStoreName(getDisplay(), current_win, title); +} + +JNIEXPORT void JNICALL Java_org_lwjgl_opengl_LinuxDisplay_setTitle(JNIEnv * env, jobject this, jstring title_obj) { + const char * title = (*env)->GetStringUTFChars(env, title_obj, NULL); + setWindowTitle(title); + (*env)->ReleaseStringUTFChars(env, title_obj, title); +} + +static void destroyWindow(void) { + if (USEGLX13) + glXDestroyWindow(getDisplay(), glx_window); + XDestroyWindow(getDisplay(), current_win); + XFreeColormap(getDisplay(), cmap); + setRepeatMode(AutoRepeatModeDefault); +} + +static bool isNetWMFullscreenSupported() { + unsigned long nitems; + Atom actual_type; + int actual_format; + unsigned long bytes_after; + Atom *supported_list; + Atom netwm_supported_atom = XInternAtom(getDisplay(), "_NET_SUPPORTED", False); + int result = XGetWindowProperty(getDisplay(), RootWindow(getDisplay(), getCurrentScreen()), netwm_supported_atom, 0, 10000, False, AnyPropertyType, &actual_type, &actual_format, &nitems, &bytes_after, (void *)&supported_list); + if (result != Success) { + printfDebug("Unable to query _NET_SUPPORTED window property\n"); + return false; + } + Atom fullscreen_atom = XInternAtom(getDisplay(), "_NET_WM_STATE_FULLSCREEN", False); + bool supported = false; + unsigned long i; + for (i = 0; i < nitems; i++) { + if (fullscreen_atom == supported_list[i]) { + supported = true; + break; + } + } + XFree(supported_list); + return supported; +} + +JNIEXPORT void JNICALL Java_org_lwjgl_opengl_LinuxDisplay_reshape(JNIEnv *env, jobject this, jint x, jint y, jint width, jint height) { + XMoveWindow(getDisplay(), getCurrentWindow(), x, y); +} + +static bool createWindow(JNIEnv* env, int x, int y, int width, int height) { + bool undecorated = getBooleanProperty(env, "org.lwjgl.opengl.Window.undecorated"); + dirty = true; + focused = true; + minimized = false; + closerequested = false; + vsync_enabled = false; + grab = false; + Window root_win; + Window win; + XSetWindowAttributes attribs; + int attribmask; + + input_released = false; + current_width = width; + current_height = height; + root_win = RootWindow(getDisplay(), getCurrentScreen()); + cmap = XCreateColormap(getDisplay(), root_win, vis_info->visual, AllocNone); + attribs.colormap = cmap; + attribs.event_mask = ExposureMask | FocusChangeMask | VisibilityChangeMask | StructureNotifyMask | KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask; + attribs.background_pixel = 0xFF000000; + attribs.win_gravity = NorthWestGravity; + attribmask = CWColormap | CWBackPixel | CWEventMask | CWWinGravity; + if (isLegacyFullscreen()) { + attribmask |= CWOverrideRedirect; + attribs.override_redirect = True; + } + win = XCreateWindow(getDisplay(), root_win, x, y, width, height, 0, vis_info->depth, InputOutput, vis_info->visual, attribmask, &attribs); + if (!checkXError(env)) { + XFreeColormap(getDisplay(), cmap); + return false; + } + printfDebug("Created window\n"); + current_win = win; + if (current_window_mode != WINDOWED || undecorated) { + // Use Motif decoration hint property and hope the window manager respects them + setDecorations(0); + } + XSizeHints * size_hints = XAllocSizeHints(); + size_hints->flags = PMinSize | PMaxSize; + size_hints->min_width = width; + size_hints->max_width = width; + size_hints->min_height = height; + size_hints->max_height = height; + XSetWMNormalHints(getDisplay(), win, size_hints); + XFree(size_hints); + delete_atom = XInternAtom(getDisplay(), "WM_DELETE_WINDOW", False); + XSetWMProtocols(getDisplay(), win, &delete_atom, 1); + if (current_window_mode == FULLSCREEN_NETWM) { + Atom fullscreen_atom = XInternAtom(getDisplay(), "_NET_WM_STATE_FULLSCREEN", False); + XChangeProperty(getDisplay(), getCurrentWindow(), XInternAtom(getDisplay(), "_NET_WM_STATE", False), + XInternAtom(getDisplay(), "ATOM", False), 32, PropModeReplace, (const unsigned char*)&fullscreen_atom, 1); + } + XMapRaised(getDisplay(), win); + waitMapped(win); + XClearWindow(getDisplay(), win); + setRepeatMode(AutoRepeatModeOff); + if (!checkXError(env)) { + destroyWindow(); + return false; + } + return true; +} + +Window getCurrentWindow(void) { + return current_win; +} + +int getWindowWidth(void) { + return current_width; +} + +int getWindowHeight(void) { + return current_height; +} + +JNIEXPORT void JNICALL Java_org_lwjgl_opengl_LinuxDisplay_update + (JNIEnv *env, jobject this) +{ + handleMessages(); +} + +static bool makeCurrent(void) { + if (USEGLX13) + return glXMakeContextCurrent(getDisplay(), glx_window, glx_window, context) == True; + else + return glXMakeCurrent(getDisplay(), getCurrentWindow(), context) == True; +} + +JNIEXPORT void JNICALL Java_org_lwjgl_opengl_LinuxDisplay_makeCurrent + (JNIEnv *env, jobject this) +{ + if (!makeCurrent()) + throwException(env, "Could not make display context current"); +} + +int convertToBPE(int bpp) { + int bpe; + switch (bpp) { + case 32: + case 24: + bpe = 8; + break; + case 16: /* Fall through */ + default: + bpe = 4; + break; + } + return bpe; +} + +GLXContext getCurrentContext(void) { + return context; +} + +static GLXFBConfig *chooseVisualGLX13FromBPP(JNIEnv *env, jobject pixel_format, int bpp, int drawable_type, bool double_buffer) { + jclass cls_pixel_format = (*env)->GetObjectClass(env, pixel_format); + int alpha = (int)(*env)->GetIntField(env, pixel_format, (*env)->GetFieldID(env, cls_pixel_format, "alpha", "I")); + int depth = (int)(*env)->GetIntField(env, pixel_format, (*env)->GetFieldID(env, cls_pixel_format, "depth", "I")); + int stencil = (int)(*env)->GetIntField(env, pixel_format, (*env)->GetFieldID(env, cls_pixel_format, "stencil", "I")); + int samples = (int)(*env)->GetIntField(env, pixel_format, (*env)->GetFieldID(env, cls_pixel_format, "samples", "I")); + int num_aux_buffers = (int)(*env)->GetIntField(env, pixel_format, (*env)->GetFieldID(env, cls_pixel_format, "num_aux_buffers", "I")); + int accum_bpp = (int)(*env)->GetIntField(env, pixel_format, (*env)->GetFieldID(env, cls_pixel_format, "accum_bpp", "I")); + int accum_alpha = (int)(*env)->GetIntField(env, pixel_format, (*env)->GetFieldID(env, cls_pixel_format, "accum_alpha", "I")); + bool stereo = (bool)(*env)->GetBooleanField(env, pixel_format, (*env)->GetFieldID(env, cls_pixel_format, "stereo", "Z")); + + int bpe = convertToBPE(bpp); + int accum_bpe = convertToBPE(accum_bpp); + attrib_list_t attrib_list; + initAttribList(&attrib_list); + putAttrib(&attrib_list, GLX_RENDER_TYPE); putAttrib(&attrib_list, GLX_RGBA_BIT); + putAttrib(&attrib_list, GLX_DOUBLEBUFFER); putAttrib(&attrib_list, double_buffer ? True : False); + putAttrib(&attrib_list, GLX_DRAWABLE_TYPE); putAttrib(&attrib_list, drawable_type); + putAttrib(&attrib_list, GLX_DEPTH_SIZE); putAttrib(&attrib_list, depth); + putAttrib(&attrib_list, GLX_RED_SIZE); putAttrib(&attrib_list, bpe); + putAttrib(&attrib_list, GLX_GREEN_SIZE); putAttrib(&attrib_list, bpe); + putAttrib(&attrib_list, GLX_BLUE_SIZE); putAttrib(&attrib_list, bpe); + putAttrib(&attrib_list, GLX_ALPHA_SIZE); putAttrib(&attrib_list, alpha); + putAttrib(&attrib_list, GLX_STENCIL_SIZE); putAttrib(&attrib_list, stencil); + putAttrib(&attrib_list, GLX_AUX_BUFFERS); putAttrib(&attrib_list, num_aux_buffers); + putAttrib(&attrib_list, GLX_ACCUM_RED_SIZE); putAttrib(&attrib_list, accum_bpe); + putAttrib(&attrib_list, GLX_ACCUM_GREEN_SIZE); putAttrib(&attrib_list, accum_bpe); + putAttrib(&attrib_list, GLX_ACCUM_BLUE_SIZE); putAttrib(&attrib_list, accum_bpe); + putAttrib(&attrib_list, GLX_ACCUM_ALPHA_SIZE); putAttrib(&attrib_list, accum_alpha); + putAttrib(&attrib_list, GLX_STEREO); putAttrib(&attrib_list, stereo ? True : False); + if (samples > 0 && extgl_Extensions.GLX_ARB_multisample) { + putAttrib(&attrib_list, GLX_SAMPLE_BUFFERS_ARB); putAttrib(&attrib_list, 1); + putAttrib(&attrib_list, GLX_SAMPLES_ARB); putAttrib(&attrib_list, samples); + } + putAttrib(&attrib_list, None); putAttrib(&attrib_list, None); + int num_formats = 0; + GLXFBConfig* configs = glXChooseFBConfig(getDisplay(), getCurrentScreen(), attrib_list.attribs, &num_formats); + if (num_formats > 0) { + return configs; + } else { + if (configs != NULL) + XFree(configs); + return NULL; + } +} + +GLXFBConfig *chooseVisualGLX13(JNIEnv *env, jobject pixel_format, bool use_display_bpp, int drawable_type, bool double_buffer) { + jclass cls_pixel_format = (*env)->GetObjectClass(env, pixel_format); + int bpp; + if (use_display_bpp) { + bpp = XDefaultDepthOfScreen(XScreenOfDisplay(getDisplay(), getCurrentScreen())); + GLXFBConfig *configs = chooseVisualGLX13FromBPP(env, pixel_format, bpp, drawable_type, double_buffer); + if (configs != NULL) + return configs; + else + bpp = 16; + } else + bpp = (int)(*env)->GetIntField(env, pixel_format, (*env)->GetFieldID(env, cls_pixel_format, "bpp", "I")); + return chooseVisualGLX13FromBPP(env, pixel_format, bpp, drawable_type, double_buffer); +} + +static XVisualInfo *chooseVisualGLX(JNIEnv *env, jobject pixel_format) { + int bpp = XDefaultDepthOfScreen(XScreenOfDisplay(getDisplay(), getCurrentScreen())); + jclass cls_pixel_format = (*env)->GetObjectClass(env, pixel_format); + int alpha = (int)(*env)->GetIntField(env, pixel_format, (*env)->GetFieldID(env, cls_pixel_format, "alpha", "I")); + int depth = (int)(*env)->GetIntField(env, pixel_format, (*env)->GetFieldID(env, cls_pixel_format, "depth", "I")); + int stencil = (int)(*env)->GetIntField(env, pixel_format, (*env)->GetFieldID(env, cls_pixel_format, "stencil", "I")); + int samples = (int)(*env)->GetIntField(env, pixel_format, (*env)->GetFieldID(env, cls_pixel_format, "samples", "I")); + int num_aux_buffers = (int)(*env)->GetIntField(env, pixel_format, (*env)->GetFieldID(env, cls_pixel_format, "num_aux_buffers", "I")); + int accum_bpp = (int)(*env)->GetIntField(env, pixel_format, (*env)->GetFieldID(env, cls_pixel_format, "accum_bpp", "I")); + int accum_alpha = (int)(*env)->GetIntField(env, pixel_format, (*env)->GetFieldID(env, cls_pixel_format, "accum_alpha", "I")); + bool stereo = (bool)(*env)->GetBooleanField(env, pixel_format, (*env)->GetFieldID(env, cls_pixel_format, "stereo", "Z")); + + int bpe = convertToBPE(bpp); + int accum_bpe = convertToBPE(accum_bpp); + attrib_list_t attrib_list; + initAttribList(&attrib_list); + putAttrib(&attrib_list, GLX_RGBA); + putAttrib(&attrib_list, GLX_DOUBLEBUFFER); + putAttrib(&attrib_list, GLX_DEPTH_SIZE); putAttrib(&attrib_list, depth); + putAttrib(&attrib_list, GLX_RED_SIZE); putAttrib(&attrib_list, bpe); + putAttrib(&attrib_list, GLX_GREEN_SIZE); putAttrib(&attrib_list, bpe); + putAttrib(&attrib_list, GLX_BLUE_SIZE); putAttrib(&attrib_list, bpe); + putAttrib(&attrib_list, GLX_ALPHA_SIZE); putAttrib(&attrib_list, alpha); + putAttrib(&attrib_list, GLX_STENCIL_SIZE); putAttrib(&attrib_list, stencil); + putAttrib(&attrib_list, GLX_AUX_BUFFERS); putAttrib(&attrib_list, num_aux_buffers); + putAttrib(&attrib_list, GLX_ACCUM_RED_SIZE); putAttrib(&attrib_list, accum_bpe); + putAttrib(&attrib_list, GLX_ACCUM_GREEN_SIZE); putAttrib(&attrib_list, accum_bpe); + putAttrib(&attrib_list, GLX_ACCUM_BLUE_SIZE); putAttrib(&attrib_list, accum_bpe); + putAttrib(&attrib_list, GLX_ACCUM_ALPHA_SIZE); putAttrib(&attrib_list, accum_alpha); + if (stereo) + putAttrib(&attrib_list, GLX_STEREO); + if (samples > 0 && extgl_Extensions.GLX_ARB_multisample) { + putAttrib(&attrib_list, GLX_SAMPLE_BUFFERS_ARB); putAttrib(&attrib_list, 1); + putAttrib(&attrib_list, GLX_SAMPLES_ARB); putAttrib(&attrib_list, samples); + } + putAttrib(&attrib_list, None); + return glXChooseVisual(getDisplay(), getCurrentScreen(), attrib_list.attribs); +} + +static void dumpVisualInfo(XVisualInfo *vis_info) { + int alpha, depth, stencil, r, g, b; + int sample_buffers = 0; + int samples = 0; + glXGetConfig(getDisplay(), vis_info, GLX_RED_SIZE, &r); + glXGetConfig(getDisplay(), vis_info, GLX_GREEN_SIZE, &g); + glXGetConfig(getDisplay(), vis_info, GLX_BLUE_SIZE, &b); + glXGetConfig(getDisplay(), vis_info, GLX_ALPHA_SIZE, &alpha); + glXGetConfig(getDisplay(), vis_info, GLX_DEPTH_SIZE, &depth); + glXGetConfig(getDisplay(), vis_info, GLX_STENCIL_SIZE, &stencil); + if (extgl_Extensions.GLX_ARB_multisample) { + glXGetConfig(getDisplay(), vis_info, GLX_SAMPLE_BUFFERS_ARB, &sample_buffers); + glXGetConfig(getDisplay(), vis_info, GLX_SAMPLES_ARB, &samples); + } + printfDebug("Pixel format info: r = %d, g = %d, b = %d, a = %d, depth = %d, stencil = %d, sample buffers = %d, samples = %d\n", r, g, b, alpha, depth, stencil, sample_buffers, samples); +} + +static void destroyContext(void) { + if (USEGLX13) { + XFree(configs); + configs = NULL; + } + XFree(vis_info); + vis_info = NULL; + glXDestroyContext(getDisplay(), context); + context = NULL; +} + +static bool initWindowGLX13(JNIEnv *env, jobject pixel_format) { + configs = chooseVisualGLX13(env, pixel_format, true, GLX_WINDOW_BIT, true); + if (configs == NULL) { + throwException(env, "Could not find a matching pixel format"); + return false; + } + context = glXCreateNewContext(getDisplay(), configs[0], GLX_RGBA_TYPE, NULL, True); + if (context == NULL) { + XFree(configs); + throwException(env, "Could not create a GLX context"); + return false; + } + jboolean allow_software_acceleration = getBooleanProperty(env, "org.lwjgl.opengl.Window.allowSoftwareOpenGL"); + if (!allow_software_acceleration && (glXIsDirect(getDisplay(), context) == False)) { + glXDestroyContext(getDisplay(), context); + XFree(configs); + throwException(env, "Could not create a direct GLX context"); + return false; + } + vis_info = glXGetVisualFromFBConfig(getDisplay(), configs[0]); + if (vis_info == NULL) { + glXDestroyContext(getDisplay(), context); + XFree(configs); + throwException(env, "Could not get visual from FB config"); + return false; + } + if (!checkXError(env)) { + glXDestroyContext(getDisplay(), context); + XFree(configs); + XFree(vis_info); + return false; + } + return true; +} + +static bool initWindowGLX(JNIEnv *env, jobject pixel_format) { + vis_info = chooseVisualGLX(env, pixel_format); + if (vis_info == NULL) { + throwException(env, "Could not find a matching pixel format"); + return false; + } + if (isDebugEnabled()) + dumpVisualInfo(vis_info); + context = glXCreateContext(getDisplay(), vis_info, NULL, True); + if (context == NULL) { + XFree(vis_info); + throwException(env, "Could not create a GLX context"); + return false; + } + jboolean allow_software_acceleration = getBooleanProperty(env, "org.lwjgl.opengl.Window.allowSoftwareOpenGL"); + if (!allow_software_acceleration && glXIsDirect(getDisplay(), context) == False) { + glXDestroyContext(getDisplay(), context); + XFree(vis_info); + throwException(env, "Could not create a direct GLX context"); + return false; + } + if (!checkXError(env)) { + glXDestroyContext(getDisplay(), context); + XFree(vis_info); + return false; + } + return true; +} + +JNIEXPORT jobjectArray JNICALL Java_org_lwjgl_opengl_LinuxDisplay_getAvailableDisplayModes(JNIEnv *env, jobject this) { + return getAvailableDisplayModes(env, getCurrentScreen()); +} + +JNIEXPORT void JNICALL Java_org_lwjgl_opengl_LinuxDisplay_switchDisplayMode(JNIEnv *env, jobject this, jobject mode) { + switchDisplayMode(env, mode, getCurrentScreen()); +} + +JNIEXPORT void JNICALL Java_org_lwjgl_opengl_LinuxDisplay_resetDisplayMode(JNIEnv *env, jobject this) { + resetDisplayMode(getCurrentScreen(), false); +} + +JNIEXPORT jint JNICALL Java_org_lwjgl_opengl_LinuxDisplay_getGammaRampLength(JNIEnv *env, jobject this) { + return (jint)getGammaRampLength(getCurrentScreen()); +} + +JNIEXPORT void JNICALL Java_org_lwjgl_opengl_LinuxDisplay_setGammaRamp(JNIEnv *env, jobject this, jobject gamma_buffer) { + setGammaRamp(env, gamma_buffer, getCurrentScreen()); +} + +JNIEXPORT jobject JNICALL Java_org_lwjgl_opengl_LinuxDisplay_init(JNIEnv *env, jobject this) { + return initDisplay(env, getCurrentScreen()); +} + +JNIEXPORT jstring JNICALL Java_org_lwjgl_opengl_LinuxDisplay_getAdapter(JNIEnv *env , jobject this) { + return NULL; +} + +JNIEXPORT jstring JNICALL Java_org_lwjgl_opengl_LinuxDisplay_getVersion(JNIEnv *env, jobject this) { + return NULL; +} + +JNIEXPORT void JNICALL Java_org_lwjgl_opengl_LinuxDisplay_createContext(JNIEnv *env, jobject this, jobject pixel_format) { + Display *disp = incDisplay(env); + if (disp == NULL) { + return; + } + current_screen = XDefaultScreen(disp); + if (!extgl_InitGLX(env, disp, current_screen)) { + decDisplay(); + throwException(env, "Could not init GLX"); + return; + } + + if (USEGLX13) { + initWindowGLX13(env, pixel_format); + } else { + initWindowGLX(env, pixel_format); + } +} + +JNIEXPORT void JNICALL Java_org_lwjgl_opengl_LinuxDisplay_destroyContext(JNIEnv *env, jobject this) { + destroyContext(); + decDisplay(); +} + +JNIEXPORT void JNICALL Java_org_lwjgl_opengl_LinuxDisplay_createWindow(JNIEnv *env, jobject this, jobject mode, jboolean fullscreen, int x, int y) { + bool current_fullscreen = fullscreen == JNI_TRUE; + if (current_fullscreen) { + if (getCurrentDisplayModeExtension() == XRANDR && isNetWMFullscreenSupported()) + current_window_mode = FULLSCREEN_NETWM; + else + current_window_mode = FULLSCREEN_LEGACY; + } else + current_window_mode = WINDOWED; + jclass cls_displayMode = (*env)->GetObjectClass(env, mode); + jfieldID fid_width = (*env)->GetFieldID(env, cls_displayMode, "width", "I"); + jfieldID fid_height = (*env)->GetFieldID(env, cls_displayMode, "height", "I"); + int width = (*env)->GetIntField(env, mode, fid_width); + int height = (*env)->GetIntField(env, mode, fid_height); + bool window_created = createWindow(env, x, y, width, height); + if (!window_created) { + return; + } + if (isDebugEnabled()) + dumpVisualInfo(vis_info); + if (USEGLX13) + glx_window = glXCreateWindow(getDisplay(), configs[0], getCurrentWindow(), NULL); + if (!makeCurrent() || !checkXError(env)) { + glXDestroyWindow(getDisplay(), glx_window); + destroyWindow(); + } +} + +JNIEXPORT void JNICALL Java_org_lwjgl_opengl_LinuxDisplay_destroyWindow(JNIEnv *env, jobject this) { + destroyWindow(); +} + +JNIEXPORT void JNICALL Java_org_lwjgl_opengl_LinuxDisplay_swapBuffers(JNIEnv * env, jobject this) +{ + dirty = false; + if (USEGLX13) + glXSwapBuffers(getDisplay(), glx_window); + else + glXSwapBuffers(getDisplay(), getCurrentWindow()); +} + +JNIEXPORT jboolean JNICALL Java_org_lwjgl_opengl_LinuxDisplay_isDirty + (JNIEnv *env, jobject this) { + bool result = dirty; + dirty = false; + return result ? JNI_TRUE : JNI_FALSE; +} + +JNIEXPORT jboolean JNICALL Java_org_lwjgl_opengl_LinuxDisplay_isVisible + (JNIEnv *env, jobject this) { + return minimized ? JNI_FALSE : JNI_TRUE; +} + +JNIEXPORT jboolean JNICALL Java_org_lwjgl_opengl_LinuxDisplay_isCloseRequested + (JNIEnv *env, jobject this) { + bool saved = closerequested; + closerequested = false; + return saved; +} + +JNIEXPORT jboolean JNICALL Java_org_lwjgl_opengl_LinuxDisplay_isActive + (JNIEnv *env, jobject this) { + return focused ? JNI_TRUE : JNI_FALSE; +} + +JNIEXPORT void JNICALL Java_org_lwjgl_opengl_LinuxDisplay_setVSyncEnabled + (JNIEnv *env, jobject this, jboolean sync) +{ + if (extgl_Extensions.GLX_SGI_swap_control) { + bool vsync = sync == JNI_TRUE ? true : false; + if (vsync != vsync_enabled) { + int interval = vsync ? 1 : 0; + glXSwapIntervalSGI(interval); + vsync_enabled = vsync; + } + } +} +