diff --git a/Android/jni/jnihooks.c b/Android/jni/jnihooks.c index e7158535..07255c7e 100644 --- a/Android/jni/jnihooks.c +++ b/Android/jni/jnihooks.c @@ -238,8 +238,8 @@ void Java_org_deadc0de_apple2ix_Apple2Activity_nativeIncreaseCPUSpeed(JNIEnv *en LOG("native set emulation percentage to %f", cpu_scale_factor); - if (video_backend->video_animation_show_cpuspeed) { - video_backend->video_animation_show_cpuspeed(); + if (video_backend->animation_showCPUSpeed) { + video_backend->animation_showCPUSpeed(); } #warning HACK TODO FIXME ... refactor timing stuff @@ -271,8 +271,8 @@ void Java_org_deadc0de_apple2ix_Apple2Activity_nativeDecreaseCPUSpeed(JNIEnv *en LOG("native set emulation percentage to %f", cpu_scale_factor); - if (video_backend->video_animation_show_cpuspeed) { - video_backend->video_animation_show_cpuspeed(); + if (video_backend->animation_showCPUSpeed) { + video_backend->animation_showCPUSpeed(); } #warning HACK TODO FIXME ... refactor timing stuff @@ -299,18 +299,21 @@ void Java_org_deadc0de_apple2ix_Apple2Activity_nativeChooseDisk(JNIEnv *env, job const char *path = (*env)->GetStringUTFChars(env, jPath, 0); int drive = driveA ? 0 : 1; int ro = readOnly ? 1 : 0; + LOG("nativeChooseDisk(%s, %s, %s)", path, driveA ? "drive A" : "drive B", readOnly ? "read only" : "read/write"); if (c_new_diskette_6(drive, path, ro)) { char *gzPath = NULL; asprintf(&gzPath, "%s.gz", path); if (c_new_diskette_6(drive, gzPath, ro)) { - // TODO FIXME : show error message disk was unreadable ... + char *diskImageUnreadable = "Disk Image Unreadable"; + unsigned int cols = strlen(diskImageUnreadable); + video_backend->animation_showMessage(diskImageUnreadable, cols, 1); } else { - // TODO FIXME : show an OpenGL message that the disk was chosen ... + video_backend->animation_showDiskChosen(drive); } FREE(gzPath); } else { - // TODO FIXME : show an OpenGL message that the disk was chosen ... + video_backend->animation_showDiskChosen(drive); } (*env)->ReleaseStringUTFChars(env, jPath, path); } diff --git a/Android/jni/sources.mk b/Android/jni/sources.mk index 156ca6d6..55517ff0 100644 --- a/Android/jni/sources.mk +++ b/Android/jni/sources.mk @@ -13,7 +13,7 @@ APPLE2_VIDEO_SRC = \ $(APPLE2_SRC_PATH)/video/glvideo.c \ $(APPLE2_SRC_PATH)/video/glnode.c \ $(APPLE2_SRC_PATH)/video/glhudmodel.c \ - $(APPLE2_SRC_PATH)/video/glcpuanim.c \ + $(APPLE2_SRC_PATH)/video/glalert.c \ $(APPLE2_SRC_PATH)/video/gltouchjoy.c \ $(APPLE2_SRC_PATH)/video_util/matrixUtil.c \ $(APPLE2_SRC_PATH)/video_util/modelUtil.c \ diff --git a/Makefile.am b/Makefile.am index 853ee561..e17ae7ef 100644 --- a/Makefile.am +++ b/Makefile.am @@ -40,7 +40,7 @@ VIDEO_SRC = \ src/video/glutinput.c \ src/video/glnode.c \ src/video/glhudmodel.c \ - src/video/glcpuanim.c \ + src/video/glalert.c \ src/video_util/matrixUtil.c \ src/video_util/modelUtil.c \ src/video_util/sourceUtil.c \ diff --git a/configure.ac b/configure.ac index a0759b03..e61e2989 100644 --- a/configure.ac +++ b/configure.ac @@ -170,7 +170,7 @@ AC_ARG_ENABLE([opengl], AS_HELP_STRING([--disable-opengl], [Disable OpenGL video opengl_supported='yes' AC_DEFINE(VIDEO_OPENGL, 1, [Use OpenGL]) AC_DEFINE(USE_GLUT, 1, [Use GLUT library]) - VIDEO_O="src/video/glvideo.o src/video/glnode.o src/video/glcpuanim.o src/video/glhudmodel.o src/video/glutinput.o src/video_util/matrixUtil.o src/video_util/modelUtil.o src/video_util/sourceUtil.o src/video_util/vectorUtil.o" + VIDEO_O="src/video/glvideo.o src/video/glnode.o src/video/glalert.o src/video/glhudmodel.o src/video/glutinput.o src/video_util/matrixUtil.o src/video_util/modelUtil.o src/video_util/sourceUtil.o src/video_util/vectorUtil.o" AC_MSG_RESULT([Building emulator with OpenGL support, w00t!]) ], [ AC_MSG_WARN([Did not find OpenGL GLEW library...]) diff --git a/src/interface.c b/src/interface.c index c0ff91c2..7052ca00 100644 --- a/src/interface.c +++ b/src/interface.c @@ -578,6 +578,12 @@ void c_interface_select_diskette( int drive ) c_interface_print_screen( screen ); continue; } + else + { + if (video_backend->animation_showDiskChosen) { + video_backend->animation_showDiskChosen(drive); + } + } break; } @@ -636,6 +642,12 @@ void c_interface_select_diskette( int drive ) c_interface_print_screen( screen ); continue; } + else + { + if (video_backend->animation_showDiskChosen) { + video_backend->animation_showDiskChosen(drive); + } + } break; } diff --git a/src/keys.c b/src/keys.c index 4db1df0e..34897d9a 100644 --- a/src/keys.c +++ b/src/keys.c @@ -253,8 +253,8 @@ void c_keys_handle_input(int scancode, int pressed, int is_cooked) if (current_key == kF9) { timing_toggle_cpu_speed(); - if (video_backend->video_animation_show_cpuspeed) { - video_backend->video_animation_show_cpuspeed(); + if (video_backend->animation_showCPUSpeed) { + video_backend->animation_showCPUSpeed(); } break; } @@ -284,8 +284,8 @@ void c_keys_handle_input(int scancode, int pressed, int is_cooked) cpu_scale_factor = scale; } - if (video_backend->video_animation_show_cpuspeed) { - video_backend->video_animation_show_cpuspeed(); + if (video_backend->animation_showCPUSpeed) { + video_backend->animation_showCPUSpeed(); } #warning HACK TODO FIXME ... refactor timing stuff timing_toggle_cpu_speed(); @@ -313,8 +313,8 @@ void c_keys_handle_input(int scancode, int pressed, int is_cooked) cpu_scale_factor = scale; } - if (video_backend->video_animation_show_cpuspeed) { - video_backend->video_animation_show_cpuspeed(); + if (video_backend->animation_showCPUSpeed) { + video_backend->animation_showCPUSpeed(); } #warning HACK TODO FIXME ... refactor timing stuff timing_toggle_cpu_speed(); diff --git a/src/video/glalert.c b/src/video/glalert.c new file mode 100644 index 00000000..0ceb74c7 --- /dev/null +++ b/src/video/glalert.c @@ -0,0 +1,262 @@ +/* + * Apple // emulator for *nix + * + * This software package is subject to the GNU General Public License + * version 2 or later (your choice) as published by the Free Software + * Foundation. + * + * THERE ARE NO WARRANTIES WHATSOEVER. + * + */ + +#include "common.h" +#include "video/glvideo.h" +#include "video/glhudmodel.h" +#include "video/glnode.h" + +#define MODEL_DEPTH -0.0625 + +static bool isEnabled = true; +static pthread_mutex_t messageMutex = { 0 }; +static char *nextMessage = NULL; +static unsigned int nextMessageCols = 0; +static unsigned int nextMessageRows = 0; +static struct timespec messageTimingBegin = { 0 }; +static GLModel *messageModel = NULL; + +// ---------------------------------------------------------------------------- + +static void _alertToModel(char *message, unsigned int messageCols, unsigned int messageRows) { + if (!message) { + return; + } + + isEnabled = false; + + do { + // create model object + + mdlDestroyModel(&messageModel); + + const unsigned int fbWidth = ((messageCols * FONT80_WIDTH_PIXELS) + INTERPOLATED_PIXEL_ADJUSTMENT); + const unsigned int fbHeight = (messageRows * FONT_HEIGHT_PIXELS); + + messageModel = mdlCreateQuad(-0.3, -0.3, 0.7, 0.7, MODEL_DEPTH, fbWidth, fbHeight, GL_RGBA/*RGBA_8888*/, (GLCustom){ + .create = &glhud_createDefault, + .destroy = &glhud_destroyDefault, + }); + if (!messageModel) { + LOG("OOPS cannot create animation message HUD model!"); + break; + } + + // setup custom message HUD elements + + GLModelHUDElement *hudElement = (GLModelHUDElement *)messageModel->custom; + hudElement->tplWidth = messageCols; + hudElement->tplHeight = messageRows; + hudElement->tpl = message; + hudElement->pixWidth = fbWidth; + hudElement->pixHeight = fbHeight; + hudElement->pixels = calloc(fbWidth * fbHeight, 1); + if (!hudElement->pixels) { + LOG("OOPS cannot create animation message intermediate framebuffer!"); + break; + } + glhud_setupDefault(messageModel); + + clock_gettime(CLOCK_MONOTONIC, &messageTimingBegin); + isEnabled = true; + return; + + } while (0); + + // error + mdlDestroyModel(&messageModel); +} + +static void alert_init(void) { + // no-op +} + +static void alert_shutdown(void) { + LOG("alert_shutdown ..."); + mdlDestroyModel(&messageModel); +} + +static void alert_render(void) { + if (nextMessage) { + pthread_mutex_lock(&messageMutex); + char *message = nextMessage; + int cols = nextMessageCols; + int rows = nextMessageRows; + nextMessage = NULL; + pthread_mutex_unlock(&messageMutex); + _alertToModel(message, cols, rows); + } + + if (!isEnabled) { + return; + } + + struct timespec now = { 0 }; + clock_gettime(CLOCK_MONOTONIC, &now); + + float alpha = 0.95; + struct timespec deltat = timespec_diff(messageTimingBegin, now, NULL); + if (deltat.tv_sec >= 1) { + isEnabled = false; + return; + } else if (deltat.tv_nsec >= NANOSECONDS_PER_SECOND/2) { + alpha -= ((float)deltat.tv_nsec-(NANOSECONDS_PER_SECOND/2)) / (float)(NANOSECONDS_PER_SECOND/2); + if (alpha < 0.0) { + alpha = 0.0; + } + } + //LOG("alpha : %f", alpha); + glUniform1f(alphaValue, alpha); + + glActiveTexture(TEXTURE_ACTIVE_MESSAGE); + glBindTexture(GL_TEXTURE_2D, messageModel->textureName); + if (messageModel->texDirty) { + messageModel->texDirty = false; + GLModelHUDElement *hudElement = (GLModelHUDElement *)(messageModel->custom); + glTexImage2D(GL_TEXTURE_2D, /*level*/0, /*internal format*/GL_RGBA, hudElement->pixWidth, hudElement->pixHeight, /*border*/0, /*format*/GL_RGBA, GL_UNSIGNED_BYTE, messageModel->texPixels); + } + glUniform1i(uniformTex2Use, TEXTURE_ID_MESSAGE); + glhud_renderDefault(messageModel); +} + +static void alert_reshape(int w, int h) { + // no-op +} + +#if INTERFACE_TOUCH +static bool alert_onTouchEvent(interface_touch_event_t action, int pointer_count, int pointer_idx, float *x_coords, float *y_coords) { + return false; // non-interactive element ... +} +#endif + +// ---------------------------------------------------------------------------- + +static void _animation_showMessage(char *messageTemplate, unsigned int cols, unsigned int rows) { + // frame the message with interface border characters + const unsigned int framedCols = cols+2; + const unsigned int framedRows = rows+2; + const unsigned int framedStride = framedCols+1/*\0*/; + const unsigned int sourceStride = cols+1/*\0*/; + + char *message = calloc(framedStride*framedRows, 1); + if (!message) { + LOG("OOPS cannot create memory for animation message!"); + return; + } + memset(message, '|', framedCols); + unsigned int indexSource = 0; + unsigned int indexFramed = 0; + unsigned int row = 0; + for (; row1", + }; + template = diskInsertedTemplate[0]; + } else { + shownCols = DISK_ANIMATION_COLS+1; + char diskInsertedTemplate[DISK_ANIMATION_ROWS][DISK_ANIMATION_COLS+2] = { + "DD ", + "DD ", + " >1L", + }; + diskInsertedTemplate[2][3] = ICONTEXT_UNLOCK; + template = diskInsertedTemplate[0]; + } + + const unsigned int x = (shownCols+1);// stride + (template+x*0)[0] = ICONTEXT_DISK_UL; + (template+x*0)[1] = ICONTEXT_DISK_UR; + (template+x*1)[0] = ICONTEXT_DISK_LL; + (template+x*1)[1] = ICONTEXT_DISK_LR; + + (template+x*2)[1] = ICONTEXT_GOTO; + (template+x*2)[2] = (drive == 0) ? '1' : '2'; + + _animation_showMessage(template, shownCols, DISK_ANIMATION_ROWS); +} + +__attribute__((constructor(CTOR_PRIORITY_LATE))) +static void _init_glalert(void) { + LOG("Initializing message animation subsystem"); + + video_backend->animation_showCPUSpeed = &_animation_showCPUSpeed; + video_backend->animation_showDiskChosen = &_animation_showDiskChosen; + video_backend->animation_showMessage = &_animation_showMessage; + + glnode_registerNode(RENDER_MIDDLE, (GLNode){ + .setup = &alert_init, + .shutdown = &alert_shutdown, + .render = &alert_render, + .reshape = &alert_reshape, +#if INTERFACE_TOUCH + .onTouchEvent = &alert_onTouchEvent, +#endif + }); +} + diff --git a/src/video/glcpuanim.c b/src/video/glcpuanim.c deleted file mode 100644 index 22980d48..00000000 --- a/src/video/glcpuanim.c +++ /dev/null @@ -1,176 +0,0 @@ -/* - * Apple // emulator for *nix - * - * This software package is subject to the GNU General Public License - * version 2 or later (your choice) as published by the Free Software - * Foundation. - * - * THERE ARE NO WARRANTIES WHATSOEVER. - * - */ - -#include "common.h" -#include "video/glvideo.h" -#include "video/glhudmodel.h" -#include "video/glnode.h" - -#define MODEL_DEPTH -0.0625 - -#define CPUTIMING_TEMPLATE_COLS 8 -#define CPUTIMING_TEMPLATE_ROWS 3 - -// HACK NOTE FIXME TODO : interpolated pixel adjustment still necessary ... -#define MESSAGE_FB_WIDTH ((CPUTIMING_TEMPLATE_COLS * FONT80_WIDTH_PIXELS) + INTERPOLATED_PIXEL_ADJUSTMENT) -#define MESSAGE_FB_HEIGHT (CPUTIMING_TEMPLATE_ROWS * FONT_HEIGHT_PIXELS) - -static bool isAvailable = false; -static bool isEnabled = true; - -static struct timespec cputiming_begin = { 0 }; - -static GLModel *cpuMessageObjModel = NULL; - -// ---------------------------------------------------------------------------- - -static void _cpuanim_show(GLModel *parent) { - GLModelHUDElement *hudElement = (GLModelHUDElement *)parent->custom; - - if (hudElement->tpl == NULL) { - // deferred construction ... - const char messageTemplate[CPUTIMING_TEMPLATE_ROWS][CPUTIMING_TEMPLATE_COLS+1] = { - "||||||||", - "| xxx% |", - "||||||||", - }; - - const unsigned int size = sizeof(messageTemplate); - hudElement->tpl = calloc(size, 1); - hudElement->tplWidth = CPUTIMING_TEMPLATE_COLS; - hudElement->tplHeight = CPUTIMING_TEMPLATE_ROWS; - memcpy(hudElement->tpl, messageTemplate, size); - - hudElement->pixels = calloc(MESSAGE_FB_WIDTH * MESSAGE_FB_HEIGHT, 1); - hudElement->pixWidth = MESSAGE_FB_WIDTH; - hudElement->pixHeight = MESSAGE_FB_HEIGHT; - } - - const unsigned int row = (CPUTIMING_TEMPLATE_COLS+1); - - char buf[8] = { 0 }; - double scale = (alt_speed_enabled ? cpu_altscale_factor : cpu_scale_factor); - int percentScale = scale * 100; - if (percentScale < 100.0) { - snprintf(buf, 3, "%d", percentScale); - ((hudElement->tpl)+(row*1))[2] = ' '; - ((hudElement->tpl)+(row*1))[3] = buf[0]; - ((hudElement->tpl)+(row*1))[4] = buf[1]; - } else if (scale == CPU_SCALE_FASTEST) { - ((hudElement->tpl)+(row*1))[2] = 'm'; - ((hudElement->tpl)+(row*1))[3] = 'a'; - ((hudElement->tpl)+(row*1))[4] = 'x'; - } else { - snprintf(buf, 4, "%d", percentScale); - ((hudElement->tpl)+(row*1))[2] = buf[0]; - ((hudElement->tpl)+(row*1))[3] = buf[1]; - ((hudElement->tpl)+(row*1))[4] = buf[2]; - } - - glhud_setupDefault(parent); - - clock_gettime(CLOCK_MONOTONIC, &cputiming_begin); -} - -static void cpuanim_init(void) { - LOG("cpuanim_init ..."); - - mdlDestroyModel(&cpuMessageObjModel); - cpuMessageObjModel = mdlCreateQuad(-0.3, -0.3, 0.6, 0.6, MODEL_DEPTH, MESSAGE_FB_WIDTH, MESSAGE_FB_HEIGHT, GL_RGBA/*RGBA_8888*/, (GLCustom){ - .create = &glhud_createDefault, - .setup = &_cpuanim_show, - .destroy = &glhud_destroyDefault, - }); - if (!cpuMessageObjModel) { - LOG("not initializing CPU speed animations"); - return; - } - - isAvailable = true; -} - -static void cpuanim_shutdown(void) { - LOG("cpuanim_shutdown ..."); - if (!isAvailable) { - return; - } - isAvailable = false; - mdlDestroyModel(&cpuMessageObjModel); -} - -static void cpuanim_render(void) { - if (!isAvailable) { - return; - } - if (!isEnabled) { - return; - } - - struct timespec now; - clock_gettime(CLOCK_MONOTONIC, &now); - - float alpha = 0.95; - struct timespec deltat = timespec_diff(cputiming_begin, now, NULL); - if (deltat.tv_sec >= 1) { - isEnabled = false; - return; - } else if (deltat.tv_nsec >= NANOSECONDS_PER_SECOND/2) { - alpha -= ((float)deltat.tv_nsec-(NANOSECONDS_PER_SECOND/2)) / (float)(NANOSECONDS_PER_SECOND/2); - if (alpha < 0.0) { - alpha = 0.0; - } - } - //LOG("alpha : %f", alpha); - glUniform1f(alphaValue, alpha); - - glActiveTexture(TEXTURE_ACTIVE_MESSAGE); - glBindTexture(GL_TEXTURE_2D, cpuMessageObjModel->textureName); - if (cpuMessageObjModel->texDirty) { - cpuMessageObjModel->texDirty = false; - glTexImage2D(GL_TEXTURE_2D, /*level*/0, /*internal format*/GL_RGBA, MESSAGE_FB_WIDTH, MESSAGE_FB_HEIGHT, /*border*/0, /*format*/GL_RGBA, GL_UNSIGNED_BYTE, cpuMessageObjModel->texPixels); - } - glUniform1i(uniformTex2Use, TEXTURE_ID_MESSAGE); - glhud_renderDefault(cpuMessageObjModel); -} - -static void cpuanim_reshape(int w, int h) { - // no-op -} - -static void cpuanim_show(void) { - if (!isAvailable) { - return; - } - _cpuanim_show(cpuMessageObjModel); - isEnabled = true; -} - -#if INTERFACE_TOUCH -static bool cpuanim_onTouchEvent(interface_touch_event_t action, int pointer_count, int pointer_idx, float *x_coords, float *y_coords) { - return false; // non-interactive element ... -} -#endif - -__attribute__((constructor(CTOR_PRIORITY_LATE))) -static void _init_glcpuanim(void) { - LOG("Initializing message animation subsystem"); - video_backend->video_animation_show_cpuspeed = &cpuanim_show; - glnode_registerNode(RENDER_MIDDLE, (GLNode){ - .setup = &cpuanim_init, - .shutdown = &cpuanim_shutdown, - .render = &cpuanim_render, - .reshape = &cpuanim_reshape, -#if INTERFACE_TOUCH - .onTouchEvent = &cpuanim_onTouchEvent, -#endif - }); -} - diff --git a/src/video/video.h b/src/video/video.h index edebac34..bc8ca1ed 100644 --- a/src/video/video.h +++ b/src/video/video.h @@ -27,8 +27,10 @@ typedef struct video_backend_s { void (*shutdown)(void); // optional functions - void (*video_animation_show_cpuspeed)(void); - void (*video_animation_show_track_sector)(int drive, int track, int sect); + void (*animation_showMessage)(char *message, unsigned int cols, unsigned int rows); + void (*animation_showCPUSpeed)(void); + void (*animation_showDiskChosen)(int drive); + void (*animation_showTrackSector)(int drive, int track, int sect); } video_backend_s; /* diff --git a/src/video_util/modelUtil.c b/src/video_util/modelUtil.c index cc6f47e6..15dbb92b 100644 --- a/src/video_util/modelUtil.c +++ b/src/video_util/modelUtil.c @@ -517,7 +517,9 @@ GLModel *mdlCreateQuad(GLfloat skew_x, GLfloat skew_y, GLfloat obj_w, GLfloat ob model->custom->create = NULL; model->custom->setup = clazz.setup; model->custom->destroy = clazz.destroy; - model->custom->setup(model); + if (model->custom->setup) { + model->custom->setup(model); + } } }