Created
February 18, 2013 11:12
-
-
Save nilium/4976671 to your computer and use it in GitHub Desktop.
Example main.cc file for using libdispatch and GLFW together on Mac OS X.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <atomic> | |
#include <stdexcept> | |
#ifdef TARGET_OS_MAC | |
#include <dispatch/dispatch.h> | |
#include <CoreFoundation/CoreFoundation.h> | |
#include <Foundation/Foundation.h> | |
#endif | |
#include <gl/glfw3.h> | |
#include <pthread.h> | |
// Main queue - used for GLFW calls and event pumping | |
static dispatch_queue_t g_main_queue; | |
// Frame loop running bool (uses atomic<bool> instead of atomic_bool because | |
// libc++ doesn't provide an atomic_bool yet) | |
static std::atomic<bool> g_frameloop_running { true }; | |
// Does what it says | |
static void sys_terminate(void *ctx); | |
// Handles frame logic and sends rendering tasks to the GL queue (or just does | |
// the rendering itself) | |
static void *sys_frameloop(void *ctx); | |
// Main routine - initializes stuff & kicks off the frame loop | |
static void sys_initialize(void *ctx); | |
// Tells the main loop to quit. Thread-safe. | |
static void sys_quit(); | |
int main(int argc, char const *argv[]) | |
{ | |
// Bootstrap | |
#ifdef TARGET_OS_MAC | |
// Play nice with ARC even if ARC isn't enabled (it isn't for this unit). | |
@autoreleasepool { | |
#endif | |
g_main_queue = dispatch_get_main_queue(); | |
// Queue up the actual main routine | |
dispatch_async_f(g_main_queue, NULL, sys_initialize); | |
#ifdef TARGET_OS_MAC | |
// On OS X, do NOT call dispatch_main, as this will terminate thread 0 (the UI | |
// thread), causing all manner of hell to break loose when you attempt to | |
// create a window. Instead, use NSRunLoop to kick off the main run-loop, | |
// which in turn keeps the main dispatch queue on thread 0 and allows OS X's | |
// UI to function as intended. | |
[[NSRunLoop mainRunLoop] run]; | |
} // @autoreleasepool | |
#else | |
// Other OSes may require their own unique bootstrapping. | |
dispatch_main(); | |
#endif | |
return 0; | |
} | |
// must be run on main queue | |
static void sys_initialize(void *ctx) | |
{ | |
pthread_t frame_thread; | |
pthread_attr_t thread_attr; | |
GLFWwindow *window = nullptr; | |
if (glfwInit()) { | |
atexit(glfwTerminate); | |
} else { | |
throw std::runtime_error("Failed to initialize GLFW"); | |
} | |
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2); | |
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); | |
// glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); | |
window = glfwCreateWindow(800, 600, "Snow", NULL, NULL); | |
if (!window) { | |
throw std::runtime_error("Failed to create GLFW window"); | |
} | |
// Launch frameloop thread - do not just call sys_frameloop because we want to | |
// keep the main queue unblocked -- it has to be free so we can poll for | |
// events and so on later. | |
if (pthread_attr_init(&thread_attr)) { | |
throw std::runtime_error("Failed to init frame thread attributes"); | |
} | |
if (pthread_create(&frame_thread, &thread_attr, | |
sys_frameloop, static_cast<void *>(window))) { | |
throw std::runtime_error("Failed to start frame loop thread"); | |
} | |
pthread_attr_destroy(&thread_attr); | |
} | |
static void sys_quit() | |
{ | |
bool from = g_frameloop_running; | |
while (!g_frameloop_running.compare_exchange_weak(from, false)); | |
} | |
// Run as single thread | |
static void *sys_frameloop(void *ctx) | |
{ | |
GLFWwindow *window = static_cast<GLFWwindow *>(ctx); | |
// Make this thread own the window's GL context. | |
glfwMakeContextCurrent(window); | |
// Initialize GL here | |
#warning TODO: Initialize OpenGL | |
while (g_frameloop_running) { | |
// Poll events from the main thread. You'll likely want to do this using | |
// dispatch_sync instead of dispatch_async. | |
dispatch_sync(g_main_queue, [] { glfwPollEvents(); }); | |
// Update game, handle events, etc. - presumably you have a thread-safe way | |
// of doing this. It's not hard, I've already done it, so I can guarantee | |
// it's easy enough. You might want to push this off to yet another thread | |
// or use dispatch queues to handle it asynchronously though. Depends on | |
// what you're doing. At some point, call sys_quit to kill the frame loop. | |
#warning TODO: Update game logic | |
glClear(GL_COLOR_BUFFER_BIT); | |
#warning TODO: Render game scene | |
glfwSwapBuffers(window); | |
} // while (running) | |
// Go back to the main thread and kill the program cleanly. | |
dispatch_async_f(g_main_queue, window, sys_terminate); | |
return NULL; | |
} | |
// must be run on main queue | |
static void sys_terminate(void *ctx) | |
{ | |
if (ctx) { | |
glfwDestroyWindow((GLFWwindow *)ctx); | |
} | |
exit(0); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment