apple2ix/src/video/glnode.c

275 lines
6.9 KiB
C

/*
* Apple // emulator for *ix
*
* This software package is subject to the GNU General Public License
* version 3 or later (your choice) as published by the Free Software
* Foundation.
*
* Copyright 2013-2015 Aaron Culliney
*
*/
#include "video/glnode.h"
#include "video/glinput.h"
bool safe_to_do_opengl_logging = false;
// WARNING : linked list designed for convenience and not performance =P ... if the amount of GLNode objects grows
// wildly, should rethink this ...
typedef struct glnode_array_node_s {
struct glnode_array_node_s *next;
struct glnode_array_node_s *last;
glnode_render_order_t order;
GLNode node;
} glnode_array_node_s;
static glnode_array_node_s *head = NULL;
static glnode_array_node_s *tail = NULL;
static video_backend_s glnode_backend = { 0 };
video_animation_s glnode_animations = { 0 };
#if USE_GLUT
static bool glut_in_main_loop = false;
#endif
// -----------------------------------------------------------------------------
void glnode_registerNode(glnode_render_order_t order, GLNode node) {
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_lock(&mutex);
glnode_array_node_s *arrayNode = MALLOC(sizeof(glnode_array_node_s));
assert(arrayNode);
arrayNode->next = NULL;
arrayNode->last = NULL;
arrayNode->order = order;
arrayNode->node = node;
if (head == NULL) {
assert(tail == NULL);
head = arrayNode;
tail = arrayNode;
} else {
glnode_array_node_s *p0 = NULL;
glnode_array_node_s *p = head;
while (p && (order > p->order)) {
p0 = p;
p = p->next;
}
if (p0) {
p0->next = arrayNode;
} else {
head = arrayNode;
}
if (p) {
p->last = arrayNode;
} else {
tail = arrayNode;
}
arrayNode->next = p;
arrayNode->last = p0;
}
pthread_mutex_unlock(&mutex);
}
// -----------------------------------------------------------------------------
#if USE_GLUT
static void _glnode_updateGLUT(int unused) {
#if FPS_LOG
static uint32_t prevCount = 0;
static uint32_t idleCount = 0;
idleCount++;
static struct timespec prev = { 0 };
struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
if (now.tv_sec != prev.tv_sec) {
LOG("%u", idleCount-prevCount);
prevCount = idleCount;
prev = now;
}
#endif
c_keys_handle_input(-1, 0, 0);
glutPostRedisplay();
glutTimerFunc(17, _glnode_updateGLUT, 0);
}
static void _glnode_initGLUTPre(void) {
glutInit(&argc, argv);
glutInitDisplayMode(/*GLUT_DOUBLE|*/GLUT_RGBA);
glutInitWindowSize(SCANWIDTH*1.5, SCANHEIGHT*1.5);
//glutInitContextVersion(4, 0); -- Is this needed?
glutInitContextProfile(GLUT_CORE_PROFILE);
/*glutWindow = */glutCreateWindow(PACKAGE_NAME);
GL_ERRQUIT("GLUT initialization");
if (glewInit()) {
ERRQUIT("Unable to initialize GLEW");
}
}
static void _glnode_reshapeGLUT(int w, int h) {
prefs_setLongValue(PREF_DOMAIN_INTERFACE, PREF_DEVICE_WIDTH, w);
prefs_setLongValue(PREF_DOMAIN_INTERFACE, PREF_DEVICE_HEIGHT, h);
prefs_setLongValue(PREF_DOMAIN_INTERFACE, PREF_DEVICE_LANDSCAPE, true);
prefs_sync(PREF_DOMAIN_INTERFACE);
}
static void _glnode_initGLUTPost(void) {
glutTimerFunc(16, _glnode_updateGLUT, 0);
glutDisplayFunc(video_render);
glutReshapeFunc(_glnode_reshapeGLUT);
glutSetOption(GLUT_ACTION_ON_WINDOW_CLOSE, GLUT_ACTION_GLUTMAINLOOP_RETURNS);
glutKeyboardFunc(gldriver_on_key_down);
glutKeyboardUpFunc(gldriver_on_key_up);
glutSpecialFunc(gldriver_on_key_special_down);
glutSpecialUpFunc(gldriver_on_key_special_up);
//glutMouseFunc(gldriver_mouse);
//glutMotionFunc(gldriver_mouse_drag);
c_joystick_reset();
}
#endif
static const char *glnode_name(void) {
return "OpenGL";
}
static void glnode_setupNodes(void *ctx) {
LOG("BEGIN glnode_setupNodes ...");
#if USE_GLUT
# if !TEST_CPU
joydriver_resetJoystick = &_glutJoystickReset;
# endif
_glnode_initGLUTPre();
#endif
safe_to_do_opengl_logging = true;
glnode_array_node_s *p = head;
while (p) {
p->node.setup();
p = p->next;
}
#if USE_GLUT
_glnode_initGLUTPost();
#endif
LOG("END glnode_setupNodes ...");
}
static void glnode_shutdownNodes(void) {
LOG("BEGIN glnode_shutdownNodes ...");
#if USE_GLUT
if (glut_in_main_loop) {
assert(!emulator_isShuttingDown());
glutLeaveMainLoop();
return;
}
#endif
glnode_array_node_s *p = tail;
while (p) {
p->node.shutdown();
p = p->last;
}
if (emulator_isShuttingDown()) {
// clean up to make Valgrind happy ...
p = head;
while (p) {
glnode_array_node_s *next = p->next;
FREE(p);
p = next;
}
head=NULL;
tail=NULL;
}
LOG("END glnode_shutdownNodes ...");
}
static void glnode_renderNodes(void) {
SCOPE_TRACE_VIDEO("glnode render");
glClear(GL_COLOR_BUFFER_BIT);
glnode_array_node_s *p = head;
while (p) {
p->node.render();
p = p->next;
}
#if USE_GLUT
glutSwapBuffers();
#endif
}
#if INTERFACE_TOUCH
static int64_t glnode_onTouchEvent(interface_touch_event_t action, int pointer_count, int pointer_idx, float *x_coords, float *y_coords) {
SCOPE_TRACE_TOUCH("glnode onTouchEvent");
glnode_array_node_s *p = tail;
int64_t flags = 0x0;
while (p) {
flags = p->node.onTouchEvent(action, pointer_count, pointer_idx, x_coords, y_coords);
if (flags & TOUCH_FLAGS_HANDLED) {
break;
}
p = p->last;
}
return flags;
}
#endif
static void glnode_mainLoop(void) {
#if USE_GLUT
LOG("BEGIN GLUT main loop...");
glut_in_main_loop = true;
glutMainLoop();
glut_in_main_loop = false;
LOG("END GLUT main loop...");
#endif
}
//----------------------------------------------------------------------------
static void _init_glnode_manager(void) {
LOG("Initializing GLNode manager subsystem");
glnode_backend.name = &glnode_name;
glnode_backend.init = &glnode_setupNodes;
glnode_backend.main_loop = &glnode_mainLoop;
glnode_backend.render = &glnode_renderNodes;
glnode_backend.shutdown = &glnode_shutdownNodes;
glnode_backend.anim = &glnode_animations;
#if INTERFACE_CLASSIC
glnode_backend.plotChar = &display_plotChar;
glnode_backend.plotLine = &display_plotLine;
#endif
glnode_backend.flashText = &display_flashText;
glnode_backend.flushScanline = &display_flushScanline;
glnode_backend.frameComplete = &display_frameComplete;
#if INTERFACE_TOUCH
interface_onTouchEvent = &glnode_onTouchEvent;
#endif
video_registerBackend(&glnode_backend, VID_PRIO_GRAPHICS_GL);
}
static __attribute__((constructor)) void __init_glnode_manager(void) {
emulator_registerStartupCallback(CTOR_PRIORITY_EARLY, &_init_glnode_manager);
}