Skip to content

Commit a0522f3

Browse files
committed
Add callback Intro5
1 parent fbbbfa0 commit a0522f3

File tree

5 files changed

+157
-23
lines changed

5 files changed

+157
-23
lines changed

src/org/lwjgl/demo/intro/Intro1.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ public static void main(String[] args) {
9696
* And that should be it for the first introduction. We learnt that LWJGL 3 provides bindings to native library
9797
* functions via static methods on a class which is named like the native library containing that function.
9898
*/
99-
System.out.println("The End!");
99+
System.out.println("Fin.");
100100
}
101101

102102
}

src/org/lwjgl/demo/intro/Intro2.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,7 @@ public static void main(String[] args) {
164164
glfwSwapBuffers(window);
165165
}
166166
glfwTerminate();
167+
System.out.println("Fin.");
167168
}
168169

169170
private static long createWindow() {

src/org/lwjgl/demo/intro/Intro3.java

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,10 @@
1414
import static org.lwjgl.system.MemoryStack.*;
1515

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

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

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

7982
/*
80-
* Now, that we don't need the allocated buffer anymore, we have to pop the "stack frame".
81-
* This will reset the state of the MemoryStack (i.e. the allocation position/pointer) to where it was before
82-
* we called stackPush() above.
83+
* Now, that we don't need the allocated buffer anymore, we have to pop the "stack frame". This will reset the
84+
* state of the MemoryStack (i.e. the allocation position/pointer) to where it was before we called stackPush()
85+
* above.
8386
*/
8487
stackPop();
8588

@@ -94,6 +97,7 @@ public static void main(String[] args) {
9497
glfwSwapBuffers(window);
9598
}
9699
glfwTerminate();
100+
System.out.println("Fin.");
97101
}
98102

99103
private static long createWindow() {

src/org/lwjgl/demo/intro/Intro4.java

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@
1919
* In Intro3 we learnt how to allocate short-lived memory using the MemoryStack class.
2020
* <p>
2121
* There was one thing missing, though, which is necessary when working with manual memory management, including the
22-
* MemoryStack, which is: Ensuring that the stackPop() call happens eventually.
23-
* This may not be the case when the code between stackPush() and stackPop() throws an exception.
22+
* MemoryStack, which is: Ensuring that the stackPop() call happens eventually. This may not be the case when the code
23+
* between stackPush() and stackPop() throws an exception.
2424
* <p>
2525
* To take care of possible exceptions, we will therefore wrap the code in a try-with-resources statement to ensure that
2626
* stackPop() will get called eventually.
@@ -34,13 +34,13 @@ public static void main(String[] args) {
3434
long window = createWindow();
3535

3636
/*
37-
* Wrap the code that is using the MemoryStack in a Java 7 try-with-resources statement.
38-
* The nice thing here is that the MemoryStack class itself implements AutoCloseable, so it is applicable to
39-
* being the resource in the try-with-resources statement.
37+
* Wrap the code that is using the MemoryStack in a Java 7 try-with-resources statement. The nice thing here is
38+
* that the MemoryStack class itself implements AutoCloseable, so it is applicable to being the resource in the
39+
* try-with-resources statement.
4040
*
4141
* What is also new here is that a call to stackPush() actually returns something, namely an instance of the
42-
* MemoryStack class. This instance represents the thread-local MemoryStack instance, which would otherwise
43-
* be accessed whenever we call stackPush(), stackPop() or one of the static stackMalloc* methods.
42+
* MemoryStack class. This instance represents the thread-local MemoryStack instance, which would otherwise be
43+
* accessed whenever we call stackPush(), stackPop() or one of the static stackMalloc* methods.
4444
*
4545
* So, the code below calls the static method stackPush() on the class MemoryStack, which returns the
4646
* 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) {
6161

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

@@ -74,11 +74,12 @@ public static void main(String[] args) {
7474
glfwSwapBuffers(window);
7575
}
7676
glfwTerminate();
77+
System.out.println("Fin.");
7778
}
7879

7980
private static long createWindow() {
8081
glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
81-
long window = glfwCreateWindow(800, 600, "Intro3", NULL, NULL);
82+
long window = glfwCreateWindow(800, 600, "Intro4", NULL, NULL);
8283
glfwMakeContextCurrent(window);
8384
createCapabilities();
8485
return window;
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
/*
2+
* Copyright LWJGL. All rights reserved.
3+
* License terms: http://lwjgl.org/license.php
4+
*/
5+
package org.lwjgl.demo.intro;
6+
7+
import static org.lwjgl.glfw.GLFW.*;
8+
import static org.lwjgl.opengl.GL.*;
9+
import static org.lwjgl.system.MemoryUtil.*;
10+
11+
import org.lwjgl.glfw.GLFWMouseButtonCallbackI;
12+
13+
/**
14+
* In this part we will see how callbacks work. Callbacks mean any method which we can register in a native library so
15+
* that the native library can call use back and invoke our callback method whenever it wants to.
16+
* <p>
17+
* One example of where callbacks occur frequently is GLFW. GLFW provides some number of different callbacks for various
18+
* events that happen on a window, such as resizing, maximizing, minimizing and mouse or keyboard events.
19+
* <p>
20+
* Now, before we go into using callback with LWJGL 3 and GLFW, we should first get a clear picture of what a callback
21+
* looks in the native library, which LWJGL 3 tries to provide a Java counterpart for.
22+
* <p>
23+
* In a native library like GLFW a callback is nothing more than a function pointer. This means that it is a physical
24+
* virtual memory address pointing to an executable piece of code, a function. The function pointer also has a type to
25+
* make it callable in C. This function type consists of the parameter types and the return type, just like a method
26+
* signature in Java including the return type. So, both caller and callee agree on a defined set of parameters and a
27+
* return type to expect when the callback function is called.
28+
* <p>
29+
* When LWJGL 3 maps this concept of a function pointer into Java, it provides the user (that means you) with a Java
30+
* interface type that contains a single method. This method has the same (or similar) signature and return type as the
31+
* native callback function. If you want to see an example, look at {@link GLFWMouseButtonCallbackI}. It is an interface
32+
* with a single non-default method which must be implemented and will be called whenever the native library calls the
33+
* callback.
34+
* <p>
35+
* The fact that it is an interface with a single method makes it applicable to be the target of a Java 8 Lambda method
36+
* or a Java 8 method reference. That means, with callbacks we need not provide an actual implementation of the callback
37+
* interface by either anonymously or explicitly creating a class implementing that interface, but we can use Java 8
38+
* Lambda methods and Java 8 method references with a compatible signature.
39+
* <p>
40+
* If you are not yet familiar with Java 8 Lambda methods or Java 8 method references, please look them up on the Oracle
41+
* documentation. We will make use of them in the example code below.
42+
*
43+
* @author Kai Burjack
44+
*/
45+
public class Intro5 {
46+
47+
/**
48+
* Callback method used with Java 8 method references. See the main() method below.
49+
*/
50+
private static void mouseCallback(long win, int button, int action, int mods) {
51+
/* Print a message when the user pressed down a mouse button */
52+
if (action == GLFW_PRESS) {
53+
System.out.println("Pressed!");
54+
}
55+
}
56+
57+
/**
58+
* In this demo we will register a callback with GLFW. We will use a mouse callback which notifies us whenever a
59+
* mouse button was pressed or released.
60+
*/
61+
public static void main(String[] args) {
62+
glfwInit();
63+
long window = createWindow();
64+
65+
/*
66+
* The following is one way of registering a callback. In this case with GLFW for receiving mouse button events
67+
* happening for the window.
68+
*
69+
* We use an instance of an anonymous class which implements the callback interface GLFWMouseButtonCallbackI.
70+
* Instead of using the GLFWMouseButtonCallbackI interface, we could also just use the GLFWMouseButtonCallback
71+
* class (without the 'I' suffix). It would work the same, since that class implements the interface. But there
72+
* is a reason why that class exists in the first place, which will be covered later.
73+
*/
74+
glfwSetMouseButtonCallback(window, new GLFWMouseButtonCallbackI() {
75+
/**
76+
* This is the single method of the callback interface which we must provide an implementation for.
77+
* <p>
78+
* This method will get called by the native library (GLFW) whenever some event happens with a mouse button.
79+
* For a more detailed explanation of the GLFW callback itself, see
80+
* <a href="http://www.glfw.org/docs/latest/group__input.html#gaef49b72d84d615bca0a6ed65485e035d">the GLFW
81+
* documentation</a>.
82+
* <p>
83+
* For now, we just do nothing in this method. We just assume that it was registered successfully.
84+
*/
85+
public void invoke(long window, int button, int action, int mods) {
86+
/* We don't do anything here. */
87+
}
88+
});
89+
90+
/*
91+
* The next possible way is to use Java 8 Lambda methods to avoid having to type the actual Java interface type
92+
* name. You either know the method signature or use an IDE with autocomplete/autosuggest support, such as
93+
* IntelliJ IDEA.
94+
*/
95+
glfwSetMouseButtonCallback(window, (long win, int button, int action, int mods) -> {
96+
/* We also don't do anything here. */
97+
});
98+
99+
/*
100+
* The last possible way to register a callback should look more familiar to C/C++ programmers. We use a Java 8
101+
* method reference. See the method mouseCallback() above for what happens when we receive a mouse event.
102+
*/
103+
glfwSetMouseButtonCallback(window, Intro5::mouseCallback);
104+
105+
/*
106+
* Now, when we start the application and click inside the window using any mouse button, we should see a
107+
* message printed.
108+
*/
109+
110+
/*
111+
* We don't render anything. Just an empty window. The focus of this introduction is just callbacks.
112+
*/
113+
while (!glfwWindowShouldClose(window)) {
114+
glfwPollEvents();
115+
}
116+
glfwTerminate();
117+
System.out.println("Fin.");
118+
}
119+
120+
private static long createWindow() {
121+
glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
122+
long window = glfwCreateWindow(800, 600, "Intro5", NULL, NULL);
123+
glfwMakeContextCurrent(window);
124+
createCapabilities();
125+
return window;
126+
}
127+
128+
}

0 commit comments

Comments
 (0)