Only use glfw for window creation;

This allows for headless operation.  Currently GLFW throws an error
if you call glfwInit without an X server running, preventing lovr
from starting at all in that situation.

Because GLFW is currently used for time functions, those had to be
moved into the platform layer.  There's lots of duplication here,
ideally the platform layer would just return raw timer values and the
timer module would handle the epoch logic, but it was too difficult to
coordinate that right now.

Also, lovrPlatformInit is guaranteed to set the time epoch to 0 now.
This commit is contained in:
bjorn 2019-12-13 19:10:24 -08:00
parent 5957a1d007
commit 63dc8f6c9b
7 changed files with 164 additions and 68 deletions

View File

@ -56,10 +56,11 @@ typedef void (*keyboardCallback)(KeyCode key, ButtonAction action);
bool lovrPlatformInit(void);
void lovrPlatformDestroy(void);
const char* lovrPlatformGetName(void);
void lovrPlatformPollEvents(void);
double lovrPlatformGetTime(void);
void lovrPlatformSetTime(double t);
void lovrPlatformSleep(double seconds);
void lovrPlatformOpenConsole(void);
void lovrPlatformPollEvents(void);
bool lovrPlatformCreateWindow(WindowFlags* flags);
bool lovrPlatformHasWindow(void);
void lovrPlatformGetWindowSize(int* width, int* height);
@ -75,7 +76,6 @@ void lovrPlatformGetMousePosition(double* x, double* y);
void lovrPlatformSetMouseMode(MouseMode mode);
bool lovrPlatformIsMouseDown(MouseButton button);
bool lovrPlatformIsKeyDown(KeyCode key);
void lovrPlatformSleep(double seconds);
#ifdef _WIN32
#include <windows.h>
HANDLE lovrPlatformGetWindow(void);

View File

@ -18,31 +18,35 @@ static struct {
windowResizeCallback onWindowResize;
mouseButtonCallback onMouseButton;
keyboardCallback onKeyboardEvent;
} state;
} glfwState;
static void onError(int code, const char* description) {
lovrThrow(description);
}
static void onWindowClose(GLFWwindow* window) {
if (state.onWindowClose) {
state.onWindowClose();
if (glfwState.onWindowClose) {
glfwState.onWindowClose();
}
}
static void onWindowResize(GLFWwindow* window, int width, int height) {
if (state.onWindowResize) {
if (glfwState.onWindowResize) {
glfwGetFramebufferSize(window, &width, &height);
state.onWindowResize(width, height);
glfwState.onWindowResize(width, height);
}
}
static void onMouseButton(GLFWwindow* window, int b, int a, int mods) {
if (state.onMouseButton && (b == GLFW_MOUSE_BUTTON_LEFT || b == GLFW_MOUSE_BUTTON_RIGHT)) {
if (glfwState.onMouseButton && (b == GLFW_MOUSE_BUTTON_LEFT || b == GLFW_MOUSE_BUTTON_RIGHT)) {
MouseButton button = (b == GLFW_MOUSE_BUTTON_LEFT) ? MOUSE_LEFT : MOUSE_RIGHT;
ButtonAction action = (a == GLFW_PRESS) ? BUTTON_PRESSED : BUTTON_RELEASED;
state.onMouseButton(button, action);
glfwState.onMouseButton(button, action);
}
}
static void onKeyboardEvent(GLFWwindow* window, int k, int scancode, int a, int mods) {
if (state.onKeyboardEvent) {
if (glfwState.onKeyboardEvent) {
KeyCode key;
switch (k) {
case GLFW_KEY_W: key = KEY_W; break;
@ -60,7 +64,7 @@ static void onKeyboardEvent(GLFWwindow* window, int k, int scancode, int a, int
default: return;
}
ButtonAction action = (a == GLFW_PRESS) ? BUTTON_PRESSED : BUTTON_RELEASED;
state.onKeyboardEvent(key, action);
glfwState.onKeyboardEvent(key, action);
}
}
@ -90,39 +94,25 @@ static int convertKeyCode(KeyCode key) {
}
}
static void onGlfwError(int code, const char* description) {
lovrThrow(description);
}
bool lovrPlatformInit() {
glfwSetErrorCallback(onGlfwError);
#ifndef EMSCRIPTEN
glfwInitHint(GLFW_COCOA_CHDIR_RESOURCES, GLFW_FALSE);
#endif
return glfwInit();
}
void lovrPlatformDestroy() {
glfwTerminate();
}
void lovrPlatformPollEvents() {
glfwPollEvents();
}
double lovrPlatformGetTime() {
return glfwGetTime();
}
void lovrPlatformSetTime(double t) {
glfwSetTime(t);
if (glfwState.window) {
glfwPollEvents();
}
}
bool lovrPlatformCreateWindow(WindowFlags* flags) {
if (state.window) {
if (glfwState.window) {
return true;
}
glfwSetErrorCallback(onError);
#ifdef __APPLE__
glfwInitHint(GLFW_COCOA_CHDIR_RESOURCES, GLFW_FALSE);
#endif
if (!glfwInit()) {
return false;
}
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
@ -143,36 +133,36 @@ bool lovrPlatformCreateWindow(WindowFlags* flags) {
glfwWindowHint(GLFW_REFRESH_RATE, mode->refreshRate);
}
state.window = glfwCreateWindow(width, height, flags->title, flags->fullscreen ? monitor : NULL, NULL);
glfwState.window = glfwCreateWindow(width, height, flags->title, flags->fullscreen ? monitor : NULL, NULL);
if (!state.window) {
if (!glfwState.window) {
return false;
}
if (flags->icon.data) {
glfwSetWindowIcon(state.window, 1, &(GLFWimage) {
glfwSetWindowIcon(glfwState.window, 1, &(GLFWimage) {
.pixels = flags->icon.data,
.width = flags->icon.width,
.height = flags->icon.height
});
}
glfwMakeContextCurrent(state.window);
glfwSetWindowCloseCallback(state.window, onWindowClose);
glfwSetWindowSizeCallback(state.window, onWindowResize);
glfwSetMouseButtonCallback(state.window, onMouseButton);
glfwSetKeyCallback(state.window, onKeyboardEvent);
glfwMakeContextCurrent(glfwState.window);
glfwSetWindowCloseCallback(glfwState.window, onWindowClose);
glfwSetWindowSizeCallback(glfwState.window, onWindowResize);
glfwSetMouseButtonCallback(glfwState.window, onMouseButton);
glfwSetKeyCallback(glfwState.window, onKeyboardEvent);
lovrPlatformSetSwapInterval(flags->vsync);
return true;
}
bool lovrPlatformHasWindow() {
return state.window;
return glfwState.window;
}
void lovrPlatformGetWindowSize(int* width, int* height) {
if (state.window) {
glfwGetWindowSize(state.window, width, height);
if (glfwState.window) {
glfwGetWindowSize(glfwState.window, width, height);
} else {
if (*width) *width = 0;
if (*height) *height = 0;
@ -180,8 +170,8 @@ void lovrPlatformGetWindowSize(int* width, int* height) {
}
void lovrPlatformGetFramebufferSize(int* width, int* height) {
if (state.window) {
glfwGetFramebufferSize(state.window, width, height);
if (glfwState.window) {
glfwGetFramebufferSize(glfwState.window, width, height);
} else {
if (*width) *width = 0;
if (*height) *height = 0;
@ -197,7 +187,7 @@ void lovrPlatformSetSwapInterval(int interval) {
}
void lovrPlatformSwapBuffers() {
glfwSwapBuffers(state.window);
glfwSwapBuffers(glfwState.window);
}
void* lovrPlatformGetProcAddress(const char* function) {
@ -205,50 +195,50 @@ void* lovrPlatformGetProcAddress(const char* function) {
}
void lovrPlatformOnWindowClose(windowCloseCallback callback) {
state.onWindowClose = callback;
glfwState.onWindowClose = callback;
}
void lovrPlatformOnWindowResize(windowResizeCallback callback) {
state.onWindowResize = callback;
glfwState.onWindowResize = callback;
}
void lovrPlatformOnMouseButton(mouseButtonCallback callback) {
state.onMouseButton = callback;
glfwState.onMouseButton = callback;
}
void lovrPlatformOnKeyboardEvent(keyboardCallback callback) {
state.onKeyboardEvent = callback;
glfwState.onKeyboardEvent = callback;
}
void lovrPlatformGetMousePosition(double* x, double* y) {
if (state.window) {
glfwGetCursorPos(state.window, x, y);
if (glfwState.window) {
glfwGetCursorPos(glfwState.window, x, y);
} else {
*x = *y = 0.;
}
}
void lovrPlatformSetMouseMode(MouseMode mode) {
if (state.window) {
if (glfwState.window) {
int m = (mode == MOUSE_MODE_GRABBED) ? GLFW_CURSOR_DISABLED : GLFW_CURSOR_NORMAL;
glfwSetInputMode(state.window, GLFW_CURSOR, m);
glfwSetInputMode(glfwState.window, GLFW_CURSOR, m);
}
}
bool lovrPlatformIsMouseDown(MouseButton button) {
return state.window ? glfwGetMouseButton(state.window, convertMouseButton(button)) == GLFW_PRESS : false;
return glfwState.window ? glfwGetMouseButton(glfwState.window, convertMouseButton(button)) == GLFW_PRESS : false;
}
bool lovrPlatformIsKeyDown(KeyCode key) {
return state.window ? glfwGetKey(state.window, convertKeyCode(key)) == GLFW_PRESS : false;
return glfwState.window ? glfwGetKey(glfwState.window, convertKeyCode(key)) == GLFW_PRESS : false;
}
#ifdef _WIN32
HANDLE lovrPlatformGetWindow() {
return (HANDLE) glfwGetWin32Window(state.window);
return (HANDLE) glfwGetWin32Window(glfwState.window);
}
HGLRC lovrPlatformGetContext() {
return glfwGetWGLContext(state.window);
return glfwGetWGLContext(glfwState.window);
}
#endif

View File

@ -4,15 +4,41 @@
#include "platform_glfw.c.h"
static uint64_t epoch;
#define NS_PER_SEC 1000000000ULL
static uint64_t getTime() {
struct timespec t;
clock_gettime(CLOCK_MONOTONIC, &t);
return (uint64_t) t.tv_sec * NS_PER_SEC + (uint64_t) t.tv_nsec;
}
bool lovrPlatformInit() {
epoch = getTime();
return true;
}
void lovrPlatformDestroy() {
glfwTerminate();
}
const char* lovrPlatformGetName() {
return "Linux";
}
double lovrPlatformGetTime() {
return (getTime() - epoch) / (double) NS_PER_SEC;
}
void lovrPlatformSetTime(double t) {
epoch = getTime() - (uint64_t) (t * NS_PER_SEC + .5);
}
void lovrPlatformSleep(double seconds) {
seconds += .5e-9;
struct timespec t;
t.tv_sec = seconds;
t.tv_nsec = (seconds - t.tv_sec) * 1e9L;
t.tv_nsec = (seconds - t.tv_sec) * NS_PER_SEC;
while (nanosleep(&t, &t));
}

View File

@ -1,15 +1,47 @@
#include "platform.h"
#include <mach-o/dyld.h>
#include <unistd.h>
#include <mach/mach_time.h>
#include <time.h>
#include "platform_glfw.c.h"
static uint64_t epoch;
static uint64_t frequency;
static uint64_t getTime() {
return mach_absolute_time();
}
bool lovrPlatformInit() {
mach_timebase_info_data_t info;
mach_timebase_info(&info);
frequency = (info.denom * 1e9) / info.numer;
epoch = getTime();
return true;
}
void lovrPlatformDestroy() {
glfwTerminate();
}
const char* lovrPlatformGetName() {
return "macOS";
}
double lovrPlatformGetTime() {
return (getTime() - epoch) / (double) frequency;
}
void lovrPlatformSetTime(double t) {
epoch = getTime() - (uint64_t) (t * frequency + .5);
}
void lovrPlatformSleep(double seconds) {
usleep((unsigned int) (seconds * 1000000));
seconds += .5e-9;
struct timespec t;
t.tv_sec = seconds;
t.tv_nsec = (seconds - t.tv_sec) * NS_PER_SEC;
while (nanosleep(&t, &t));
}
void lovrPlatformOpenConsole() {

View File

@ -3,12 +3,32 @@
#include "platform_glfw.c.h"
static double epoch;
#define TIMER_FREQUENCY 1000
bool lovrPlatformInit() {
epoch = emscripten_get_now();
return true;
}
void lovrPlatformDestroy() {
glfwTerminate();
}
const char* lovrPlatformGetName() {
return "Web";
}
double lovrPlatformGetTime() {
return (emscripten_get_now() - epoch) / TIMER_FREQUENCY;
}
void lovrPlatformSetTime(double t) {
epoch = emscripten_get_now() - (t * TIMER_FREQUENCY);
}
void lovrPlatformSleep(double seconds) {
emscripten_sleep((unsigned int) (seconds * 1000));
emscripten_sleep((unsigned int) (seconds * 1000 + .5));
}
void lovrPlatformOpenConsole() {

View File

@ -4,10 +4,39 @@
#include "platform_glfw.c.h"
static uint64_t epoch;
static uint64_t frequency;
static uint64_t getTime() {
LARGE_INTEGER t;
QueryPerformanceCounter(&t);
return t.QuadPart;
}
bool lovrPlatformInit() {
LARGE_INTEGER f;
QueryPerformanceFrequency(&f);
frequency = f.QuadPart;
epoch = getTime();
return true;
}
void lovrPlatformDestroy() {
glfwTerminate();
}
const char* lovrPlatformGetName() {
return "Windows";
}
double lovrPlatformGetTime() {
return (getTime() - epoch) / (double) frequency;
}
void lovrPlatformSetTime(double t) {
epoch = getTime() - (uint64_t) (t * frequency + .5);
}
void lovrPlatformSleep(double seconds) {
Sleep((unsigned int) (seconds * 1000));
}

View File

@ -104,7 +104,6 @@ typedef enum { // What flag is being searched for?
lua_State* lovrInit(lua_State* L, int argc, char** argv) {
lovrAssert(lovrPlatformInit(), "Failed to initialize platform");
lovrPlatformSetTime(0.);
// arg table
// Args follow the lua standard https://en.wikibooks.org/wiki/Lua_Programming/command_line_parameter