Skip to content

Commit

Permalink
Add callback Intro5
Browse files Browse the repository at this point in the history
  • Loading branch information
httpdigest committed Oct 24, 2016
1 parent fbbbfa0 commit 3774f7b
Show file tree
Hide file tree
Showing 5 changed files with 157 additions and 23 deletions.
2 changes: 1 addition & 1 deletion src/org/lwjgl/demo/intro/Intro1.java
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ public static void main(String[] args) {
* And that should be it for the first introduction. We learnt that LWJGL 3 provides bindings to native library
* functions via static methods on a class which is named like the native library containing that function.
*/
System.out.println("The End!");
System.out.println("Fin.");
}

}
1 change: 1 addition & 0 deletions src/org/lwjgl/demo/intro/Intro2.java
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ public static void main(String[] args) {
glfwSwapBuffers(window);
}
glfwTerminate();
System.out.println("Fin.");
}

private static long createWindow() {
Expand Down
28 changes: 16 additions & 12 deletions src/org/lwjgl/demo/intro/Intro3.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@
import static org.lwjgl.system.MemoryStack.*;

/**
* In Intro2 we learnt how to allocate an off-heap memory buffer using MemoryUtil. This was done by first calling one
* of the memAlloc*() methods which return a Java NIO Buffer instance representing the allocated memory region. Once
* we were done with the buffer, we called the memFree() method to deallocate/free the off-heap memory represented by
* the Java NIO Buffer.
* In Intro2 we learnt how to allocate an off-heap memory buffer using MemoryUtil. This was done by first calling one of
* the memAlloc*() methods which return a Java NIO Buffer instance representing the allocated memory region. Once we
* were done with the buffer, we called the memFree() method to deallocate/free the off-heap memory represented by the
* Java NIO Buffer.
* <p>
* This manual memory management is necessary when a buffer needs to live for an extended amount of time in our
* application, meaning that the time between allocation and deallocation spans beyond one method.
Expand All @@ -26,9 +26,12 @@
* the VBO in Intro2. Memory was allocated, filled with data, given to OpenGL and then freed again.
* <p>
* LWJGL 3 provides a better way to handle such situations, which is by using the MemoryStack class. This class allows
* to retrieve a small chunk of memory from a pre-allocated thread-local memory region of a fixed size. It is a stack
* because allocations/deallocations must be issued in LIFO order, in that allocations cannot be freed randomly bust
* must be freed in the reverse allocation order. This allows to avoid any heap allocation and compaction strategies.
* to retrieve a small chunk of memory from a pre-allocated thread-local memory region of a fixed size. By default the
* maximum size allocatable from the MemoryStack is 8 kilobytes.
* <p>
* By the way: It is called a stack because allocations/deallocations must be issued in LIFO order, in that allocations
* cannot be freed randomly bust must be freed in the reverse allocation order. This allows to avoid any heap allocation
* and compaction strategies.
* <p>
* Also note that the pre-allocated memory of the MemoryStack is per thread. That means, every thread will get its own
* memory region and MemoryStack instances should not be shared among different threads.
Expand Down Expand Up @@ -58,8 +61,8 @@ public static void main(String[] args) {

/*
* No we reserve some space on the stack and return it as a new Java NIO Buffer using the MemoryStack's
* stackMallocFloat() method.
* The MemoryStack provides other stackMalloc* methods as well, which return other typed Java NIO Buffer views.
* stackMallocFloat() method. The MemoryStack provides other stackMalloc* methods as well, which return other
* typed Java NIO Buffer views.
*/
FloatBuffer buffer = stackMallocFloat(3 * 2);

Expand All @@ -77,9 +80,9 @@ public static void main(String[] args) {
glBufferData(GL_ARRAY_BUFFER, buffer, GL_STATIC_DRAW);

/*
* Now, that we don't need the allocated buffer anymore, we have to pop the "stack frame".
* This will reset the state of the MemoryStack (i.e. the allocation position/pointer) to where it was before
* we called stackPush() above.
* Now, that we don't need the allocated buffer anymore, we have to pop the "stack frame". This will reset the
* state of the MemoryStack (i.e. the allocation position/pointer) to where it was before we called stackPush()
* above.
*/
stackPop();

Expand All @@ -94,6 +97,7 @@ public static void main(String[] args) {
glfwSwapBuffers(window);
}
glfwTerminate();
System.out.println("Fin.");
}

private static long createWindow() {
Expand Down
21 changes: 11 additions & 10 deletions src/org/lwjgl/demo/intro/Intro4.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@
* In Intro3 we learnt how to allocate short-lived memory using the MemoryStack class.
* <p>
* There was one thing missing, though, which is necessary when working with manual memory management, including the
* MemoryStack, which is: Ensuring that the stackPop() call happens eventually.
* This may not be the case when the code between stackPush() and stackPop() throws an exception.
* MemoryStack, which is: Ensuring that the stackPop() call happens eventually. This may not be the case when the code
* between stackPush() and stackPop() throws an exception.
* <p>
* To take care of possible exceptions, we will therefore wrap the code in a try-with-resources statement to ensure that
* stackPop() will get called eventually.
Expand All @@ -34,13 +34,13 @@ public static void main(String[] args) {
long window = createWindow();

/*
* Wrap the code that is using the MemoryStack in a Java 7 try-with-resources statement.
* The nice thing here is that the MemoryStack class itself implements AutoCloseable, so it is applicable to
* being the resource in the try-with-resources statement.
* Wrap the code that is using the MemoryStack in a Java 7 try-with-resources statement. The nice thing here is
* that the MemoryStack class itself implements AutoCloseable, so it is applicable to being the resource in the
* try-with-resources statement.
*
* What is also new here is that a call to stackPush() actually returns something, namely an instance of the
* MemoryStack class. This instance represents the thread-local MemoryStack instance, which would otherwise
* be accessed whenever we call stackPush(), stackPop() or one of the static stackMalloc* methods.
* MemoryStack class. This instance represents the thread-local MemoryStack instance, which would otherwise be
* accessed whenever we call stackPush(), stackPop() or one of the static stackMalloc* methods.
*
* So, the code below calls the static method stackPush() on the class MemoryStack, which returns the
* MemoryStack instance of the current thread. At the end of the try-with-resources statement, a call to
Expand All @@ -61,8 +61,8 @@ public static void main(String[] args) {

/*
* Notice that we do not need a stackPop() here! It will be done automatically at the end of the
* the try-with-resources statement, even in the event of an exception, which was the sole purpose of
* doing it this way, in the first place.
* try-with-resources statement, even in the event of an exception, which was the sole purpose of doing it
* this way, in the first place.
*/
}

Expand All @@ -74,11 +74,12 @@ public static void main(String[] args) {
glfwSwapBuffers(window);
}
glfwTerminate();
System.out.println("Fin.");
}

private static long createWindow() {
glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
long window = glfwCreateWindow(800, 600, "Intro3", NULL, NULL);
long window = glfwCreateWindow(800, 600, "Intro4", NULL, NULL);
glfwMakeContextCurrent(window);
createCapabilities();
return window;
Expand Down
128 changes: 128 additions & 0 deletions src/org/lwjgl/demo/intro/Intro5.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
/*
* Copyright LWJGL. All rights reserved.
* License terms: http://lwjgl.org/license.php
*/
package org.lwjgl.demo.intro;

import static org.lwjgl.glfw.GLFW.*;
import static org.lwjgl.opengl.GL.*;
import static org.lwjgl.system.MemoryUtil.*;

import org.lwjgl.glfw.GLFWMouseButtonCallbackI;

/**
* In this part we will see how callbacks work. Callbacks mean any method which we can register in a native library so
* that the native library can call use back and invoke our callback method whenever it wants to.
* <p>
* One example of where callbacks occur frequently is GLFW. GLFW provides some number of different callbacks for various
* events that happen on a window, such as resizing, maximizing, minimizing and mouse or keyboard events.
* <p>
* Now, before we go into using callback with LWJGL 3 and GLFW, we should first get a clear picture of what a callback
* looks like in a native library, which LWJGL 3 tries to provide a Java counterpart for.
* <p>
* In a native library like GLFW a callback is nothing more than a function pointer. This means that it is a physical
* virtual memory address pointing to an executable piece of code, a function. The function pointer also has a type to
* make it callable in C. This function type consists of the parameter types and the return type, just like a method
* signature in Java including the return type. So, both caller and callee agree on a defined set of parameters and a
* return type to expect when the callback function is called.
* <p>
* When LWJGL 3 maps this concept of a function pointer into Java, it provides the user (that means you) with a Java
* interface type that contains a single method. This method has the same (or similar) signature and return type as the
* native callback function. If you want to see an example, look at {@link GLFWMouseButtonCallbackI}. It is an interface
* with a single non-default method which must be implemented and will be called whenever the native library calls the
* callback.
* <p>
* The fact that it is an interface with a single method makes it applicable to be the target of a Java 8 Lambda method
* or a Java 8 method reference. That means, with callbacks we need not provide an actual implementation of the callback
* interface by either anonymously or explicitly creating a class implementing that interface, but we can use Java 8
* Lambda methods and Java 8 method references with a compatible signature.
* <p>
* If you are not yet familiar with Java 8 Lambda methods or Java 8 method references, please look them up on the Oracle
* documentation. We will make use of them in the example code below.
*
* @author Kai Burjack
*/
public class Intro5 {

/**
* Callback method used with Java 8 method references. See the main() method below.
*/
private static void mouseCallback(long win, int button, int action, int mods) {
/* Print a message when the user pressed down a mouse button */
if (action == GLFW_PRESS) {
System.out.println("Pressed!");
}
}

/**
* In this demo we will register a callback with GLFW. We will use a mouse callback which notifies us whenever a
* mouse button was pressed or released.
*/
public static void main(String[] args) {
glfwInit();
long window = createWindow();

/*
* The following is one way of registering a callback. In this case with GLFW for receiving mouse button events
* happening for the window.
*
* We use an instance of an anonymous class which implements the callback interface GLFWMouseButtonCallbackI.
* Instead of using the GLFWMouseButtonCallbackI interface, we could also just use the GLFWMouseButtonCallback
* class (without the 'I' suffix). It would work the same, since that class implements the interface. But there
* is a reason why that class exists in the first place, which will be covered later.
*/
glfwSetMouseButtonCallback(window, new GLFWMouseButtonCallbackI() {
/**
* This is the single method of the callback interface which we must provide an implementation for.
* <p>
* This method will get called by the native library (GLFW) whenever some event happens with a mouse button.
* For a more detailed explanation of the GLFW callback itself, see
* <a href="http://www.glfw.org/docs/latest/group__input.html#gaef49b72d84d615bca0a6ed65485e035d">the GLFW
* documentation</a>.
* <p>
* For now, we just do nothing in this method. We just assume that it was registered successfully.
*/
public void invoke(long window, int button, int action, int mods) {
/* We don't do anything here. */
}
});

/*
* The next possible way is to use Java 8 Lambda methods to avoid having to type the actual Java interface type
* name. You either know the method signature or use an IDE with autocomplete/autosuggest support, such as
* IntelliJ IDEA.
*/
glfwSetMouseButtonCallback(window, (long win, int button, int action, int mods) -> {
/* We also don't do anything here. */
});

/*
* The last possible way to register a callback should look more familiar to C/C++ programmers. We use a Java 8
* method reference. See the method mouseCallback() above for what happens when we receive a mouse event.
*/
glfwSetMouseButtonCallback(window, Intro5::mouseCallback);

/*
* Now, when we start the application and click inside the window using any mouse button, we should see a
* message printed.
*/

/*
* We don't render anything. Just an empty window. The focus of this introduction is just callbacks.
*/
while (!glfwWindowShouldClose(window)) {
glfwPollEvents();
}
glfwTerminate();
System.out.println("Fin.");
}

private static long createWindow() {
glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
long window = glfwCreateWindow(800, 600, "Intro5", NULL, NULL);
glfwMakeContextCurrent(window);
createCapabilities();
return window;
}

}

0 comments on commit 3774f7b

Please sign in to comment.