diff --git a/src/org/lwjgl/demo/intro/Intro1.java b/src/org/lwjgl/demo/intro/Intro1.java index 83dd141a..877f8885 100644 --- a/src/org/lwjgl/demo/intro/Intro1.java +++ b/src/org/lwjgl/demo/intro/Intro1.java @@ -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."); } } diff --git a/src/org/lwjgl/demo/intro/Intro2.java b/src/org/lwjgl/demo/intro/Intro2.java index 6e4766f3..7829fe04 100644 --- a/src/org/lwjgl/demo/intro/Intro2.java +++ b/src/org/lwjgl/demo/intro/Intro2.java @@ -164,6 +164,7 @@ public static void main(String[] args) { glfwSwapBuffers(window); } glfwTerminate(); + System.out.println("Fin."); } private static long createWindow() { diff --git a/src/org/lwjgl/demo/intro/Intro3.java b/src/org/lwjgl/demo/intro/Intro3.java index 305b0e13..20e77a4b 100644 --- a/src/org/lwjgl/demo/intro/Intro3.java +++ b/src/org/lwjgl/demo/intro/Intro3.java @@ -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. *

* 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. @@ -26,9 +26,12 @@ * the VBO in Intro2. Memory was allocated, filled with data, given to OpenGL and then freed again. *

* 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. + *

+ * 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. *

* 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. @@ -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); @@ -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(); @@ -94,6 +97,7 @@ public static void main(String[] args) { glfwSwapBuffers(window); } glfwTerminate(); + System.out.println("Fin."); } private static long createWindow() { diff --git a/src/org/lwjgl/demo/intro/Intro4.java b/src/org/lwjgl/demo/intro/Intro4.java index a58cf4d9..2ef7897e 100644 --- a/src/org/lwjgl/demo/intro/Intro4.java +++ b/src/org/lwjgl/demo/intro/Intro4.java @@ -19,8 +19,8 @@ * In Intro3 we learnt how to allocate short-lived memory using the MemoryStack class. *

* 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. *

* 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. @@ -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 @@ -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. */ } @@ -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; diff --git a/src/org/lwjgl/demo/intro/Intro5.java b/src/org/lwjgl/demo/intro/Intro5.java new file mode 100644 index 00000000..7f65878f --- /dev/null +++ b/src/org/lwjgl/demo/intro/Intro5.java @@ -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. + *

+ * 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. + *

+ * 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. + *

+ * 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. + *

+ * 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. + *

+ * 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. + *

+ * 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. + *

+ * 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 + * the GLFW + * documentation. + *

+ * 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; + } + +}