package org.lwjgl.opengl.swt; import static org.lwjgl.opengl.GLX.*; import static org.lwjgl.opengl.GLX13.*; import static org.lwjgl.opengl.GLXARBContextFlushControl.*; import static org.lwjgl.opengl.GLXARBCreateContext.*; import static org.lwjgl.opengl.GLXARBCreateContextNoError.*; import static org.lwjgl.opengl.GLXARBCreateContextProfile.*; import static org.lwjgl.opengl.GLXARBCreateContextRobustness.*; import static org.lwjgl.opengl.GLXARBMultisample.*; import static org.lwjgl.opengl.GLXARBRobustnessApplicationIsolation.*; import static org.lwjgl.opengl.GLXEXTCreateContextES2Profile.*; import static org.lwjgl.opengl.GLXEXTFramebufferSRGB.*; import static org.lwjgl.opengl.GLXNVDelayBeforeSwap.*; import static org.lwjgl.opengl.GLXNVMultisampleCoverage.*; import java.nio.IntBuffer; import org.eclipse.swt.SWT; import org.eclipse.swt.SWTException; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.internal.DPIUtil; import org.eclipse.swt.internal.gtk.GDK; import org.eclipse.swt.internal.gtk.GTK; import org.eclipse.swt.internal.gtk.GdkWindowAttr; import org.eclipse.swt.widgets.Listener; import org.lwjgl.BufferUtils; import org.lwjgl.PointerBuffer; import org.lwjgl.opengl.GL; import org.lwjgl.opengl.GLXCapabilities; import org.lwjgl.opengl.KHRNoError; import org.lwjgl.opengl.swt.GLData.API; import org.lwjgl.opengl.swt.GLData.Profile; import org.lwjgl.opengl.swt.GLData.ReleaseBehavior; import org.lwjgl.system.linux.XVisualInfo; /** * Linux-specific implementation of methods for GLCanvas. * * @author Joshua Slack */ class PlatformLinuxGLCanvas extends AbstractPlatformGLCanvas { @Override public long create(GLCanvas canvas, GLData data, GLData effective) { // Validate context attributes validateAttributes(data); // make sure our canvas has resources assigned GTK.gtk_widget_realize(canvas.handle); // grab handles to our window/display long window = GTK.gtk_widget_get_window(canvas.handle); long xDisplay = gdk_x11_display_get_xdisplay(window); // generate a list of config options for our frame buffer from the supplied data IntBuffer attribList = BufferUtils.createIntBuffer(64); populateFBConfigAttribs(data, attribList); // ask for matching frame buffer configs PointerBuffer fbCfg = glXChooseFBConfig(xDisplay, 0, attribList); if (fbCfg == null || !fbCfg.hasRemaining()) { canvas.dispose(); throw new SWTException("Unable to find matching FB Config"); } // convert our fbconfig to a visualinfo so we can apply it to the widget XVisualInfo viz = glXGetVisualFromFBConfig(xDisplay, fbCfg.get(0)); // grab our default screen for the default display long screen = GDK.gdk_screen_get_default(); // ask the screen for a GdkVisual that matches the given info long gdkvisual = GDK.gdk_x11_screen_lookup_visual(screen, (int) viz.visualid()); // put together attributes for a new window using the visual GdkWindowAttr winAttrs = new GdkWindowAttr(); winAttrs.width = 1; winAttrs.height = 1; winAttrs.event_mask = GDK.GDK_KEY_PRESS_MASK | GDK.GDK_KEY_RELEASE_MASK | GDK.GDK_FOCUS_CHANGE_MASK | GDK.GDK_POINTER_MOTION_MASK | GDK.GDK_BUTTON_PRESS_MASK | GDK.GDK_BUTTON_RELEASE_MASK | GDK.GDK_ENTER_NOTIFY_MASK | GDK.GDK_LEAVE_NOTIFY_MASK | GDK.GDK_EXPOSURE_MASK | GDK.GDK_POINTER_MOTION_HINT_MASK; winAttrs.window_type = GDK.GDK_WINDOW_CHILD; winAttrs.visual = gdkvisual; // create the new window - this gives us a glxdrawable too canvas.glWindow = GDK.gdk_window_new(window, winAttrs, GDK.GDK_WA_VISUAL); // sets the user data as the widget that owns the window - historical // see: https://developer.gnome.org/gdk3/stable/gdk3-Windows.html#gdk-window-set-user-data GDK.gdk_window_set_user_data(canvas.glWindow, canvas.handle); // get the X id of the new window and call to show it canvas.xWindow = GDK.gdk_x11_window_get_xid(canvas.glWindow); GDK.gdk_window_show(canvas.glWindow); // context generation time - put we'll use our fbconfig here to get a core compatible context // start by generating our list of attributes attribList.rewind(); GLXCapabilities caps = GL.getCapabilitiesGLX(); populateContextAttribs(data, attribList, caps); // create the context... pass our display, fbconfig, attributes and any shared context long share = data.shareContext != null ? data.shareContext.context : 0; long context = glXCreateContextAttribsARB(xDisplay, fbCfg.get(0), share, true, attribList); if (context == 0) throw new SWTException("Unable to create context"); // Set up SWT event listeners to handle disposal and resize Listener listener = event -> { switch (event.type) { case SWT.Resize: Rectangle clientArea = DPIUtil.autoScaleUp(canvas.getClientArea()); GDK.gdk_window_move(canvas.glWindow, clientArea.x, clientArea.y); GDK.gdk_window_resize(canvas.glWindow, clientArea.width, clientArea.height); break; case SWT.Dispose: deleteContext(canvas, context); break; } }; canvas.addListener(SWT.Resize, listener); canvas.addListener(SWT.Dispose, listener); // Done! Return our context. return context; } private void populateFBConfigAttribs(GLData data, IntBuffer attribList) { if (data.redSize > 0) attribList.put(GLX_RED_SIZE).put(data.redSize); if (data.greenSize > 0) attribList.put(GLX_GREEN_SIZE).put(data.greenSize); if (data.blueSize > 0) attribList.put(GLX_BLUE_SIZE).put(data.blueSize); if (data.alphaSize > 0) attribList.put(GLX_ALPHA_SIZE).put(data.alphaSize); if (data.depthSize > 0) attribList.put(GLX_DEPTH_SIZE).put(data.depthSize); if (data.stencilSize > 0) attribList.put(GLX_STENCIL_SIZE).put(data.stencilSize); if (data.doubleBuffer) attribList.put(GLX_DOUBLEBUFFER).put(1); if (data.stereo) attribList.put(GLX_STEREO).put(1); if (data.sRGB) attribList.put(GLX_FRAMEBUFFER_SRGB_CAPABLE_EXT).put(1); if (data.accumRedSize > 0) attribList.put(GLX_ACCUM_RED_SIZE).put(data.accumRedSize); if (data.accumGreenSize > 0) attribList.put(GLX_ACCUM_GREEN_SIZE).put(data.accumGreenSize); if (data.accumBlueSize > 0) attribList.put(GLX_ACCUM_BLUE_SIZE).put(data.accumBlueSize); if (data.accumAlphaSize > 0) attribList.put(GLX_ACCUM_ALPHA_SIZE).put(data.accumAlphaSize); if (data.samples > 0) { attribList.put(GLX_SAMPLE_BUFFERS_ARB).put(1); attribList.put(GLX_SAMPLES_ARB).put(data.samples); if (data.colorSamplesNV > 0) { attribList.put(GLX_COLOR_SAMPLES_NV).put(data.colorSamplesNV); } } attribList.put(0); attribList.flip(); } private void populateContextAttribs(GLData data, IntBuffer attribList, GLXCapabilities caps) { attribList.put(GLX_CONTEXT_MAJOR_VERSION_ARB).put(data.majorVersion); attribList.put(GLX_CONTEXT_MINOR_VERSION_ARB).put(data.minorVersion); int profile = 0; if (data.api == API.GL) { if (data.profile == Profile.COMPATIBILITY) { profile = GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB; } else if (data.profile == Profile.CORE) { profile = GLX_CONTEXT_CORE_PROFILE_BIT_ARB; } } else if (data.api == API.GLES) { if (!caps.GLX_EXT_create_context_es2_profile) { throw new SWTException("OpenGL ES API requested but GLX_EXT_create_context_es2_profile is unavailable"); } profile = GLX_CONTEXT_ES2_PROFILE_BIT_EXT; } if (profile > 0) { if (!caps.GLX_ARB_create_context_profile) { throw new SWTException("OpenGL profile requested but GLX_ARB_create_context_profile is unavailable"); } attribList.put(GLX_CONTEXT_PROFILE_MASK_ARB).put(profile); } // Context Flags int contextFlags = 0; if (data.debug) contextFlags |= GLX_CONTEXT_DEBUG_BIT_ARB; if (data.forwardCompatible) contextFlags |= GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB; if (data.noErrorContext) { contextFlags |= KHRNoError.GL_CONTEXT_FLAG_NO_ERROR_BIT_KHR; attribList.put(GLX_CONTEXT_OPENGL_NO_ERROR_ARB).put(1); } if (data.robustness) { // Check for GLX_ARB_create_context_robustness if (!caps.GLX_ARB_create_context_robustness) { throw new SWTException("Context with robust buffer access requested but GLX_ARB_create_context_robustness is unavailable"); } contextFlags |= GLX_CONTEXT_ROBUST_ACCESS_BIT_ARB; if (data.loseContextOnReset) { attribList.put(GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB).put( GLX_LOSE_CONTEXT_ON_RESET_ARB); // Note: GLX_NO_RESET_NOTIFICATION_ARB is default behaviour and need not be specified. } if (data.contextResetIsolation) { // Check for GLX_ARB_robustness_application_isolation or GLX_ARB_robustness_share_group_isolation if (!caps.GLX_ARB_robustness_application_isolation && !caps.GLX_ARB_robustness_share_group_isolation) { throw new SWTException( "Robustness isolation requested but neither GLX_ARB_robustness_application_isolation nor GLX_ARB_robustness_share_group_isolation available"); } contextFlags |= GLX_CONTEXT_RESET_ISOLATION_BIT_ARB; } } if (contextFlags > 0) attribList.put(GLX_CONTEXT_FLAGS_ARB).put(contextFlags); // Release behavior if (data.contextReleaseBehavior != null) { if (!caps.GLX_ARB_context_flush_control) { throw new SWTException("Context release behavior requested but GLX_ARB_context_flush_control is unavailable"); } if (data.contextReleaseBehavior == ReleaseBehavior.NONE) attribList.put(GLX_CONTEXT_RELEASE_BEHAVIOR_ARB).put(GLX_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB); else if (data.contextReleaseBehavior == ReleaseBehavior.FLUSH) attribList.put(GLX_CONTEXT_RELEASE_BEHAVIOR_ARB).put(GLX_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB); } attribList.put(0); attribList.flip(); } @Override public boolean isCurrent(long context) { return glXGetCurrentContext () == context; } @Override public boolean makeCurrent(GLCanvas canvas, long context) { long window = GTK.gtk_widget_get_window(canvas.handle); long xDisplay = gdk_x11_display_get_xdisplay(window); return glXMakeCurrent(xDisplay, canvas.xWindow, context); } @Override public boolean deleteContext(GLCanvas canvas, long context) { long window = GTK.gtk_widget_get_window(canvas.handle); long xDisplay = gdk_x11_display_get_xdisplay(window); if (context != 0) { if (glXGetCurrentContext() == context) { glXMakeCurrent(xDisplay, 0, 0); } glXDestroyContext(xDisplay, context); canvas.context = 0; } if (canvas.glWindow != 0) { GDK.gdk_window_destroy(canvas.glWindow); canvas.glWindow = 0; } return true; } @Override public boolean swapBuffers(GLCanvas canvas) { long window = GTK.gtk_widget_get_window(canvas.handle); long xDisplay = gdk_x11_display_get_xdisplay(window); glXSwapBuffers(xDisplay, canvas.xWindow); return false; } @Override public boolean delayBeforeSwapNV(GLCanvas canvas, float seconds) { long window = GTK.gtk_widget_get_window(canvas.handle); long xDisplay = gdk_x11_display_get_xdisplay(window); return glXDelayBeforeSwapNV(xDisplay, canvas.xWindow, seconds); } private long gdk_x11_display_get_xdisplay(long window) { long display = GDK.gdk_window_get_display(window); return GDK.gdk_x11_display_get_xdisplay(display); } }