Skip to content

Commit

Permalink
gdx-lwjgl3-glfw-awt-macos extensions to add support for mixing GLFW a…
Browse files Browse the repository at this point in the history
…nd AWT on macOS (libgdx#6772)

* Apply formatter

* Add support for LWJGL3 + AWT on macOS

GLFW and AWT both compete for the AppKit run loop. LWJGL3 apps on macOS thus need to specify the -XstartOnFirstThread JVM option. This prevents AWT (and by proxy Swing, ImageIO, etc.) from working together with LWJGL3 on macOS.

This commit injects a modified libglfw.dylib (see https://github.com/badlogic/glfw). The GLFW fork is modified such that all calls into AppKit APIs are expected to be performed on a thread != main thread. This way, AWT can take over the run loop, while GLFW (and libGDX LWJGL3) apps can live on a separate thread.

To use this, add `gdx-lwjgl3-glfw-awt-macos` as a dependency to your libGDX desktop project. You can now use both libGDX and AWT/Swing/ImageIO on macOS.

Note: this extensions does NOT allow rendering your libGDX content to an AWT/Swing window.

* Apply formatter

* Only initialize AWT if not on EDT

- Update Wav and Ogg classes in LWJGL3 backend with latest changes from LWJGL2 backend.
- Fix mip map generation when using ANGLE

* Resize callback must be called on rendering thread on macOS

* Added Lwjgl3Window.flash()

* Expose window state

* Add awt extension to LWJGL3 tests.

* Update libglfw with latest from https://github.com/badlogic/glfw

* Pull libglfw.dylib from https://github.com/badlogic/glfw

* Fix for libgdx#6770, don't apply uberJar task to Android tests.

* Updated CHANGES

* Removed pom.xml from and fixed Gradle dependency definition in gdx-lwjgl3-glfw-awt-macos, updated CHANGES.

* Fixed incorrect PR URL in CHANGES.

* Check if shape is not null (libgdx#6765)

* Add explicit permissions for fix-formatting.yml (libgdx#6767)

* Add explicit permissions for fix-formatting.yml

* Add explicit permissions to build-pullrequest.yml

* Add missing method to emulated timer class (libgdx#6762)

The isEmpty method from the core Timer class was missing in the emulated
Timer for the GWT backend. This caused the html build to fail if the
isEmpty method was used.

* Update Tooltip.java (libgdx#6758)

* Update Tooltip.java

Added option to let user set touch independency for tooltips, which prevents tooltip from showing when targetActor's hitbox is entered with screen already touched

* Update Tooltip.java

Formatting

* Add NWSEResize, NESWResize, AllResize, and NotAllowed SystemCursors (libgdx#6756)

* Create SystemCursorTest

* Add NWSEResize, NESWResize, AllResize, and NotAllowed SystemCursors

* Remove dead code in OpenALAudioDevice (libgdx#6753)

* Add new Pixmap constructor for loading from a ByteBuffer (libgdx#6741)

* Add new Pixmap constructor for loading from a ByteBuffer

* Prevent SIGSEGV if called with a non-direct ByteBuffer.

* Updated documentation for getDensity() to warn about potentially costly operation (libgdx#6698)

Co-authored-by: Johannes Mario Ringheim <[email protected]>

* Box2D Shape impl disposable (libgdx#6528)

* Get js heap size on gwt (libgdx#6425)

* Get js heap size on gwt

* Use casting for converting to long

* Format

* Add missing CHANGES entries [ci skip]

Add changelog entries for the notable changes in libgdx#6425, libgdx#6528, libgdx#6758, libgdx#6762.

* Test for unique name jni issue on ios

* Removed unused local.

libgdx#6753

Co-authored-by: GitHub Action <[email protected]>
Co-authored-by: Natan <[email protected]>
Co-authored-by: intrigus <[email protected]>
Co-authored-by: 6money <[email protected]>
Co-authored-by: redy5 <[email protected]>
Co-authored-by: Kyle McLean <[email protected]>
Co-authored-by: Hangman <[email protected]>
Co-authored-by: PokeMMO <[email protected]>
Co-authored-by: funkyfourier <[email protected]>
Co-authored-by: Johannes Mario Ringheim <[email protected]>
Co-authored-by: Fabiitch <[email protected]>
Co-authored-by: SimonIT <[email protected]>
Co-authored-by: damios <[email protected]>
Co-authored-by: Tom <[email protected]>
Co-authored-by: Nathan Sweet <[email protected]>
  • Loading branch information
16 people authored Jan 19, 2022
1 parent 295c9fa commit a0e584f
Show file tree
Hide file tree
Showing 16 changed files with 319 additions and 33 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ libgdx-*.zip.MD5
/extensions/gdx-freetype/libs/
/extensions/gdx-image/libs/
/extensions/gdx-lwjgl3-angle/res
/extensions/gdx-lwjgl3-glfw-awt-macos/res/
/extensions/gdx-setup/gdx-setup.jar
/extensions/gdx-setup/test/

Expand Down
1 change: 1 addition & 0 deletions CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
- [BREAKING CHANGE] Linux: Shared libraries are now built on Ubuntu 18.04 (up from Ubuntu 16.04)
- [BREAKING CHANGE] The built-in font files arial-15.fnt and arial-15.png have been replaced with lsans-15.fnt and lsans-15.png; this may change some text layout that uses the built-in font, and code that expects arial-15 assets to be present must change to lsans-15.
- [BREAKING CHANGE] Legacy LWJGL3 projects must update the sourceCompatibility to 1.8 or higher.
- LWJGL3 extension: Added gdx-lwjgl3-glfw-awt-macos extension. Fixes GLFW in such a way, that the LWJGL3/libGDX must no longer run on the main thread in macOS, which allows AWT to work in parallel, i.e. file dialogs, JFrames, ImageIO, etc. You no longer need to pass `-XstartOnFirstThread` when starting an LWJGL3 app on macOS. See `AwtTestLWJGL` in gdx-tests-lwjgl3. For more information, see https://github.com/libgdx/libgdx/pull/6772
- API Addition: Added LWJGL3 ANGLE support for x86_64 Windows, Linux, and macOS. Emulates OpenGL ES 2.0 through DirectX (Windows), desktop OpenGL (Linux), and Metal (macOS). May become the preferred method of rendering on macOS if Apple removes OpenGL support entirely. May fix some OpenGL driver issues. More information here: https://github.com/libgdx/libgdx/pull/6672
- iOS: Update to MobiVM 2.3.15
- Update to LWJGL 3.3.0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
import com.badlogic.gdx.utils.GdxRuntimeException;
import com.badlogic.gdx.utils.ObjectMap;
import com.badlogic.gdx.utils.SharedLibraryLoader;
import org.lwjgl.system.Configuration;

public class Lwjgl3Application implements Lwjgl3ApplicationBase {
private final Lwjgl3ApplicationConfiguration config;
Expand All @@ -80,6 +81,7 @@ public class Lwjgl3Application implements Lwjgl3ApplicationBase {

static void initializeGlfw () {
if (errorCallback == null) {
if (SharedLibraryLoader.isMac) loadGlfwAwtMacos();
Lwjgl3NativesLoader.load();
errorCallback = GLFWErrorCallback.createPrint(System.err);
GLFW.glfwSetErrorCallback(errorCallback);
Expand Down Expand Up @@ -115,6 +117,20 @@ static void postLoadANGLE () {
}
}

static void loadGlfwAwtMacos () {
try {
Class loader = Class.forName("com.badlogic.gdx.backends.lwjgl3.awt.GlfwAWTLoader");
Method load = loader.getMethod("load");
File sharedLib = (File)load.invoke(loader);
Configuration.GLFW_LIBRARY_NAME.set(sharedLib.getAbsolutePath());
Configuration.GLFW_CHECK_THREAD0.set(false);
} catch (ClassNotFoundException t) {
return;
} catch (Throwable t) {
throw new GdxRuntimeException("Couldn't load GLFW AWT for macOS.", t);
}
}

public Lwjgl3Application (ApplicationListener listener, Lwjgl3ApplicationConfiguration config) {
if (config.glEmulation == Lwjgl3ApplicationConfiguration.GLEmulation.ANGLE_GLES20) loadANGLE();
initializeGlfw();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@

import com.badlogic.gdx.AbstractGraphics;
import com.badlogic.gdx.Application;
import com.badlogic.gdx.Gdx;

import org.lwjgl.BufferUtils;
import org.lwjgl.PointerBuffer;
import org.lwjgl.glfw.GLFW;
Expand Down Expand Up @@ -49,6 +51,7 @@ public class Lwjgl3Graphics extends AbstractGraphics implements Disposable {
private BufferFormat bufferFormat;
private long lastFrameTime = -1;
private float deltaTime;
private boolean resetDeltaTime = false;
private long frameId;
private long frameCounterStart = 0;
private int frames;
Expand All @@ -67,15 +70,20 @@ public class Lwjgl3Graphics extends AbstractGraphics implements Disposable {
private GLFWFramebufferSizeCallback resizeCallback = new GLFWFramebufferSizeCallback() {
@Override
public void invoke (long windowHandle, final int width, final int height) {
updateFramebufferInfo();
if (!window.isListenerInitialized()) {
return;
}
window.makeCurrent();
gl20.glViewport(0, 0, width, height);
window.getListener().resize(getWidth(), getHeight());
window.getListener().render();
GLFW.glfwSwapBuffers(windowHandle);
Gdx.app.postRunnable(new Runnable() {
@Override
public void run () {
updateFramebufferInfo();
if (!window.isListenerInitialized()) {
return;
}
window.makeCurrent();
gl20.glViewport(0, 0, width, height);
window.getListener().resize(getWidth(), getHeight());
window.getListener().render();
GLFW.glfwSwapBuffers(windowHandle);
}
});
}
};

Expand Down Expand Up @@ -139,7 +147,11 @@ void updateFramebufferInfo () {
void update () {
long time = System.nanoTime();
if (lastFrameTime == -1) lastFrameTime = time;
deltaTime = (time - lastFrameTime) / 1000000000.0f;
if (resetDeltaTime) {
resetDeltaTime = false;
deltaTime = 0;
} else
deltaTime = (time - lastFrameTime) / 1000000000.0f;
lastFrameTime = time;

if (time - frameCounterStart >= 1000000000) {
Expand Down Expand Up @@ -222,6 +234,10 @@ public float getDeltaTime () {
return deltaTime;
}

public void resetDeltaTime () {
resetDeltaTime = true;
}

@Override
public int getFramesPerSecond () {
return fps;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ public class Lwjgl3Window implements Disposable {
private final IntBuffer tmpBuffer;
private final IntBuffer tmpBuffer2;
boolean iconified = false;
boolean focused = false;
private boolean requestRendering = false;

private final GLFWWindowFocusCallback focusCallback = new GLFWWindowFocusCallback() {
Expand All @@ -65,6 +66,7 @@ public void run () {
} else {
windowListener.focusLost();
}
Lwjgl3Window.this.focused = focused;
}
}
});
Expand Down Expand Up @@ -242,6 +244,11 @@ public void iconifyWindow () {
GLFW.glfwIconifyWindow(windowHandle);
}

/** Whether the window is iconfieid */
public boolean isIconified () {
return iconified;
}

/** De-minimizes (de-iconifies) and de-maximizes the window. */
public void restoreWindow () {
GLFW.glfwRestoreWindow(windowHandle);
Expand All @@ -257,6 +264,10 @@ public void focusWindow () {
GLFW.glfwFocusWindow(windowHandle);
}

public boolean isFocused () {
return focused;
}

/** Sets the icon that will be used in the window's title bar. Has no effect in macOS, which doesn't use window icons.
* @param image One or more images. The one closest to the system's desired size will be scaled. Good sizes include 16x16,
* 32x32 and 48x48. Pixmap format {@link com.badlogic.gdx.graphics.Pixmap.Format#RGBA8888 RGBA8888} is preferred so
Expand Down Expand Up @@ -456,4 +467,8 @@ public boolean equals (Object obj) {
if (windowHandle != other.windowHandle) return false;
return true;
}

public void flash () {
GLFW.glfwRequestWindowAttention(windowHandle);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ public OggInputStream (InputStream input) {
*
* @param input The input stream from which to read the OGG file
* @param previousStream The stream instance to reuse buffers from, may be null */
OggInputStream (InputStream input, OggInputStream previousStream) {
public OggInputStream (InputStream input, OggInputStream previousStream) {
if (previousStream == null) {
convbuffer = new byte[convsize];
pcmBuffer = BufferUtils.createByteBuffer(4096 * 500);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,11 @@ public Sound (OpenALLwjgl3Audio audio, FileHandle file) {
}

/** @author Nathan Sweet */
static private class WavInputStream extends FilterInputStream {
int channels, sampleRate, dataRemaining;
static public class WavInputStream extends FilterInputStream {

WavInputStream (FileHandle file) {
public int channels, sampleRate, dataRemaining;

public WavInputStream (FileHandle file) {
super(file.read());
try {
if (read() != 'R' || read() != 'I' || read() != 'F' || read() != 'F')
Expand All @@ -87,8 +88,32 @@ static private class WavInputStream extends FilterInputStream {

int fmtChunkLength = seekToChunk('f', 'm', 't', ' ');

// http://www-mmsp.ece.mcgill.ca/Documents/AudioFormats/WAVE/WAVE.html
// http://soundfile.sapp.org/doc/WaveFormat/
int type = read() & 0xff | (read() & 0xff) << 8;
if (type != 1) throw new GdxRuntimeException("WAV files must be PCM: " + type);
if (type != 1) {
String name;
switch (type) {
case 0x0002:
name = "ADPCM";
break;
case 0x0003:
name = "IEEE float";
break;
case 0x0006:
name = "8-bit ITU-T G.711 A-law";
break;
case 0x0007:
name = "8-bit ITU-T G.711 u-law";
break;
case 0xFFFE:
name = "Extensible";
break;
default:
name = "Unknown";
}
throw new GdxRuntimeException("WAV files must be PCM, unsupported format: " + name + " (" + type + ")");
}

channels = read() & 0xff | (read() & 0xff) << 8;
if (channels != 1 && channels != 2) throw new GdxRuntimeException("WAV files must have 1 or 2 channels: " + channels);
Expand Down
3 changes: 2 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -168,10 +168,11 @@ task fetchNativesZip() {
def zip = file("build/natives.zip");
new URL('https://libgdx-nightlies.s3.eu-central-1.amazonaws.com/libgdx-nightlies/natives.zip').withInputStream{ i -> file("build/natives.zip").withOutputStream{ it << i }}
new URL('https://raw.githubusercontent.com/libgdx/gdx-angle-natives/master/gdx-angle-natives.zip').withInputStream{ i -> file("build/gdx-angle-natives.zip").withOutputStream{ it << i }}

new URL('https://raw.githubusercontent.com/badlogic/glfw/master/libglfw.dylib').withInputStream{ i -> file("extensions/gdx-lwjgl3-glfw-awt-macos/res/macosx64/libglfw.dylib").withOutputStream{ it << i }}
}
outputs.file(file("build/natives.zip"))
outputs.file(file("build/gdx-angle-natives.zip"))
outputs.file(file("extensions/gdx-lwjgl3-glfw-awt-macos/res/macosx64/libglfw.dylib"))
}

task fetchNativesLwjglAngle(dependsOn: fetchNativesZip, type: Copy) {
Expand Down
29 changes: 29 additions & 0 deletions extensions/gdx-lwjgl3-glfw-awt-macos/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*******************************************************************************
* Copyright 2011 See AUTHORS file.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/

dependencies {
implementation project(":gdx")
}

sourceSets.main.java.srcDirs = ["src"]
sourceSets.main.resources.srcDirs = ["res"]

// Workaround needed for IDEA to have resources on classpath when running tests (like gdx-tests-lwjgl3)
task copyIdeaResources(type: Copy) {
from "${projectDir}/res"
into "${buildDir}/classes/java/main/"
}
processResources.dependsOn copyIdeaResources
Loading

0 comments on commit a0e584f

Please sign in to comment.