Search
lxdream.org :: lxdream/src/gui_android.c
lxdream 0.9.1
released Jun 29
Download Now
filename src/gui_android.c
changeset 1275:83b15705cdde
prev1253:5aa14eeaad5a
next1277:f727227cc4f8
author nkeynes
date Tue Mar 20 08:29:38 2012 +1000 (10 years ago)
permissions -rw-r--r--
last change More android WIP
- Implement onPause/onResume (although resume is not actually working yet)
- Implement BGRA => RGBA texture conversion (BGRA doesn't seem to work on the TFP)

Boot swirl is now displayed, albeit depth buffering seems to be broken.
file annotate diff log raw
nkeynes@1239
     1
/**
nkeynes@1239
     2
 * $Id$
nkeynes@1239
     3
 *
nkeynes@1245
     4
 * Native shims for the Android front-end (implemented in Java).
nkeynes@1245
     5
 *
nkeynes@1245
     6
 * This is complicated by the fact that all the emulation runs in a
nkeynes@1245
     7
 * separate thread.
nkeynes@1239
     8
 *
nkeynes@1239
     9
 * Copyright (c) 2012 Nathan Keynes.
nkeynes@1239
    10
 *
nkeynes@1239
    11
 * This program is free software; you can redistribute it and/or modify
nkeynes@1239
    12
 * it under the terms of the GNU General Public License as published by
nkeynes@1239
    13
 * the Free Software Foundation; either version 2 of the License, or
nkeynes@1239
    14
 * (at your option) any later version.
nkeynes@1239
    15
 *
nkeynes@1239
    16
 * This program is distributed in the hope that it will be useful,
nkeynes@1239
    17
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
nkeynes@1239
    18
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
nkeynes@1239
    19
 * GNU General Public License for more details.
nkeynes@1239
    20
 */
nkeynes@1239
    21
nkeynes@1239
    22
#include <jni.h>
nkeynes@1245
    23
#include <pthread.h>
nkeynes@1239
    24
#include <android/log.h>
nkeynes@1245
    25
#include <android/native_window_jni.h>
nkeynes@1239
    26
#include <libisofs.h>
nkeynes@1245
    27
#include "dream.h"
nkeynes@1239
    28
#include "dreamcast.h"
nkeynes@1239
    29
#include "gui.h"
nkeynes@1239
    30
#include "config.h"
nkeynes@1241
    31
#include "lxpaths.h"
nkeynes@1245
    32
#include "tqueue.h"
nkeynes@1239
    33
#include "display.h"
nkeynes@1239
    34
#include "gdlist.h"
nkeynes@1245
    35
#include "gdrom/gdrom.h"
nkeynes@1239
    36
#include "hotkeys.h"
nkeynes@1239
    37
#include "serial.h"
nkeynes@1239
    38
#include "aica/audio.h"
nkeynes@1245
    39
#include "drivers/video_egl.h"
nkeynes@1239
    40
#include "maple/maple.h"
nkeynes@1239
    41
#include "vmu/vmulist.h"
nkeynes@1239
    42
nkeynes@1245
    43
struct surface_info {
nkeynes@1245
    44
    ANativeWindow *win;
nkeynes@1245
    45
    int width, height, format;
nkeynes@1245
    46
};
nkeynes@1245
    47
nkeynes@1275
    48
static struct surface_info current_surface = { NULL, 0, 0, 0 };
nkeynes@1245
    49
static const char *appHome = NULL;
nkeynes@1245
    50
nkeynes@1245
    51
/**
nkeynes@1245
    52
 * Count of running nanoseconds - used to cut back on the GUI runtime
nkeynes@1245
    53
 */
nkeynes@1245
    54
static uint32_t android_gui_nanos = 0;
nkeynes@1245
    55
static uint32_t android_gui_ticks = 0;
nkeynes@1245
    56
static struct timeval android_gui_lasttv;
nkeynes@1245
    57
nkeynes@1245
    58
nkeynes@1245
    59
void android_gui_start( void )
nkeynes@1239
    60
{
nkeynes@1245
    61
    /* Dreamcast starting up hook */
nkeynes@1245
    62
}
nkeynes@1245
    63
nkeynes@1245
    64
void android_gui_stop( void )
nkeynes@1245
    65
{
nkeynes@1245
    66
    /* Dreamcast stopping hook */
nkeynes@1245
    67
}
nkeynes@1245
    68
nkeynes@1245
    69
void android_gui_reset( void )
nkeynes@1245
    70
{
nkeynes@1245
    71
    /* Dreamcast reset hook */
nkeynes@1245
    72
}
nkeynes@1245
    73
nkeynes@1245
    74
/**
nkeynes@1245
    75
 * The main emulation thread. (as opposed to the UI thread).
nkeynes@1245
    76
 */
nkeynes@1245
    77
void *android_thread_main(void *data)
nkeynes@1245
    78
{
nkeynes@1245
    79
    while(1) {
nkeynes@1245
    80
        tqueue_process_wait();
nkeynes@1245
    81
    }
nkeynes@1245
    82
    return NULL;
nkeynes@1245
    83
}
nkeynes@1245
    84
nkeynes@1245
    85
int android_set_surface(void *data)
nkeynes@1245
    86
{
nkeynes@1245
    87
    struct surface_info *surface = (struct surface_info *)data;
nkeynes@1245
    88
    video_egl_set_window(surface->win, surface->width, surface->height, surface->format);
nkeynes@1245
    89
    return 0;
nkeynes@1245
    90
}
nkeynes@1245
    91
nkeynes@1275
    92
int android_do_pause(void *data)
nkeynes@1275
    93
{
nkeynes@1275
    94
    if( dreamcast_is_running() ) {
nkeynes@1275
    95
        dreamcast_stop();
nkeynes@1275
    96
    }
nkeynes@1275
    97
    video_egl_clear_window();
nkeynes@1275
    98
    INFO( "Paused" );
nkeynes@1275
    99
    return 0;
nkeynes@1275
   100
}
nkeynes@1275
   101
nkeynes@1275
   102
int android_do_resume(void *data)
nkeynes@1275
   103
{
nkeynes@1275
   104
    struct surface_info *surface = (struct surface_info *)data;
nkeynes@1275
   105
    if( surface->win != NULL )
nkeynes@1275
   106
        video_egl_set_window(surface->win, surface->width, surface->height, surface->format);
nkeynes@1275
   107
    INFO( "Resumed" );
nkeynes@1275
   108
    return 0;
nkeynes@1275
   109
}
nkeynes@1275
   110
nkeynes@1245
   111
int android_clear_surface(void *data)
nkeynes@1245
   112
{
nkeynes@1245
   113
    struct surface_info *surface = (struct surface_info *)data;
nkeynes@1245
   114
nkeynes@1275
   115
    android_do_pause(data); /* If we haven't already stopped, stop now */
nkeynes@1245
   116
    ANativeWindow_release(surface->win);
nkeynes@1245
   117
    surface->win = NULL;
nkeynes@1245
   118
    return 0;
nkeynes@1245
   119
}
nkeynes@1245
   120
nkeynes@1245
   121
static pthread_t dreamcast_thread;
nkeynes@1245
   122
nkeynes@1245
   123
void android_start_thread()
nkeynes@1245
   124
{
nkeynes@1245
   125
    pthread_attr_t attr;
nkeynes@1245
   126
nkeynes@1245
   127
    pthread_attr_init(&attr);
nkeynes@1245
   128
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
nkeynes@1245
   129
    int status = pthread_create(&dreamcast_thread, &attr, android_thread_main, NULL);
nkeynes@1245
   130
    if( status != 0 ) {
nkeynes@1245
   131
        /* Handle errors */
nkeynes@1245
   132
    }
nkeynes@1245
   133
}
nkeynes@1245
   134
nkeynes@1245
   135
/** tqueue callback wrapper to get the right call type for simple events */
nkeynes@1245
   136
int android_callback_wrapper( void *fn )
nkeynes@1245
   137
{
nkeynes@1245
   138
    void (*cast_fn)(void) = fn;
nkeynes@1245
   139
    cast_fn();
nkeynes@1245
   140
}
nkeynes@1245
   141
nkeynes@1245
   142
int android_mount_disc( void *data )
nkeynes@1245
   143
{
nkeynes@1245
   144
    char *s = (char *)data;
nkeynes@1245
   145
    ERROR err;
nkeynes@1245
   146
    gboolean result = gdrom_mount_image( s, &err ); /* TODO: Report error */
nkeynes@1241
   147
    return result;
nkeynes@1241
   148
}
nkeynes@1241
   149
nkeynes@1245
   150
int android_toggle_run( void *data )
nkeynes@1245
   151
{
nkeynes@1245
   152
    if( dreamcast_is_running() ) {
nkeynes@1245
   153
        dreamcast_stop();
nkeynes@1245
   154
    } else {
nkeynes@1245
   155
        dreamcast_run();
nkeynes@1245
   156
    }
nkeynes@1245
   157
}
nkeynes@1241
   158
nkeynes@1245
   159
uint32_t android_gui_run_slice( uint32_t nanosecs )
nkeynes@1241
   160
{
nkeynes@1245
   161
    android_gui_nanos += nanosecs;
nkeynes@1245
   162
    if( android_gui_nanos > GUI_TICK_PERIOD ) { /* 10 ms */
nkeynes@1245
   163
        android_gui_nanos -= GUI_TICK_PERIOD;
nkeynes@1245
   164
        android_gui_ticks ++;
nkeynes@1245
   165
        uint32_t current_period = android_gui_ticks * GUI_TICK_PERIOD;
nkeynes@1245
   166
nkeynes@1245
   167
        // Run the event loop
nkeynes@1245
   168
        tqueue_process_all();
nkeynes@1245
   169
nkeynes@1245
   170
        struct timeval tv;
nkeynes@1245
   171
        gettimeofday(&tv,NULL);
nkeynes@1245
   172
        uint32_t ns = ((tv.tv_sec - android_gui_lasttv.tv_sec) * 1000000000) +
nkeynes@1245
   173
        (tv.tv_usec - android_gui_lasttv.tv_usec)*1000;
nkeynes@1245
   174
        if( (ns * 1.05) < current_period ) {
nkeynes@1245
   175
            // We've gotten ahead - sleep for a little bit
nkeynes@1245
   176
            struct timespec tv;
nkeynes@1245
   177
            tv.tv_sec = 0;
nkeynes@1245
   178
            tv.tv_nsec = current_period - ns;
nkeynes@1245
   179
            nanosleep(&tv, &tv);
nkeynes@1245
   180
        }
nkeynes@1245
   181
nkeynes@1245
   182
#if 0
nkeynes@1245
   183
        /* Update the display every 10 ticks (ie 10 times a second) and
nkeynes@1245
   184
         * save the current tv value */
nkeynes@1245
   185
        if( android_gui_ticks > 10 ) {
nkeynes@1245
   186
            android_gui_ticks -= 10;
nkeynes@1245
   187
nkeynes@1245
   188
            double speed = (float)( (double)current_period * 100.0 / ns );
nkeynes@1245
   189
            android_gui_lasttv.tv_sec = tv.tv_sec;
nkeynes@1245
   190
            android_gui_lasttv.tv_usec = tv.tv_usec;
nkeynes@1245
   191
            main_window_set_speed( main_win, speed );
nkeynes@1245
   192
        }
nkeynes@1245
   193
#endif
nkeynes@1245
   194
    }
nkeynes@1245
   195
    return nanosecs;
nkeynes@1245
   196
nkeynes@1245
   197
nkeynes@1245
   198
}
nkeynes@1245
   199
nkeynes@1245
   200
struct dreamcast_module android_gui_module = { "gui", NULL,
nkeynes@1245
   201
        android_gui_reset,
nkeynes@1245
   202
        android_gui_start,
nkeynes@1245
   203
        android_gui_run_slice,
nkeynes@1245
   204
        android_gui_stop,
nkeynes@1245
   205
        NULL, NULL };
nkeynes@1245
   206
nkeynes@1245
   207
gboolean gui_error_dialog( const char *fmt, ... )
nkeynes@1245
   208
{
nkeynes@1245
   209
    return FALSE; /* TODO */
nkeynes@1245
   210
}
nkeynes@1245
   211
nkeynes@1245
   212
void gui_update_state()
nkeynes@1245
   213
{
nkeynes@1245
   214
    /* TODO */
nkeynes@1245
   215
}
nkeynes@1245
   216
nkeynes@1245
   217
void gui_set_use_grab( gboolean grab )
nkeynes@1245
   218
{
nkeynes@1245
   219
    /* No implementation - mouse grab doesn't exist */
nkeynes@1245
   220
}
nkeynes@1245
   221
nkeynes@1245
   222
void gui_update_io_activity( io_activity_type activity, gboolean active )
nkeynes@1245
   223
{
nkeynes@1245
   224
    /* No implementation */
nkeynes@1245
   225
}
nkeynes@1245
   226
nkeynes@1245
   227
void gui_do_later( do_later_callback_t func )
nkeynes@1245
   228
{
nkeynes@1245
   229
    func(); /* TODO */
nkeynes@1245
   230
}
nkeynes@1245
   231
nkeynes@1245
   232
static void android_init( const char *appHomeDir )
nkeynes@1245
   233
{
nkeynes@1245
   234
    set_global_log_level("info");
nkeynes@1245
   235
    appHome = appHomeDir;
nkeynes@1241
   236
    const char *confFile = g_strdup_printf("%s/lxdreamrc", appHome);
nkeynes@1241
   237
    set_user_data_path(appHome);
nkeynes@1241
   238
    lxdream_set_config_filename( confFile );
nkeynes@1239
   239
    lxdream_make_config_dir( );
nkeynes@1239
   240
    lxdream_load_config( );
nkeynes@1239
   241
    iso_init();
nkeynes@1239
   242
    gdrom_list_init();
nkeynes@1239
   243
    vmulist_init();
nkeynes@1239
   244
    dreamcast_init(1);
nkeynes@1239
   245
nkeynes@1245
   246
    dreamcast_register_module( &android_gui_module );
nkeynes@1239
   247
    audio_init_driver(NULL);
nkeynes@1239
   248
    display_driver_t display_driver = get_display_driver_by_name(NULL);
nkeynes@1239
   249
    display_set_driver(display_driver);
nkeynes@1239
   250
nkeynes@1239
   251
    hotkeys_init();
nkeynes@1239
   252
    serial_init();
nkeynes@1239
   253
    maple_reattach_all();
nkeynes@1253
   254
nkeynes@1253
   255
    ERROR err;
nkeynes@1253
   256
    gchar *disc_file = lxdream_get_global_config_path_value( CONFIG_GDROM );
nkeynes@1253
   257
    if( disc_file != NULL ) {
nkeynes@1253
   258
        gboolean ok = gdrom_mount_image( disc_file, &err );
nkeynes@1253
   259
        g_free(disc_file);
nkeynes@1253
   260
        if( !ok ) {
nkeynes@1253
   261
            WARN( err.msg );
nkeynes@1253
   262
        } else {
nkeynes@1253
   263
            INFO( "Mounted %s", disc_file );
nkeynes@1253
   264
        }
nkeynes@1253
   265
    }
nkeynes@1253
   266
nkeynes@1239
   267
    INFO( "%s! ready...", APP_NAME );
nkeynes@1245
   268
    android_start_thread();
nkeynes@1239
   269
}
nkeynes@1239
   270
nkeynes@1245
   271
nkeynes@1245
   272
/************************* Dreamcast native entry points **************************/
nkeynes@1245
   273
nkeynes@1245
   274
static char *getStringChars( JNIEnv *env, jstring str );
nkeynes@1245
   275
nkeynes@1245
   276
/**
nkeynes@1245
   277
 * Main initialization entry point. We need to do all the setup that main()
nkeynes@1245
   278
 * would normally do, as well as any UI specific setup.
nkeynes@1245
   279
 */
nkeynes@1245
   280
JNIEXPORT void JNICALL Java_org_lxdream_Dreamcast_init(JNIEnv * env, jclass obj,  jstring homeDir )
nkeynes@1239
   281
{
nkeynes@1245
   282
    android_init( getStringChars(env, homeDir) );
nkeynes@1239
   283
}
nkeynes@1239
   284
nkeynes@1241
   285
JNIEXPORT void JNICALL Java_org_lxdream_Dreamcast_run(JNIEnv * env, jclass obj)
nkeynes@1239
   286
{
nkeynes@1245
   287
    tqueue_post_message( android_callback_wrapper, dreamcast_run );
nkeynes@1245
   288
}
nkeynes@1245
   289
nkeynes@1245
   290
JNIEXPORT void JNICALL Java_org_lxdream_Dreamcast_toggleRun(JNIEnv * env, jclass obj)
nkeynes@1245
   291
{
nkeynes@1245
   292
    tqueue_post_message( android_toggle_run, NULL );
nkeynes@1245
   293
}
nkeynes@1245
   294
nkeynes@1245
   295
JNIEXPORT void JNICALL Java_org_lxdream_Dreamcast_reset(JNIEnv * env, jclass obj)
nkeynes@1245
   296
{
nkeynes@1245
   297
    tqueue_post_message( android_callback_wrapper, dreamcast_reset );
nkeynes@1239
   298
}
nkeynes@1239
   299
nkeynes@1241
   300
JNIEXPORT void JNICALL Java_org_lxdream_Dreamcast_stop(JNIEnv * env, jclass obj)
nkeynes@1239
   301
{
nkeynes@1245
   302
    /* Need to make sure this completely shuts down before we return */
nkeynes@1245
   303
    tqueue_send_message( android_callback_wrapper, dreamcast_stop );
nkeynes@1239
   304
}
nkeynes@1239
   305
nkeynes@1275
   306
JNIEXPORT void JNICALL Java_org_lxdream_Dreamcast_onAppPause(JNIEnv * env, jclass obj)
nkeynes@1275
   307
{
nkeynes@1275
   308
    /* Need to make sure this completely shuts down before we return */
nkeynes@1275
   309
    tqueue_send_message( android_do_pause, &current_surface );
nkeynes@1275
   310
}
nkeynes@1275
   311
nkeynes@1275
   312
JNIEXPORT void JNICALL Java_org_lxdream_Dreamcast_onAppResume(JNIEnv * env, jclass obj)
nkeynes@1275
   313
{
nkeynes@1275
   314
    tqueue_post_message( android_do_resume, &current_surface );
nkeynes@1275
   315
}
nkeynes@1275
   316
nkeynes@1245
   317
JNIEXPORT jboolean JNICALL Java_org_lxdream_Dreamcast_isRunning(JNIEnv *env, jclass obj)
nkeynes@1239
   318
{
nkeynes@1245
   319
    return dreamcast_is_running();
nkeynes@1239
   320
}
nkeynes@1239
   321
nkeynes@1245
   322
JNIEXPORT jboolean JNICALL Java_org_lxdream_Dreamcast_isRunnable(JNIEnv *env, jclass obj)
nkeynes@1239
   323
{
nkeynes@1245
   324
    return dreamcast_can_run();
nkeynes@1239
   325
}
nkeynes@1239
   326
nkeynes@1245
   327
JNIEXPORT jboolean JNICALL Java_org_lxdream_Dreamcast_mount(JNIEnv *env, jclass obj, jstring str)
nkeynes@1245
   328
{
nkeynes@1245
   329
    char *s = getStringChars(env, str);
nkeynes@1245
   330
    return tqueue_send_message( android_mount_disc, s );
nkeynes@1239
   331
}
nkeynes@1239
   332
nkeynes@1245
   333
JNIEXPORT jboolean JNICALL Java_org_lxdream_Dreamcast_unmount(JNIEnv *env, jclass obj)
nkeynes@1239
   334
{
nkeynes@1245
   335
    tqueue_post_message( android_callback_wrapper, gdrom_unmount_disc );
nkeynes@1239
   336
}
nkeynes@1239
   337
nkeynes@1245
   338
nkeynes@1245
   339
/************************* LxdreamView native entry points **************************/
nkeynes@1245
   340
nkeynes@1245
   341
JNIEXPORT void JNICALL Java_org_lxdream_LxdreamView_setSurface(JNIEnv * env, jobject view, jobject surface, jint width, jint height)
nkeynes@1239
   342
{
nkeynes@1245
   343
    current_surface.win = ANativeWindow_fromSurface(env, surface);
nkeynes@1245
   344
    current_surface.width = width;
nkeynes@1245
   345
    current_surface.height = height;
nkeynes@1245
   346
    int fmt = ANativeWindow_getFormat(current_surface.win);
nkeynes@1245
   347
    if( fmt == WINDOW_FORMAT_RGB_565 ) {
nkeynes@1245
   348
        current_surface.format = COLFMT_RGB565;
nkeynes@1245
   349
    } else {
nkeynes@1245
   350
        current_surface.format = COLFMT_RGB888;
nkeynes@1245
   351
    }
nkeynes@1275
   352
    INFO( "Setting surface" );
nkeynes@1245
   353
    tqueue_post_message( android_set_surface, &current_surface );
nkeynes@1239
   354
}
nkeynes@1239
   355
nkeynes@1245
   356
JNIEXPORT void JNICALL Java_org_lxdream_LxdreamView_clearSurface(JNIEnv * env, jobject view, jobject surface)
nkeynes@1239
   357
{
nkeynes@1245
   358
    /* Need to make sure this completely shuts down before we return */
nkeynes@1245
   359
    tqueue_send_message( android_clear_surface, &current_surface );
nkeynes@1239
   360
}
nkeynes@1239
   361
nkeynes@1245
   362
nkeynes@1245
   363
/************************* JNI Support functions **************************/
nkeynes@1245
   364
nkeynes@1245
   365
static char *getStringChars( JNIEnv *env, jstring str )
nkeynes@1239
   366
{
nkeynes@1245
   367
    jboolean iscopy;
nkeynes@1245
   368
    const char *p = (*env)->GetStringUTFChars(env, str, &iscopy);
nkeynes@1245
   369
    char *result = strdup(p);
nkeynes@1245
   370
    (*env)->ReleaseStringUTFChars(env,str,p);
nkeynes@1245
   371
    return result;
nkeynes@1239
   372
}
.