boinc/api/x_opengl.C

305 lines
8.8 KiB
C

#include <stdlib.h>
#include <stdio.h>
#include <setjmp.h>
#include <unistd.h>
#include <pthread.h>
#include <signal.h>
#include "x_opengl.h"
#include "app_ipc.h"
#include "util.h"
#include "boinc_gl.h"
#include "graphics_api.h"
#include "graphics_impl.h"
#define BOINC_WINDOW_CLASS_NAME "BOINC_app"
#define TIMER_INTERVAL_MSEC 30
static bool visible = true;
static int current_graphics_mode = MODE_HIDE_GRAPHICS;
static int acked_graphics_mode;
static int xpos = 100, ypos = 100;
static int clicked_button;
static int win=0;
static void set_mode(int mode);
static APP_INIT_DATA aid;
// This callback is invoked when a user presses a key.
//
void keyboardD(unsigned char key, int x, int y) {
if (current_graphics_mode == MODE_FULLSCREEN) {
set_mode(MODE_HIDE_GRAPHICS);
}
}
void keyboardU(unsigned char key, int x, int y) {
if (current_graphics_mode == MODE_FULLSCREEN) {
set_mode(MODE_HIDE_GRAPHICS);
}
}
void mouse_click(int button, int state, int x, int y){
clicked_button = button;
if (current_graphics_mode == MODE_FULLSCREEN) {
set_mode(MODE_HIDE_GRAPHICS);
} else {
if (state) {
boinc_app_mouse_button(x, y, button, false);
} else {
boinc_app_mouse_button(x, y, button, true);
}
}
}
void mouse_click_move(int x, int y){
if (current_graphics_mode == MODE_FULLSCREEN){
set_mode(MODE_HIDE_GRAPHICS);
} else if (clicked_button == 2){
boinc_app_mouse_move(x, y, false, false, true);
} else if (clicked_button == 1){
boinc_app_mouse_move(x, y, false, true, false);
} else if (clicked_button == 0){
boinc_app_mouse_move(x, y, true, false, false);
} else{
boinc_app_mouse_move(x, y, false, false, false);
}
}
static void maybe_render() {
int width, height;
if (visible && (current_graphics_mode != MODE_HIDE_GRAPHICS)) {
width = glutGet(GLUT_WINDOW_WIDTH);
height = glutGet(GLUT_WINDOW_HEIGHT);
if (throttled_app_render(width, height, dtime())) {
glutSwapBuffers();
}
}
}
static void close_func() {
if (boinc_is_standalone()) {
exit(0);
} else {
set_mode(MODE_HIDE_GRAPHICS);
}
}
static void make_new_window(int mode){
if (mode == MODE_WINDOW || mode == MODE_FULLSCREEN){
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
glutInitWindowPosition(xpos, ypos);
glutInitWindowSize(600, 400);
g_bmsp->boinc_get_init_data_hook(aid);
if (!strlen(aid.app_name)) {
strcpy(aid.app_name, "BOINC Application");
}
win = glutCreateWindow(aid.app_name);
glutReshapeFunc(app_graphics_resize);
glutKeyboardFunc(keyboardD);
glutKeyboardUpFunc(keyboardU);
glutMouseFunc(mouse_click);
glutMotionFunc(mouse_click_move);
glutDisplayFunc(maybe_render);
app_graphics_init();
glEnable(GL_DEPTH_TEST);
if (mode == MODE_FULLSCREEN) {
glutFullScreen();
}
}
}
void KillWindow() {
if (win) {
glutDestroyWindow(win);
win = 0;
}
}
void set_mode(int mode) {
if (mode == current_graphics_mode) return;
KillWindow();
current_graphics_mode = mode;
if (mode != MODE_HIDE_GRAPHICS) {
make_new_window(mode);
}
}
static void wait_for_initial_message() {
(*g_bmsp->app_client_shmp)->shm->graphics_reply.send_msg(
xml_graphics_modes[MODE_HIDE_GRAPHICS]
);
acked_graphics_mode = MODE_HIDE_GRAPHICS;
while (1) {
if ((*g_bmsp->app_client_shmp)->shm->graphics_request.has_msg()) {
break;
}
sleep(1);
}
}
static void timer_handler(int) {
char buf[MSG_CHANNEL_SIZE];
GRAPHICS_MSG m;
int new_mode;
if (*g_bmsp->app_client_shmp) {
if ((*g_bmsp->app_client_shmp)->shm->graphics_request.get_msg(buf)) {
(*g_bmsp->app_client_shmp)->decode_graphics_msg(buf, m);
switch (m.mode) {
case MODE_REREAD_PREFS:
//only reread graphics prefs if we have a window open
//
switch (current_graphics_mode){
case MODE_WINDOW:
case MODE_FULLSCREEN:
app_graphics_reread_prefs();
break;
}
break;
case MODE_HIDE_GRAPHICS:
case MODE_WINDOW:
case MODE_FULLSCREEN:
case MODE_BLANKSCREEN:
set_mode(m.mode);
break;
}
}
if (acked_graphics_mode != current_graphics_mode) {
bool sent = (*g_bmsp->app_client_shmp)->shm->graphics_reply.send_msg(
xml_graphics_modes[current_graphics_mode]
);
if (sent) acked_graphics_mode = current_graphics_mode;
}
}
maybe_render();
glutTimerFunc(TIMER_INTERVAL_MSEC, timer_handler, 0);
}
static jmp_buf jbuf;
static pthread_t graphics_thread;
static struct sigaction original_signal_handler;
static bool tried_to_install_abort_handler=false;
void restart() {
if (pthread_equal(pthread_self(), graphics_thread)) {
atexit(restart);
longjmp(jbuf, 1);
}
}
// we will only arrive here if the signal handler was sucessfully
// installed. If we are in the graphics thread, we just return back
// into xwin_graphics_event_loop. If we are in the main thread, then
// restore the old signal handler and raise SIGABORT.
void restart_sig(int signal_number /* not used, always == SIGABRT */) {
if (pthread_equal(pthread_self(), graphics_thread)) {
// alternative approach is for the signal hander to call exit().
fprintf(stderr, "Caught SIGABRT in graphics thread\n");
longjmp(jbuf, 1);
}
else {
// In non-graphics thread: use original signal handler
fprintf(stderr, "Caught SIGABRT in non-graphics thread\n");
if (sigaction(SIGABRT, &original_signal_handler, NULL)) {
perror("Unable to restore SIGABRT signal handler in non-graphics thread\n");
// what to do? call abort(3)?? call exit(nonzero)???
// boinc_finish()???? Here we just return.
}
else {
// we could conceivably try to call the old signal handler
// directly. But this means checking how its flags/masks
// are set, making sure it's not NULL (for no action) and so
// on. This seems simpler and more robust. On the other
// hand it may take some time for the signal to be
// delivered, and during that time, what's going to be
// executing?
raise(SIGABRT);
}
return;
}
}
void xwin_graphics_event_loop() {
char* args[] = {"foobar", 0};
int one=1;
static bool glut_inited = false;
int restarted;
int retry_interval_sec=32;
struct sigaction sa;
sa.sa_handler = restart_sig;
sa.sa_flags = SA_RESTART;
graphics_thread = pthread_self();
atexit(restart);
try_again:
restarted = setjmp(jbuf);
// install signal handler to catch abort() from GLUT. Note that
// signal handlers are global to ALL threads, so the signal
// handler that we record here in &original_signal_handler is the
// previous one that applied to BOTH threads
if (!tried_to_install_abort_handler) {
tried_to_install_abort_handler=true;
if (sigaction(SIGABRT, &sa, &original_signal_handler)) {
perror("unable to install signal handler to catch SIGABORT");
}
}
if (restarted) {
//fprintf(stderr, "graphics thread restarted\n"); fflush(stderr);
if (glut_inited) {
// here the user must have closed the window,
// which causes GLUT to call exit().
//
#ifdef __APPLE_CC__
glutInit(&one, args);
#endif
#if 0
// NOTE: currently when run standalone, killing the
// graphics window does NOT kill the application. If it's
// desired to kill the application in standalone mode,
// insert something here like this
if (boinc_is_standalone())
exit(0);
#endif
set_mode(MODE_HIDE_GRAPHICS);
} else {
// here glutInit() must have failed and called exit() or
// raised SIGABRT.
//
retry_interval_sec *= 2;
sleep(retry_interval_sec);
goto try_again;
}
} else {
glutInit(&one, args);
glut_inited = true;
if (boinc_is_standalone()) {
set_mode(MODE_WINDOW);
} else {
// don't proceed until we've opened a window;
// otherwise glutMainLoop() will abort
//
while (current_graphics_mode == MODE_HIDE_GRAPHICS) {
wait_for_initial_message();
timer_handler(0);
}
}
}
glutTimerFunc(TIMER_INTERVAL_MSEC, timer_handler, 0);
glutMainLoop();
}
const char *BOINC_RCSID_c457a14644 = "$Id$";