-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathWindow.java
More file actions
318 lines (270 loc) · 9.86 KB
/
Copy pathWindow.java
File metadata and controls
318 lines (270 loc) · 9.86 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
/*
George Zhang
Window class.
*/
package geetransit.minecraft05.engine;
import org.lwjgl.glfw.*;
import org.lwjgl.opengl.*;
import static org.lwjgl.glfw.Callbacks.*;
import static org.lwjgl.glfw.GLFW.*;
import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.system.MemoryUtil.*;
public class Window {
private long handle;
private Timer timer;
private Object lock;
public final Bucket next;
private String title;
private int xPos;
private int yPos;
private int width;
private int height;
private int mode;
public static final int WINDOWED = 0;
public static final int BORDERLESS = 1;
public static final int FULLSCREEN = 2;
private boolean vSync;
private int targetFps;
private int targetUps;
private float elapsedTime;
private float accumulatedTime;
private boolean destroyed = false;
private long monitor;
private GLFWVidMode vidmode;
public Window(
String title,
int width,
int height,
int mode,
boolean vSync,
int targetFps,
int targetUps
) {
this.title = title;
this.width = width;
this.height = height;
this.mode = mode;
this.vSync = vSync;
this.targetFps = targetFps;
this.targetUps = targetUps;
this.timer = new Timer();
this.lock = new Object();
this.next = new Bucket();
}
public void createWindow() {
// Setup an error callback. The default implementation
// will print the error message in System.err.
GLFWErrorCallback.createPrint(System.err).set();
// Initialize GLFW. Most GLFW functions will not work before doing this.
if (!glfwInit())
throw new IllegalStateException("Unable to initialize GLFW");
// Configure our window
glfwDefaultWindowHints(); // optional, the current window hints are already the default
glfwWindowHint(GLFW_VISIBLE, GL_FALSE); // the window will stay hidden after creation
glfwWindowHint(GLFW_RESIZABLE, GL_TRUE); // the window will be resizable
glfwWindowHint(GLFW_FOCUSED, GL_TRUE); // get focus when shown
// Get the resolution of the primary monitor
this.monitor = glfwGetPrimaryMonitor();
this.vidmode = glfwGetVideoMode(monitor);
// Create the window
this.handle = glfwCreateWindow(this.width, this.height, this.title, NULL, NULL);
if (this.handle == NULL)
throw new RuntimeException("Failed to create the GLFW window");
// Setup resize callback
glfwSetFramebufferSizeCallback(this.handle, (window, width, height) -> {
if (width > 0 && height > 0)
this.next.add("size", () -> {
if (this.isWindowed())
this.setSize(width, height);
});
});
// Center our window
this.xPos = this.getCenteredXPos();
this.yPos = this.getCenteredYPos();
this.updateWindowMonitor();
}
public void eventThread() {
this.eventLoop();
this.eventDestroy();
this.eventTerminate();
}
private void eventLoop() {
// Make the window visible
glfwShowWindow(this.handle);
while (!glfwWindowShouldClose(this.handle)) {
// Only force focus when in borderless fullscreen
if (this.isBorderless())
glfwFocusWindow(this.handle);
// This will block until an event occurs.
// (Helps reduce CPU usage.)
glfwWaitEvents();
}
}
private void eventDestroy() {
synchronized (this.lock) {
this.destroyed = true;
// Release window (safely)
glfwDestroyWindow(this.handle);
}
// Release window callbacks
glfwFreeCallbacks(this.handle);
}
public void eventTerminate() {
// Terminate GLFW and release the error function
glfwTerminate();
glfwSetErrorCallback(null).free();
}
public void renderThread(Scene scene) {
try {
this.renderInit(scene);
this.renderLoop(scene);
} catch (Exception e) {
e.printStackTrace();
System.exit(1);
} finally {
scene.cleanup();
}
}
private void renderInit(Scene scene) throws Exception {
// This adds the OpenGL context into this function.
glfwMakeContextCurrent(this.handle);
// This line is critical for LWJGL's interoperation with GLFW's
// OpenGL context, or any context that is managed externally.
// LWJGL detects the context that is current in the current thread,
// creates the ContextCapabilities instance and makes the OpenGL
// bindings available for use.
GL.createCapabilities();
// Check vSync
glfwSwapInterval(this.isVSync() ? 1 : 0);
// init
scene.init(this);
// Start timer.
this.timer.init();
}
// Render loop.
private void renderLoop(Scene scene) {
while (!this.isDestroyed()) {
this.elapsedTime = this.timer.getElapsedTime();
this.accumulatedTime += this.elapsedTime;
// bucket
this.next.run("mode");
this.next.run("size");
this.next.run("vSync");
this.next.run("inputMode");
this.next.run("attrib");
this.next.run("targetFps");
this.next.run("targetUps");
// input
scene.input(this);
// update
float interval = 1f / this.getTargetUps();
while (this.accumulatedTime >= interval) {
scene.update(interval);
this.accumulatedTime -= interval;
}
// render
scene.render(this);
this.renderUpdate();
if (!this.isVSync())
this.renderSync();
}
}
// Sync with target FPS.
private void renderSync() {
float loopSlot = 1f / this.getTargetFps();
double endTime = this.timer.getLastLoopTime() + loopSlot;
while (timer.getTime() < endTime && this.next.empty()) {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
}
}
}
private void renderUpdate() {
// This can fail if not sync'd. (Can only swap when window exists)
synchronized (this.lock) {
if (!this.destroyed)
glfwSwapBuffers(this.handle); // swap the color buffers
}
}
protected void updateWindowPos() {
int[] xPos = new int[1];
int[] yPos = new int[1];
glfwGetWindowPos(this.getHandle(), xPos, yPos);
this.xPos = xPos[0];
this.yPos = yPos[0];
}
protected void updateWindowMonitor() {
this.setAttrib(GLFW_DECORATED, this.isBorderless() ? 0 : 1);
glfwSetWindowMonitor(
this.getHandle(), this.getCurrentMonitor(), this.getXPos(), this.getYPos(),
this.getWidth(), this.getHeight(), GLFW_DONT_CARE
);
}
protected void updateSwapInterval(boolean vSync) { glfwSwapInterval(vSync ? 1 : 0); }
protected void updateSwapInterval() { this.updateSwapInterval(this.isVSync()); }
protected long getCurrentMonitor() { return this.isFullscreen() ? this.monitor : NULL; }
public void setShouldClose(boolean shouldClose) { glfwSetWindowShouldClose(this.getHandle(), true); }
public void clear(int bits) { glClear(bits); }
public void clearColor(float r, float g, float b, float a) { glClearColor(r, g, b, a); }
public GLFWKeyCallback setKeyCallback(GLFWKeyCallbackI keyCallback) { return glfwSetKeyCallback(this.getHandle(), keyCallback); }
public void setAttrib(int attrib, int value) { glfwSetWindowAttrib(this.getHandle(), attrib, value); }
public long getAttrib(int attrib) { return glfwGetWindowAttrib(this.getHandle(), attrib); }
public void setInputMode(int mode, int value) { glfwSetInputMode(this.getHandle(), mode, value); }
public long getInputMode(int mode) { return glfwGetInputMode(this.getHandle(), mode); }
public int getKey(int key) { return glfwGetKey(this.getHandle(), key); }
public boolean isKeyDown(int key) { return (this.getKey(key) == GLFW_PRESS); }
public void postEmptyEvent() { glfwPostEmptyEvent(); }
public long getHandle() { return this.handle; }
public Object getLock() { return this.lock; }
public boolean isDestroyed() { return this.destroyed; }
public float getElapsedTime() { return this.elapsedTime; }
public float getAccumulatedTime() { return this.accumulatedTime; }
public int getTargetFps() { return this.targetFps; }
public int getTargetUps() { return this.targetUps; }
public void setTargetFps(int targetFps) { this.targetFps = targetFps; }
public void setTargetUps(int targetFps) { this.targetUps = targetUps; }
// width, height
public int getWidth() { return !this.isWindowed() ? this.getScreenWidth() : this.getWindowWidth(); }
public int getHeight() { return !this.isWindowed() ? this.getScreenHeight() : this.getWindowHeight(); }
public int getWindowWidth() { return this.width; }
public int getWindowHeight() { return this.height; }
public int getScreenWidth() { return this.vidmode.width(); }
public int getScreenHeight() { return this.vidmode.height(); }
// xpos, ypos
public int getXPos() { return !this.isWindowed() ? this.getScreenXPos() : this.getWindowXPos(); }
public int getYPos() { return !this.isWindowed() ? this.getScreenYPos() : this.getWindowYPos(); }
public int getWindowXPos() { return this.xPos; }
public int getWindowYPos() { return this.yPos; }
public int getScreenXPos() { return 0; }
public int getScreenYPos() { return 0; }
public int getCenteredXPos() { return !this.isWindowed() ? 0 : (this.getScreenWidth() - this.getWindowWidth()) / 2; }
public int getCenteredYPos() { return !this.isWindowed() ? 0 : (this.getScreenHeight() - this.getWindowHeight()) / 2; }
// setter
public void setSize(int width, int height) {
this.width = width;
this.height = height;
this.updateWindowPos();
glViewport(0, 0, this.getWidth(), this.getHeight());
this.updateWindowMonitor();
}
public int getMode() { return this.mode; }
public boolean isWindowed() { return this.mode == WINDOWED; }
public boolean isBorderless() { return this.mode == BORDERLESS; }
public boolean isFullscreen() { return this.mode == FULLSCREEN; }
public void setMode(int mode) {
if (this.isWindowed())
this.updateWindowPos();
this.mode = mode;
if (this.isFullscreen())
// workaround to ensure vSync is correct after real fullscreen
this.next.add("vSync", () -> this.setVSync(this.isVSync()));
glViewport(0, 0, this.getWidth(), this.getHeight());
this.updateWindowMonitor();
}
public boolean isVSync() { return this.vSync; }
public void setVSync(boolean vSync) {
this.vSync = vSync;
this.updateSwapInterval();
}
}