Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
127 changes: 127 additions & 0 deletions jme3-examples/src/main/java/jme3test/niftygui/TestIssue1013.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
/*
* Copyright (c) 2009-2020 jMonkeyEngine
* 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 'jMonkeyEngine' 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.
*/
package jme3test.niftygui;

import com.jme3.app.SimpleApplication;
import com.jme3.material.Material;
import com.jme3.niftygui.NiftyJmeDisplay;
import com.jme3.scene.Geometry;
import com.jme3.scene.shape.Box;
import de.lessvoid.nifty.Nifty;
import de.lessvoid.nifty.builder.LayerBuilder;
import de.lessvoid.nifty.builder.PanelBuilder;
import de.lessvoid.nifty.builder.ScreenBuilder;
import de.lessvoid.nifty.controls.button.builder.ButtonBuilder;
import de.lessvoid.nifty.screen.Screen;
import de.lessvoid.nifty.screen.ScreenController;

public class TestIssue1013 extends SimpleApplication implements ScreenController {

public static void main(String[] args) {
new TestIssue1013().start();
}

private NiftyJmeDisplay niftyDisplay;

@Override
public void simpleInitApp() {

// this box here always renders
Box b = new Box(1, 1, 1);
Geometry geom = new Geometry("Box", b);
Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
mat.setTexture("ColorMap", assetManager.loadTexture("/com/jme3/app/Monkey.png"));
geom.setMaterial(mat);
rootNode.attachChild(geom);

niftyDisplay = NiftyJmeDisplay.newNiftyJmeDisplay(assetManager, inputManager, audioRenderer, guiViewPort);

Nifty nifty = niftyDisplay.getNifty();
nifty.loadStyleFile("nifty-default-styles.xml");
nifty.loadControlFile("nifty-default-controls.xml");

ScreenController ctrl = this;

new ScreenBuilder("start") {
{
controller(ctrl);
layer(new LayerBuilder() {
{
childLayoutVertical();
panel(new PanelBuilder() {
{
childLayoutCenter();
width("100%");
height("50%");
backgroundColor("#ff0000");
}
});
control(new ButtonBuilder("RestartButton", "Restart Context") {
{
alignCenter();
valignCenter();
height("32px");
width("480px");
interactOnClick("restartContext()");
}
});
}
});
}
}.build(nifty);

guiViewPort.addProcessor(niftyDisplay);
nifty.gotoScreen("start");

flyCam.setDragToRotate(true);
}

@Override
public void bind(Nifty nifty, Screen screen) {
}

@Override
public void onStartScreen() {
}

@Override
public void onEndScreen() {
}

public void restartContext() {
// even without changing settings, stuff breaks!
restart();
// ...and re-adding the processor doesn't help at all
guiViewPort.addProcessor(niftyDisplay);
}

}
28 changes: 23 additions & 5 deletions jme3-lwjgl3/src/main/java/com/jme3/input/lwjgl/GlfwKeyInput.java
Original file line number Diff line number Diff line change
Expand Up @@ -83,11 +83,33 @@ public GlfwKeyInput(final LwjglWindow context) {

@Override
public void initialize() {
if (!context.isRenderable()) {
return;
}
initCallbacks();

initialized = true;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't resetted when resetting the context.
Do you know about the special purpose of it?
Like I wonder if we should reset this value, like when initCallback fails and thus initialized is false after a restart.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I use the flag to see if I need to re-create the callbacks. I'm unsure what the flag otherwise symbolizes, it was there already.

logger.fine("Keyboard created.");
}

/**
* Re-initializes the key input context window specific callbacks
*/
public void resetContext() {
if (!context.isRenderable()) {
return;
}

closeCallbacks();
initCallbacks();
}

private void closeCallbacks() {
keyCallback.close();
charCallback.close();
}

private void initCallbacks() {
glfwSetKeyCallback(context.getWindowHandle(), keyCallback = new GLFWKeyCallback() {
@Override
public void invoke(final long window, final int key, final int scancode, final int action, final int mods) {
Expand Down Expand Up @@ -122,9 +144,6 @@ public void invoke(final long window, final int codepoint) {
keyInputEvents.add(released);
}
});

initialized = true;
logger.fine("Keyboard created.");
}

public int getKeyCount() {
Expand All @@ -149,8 +168,7 @@ public void destroy() {
return;
}

keyCallback.close();
charCallback.close();
closeCallbacks();
logger.fine("Keyboard destroyed.");
}

Expand Down
41 changes: 30 additions & 11 deletions jme3-lwjgl3/src/main/java/com/jme3/input/lwjgl/GlfwMouseInput.java
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,30 @@ private void onMouseButton(final long window, final int button, final int action

@Override
public void initialize() {
initCallbacks();

if (listener != null) {
sendFirstMouseEvent();
}

setCursorVisible(cursorVisible);
logger.fine("Mouse created.");
initialized = true;
Comment on lines +181 to +187
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't needed when restarting?
I mean setCursorVisible probably isn't, as for that you don't have to restart the context.

What about sendFirstMouseEvent?
The initialized question from above also applies here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess this all just mimics LWJGL 2. It doesn't seem to send either after restart.

}

/**
* Re-initializes the mouse input context window specific callbacks
*/
public void resetContext() {
if (!context.isRenderable()) {
return;
}

closeCallbacks();
initCallbacks();
}

private void initCallbacks() {
final long window = context.getWindowHandle();

try (MemoryStack stack = MemoryStack.stackPush()) {
Expand Down Expand Up @@ -219,14 +242,6 @@ public void invoke(final long window, final int width, final int height) {
currentWidth = width;
}
});

if(listener != null) {
sendFirstMouseEvent();
}

setCursorVisible(cursorVisible);
logger.fine("Mouse created.");
initialized = true;
}

private void initCurrentMousePosition(long window) {
Expand Down Expand Up @@ -295,9 +310,7 @@ public void destroy() {
return;
}

cursorPosCallback.close();
scrollCallback.close();
mouseButtonCallback.close();
closeCallbacks();

currentCursor = null;
currentCursorDelays = null;
Expand All @@ -313,6 +326,12 @@ public void destroy() {
logger.fine("Mouse destroyed.");
}

private void closeCallbacks() {
cursorPosCallback.close();
scrollCallback.close();
mouseButtonCallback.close();
}

@Override
public void setCursorVisible(boolean visible) {
cursorVisible = visible;
Expand Down
49 changes: 33 additions & 16 deletions jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglWindow.java
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ public LwjglWindow(final JmeContext.Type type) {
/**
* @return Type.Display or Type.Canvas
*/
@Override
public JmeContext.Type getType() {
return type;
}
Expand All @@ -154,6 +155,7 @@ public JmeContext.Type getType() {
*
* @param title the title to set
*/
@Override
public void setTitle(final String title) {
if (created.get() && window != NULL) {
glfwSetWindowTitle(window, title);
Expand All @@ -163,6 +165,7 @@ public void setTitle(final String title) {
/**
* Restart if it's a windowed or full-screen display.
*/
@Override
public void restart() {
if (created.get()) {
needRestart.set(true);
Expand Down Expand Up @@ -295,6 +298,11 @@ public void invoke(final long window, final boolean focus) {
showWindow();

allowSwapBuffers = settings.isSwapBuffers();

// Create OpenCL
if (settings.isOpenCLSupport()) {
initOpenCL(window);
}
Comment on lines +301 to +305
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any specific reason why this should now only happen when restarting and not as part of the general init process? Wouldn't this prevent openCL init when regularly starting?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is part of the general init process. This is called in createContext which in turn is used by the restart and the initial context.

}

protected void showWindow() {
Expand All @@ -303,6 +311,8 @@ protected void showWindow() {

/**
* Set custom icons to the window of this application.
*
* @param settings settings for getting the icons
*/
protected void setWindowIcon(final AppSettings settings) {

Expand Down Expand Up @@ -409,7 +419,6 @@ protected void destroyContext() {
window = NULL;
}

glfwTerminate();
} catch (final Exception ex) {
listener.handleError("Failed to destroy context", ex);
}
Expand All @@ -429,6 +438,8 @@ public void create(boolean waitFor) {

/**
* Does LWJGL display initialization in the OpenGL thread
*
* @return returns {@code true} if the context initialization was successful
*/
protected boolean initInThread() {
try {
Expand All @@ -455,13 +466,6 @@ protected boolean initInThread() {

created.set(true);
super.internalCreate();

//create OpenCL
//Must be done here because the window handle is needed
if (settings.isOpenCLSupport()) {
initOpenCL(window);
}

} catch (Exception ex) {
try {
if (window != NULL) {
Expand All @@ -486,14 +490,7 @@ protected boolean initInThread() {
protected void runLoop() {
// If a restart is required, lets recreate the context.
if (needRestart.getAndSet(false)) {
try {
destroyContext();
createContext(settings);
} catch (Exception ex) {
LOGGER.log(Level.SEVERE, "Failed to set display settings!", ex);
}

LOGGER.fine("Display restarted.");
restartContext();
}

if (!created.get()) {
Expand Down Expand Up @@ -549,6 +546,25 @@ protected void runLoop() {
glfwPollEvents();
}

private void restartContext() {
try {
destroyContext();
createContext(settings);
} catch (Exception ex) {
LOGGER.log(Level.SEVERE, "Failed to set display settings!", ex);
}

// We need to reinit the mouse and keyboard input as they are tied to a window handle
if (keyInput != null && keyInput.isInitialized()) {
keyInput.resetContext();
}
if (mouseInput != null && mouseInput.isInitialized()) {
mouseInput.resetContext();
}

LOGGER.fine("Display restarted.");
}

private void setFrameRateLimit(int frameRateLimit) {
this.frameRateLimit = frameRateLimit;
frameSleepTime = 1000.0 / this.frameRateLimit;
Expand All @@ -562,6 +578,7 @@ protected void deinitInThread() {

destroyContext();
super.internalDestroy();
glfwTerminate();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just wonder here if we can't/shouldn't arrange it that the restart could call deinitInThread() and initInThread instead of manually re-creating the context.

I think all that would be required is moving the glfwTerminate post deinitInThread, right?
I don't know if that works by the API.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, I tried it just now. Seems to actually fix #1013 . But also causes some side effects. Cursor is somehow gone as well as some other elements and all inputs... Need to investigate this a bit more...


LOGGER.fine("Display destroyed.");
}
Expand Down