initial commit
@@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="name.nick.jubanka.colleen"
|
||||
android:installLocation="auto"
|
||||
android:versionCode="300"
|
||||
android:versionName="3.0">
|
||||
<uses-sdk
|
||||
android:minSdkVersion="4"
|
||||
android:targetSdkVersion="20"/>
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
||||
<application
|
||||
android:label="@string/app_name"
|
||||
android:icon="@drawable/icon">
|
||||
<activity
|
||||
android:name="MainActivity"
|
||||
android:label="@string/app_name"
|
||||
android:theme="@style/MainTheme">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity android:name=".FileSelector"/>
|
||||
<activity android:name=".Preferences"/>
|
||||
</application>
|
||||
</manifest>
|
||||
@@ -0,0 +1,92 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project name="colleen" default="help">
|
||||
|
||||
<!-- The local.properties file is created and updated by the 'android' tool.
|
||||
It contains the path to the SDK. It should *NOT* be checked into
|
||||
Version Control Systems. -->
|
||||
<property file="local.properties" />
|
||||
|
||||
<!-- The ant.properties file can be created by you. It is only edited by the
|
||||
'android' tool to add properties to it.
|
||||
This is the place to change some Ant specific build properties.
|
||||
Here are some properties you may want to change/update:
|
||||
|
||||
source.dir
|
||||
The name of the source directory. Default is 'src'.
|
||||
out.dir
|
||||
The name of the output directory. Default is 'bin'.
|
||||
|
||||
For other overridable properties, look at the beginning of the rules
|
||||
files in the SDK, at tools/ant/build.xml
|
||||
|
||||
Properties related to the SDK location or the project target should
|
||||
be updated using the 'android' tool with the 'update' action.
|
||||
|
||||
This file is an integral part of the build system for your
|
||||
application and should be checked into Version Control Systems.
|
||||
|
||||
-->
|
||||
<property file="ant.properties" />
|
||||
|
||||
<!-- if sdk.dir was not set from one of the property file, then
|
||||
get it from the ANDROID_HOME env var.
|
||||
This must be done before we load project.properties since
|
||||
the proguard config can use sdk.dir -->
|
||||
<property environment="env" />
|
||||
<condition property="sdk.dir" value="${env.ANDROID_HOME}">
|
||||
<isset property="env.ANDROID_HOME" />
|
||||
</condition>
|
||||
|
||||
<!-- The project.properties file is created and updated by the 'android'
|
||||
tool, as well as ADT.
|
||||
|
||||
This contains project specific properties such as project target, and library
|
||||
dependencies. Lower level build properties are stored in ant.properties
|
||||
(or in .classpath for Eclipse projects).
|
||||
|
||||
This file is an integral part of the build system for your
|
||||
application and should be checked into Version Control Systems. -->
|
||||
<loadproperties srcFile="project.properties" />
|
||||
|
||||
<!-- quick check on sdk.dir -->
|
||||
<fail
|
||||
message="sdk.dir is missing. Make sure to generate local.properties using 'android update project' or to inject it through the ANDROID_HOME environment variable."
|
||||
unless="sdk.dir"
|
||||
/>
|
||||
|
||||
<!--
|
||||
Import per project custom build rules if present at the root of the project.
|
||||
This is the place to put custom intermediary targets such as:
|
||||
-pre-build
|
||||
-pre-compile
|
||||
-post-compile (This is typically used for code obfuscation.
|
||||
Compiled code location: ${out.classes.absolute.dir}
|
||||
If this is not done in place, override ${out.dex.input.absolute.dir})
|
||||
-post-package
|
||||
-post-build
|
||||
-pre-clean
|
||||
-->
|
||||
<import file="custom_rules.xml" optional="true" />
|
||||
|
||||
<!-- Import the actual build file.
|
||||
|
||||
To customize existing targets, there are two options:
|
||||
- Customize only one target:
|
||||
- copy/paste the target into this file, *before* the
|
||||
<import> task.
|
||||
- customize it to your needs.
|
||||
- Customize the whole content of build.xml
|
||||
- copy/paste the content of the rules files (minus the top node)
|
||||
into this file, replacing the <import> task.
|
||||
- customize to your needs.
|
||||
|
||||
***********************
|
||||
****** IMPORTANT ******
|
||||
***********************
|
||||
In all cases you must update the value of version-tag below to read 'custom' instead of an integer,
|
||||
in order to avoid having your file be overridden by tools such as "android update project"
|
||||
-->
|
||||
<!-- version-tag: 1 -->
|
||||
<import file="${sdk.dir}/tools/ant/build.xml" />
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,47 @@
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_MODULE := atari800
|
||||
|
||||
A800_CORE_OBJS := \
|
||||
afile.o \
|
||||
antic.o \
|
||||
atari.o \
|
||||
binload.o \
|
||||
cartridge.o \
|
||||
cassette.o \
|
||||
compfile.o \
|
||||
cfg.o \
|
||||
cpu.o \
|
||||
crc32.o \
|
||||
devices.o \
|
||||
emuos.o \
|
||||
esc.o \
|
||||
gtia.o \
|
||||
img_tape.o \
|
||||
log.o \
|
||||
memory.o \
|
||||
monitor.o \
|
||||
pbi.o \
|
||||
pia.o \
|
||||
pokey.o \
|
||||
rtime.o \
|
||||
sio.o \
|
||||
sysrom.o \
|
||||
util.o \
|
||||
@OBJS@
|
||||
A800_CORE_LIBS := @LIBS@
|
||||
|
||||
ANDROID_SRCS := platform.c \
|
||||
sound.c \
|
||||
graphics.c \
|
||||
jni.c \
|
||||
androidinput.c
|
||||
ANDROID_LIBS := -llog -lGLESv1_CM
|
||||
|
||||
LOCAL_C_INCLUDES := $(LOCAL_PATH)/../..
|
||||
LOCAL_SRC_FILES := $(A800_CORE_OBJS:%.o=../../%.c) $(ANDROID_SRCS)
|
||||
LOCAL_LDLIBS := $(A800_CORE_LIBS) $(ANDROID_LIBS)
|
||||
|
||||
include $(BUILD_SHARED_LIBRARY)
|
||||
@@ -0,0 +1 @@
|
||||
APP_PLATFORM := android-9
|
||||
@@ -0,0 +1,547 @@
|
||||
/*
|
||||
* androidinput.c - handle touch & keyboard events from android
|
||||
*
|
||||
* Copyright (C) 2010 Kostas Nakos
|
||||
* Copyright (C) 2010 Atari800 development team (see DOC/CREDITS)
|
||||
*
|
||||
* This file is part of the Atari800 emulator project which emulates
|
||||
* the Atari 400, 800, 800XL, 130XE, and 5200 8-bit computers.
|
||||
*
|
||||
* Atari800 is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Atari800 is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Atari800; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include "input.h"
|
||||
#include "akey.h"
|
||||
#include "pokey.h"
|
||||
|
||||
#include "graphics.h"
|
||||
#include "androidinput.h"
|
||||
#include "keys.inc"
|
||||
|
||||
#define HIT_OPACITY 0.6f
|
||||
#define POTLIMIT 228
|
||||
|
||||
#define KBD_MAXKEYS (1 << 4)
|
||||
#define KBD_MASK (KBD_MAXKEYS - 1)
|
||||
|
||||
struct touchstate
|
||||
{
|
||||
int x;
|
||||
int y;
|
||||
int s;
|
||||
};
|
||||
enum
|
||||
{
|
||||
PTRSTL = -1,
|
||||
PTRJOY = 0,
|
||||
PTRTRG,
|
||||
MAXPOINTERS
|
||||
};
|
||||
/* always: pointer 0 is joystick pointer, 1 is fire pointer */
|
||||
static struct touchstate prevtc[MAXPOINTERS];
|
||||
static int prevconptr;
|
||||
|
||||
int Android_Joyleft = TRUE;
|
||||
float Android_Splitpct = 0.5f;
|
||||
int Android_Split;
|
||||
|
||||
int Android_Paddle = FALSE;
|
||||
SWORD Android_POTX = 0;
|
||||
SWORD Android_POTY = 0;
|
||||
int Android_PlanetaryDefense = FALSE;
|
||||
UBYTE Android_ReversePddle = 0;
|
||||
|
||||
struct joy_overlay_state AndroidInput_JoyOvl;
|
||||
struct consolekey_overlay_state AndroidInput_ConOvl;
|
||||
|
||||
UWORD Android_PortStatus;
|
||||
UBYTE Android_TrigStatus;
|
||||
static int Android_Keyboard[KBD_MAXKEYS];
|
||||
static int key_head = 0, key_tail = 0;
|
||||
static int Android_key_control;
|
||||
static pthread_mutex_t key_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
static key_last = AKEY_NONE;
|
||||
|
||||
static const int derot_lut[2][4] =
|
||||
{
|
||||
{ KEY_RIGHT, KEY_LEFT, KEY_UP, KEY_DOWN }, /* derot left */
|
||||
{ KEY_LEFT, KEY_RIGHT, KEY_DOWN, KEY_UP } /* derot right */
|
||||
};
|
||||
UBYTE softjoymap[SOFTJOY_MAXKEYS + SOFTJOY_MAXACTIONS][2] =
|
||||
{
|
||||
{ KEY_LEFT, INPUT_STICK_LEFT },
|
||||
{ KEY_RIGHT, INPUT_STICK_RIGHT },
|
||||
{ KEY_UP, INPUT_STICK_FORWARD },
|
||||
{ KEY_DOWN, INPUT_STICK_BACK },
|
||||
{ '2', 0 },
|
||||
{ ACTION_NONE, AKEY_NONE },
|
||||
{ ACTION_NONE, AKEY_NONE },
|
||||
{ ACTION_NONE, AKEY_NONE }
|
||||
};
|
||||
int Android_SoftjoyEnable = TRUE;
|
||||
int Android_DerotateKeys = 0;
|
||||
|
||||
int Android_TouchEvent(int x1, int y1, int s1, int x2, int y2, int s2)
|
||||
{
|
||||
int joyptr; /* will point to joystick touch of input set */
|
||||
int tmpfire; /* flag: both pointers on fire side */
|
||||
int dx, dy, dx2, dy2;
|
||||
struct touchstate newtc[MAXPOINTERS];
|
||||
UBYTE newjoy, newtrig;
|
||||
struct joy_overlay_state *jovl;
|
||||
struct consolekey_overlay_state *covl;
|
||||
int conptr; /* will point to stolen ptr, PTRSTL otherwise */
|
||||
int i;
|
||||
float a, potx, poty;
|
||||
int ret = 0;
|
||||
|
||||
jovl = &AndroidInput_JoyOvl;
|
||||
covl = &AndroidInput_ConOvl;
|
||||
prevtc[PTRJOY].x = jovl->joyarea.l + ((jovl->joyarea.r - jovl->joyarea.l) >> 1);
|
||||
prevtc[PTRJOY].y = jovl->joyarea.t + ((jovl->joyarea.b - jovl->joyarea.t) >> 1);
|
||||
|
||||
/* establish joy ptr & fire ptr for new input */
|
||||
/* note: looks complicated & uses boolean magick but gets rid of a labyrinth of ifs :-) */
|
||||
if ((x1 >= Android_Split) ^ (x2 >= Android_Split)) { /* pointers on opposite sides */
|
||||
joyptr = (x1 < Android_Split) ^ Android_Joyleft;
|
||||
} else { /* both pointers either on joystick or on fire side */
|
||||
tmpfire = (x1 >= Android_Split) ^ (!Android_Joyleft); /* both pointers on fire side */
|
||||
dx = (x1 - prevtc[tmpfire].x); /* figure out which is closer to previous */
|
||||
dx2 = (x2 - prevtc[tmpfire].x);
|
||||
dy = (y1 - prevtc[tmpfire].y);
|
||||
dy2 = (y2 - prevtc[tmpfire].y);
|
||||
joyptr = ((dx2*dx2 + dy2*dy2) > (dx*dx + dy*dy)) ^ !tmpfire;
|
||||
s1 &= joyptr ^ (!tmpfire); /* unpress unrelated touch */
|
||||
s2 &= !(joyptr ^ (!tmpfire));
|
||||
}
|
||||
if (joyptr) {
|
||||
newtc[PTRTRG].x = x1; newtc[PTRTRG].y = y1; newtc[PTRTRG].s = s1;
|
||||
newtc[PTRJOY].x = x2; newtc[PTRJOY].y = y2; newtc[PTRJOY].s = s2;
|
||||
} else {
|
||||
newtc[PTRJOY].x = x1; newtc[PTRJOY].y = y1; newtc[PTRJOY].s = s1;
|
||||
newtc[PTRTRG].x = x2; newtc[PTRTRG].y = y2; newtc[PTRTRG].s = s2;
|
||||
}
|
||||
|
||||
if (newtc[PTRJOY].s || newtc[PTRTRG].s)
|
||||
ret = 1;
|
||||
|
||||
/* console keys */
|
||||
conptr = PTRSTL;
|
||||
covl->hitkey = CONK_NOKEY;
|
||||
if (covl->ovl_visible >= COVL_READY) { /* first a quick bounding box check */
|
||||
if (newtc[PTRJOY].s &&
|
||||
newtc[PTRJOY].x >= covl->bbox.l &&
|
||||
newtc[PTRJOY].x < covl->bbox.r &&
|
||||
newtc[PTRJOY].y >= covl->bbox.t &&
|
||||
newtc[PTRJOY].y < covl->bbox.b)
|
||||
conptr = PTRJOY; /* implicit: mask fire by joy pointer */
|
||||
else if (newtc[PTRTRG].s &&
|
||||
newtc[PTRTRG].x >= covl->bbox.l &&
|
||||
newtc[PTRTRG].x < covl->bbox.r &&
|
||||
newtc[PTRTRG].y >= covl->bbox.t &&
|
||||
newtc[PTRTRG].y < covl->bbox.b)
|
||||
conptr = PTRTRG;
|
||||
if (conptr != PTRSTL) { /* if bb is exact on top & bottom => check only horiz/lly */
|
||||
dy = covl->keycoo[i + 1] - newtc[conptr].y;
|
||||
for (i = 0; i < CONK_VERT_MAX; i += 8) {
|
||||
a = ((float) covl->keycoo[i + 6] - covl->keycoo[i ]) /
|
||||
((float) covl->keycoo[i + 1] - covl->keycoo[i + 7]);
|
||||
dx = covl->keycoo[i] + a * dy;
|
||||
if (newtc[conptr].x < dx) continue; /* off left edge */
|
||||
a = ((float) covl->keycoo[i + 4] - covl->keycoo[i + 2]) /
|
||||
((float) covl->keycoo[i + 3] - covl->keycoo[i + 5]);
|
||||
dx = covl->keycoo[i + 2] + a * dy;
|
||||
if (newtc[conptr].x > dx) continue; /* off right edge */
|
||||
covl->hitkey = i / 8; /* hit inside */
|
||||
break;
|
||||
}
|
||||
if (covl->hitkey != CONK_NOKEY) {
|
||||
covl->opacity = COVL_MAX_OPACITY;
|
||||
covl->statecnt = COVL_HOLD_TIME;
|
||||
covl->ovl_visible = COVL_READY;
|
||||
switch (covl->hitkey) {
|
||||
case CONK_START:
|
||||
INPUT_key_consol = INPUT_CONSOL_NONE ^ INPUT_CONSOL_START;
|
||||
break;
|
||||
case CONK_SELECT:
|
||||
INPUT_key_consol = INPUT_CONSOL_NONE ^ INPUT_CONSOL_SELECT;
|
||||
break;
|
||||
case CONK_OPTION:
|
||||
INPUT_key_consol = INPUT_CONSOL_NONE ^ INPUT_CONSOL_OPTION;
|
||||
break;
|
||||
case CONK_HELP:
|
||||
Keyboard_Enqueue(AKEY_HELP);
|
||||
break;
|
||||
/* RESET is handled at the overlay update */
|
||||
}
|
||||
} else {
|
||||
conptr = PTRSTL; /* didn't hit - let others handle it */
|
||||
}
|
||||
}
|
||||
if (prevconptr != PTRSTL && conptr == PTRSTL) { /* unpressed overlay key */
|
||||
if (Keyboard_Peek() == AKEY_HELP)
|
||||
Keyboard_Enqueue(AKEY_NONE);
|
||||
INPUT_key_consol = INPUT_CONSOL_NONE;
|
||||
covl->resetcnt = 0;
|
||||
}
|
||||
} else if (newtc[PTRJOY].s && newtc[PTRJOY].x > Android_ScreenW - covl->hotlen
|
||||
&& newtc[PTRJOY].y < covl->hotlen) {
|
||||
covl->ovl_visible = COVL_FADEIN; /* touched overlay hotspot */
|
||||
conptr = PTRJOY;
|
||||
} else if (newtc[PTRTRG].s && newtc[PTRTRG].x > Android_ScreenW - covl->hotlen
|
||||
&& newtc[PTRTRG].y < covl->hotlen) {
|
||||
covl->ovl_visible = COVL_FADEIN;
|
||||
conptr = PTRTRG;
|
||||
}
|
||||
if (conptr == PTRSTL)
|
||||
if (newtc[PTRJOY].s &&
|
||||
( (!prevtc[PTRJOY].s && newtc[PTRJOY].y < covl->hotlen) || /* menu area */
|
||||
prevconptr != PTRSTL) && /* still held */
|
||||
!(covl->ovl_visible != COVL_HIDDEN &&
|
||||
newtc[PTRJOY].x >= covl->bbox.l - COVL_SHADOW_OFF && /* outside bbox */
|
||||
newtc[PTRJOY].y <= covl->bbox.b + COVL_SHADOW_OFF) ) {
|
||||
conptr = PTRJOY; /* touched menu area */
|
||||
ret = 2;
|
||||
} else if (newtc[PTRTRG].s &&
|
||||
( (!prevtc[PTRTRG].s && newtc[PTRTRG].y < covl->hotlen) ||
|
||||
prevconptr != PTRSTL) &&
|
||||
!(covl->ovl_visible != COVL_HIDDEN &&
|
||||
newtc[PTRTRG].x >= covl->bbox.l - COVL_SHADOW_OFF &&
|
||||
newtc[PTRTRG].y <= covl->bbox.b + COVL_SHADOW_OFF) ) {
|
||||
conptr = PTRTRG;
|
||||
ret = 2;
|
||||
}
|
||||
|
||||
/* joystick */
|
||||
newjoy = INPUT_STICK_CENTRE;
|
||||
if (newtc[PTRJOY].s && conptr != PTRJOY) {
|
||||
if (!Android_Paddle) {
|
||||
dx2 = (jovl->joyarea.r - jovl->joyarea.l) >> 1;
|
||||
dy2 = (jovl->joyarea.b - jovl->joyarea.t) >> 1;
|
||||
dx = dx2 - dx2 * jovl->deadarea;
|
||||
dy = dy2 - dy2 * jovl->deadarea;
|
||||
dx2 = (jovl->joyarea.r - jovl->joyarea.l) * jovl->gracearea;
|
||||
}
|
||||
if (Android_Paddle) {
|
||||
potx = ((float) (newtc[PTRJOY].x - jovl->joyarea.l)) /
|
||||
((float) (jovl->joyarea.r - jovl->joyarea.l));
|
||||
poty = (float) newtc[PTRJOY].y / (float) Android_ScreenH;
|
||||
Android_POTX = POTLIMIT - (UBYTE) (potx * ((float) POTLIMIT) + 0.5f);
|
||||
Android_POTY = POTLIMIT - (UBYTE) (poty * ((float) POTLIMIT) + 0.5f);
|
||||
if (Android_ReversePddle & 1)
|
||||
Android_POTX = POTLIMIT - Android_POTX;
|
||||
if (Android_ReversePddle & 2)
|
||||
Android_POTY = POTLIMIT - Android_POTY;
|
||||
if (Android_POTX < 0) Android_POTX = 0;
|
||||
if (Android_POTY < 0) Android_POTY = 0;
|
||||
if (Android_POTX > POTLIMIT) Android_POTX = POTLIMIT;
|
||||
if (Android_POTY > POTLIMIT) Android_POTY = POTLIMIT;
|
||||
|
||||
jovl->joystick.x = newtc[PTRJOY].x;
|
||||
jovl->joystick.y = newtc[PTRJOY].y;
|
||||
jovl->stickopacity = HIT_OPACITY;
|
||||
if (!jovl->anchor) {
|
||||
dy = (jovl->joyarea.b - jovl->joyarea.t) >> 1;
|
||||
if (newtc[PTRJOY].y - dy < 0) newtc[PTRJOY].y -= newtc[PTRJOY].y - dy;
|
||||
if (newtc[PTRJOY].y + dy > Android_ScreenH)
|
||||
newtc[PTRJOY].y -= newtc[PTRJOY].y + dy - Android_ScreenH;
|
||||
jovl->joyarea.t = newtc[PTRJOY].y - dy;
|
||||
jovl->joyarea.b = newtc[PTRJOY].y + dy;
|
||||
jovl->areaopacitycur = jovl->areaopacityset;
|
||||
jovl->areaopacityfrm = 0;
|
||||
}
|
||||
} else if ( (newtc[PTRJOY].x >= jovl->joyarea.l - dx2 &&
|
||||
newtc[PTRJOY].x <= jovl->joyarea.r + dx2 &&
|
||||
newtc[PTRJOY].y >= jovl->joyarea.t - dx2 &&
|
||||
newtc[PTRJOY].y <= jovl->joyarea.b + dx2) ||
|
||||
jovl->anchor ) {
|
||||
|
||||
if (newtc[PTRJOY].x <= jovl->joyarea.l + dx) {
|
||||
newjoy &= INPUT_STICK_LEFT;
|
||||
} else if (newtc[PTRJOY].x >= jovl->joyarea.r - dx) {
|
||||
newjoy &= INPUT_STICK_RIGHT;
|
||||
}
|
||||
if (newtc[PTRJOY].y <= jovl->joyarea.t + dy) {
|
||||
newjoy &= INPUT_STICK_FORWARD;
|
||||
} else if (newtc[PTRJOY].y >= jovl->joyarea.b - dy) {
|
||||
newjoy &= INPUT_STICK_BACK;
|
||||
}
|
||||
|
||||
if (!jovl->anchor) {
|
||||
if (newtc[PTRJOY].x > jovl->joyarea.r) { /* grace area */
|
||||
dx = newtc[PTRJOY].x - jovl->joyarea.r;
|
||||
jovl->joyarea.l += dx;
|
||||
jovl->joyarea.r += dx;
|
||||
} else if (newtc[PTRJOY].x < jovl->joyarea.l) {
|
||||
dx = jovl->joyarea.l - newtc[PTRJOY].x;
|
||||
jovl->joyarea.r -= dx;
|
||||
jovl->joyarea.l -= dx;
|
||||
}
|
||||
if (newtc[PTRJOY].y > jovl->joyarea.b) {
|
||||
dy = newtc[PTRJOY].y - jovl->joyarea.b;
|
||||
jovl->joyarea.t += dy;
|
||||
jovl->joyarea.b += dy;
|
||||
} else if (newtc[PTRJOY].y < jovl->joyarea.t) {
|
||||
dy = jovl->joyarea.t - newtc[PTRJOY].y;
|
||||
jovl->joyarea.b -= dy;
|
||||
jovl->joyarea.t -= dy;
|
||||
}
|
||||
}
|
||||
|
||||
jovl->joystick.x = newtc[PTRJOY].x;
|
||||
jovl->joystick.y = newtc[PTRJOY].y;
|
||||
jovl->stickopacity = HIT_OPACITY;
|
||||
jovl->areaopacitycur = jovl->areaopacityset;
|
||||
jovl->areaopacityfrm = 0;
|
||||
} else {
|
||||
if (prevtc[PTRJOY].s) { /* drag area along */
|
||||
if (newtc[PTRJOY].x > jovl->joyarea.r) {
|
||||
dx = newtc[PTRJOY].x - jovl->joyarea.r;
|
||||
jovl->joyarea.l += dx;
|
||||
jovl->joyarea.r += dx;
|
||||
newjoy &= INPUT_STICK_RIGHT;
|
||||
} else if (newtc[PTRJOY].x < jovl->joyarea.l) {
|
||||
dx = jovl->joyarea.l - newtc[PTRJOY].x;
|
||||
jovl->joyarea.r -= dx;
|
||||
jovl->joyarea.l -= dx;
|
||||
newjoy &= INPUT_STICK_LEFT;
|
||||
} else if (newtc[PTRJOY].x <= jovl->joyarea.l + dx) {
|
||||
newjoy &= INPUT_STICK_LEFT;
|
||||
} else if (newtc[PTRJOY].x >= jovl->joyarea.r - dx) {
|
||||
newjoy &= INPUT_STICK_RIGHT;
|
||||
}
|
||||
if (newtc[PTRJOY].y > jovl->joyarea.b) {
|
||||
dy = newtc[PTRJOY].y - jovl->joyarea.b;
|
||||
jovl->joyarea.t += dy;
|
||||
jovl->joyarea.b += dy;
|
||||
newjoy &= INPUT_STICK_BACK;
|
||||
} else if (newtc[PTRJOY].y < jovl->joyarea.t) {
|
||||
dy = jovl->joyarea.t - newtc[PTRJOY].y;
|
||||
jovl->joyarea.b -= dy;
|
||||
jovl->joyarea.t -= dy;
|
||||
newjoy &= INPUT_STICK_FORWARD;
|
||||
} else if (newtc[PTRJOY].y <= jovl->joyarea.t + dy) {
|
||||
newjoy &= INPUT_STICK_FORWARD;
|
||||
} else if (newtc[PTRJOY].y >= jovl->joyarea.b - dy) {
|
||||
newjoy &= INPUT_STICK_BACK;
|
||||
}
|
||||
|
||||
jovl->joystick.x = newtc[PTRJOY].x;
|
||||
jovl->joystick.y = newtc[PTRJOY].y;
|
||||
jovl->stickopacity = HIT_OPACITY;
|
||||
} else { /* recenter area */
|
||||
dx = (jovl->joyarea.r - jovl->joyarea.l) >> 1;
|
||||
dy = (jovl->joyarea.b - jovl->joyarea.t) >> 1;
|
||||
if (Android_Joyleft) {
|
||||
if (newtc[PTRJOY].x + dx > Android_Split)
|
||||
newtc[PTRJOY].x = Android_Split - dx;
|
||||
} else {
|
||||
if (newtc[PTRJOY].x - dx < Android_Split)
|
||||
newtc[PTRJOY].x = Android_Split + dx;
|
||||
}
|
||||
if (newtc[PTRJOY].x - dx < 0) newtc[PTRJOY].x -= newtc[PTRJOY].x - dx;
|
||||
if (newtc[PTRJOY].y - dy < 0) newtc[PTRJOY].y -= newtc[PTRJOY].y - dy;
|
||||
if (newtc[PTRJOY].y + dy > Android_ScreenH)
|
||||
newtc[PTRJOY].y -= newtc[PTRJOY].y + dy - Android_ScreenH;
|
||||
jovl->joyarea.l = newtc[PTRJOY].x - dx;
|
||||
jovl->joyarea.r = newtc[PTRJOY].x + dx;
|
||||
jovl->joyarea.t = newtc[PTRJOY].y - dy;
|
||||
jovl->joyarea.b = newtc[PTRJOY].y + dy;
|
||||
}
|
||||
jovl->areaopacitycur = jovl->areaopacityset;
|
||||
jovl->areaopacityfrm = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* trigger */
|
||||
newtrig = 1;
|
||||
if ( (newtc[PTRTRG].s && conptr != PTRTRG) || /* normal trigger */
|
||||
(newtc[PTRJOY].s && conptr != PTRJOY && Android_PlanetaryDefense) ) {
|
||||
newtrig = 0;
|
||||
jovl->fire.x = newtc[PTRTRG].x;
|
||||
jovl->fire.y = newtc[PTRTRG].y;
|
||||
jovl->fireopacity = HIT_OPACITY;
|
||||
}
|
||||
|
||||
/* thread unsafe => "no" problem */
|
||||
if (!Android_Paddle){
|
||||
Android_PortStatus = 0xFFF0 | newjoy;
|
||||
Android_TrigStatus = 0xE | newtrig;
|
||||
} else {
|
||||
POKEY_POT_input[INPUT_mouse_port << 1] = Android_POTX;
|
||||
POKEY_POT_input[(INPUT_mouse_port << 1) + 1] = Android_POTY;
|
||||
INPUT_mouse_buttons = !newtrig;
|
||||
}
|
||||
|
||||
memcpy(prevtc, newtc, sizeof(struct touchstate) * MAXPOINTERS);
|
||||
prevconptr = conptr;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void Android_KeyEvent(int k, int s)
|
||||
{
|
||||
int i, shft;
|
||||
|
||||
if (Android_SoftjoyEnable) {
|
||||
for (i = 0; i < 4; i++)
|
||||
if (softjoymap[i][0] == k) {
|
||||
if (s)
|
||||
Android_PortStatus &= 0xFFF0 | softjoymap[i][1];
|
||||
else
|
||||
Android_PortStatus |= ~softjoymap[i][1];
|
||||
return;
|
||||
}
|
||||
if (softjoymap[SOFTJOY_FIRE][0] == k) {
|
||||
Android_TrigStatus = Android_TrigStatus & (~(s != 0)) | (s == 0);
|
||||
return;
|
||||
}
|
||||
for (i = SOFTJOY_ACTIONBASE; i < SOFTJOY_MAXKEYS + SOFTJOY_MAXACTIONS; i++)
|
||||
if (softjoymap[i][0] == k && softjoymap[i][1] != AKEY_NONE) {
|
||||
k = softjoymap[i][1];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (Android_DerotateKeys && k <= KEY_UP && k >= KEY_RIGHT)
|
||||
k = derot_lut[Android_DerotateKeys - 1][KEY_UP - k];
|
||||
|
||||
switch (k) {
|
||||
case KEY_SHIFT:
|
||||
INPUT_key_shift = (s) ? AKEY_SHFT : 0;
|
||||
break;
|
||||
case KEY_CONTROL:
|
||||
Android_key_control = (s) ? AKEY_CTRL : 0;
|
||||
break;
|
||||
case KEY_FIRE:
|
||||
Android_TrigStatus = Android_TrigStatus & (~(s != 0)) | (s == 0);
|
||||
break;
|
||||
default:
|
||||
if (k >= STATIC_MAXKEYS)
|
||||
Log_print("Unmappable key %d", k);
|
||||
else {
|
||||
if (k == '+' || k == '<' || k == '>' || k == '*')
|
||||
shft = 0;
|
||||
else
|
||||
shft == INPUT_key_shift;
|
||||
Keyboard_Enqueue( (s) ? (skeyxlat[k] | Android_key_control | shft) : AKEY_NONE );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Input_Initialize(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
memset(prevtc, 0, 2 * sizeof(struct touchstate));
|
||||
prevconptr = PTRSTL;
|
||||
|
||||
memset(&AndroidInput_JoyOvl, 0, sizeof(struct joy_overlay_state));
|
||||
AndroidInput_JoyOvl.ovl_visible = 1;
|
||||
AndroidInput_JoyOvl.areaopacitycur = AndroidInput_JoyOvl.areaopacityset = 0.25f;
|
||||
AndroidInput_JoyOvl.deadarea = 0.3f;
|
||||
AndroidInput_JoyOvl.gracearea = 0.3f;
|
||||
AndroidInput_JoyOvl.joyarea.t = AndroidInput_JoyOvl.joyarea.l = 10;
|
||||
AndroidInput_JoyOvl.joyarea.b = AndroidInput_JoyOvl.joyarea.r = 74;
|
||||
AndroidInput_JoyOvl.anchor = 0;
|
||||
|
||||
memset(&AndroidInput_ConOvl, 0, sizeof(struct consolekey_overlay_state));
|
||||
AndroidInput_ConOvl.hitkey = CONK_NOKEY;
|
||||
AndroidInput_ConOvl.opacity = COVL_MAX_OPACITY;
|
||||
AndroidInput_ConOvl.ovl_visible = COVL_READY;
|
||||
AndroidInput_ConOvl.statecnt = COVL_HOLD_TIME >> 1;
|
||||
|
||||
Android_PortStatus = 0xFFFF;
|
||||
Android_TrigStatus = 0xF;
|
||||
|
||||
for (i = 0; i < KBD_MAXKEYS; Android_Keyboard[i] = AKEY_NONE, i++);
|
||||
INPUT_key_consol = INPUT_CONSOL_NONE;
|
||||
INPUT_key_shift = FALSE;
|
||||
Android_key_control = 0;
|
||||
}
|
||||
|
||||
void Joy_Reposition(void)
|
||||
{
|
||||
int dx = 0, dy = 0;
|
||||
|
||||
if (Android_ScreenW == 0) return; /* we're going to get called again @ initgraphics() */
|
||||
if (Android_Joyleft) {
|
||||
if (AndroidInput_JoyOvl.joyarea.r > Android_Split)
|
||||
dx = -(AndroidInput_JoyOvl.joyarea.r - Android_Split);
|
||||
} else {
|
||||
if (AndroidInput_JoyOvl.joyarea.l < Android_Split)
|
||||
dx = Android_Split - AndroidInput_JoyOvl.joyarea.l;
|
||||
}
|
||||
if (AndroidInput_JoyOvl.joyarea.l < 0)
|
||||
dx = -AndroidInput_JoyOvl.joyarea.l;
|
||||
else if (AndroidInput_JoyOvl.joyarea.r > Android_ScreenW)
|
||||
dx = -(AndroidInput_JoyOvl.joyarea.r - Android_ScreenW);
|
||||
if (AndroidInput_JoyOvl.joyarea.t < 0)
|
||||
dy = -AndroidInput_JoyOvl.joyarea.t;
|
||||
else if (AndroidInput_JoyOvl.joyarea.b > Android_ScreenH)
|
||||
dy = -(AndroidInput_JoyOvl.joyarea.b - Android_ScreenH);
|
||||
|
||||
AndroidInput_JoyOvl.joyarea.l += dx;
|
||||
AndroidInput_JoyOvl.joyarea.r += dx;
|
||||
AndroidInput_JoyOvl.joyarea.t += dy;
|
||||
AndroidInput_JoyOvl.joyarea.b += dy;
|
||||
}
|
||||
|
||||
void Android_SplitCalc(void)
|
||||
{
|
||||
if (Android_Joyleft)
|
||||
Android_Split = Android_Splitpct * Android_ScreenW;
|
||||
else
|
||||
Android_Split = (1.0f - Android_Splitpct) * Android_ScreenW;
|
||||
}
|
||||
|
||||
void Keyboard_Enqueue(int key)
|
||||
{
|
||||
pthread_mutex_lock(&key_mutex);
|
||||
|
||||
if ((key_head + 1) & KBD_MASK == key_tail)
|
||||
key_head = key_tail; /* on overflow, discard previous keys */
|
||||
Android_Keyboard[key_head++] = key;
|
||||
key_head &= KBD_MASK;
|
||||
|
||||
pthread_mutex_unlock(&key_mutex);
|
||||
}
|
||||
|
||||
int Keyboard_Dequeue(void)
|
||||
{
|
||||
pthread_mutex_lock(&key_mutex);
|
||||
|
||||
if (key_head != key_tail) {
|
||||
key_last = Android_Keyboard[key_tail++];
|
||||
key_tail &= KBD_MASK;
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&key_mutex);
|
||||
|
||||
return key_last;
|
||||
}
|
||||
|
||||
int Keyboard_Peek(void)
|
||||
{
|
||||
int tmp_key;
|
||||
|
||||
tmp_key = key_last;
|
||||
if (key_head != key_tail)
|
||||
tmp_key = Android_Keyboard[key_tail];
|
||||
return tmp_key;
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
#include "atari.h"
|
||||
|
||||
struct RECT
|
||||
{
|
||||
int l;
|
||||
int t;
|
||||
union {
|
||||
int r;
|
||||
int w;
|
||||
};
|
||||
union {
|
||||
int b;
|
||||
int h;
|
||||
};
|
||||
};
|
||||
struct POINT
|
||||
{
|
||||
int x;
|
||||
int y;
|
||||
};
|
||||
struct joy_overlay_state
|
||||
{
|
||||
int ovl_visible;
|
||||
|
||||
struct RECT joyarea;
|
||||
float areaopacitycur;
|
||||
float areaopacityset;
|
||||
int areaopacityfrm;
|
||||
int anchor;
|
||||
float deadarea;
|
||||
float gracearea;
|
||||
|
||||
struct POINT joystick;
|
||||
float stickopacity;
|
||||
|
||||
struct POINT fire;
|
||||
float fireopacity;
|
||||
int firewid;
|
||||
};
|
||||
|
||||
enum con_vst {
|
||||
COVL_HIDDEN = 0,
|
||||
COVL_FADEIN,
|
||||
COVL_READY,
|
||||
COVL_FADEOUT
|
||||
};
|
||||
enum con_key {
|
||||
CONK_NOKEY = -1,
|
||||
CONK_HELP = 0,
|
||||
CONK_START,
|
||||
CONK_SELECT,
|
||||
CONK_OPTION,
|
||||
CONK_RESET
|
||||
};
|
||||
struct consolekey_overlay_state
|
||||
{
|
||||
enum con_vst ovl_visible;
|
||||
|
||||
UWORD *keycoo;
|
||||
struct RECT bbox;
|
||||
float opacity;
|
||||
enum con_key hitkey;
|
||||
int statecnt;
|
||||
int resetcnt;
|
||||
int hotlen;
|
||||
|
||||
#define COVL_MAX_OPACITY 0.5f
|
||||
#define COVL_HOLD_TIME 150
|
||||
|
||||
#define RESET_SOFT 30
|
||||
#define RESET_HARD 60
|
||||
};
|
||||
|
||||
extern struct joy_overlay_state AndroidInput_JoyOvl;
|
||||
extern struct consolekey_overlay_state AndroidInput_ConOvl;
|
||||
|
||||
extern UWORD Android_PortStatus;
|
||||
extern UBYTE Android_TrigStatus;
|
||||
|
||||
enum
|
||||
{
|
||||
SOFTJOY_LEFT = 0,
|
||||
SOFTJOY_RIGHT,
|
||||
SOFTJOY_UP,
|
||||
SOFTJOY_DOWN,
|
||||
SOFTJOY_FIRE,
|
||||
SOFTJOY_MAXKEYS
|
||||
};
|
||||
#define SOFTJOY_MAXACTIONS 3
|
||||
#define SOFTJOY_ACTIONBASE SOFTJOY_MAXKEYS
|
||||
#define ACTION_NONE 0xFF
|
||||
extern UBYTE softjoymap[SOFTJOY_MAXKEYS + SOFTJOY_MAXACTIONS][2];
|
||||
|
||||
extern int Android_SoftjoyEnable;
|
||||
extern int Android_Joyleft;
|
||||
extern float Android_Splitpct;
|
||||
extern int Android_Split;
|
||||
extern int Android_DerotateKeys;
|
||||
extern int Android_Paddle;
|
||||
extern int Android_PlanetaryDefense;
|
||||
extern SWORD Android_POTX;
|
||||
extern SWORD Android_POTY;
|
||||
extern UBYTE Android_ReversePddle;
|
||||
|
||||
int Android_TouchEvent(int x1, int y1, int s1, int x2, int y2, int s2);
|
||||
void Android_KeyEvent(int k, int s);
|
||||
void Input_Initialize(void);
|
||||
void Keyboard_Enqueue(int key);
|
||||
int Keyboard_Dequeue(void);
|
||||
int Keyboard_Peek(void);
|
||||
void Android_SplitCalc(void);
|
||||
@@ -0,0 +1,480 @@
|
||||
/*
|
||||
* graphics.c - android drawing
|
||||
*
|
||||
* Copyright (C) 2010 Kostas Nakos
|
||||
* Copyright (C) 2010 Atari800 development team (see DOC/CREDITS)
|
||||
*
|
||||
* This file is part of the Atari800 emulator project which emulates
|
||||
* the Atari 400, 800, 800XL, 130XE, and 5200 8-bit computers.
|
||||
*
|
||||
* Atari800 is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Atari800 is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Atari800; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <malloc.h>
|
||||
#include <string.h>
|
||||
#include <GLES/gl.h>
|
||||
#include <GLES/glext.h>
|
||||
|
||||
#include "atari.h"
|
||||
#include "screen.h"
|
||||
#include "colours.h"
|
||||
#include "akey.h"
|
||||
#include "cpu.h"
|
||||
|
||||
#include "androidinput.h"
|
||||
#include "graphics.h"
|
||||
|
||||
#define TEXTURE_WIDTH 512
|
||||
#define TEXTURE_HEIGHT 256
|
||||
|
||||
#define OPACITY_CUTOFF 0.05f
|
||||
#define OPACITY_STEP 0.02f
|
||||
#define OPACITY_FRMSTR 75
|
||||
|
||||
#define BORDER_PCT 0.05f
|
||||
|
||||
int Android_ScreenW = 0;
|
||||
int Android_ScreenH = 0;
|
||||
int Android_Aspect;
|
||||
int Android_CropScreen[] = {0, SCREEN_HEIGHT, SCANLINE_LEN, -SCREEN_HEIGHT};
|
||||
static struct RECT screenrect;
|
||||
static int screenclear;
|
||||
int Android_Bilinear;
|
||||
float Android_Joyscale = 0.15f;
|
||||
|
||||
extern int *ovl_texpix;
|
||||
extern int ovl_texw;
|
||||
extern int ovl_texh;
|
||||
|
||||
/* graphics conversion */
|
||||
static UWORD *palette = NULL;
|
||||
static UWORD *hicolor_screen = NULL;
|
||||
|
||||
/* standard gl textures */
|
||||
enum {
|
||||
TEX_SCREEN = 0,
|
||||
TEX_OVL,
|
||||
TEX_MAXNAMES
|
||||
};
|
||||
static GLuint texture[TEX_MAXNAMES];
|
||||
static UWORD conkey_vrt[CONK_VERT_MAX];
|
||||
static int conkey_lbl[CONK_VERT_MAX >> 2];
|
||||
static UWORD conkey_shadow[2 * 4];
|
||||
|
||||
void Android_PaletteUpdate(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!palette) {
|
||||
if ( !(palette = malloc(256 * sizeof(UWORD))) ) {
|
||||
Log_print("Cannot allocate memory for palette conversion.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
memset(palette, 0, 256 * sizeof(UWORD));
|
||||
|
||||
for (i = 0; i < 256; i++)
|
||||
palette[i] = ( (Colours_GetR(i) & 0xf8) << 8 ) |
|
||||
( (Colours_GetG(i) & 0xfc) << 3 ) |
|
||||
( (Colours_GetB(i) & 0xf8) >> 3 );
|
||||
/* force full redraw */
|
||||
Screen_EntireDirty();
|
||||
}
|
||||
|
||||
int Android_InitGraphics(void)
|
||||
{
|
||||
const UWORD poly[] = { 0,16, 24,16, 32,0, 8,0 };
|
||||
int i, tmp, w, h;
|
||||
float tmp2, tmp3;
|
||||
struct RECT *r;
|
||||
|
||||
/* Allocate stuff */
|
||||
if (!hicolor_screen) {
|
||||
if ( !(hicolor_screen = malloc(TEXTURE_WIDTH * TEXTURE_HEIGHT * sizeof(UWORD))) ) {
|
||||
Log_print("Cannot allocate memory for hicolor screen.");
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
memset(hicolor_screen, 0, TEXTURE_WIDTH * TEXTURE_HEIGHT * sizeof(UWORD));
|
||||
|
||||
/* Setup GL */
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glGenTextures(TEX_MAXNAMES, texture);
|
||||
glPixelStorei(GL_PACK_ALIGNMENT, 8);
|
||||
|
||||
/* overlays texture */
|
||||
glBindTexture(GL_TEXTURE_2D, texture[TEX_OVL]);
|
||||
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, ovl_texw, ovl_texh, 0, GL_RGBA,
|
||||
GL_UNSIGNED_BYTE, ovl_texpix);
|
||||
|
||||
/* playfield texture */
|
||||
glBindTexture(GL_TEXTURE_2D, texture[TEX_SCREEN]);
|
||||
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
|
||||
Android_Bilinear ? GL_LINEAR : GL_NEAREST);
|
||||
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
|
||||
Android_Bilinear ? GL_LINEAR : GL_NEAREST);
|
||||
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
|
||||
glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, Android_CropScreen);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, TEXTURE_WIDTH, TEXTURE_HEIGHT, 0, GL_RGB,
|
||||
GL_UNSIGNED_SHORT_5_6_5, hicolor_screen);
|
||||
|
||||
/* Setup view for console key polygons */
|
||||
glLoadIdentity();
|
||||
glOrthof(0, Android_ScreenW, Android_ScreenH, 0, 0, 1);
|
||||
glEnableClientState(GL_VERTEX_ARRAY);
|
||||
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
/* Finsh GL init with an error check */
|
||||
if (glGetError() != GL_NO_ERROR) {
|
||||
Log_print("Cannot initialize OpenGL");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Console keys' polygons */
|
||||
tmp2 = ((float) (Android_ScreenW >> 1)) / ((float) 4.5f * poly[4]);
|
||||
tmp3 = ((float) Android_ScreenH) / ((float) 14 * poly[1]);
|
||||
if (tmp2 > tmp3)
|
||||
tmp2 = tmp3;
|
||||
if (tmp2 < 2.0f)
|
||||
tmp2 = 2.0f;
|
||||
for (i = 0; i < CONK_VERT_MAX; i += 2) {
|
||||
/* generate & scale */
|
||||
conkey_vrt[i ] = poly[i % 8] * tmp2 +
|
||||
((i > 7) ? (conkey_vrt[(i / 8 - 1) * 8 + 2] + 4) : 0);
|
||||
conkey_vrt[i + 1] = poly[(i + 1) % 8] * tmp2;
|
||||
}
|
||||
tmp = Android_ScreenW - conkey_vrt[CONK_VERT_MAX - 4];
|
||||
for (i = 0; i < CONK_VERT_MAX; i += 2) {
|
||||
/* translate */
|
||||
conkey_vrt[i ] += tmp;
|
||||
conkey_vrt[i + 1] += 4;
|
||||
}
|
||||
for (i = 0; i < CONK_VERT_MAX; i += 8) {
|
||||
conkey_lbl[i >> 2] = conkey_vrt[i] + 6;
|
||||
conkey_lbl[(i >> 2) + 1] = Android_ScreenH - (conkey_vrt[i + 1] - 1);
|
||||
}
|
||||
AndroidInput_ConOvl.keycoo = conkey_vrt;
|
||||
AndroidInput_ConOvl.bbox.l = conkey_vrt[0];
|
||||
AndroidInput_ConOvl.bbox.b = conkey_vrt[1];
|
||||
AndroidInput_ConOvl.bbox.r = conkey_vrt[CONK_VERT_MAX - 4];
|
||||
AndroidInput_ConOvl.bbox.t = conkey_vrt[CONK_VERT_MAX - 3];
|
||||
AndroidInput_ConOvl.hotlen = 0.1f *
|
||||
(Android_ScreenW < Android_ScreenH ? Android_ScreenW : Android_ScreenH);
|
||||
r = &(AndroidInput_ConOvl.bbox);
|
||||
conkey_shadow[0] = r->l - COVL_SHADOW_OFF;
|
||||
conkey_shadow[1] = r->b + COVL_SHADOW_OFF;
|
||||
conkey_shadow[2] = r->r;
|
||||
conkey_shadow[3] = r->b + COVL_SHADOW_OFF;
|
||||
conkey_shadow[4] = r->r;
|
||||
conkey_shadow[5] = r->t - COVL_SHADOW_OFF;
|
||||
conkey_shadow[6] = r->l - COVL_SHADOW_OFF;
|
||||
conkey_shadow[7] = r->t - COVL_SHADOW_OFF;
|
||||
|
||||
/* Scale joystick overlays */
|
||||
Joyovl_Scale();
|
||||
Joy_Reposition();
|
||||
|
||||
/* Aspect correct scaling */
|
||||
memset(&screenrect, 0, sizeof(struct RECT));
|
||||
if ( ((Android_ScreenW > Android_ScreenH) + 1) & Android_Aspect) {
|
||||
w = Android_CropScreen[2];
|
||||
h = -Android_CropScreen[3];
|
||||
/* fit horizontally */
|
||||
tmp2 = ((float) Android_ScreenW) / ((float) w);
|
||||
screenrect.h = tmp2 * h;
|
||||
if (screenrect.h > Android_ScreenH) {
|
||||
/* fit vertically */
|
||||
tmp2 = ((float) Android_ScreenH) / ((float) h);
|
||||
screenrect.h = Android_ScreenH;
|
||||
}
|
||||
screenrect.w = tmp2 * w;
|
||||
/* center */
|
||||
tmp = (Android_ScreenW - screenrect.r + 1) / 2;
|
||||
screenrect.l += tmp;
|
||||
h = Android_ScreenH;
|
||||
if (Android_ScreenH > Android_ScreenW)
|
||||
h >>= 1; /* assume keyboard takes up half the height in portrait */
|
||||
tmp = (h - screenrect.b + 1) / 2;
|
||||
if (tmp < 0)
|
||||
tmp = 0;
|
||||
tmp = (Android_ScreenH - h) + tmp;
|
||||
screenrect.t += tmp;
|
||||
screenclear = TRUE;
|
||||
} else {
|
||||
screenrect.t = screenrect.l = 0;
|
||||
screenrect.w = Android_ScreenW;
|
||||
screenrect.h = Android_ScreenH;
|
||||
screenclear = FALSE;
|
||||
}
|
||||
|
||||
/* Initialize palette */
|
||||
Android_PaletteUpdate();
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void Joyovl_Scale(void)
|
||||
{
|
||||
int tmp;
|
||||
|
||||
tmp = ( (Android_ScreenW > Android_ScreenH) ?
|
||||
Android_ScreenW : Android_ScreenH ) * Android_Joyscale;
|
||||
if (!Android_Paddle) {
|
||||
AndroidInput_JoyOvl.joyarea.r = AndroidInput_JoyOvl.joyarea.l + tmp;
|
||||
AndroidInput_JoyOvl.joyarea.b = AndroidInput_JoyOvl.joyarea.t + tmp;
|
||||
} else {
|
||||
if (!Android_PlanetaryDefense) {
|
||||
AndroidInput_JoyOvl.joyarea.l = Android_Joyleft ? BORDER_PCT * Android_ScreenW : Android_Split;
|
||||
AndroidInput_JoyOvl.joyarea.r = Android_Joyleft ? Android_Split : (1.0f - BORDER_PCT) * Android_ScreenW;
|
||||
AndroidInput_JoyOvl.joyarea.b = AndroidInput_JoyOvl.joyarea.t + 8 + (tmp >> 3);
|
||||
} else {
|
||||
AndroidInput_JoyOvl.joyarea.l = AndroidInput_JoyOvl.joyarea.t = 0;
|
||||
AndroidInput_JoyOvl.joyarea.r = Android_ScreenW;
|
||||
AndroidInput_JoyOvl.joyarea.b = Android_ScreenH;
|
||||
}
|
||||
}
|
||||
AndroidInput_JoyOvl.firewid = tmp >> 3;
|
||||
}
|
||||
|
||||
void Android_ConvertScreen(void)
|
||||
{
|
||||
int x, y;
|
||||
UBYTE *src, *src_line;
|
||||
UWORD *dst, *dst_line;
|
||||
#ifdef DIRTYRECT
|
||||
UBYTE *dirty, *dirty_line;
|
||||
#endif
|
||||
|
||||
#ifdef DIRTYRECT
|
||||
dirty_line = Screen_dirty + SCANLINE_START / 8;
|
||||
#endif
|
||||
src_line = ((UBYTE *) Screen_atari) + SCANLINE_START;
|
||||
dst_line = hicolor_screen;
|
||||
|
||||
for (y = 0; y < SCREEN_HEIGHT; y++) {
|
||||
#ifdef DIRTYRECT
|
||||
dirty = dirty_line;
|
||||
#else
|
||||
src = src_line;
|
||||
dst = dst_line;
|
||||
#endif
|
||||
for (x = 0; x < SCANLINE_LEN; x += 8) {
|
||||
#ifdef DIRTYRECT
|
||||
if (*dirty) {
|
||||
src = src_line + x;
|
||||
dst = dst_line + x;
|
||||
do {
|
||||
#endif
|
||||
*dst++ = palette[*src++]; *dst++ = palette[*src++];
|
||||
*dst++ = palette[*src++]; *dst++ = palette[*src++];
|
||||
*dst++ = palette[*src++]; *dst++ = palette[*src++];
|
||||
*dst++ = palette[*src++]; *dst++ = palette[*src++];
|
||||
#ifdef DIRTYRECT
|
||||
*dirty++ = 0;
|
||||
x += 8;
|
||||
} while (*dirty && x < SCANLINE_LEN);
|
||||
}
|
||||
dirty++;
|
||||
#endif
|
||||
}
|
||||
#ifdef DIRTYRECT
|
||||
dirty_line += SCREEN_WIDTH / 8;
|
||||
#endif
|
||||
src_line += SCREEN_WIDTH;
|
||||
dst_line += SCANLINE_LEN;
|
||||
}
|
||||
}
|
||||
|
||||
void Android_Render(void)
|
||||
{
|
||||
const static int crop_joy[] = {0, 0, 64, 64};
|
||||
const static int crop_fire[] = {65, 0, 16, 15};
|
||||
const static int crop_lbl[][4] = { {65, 64, 40, -9},
|
||||
{65, 24, 40, -9},
|
||||
{65, 34, 40, -9},
|
||||
{65, 44, 40, -9},
|
||||
{65, 54, 40, -9} };
|
||||
const static int crop_all[] = {0, 64, 128, -64};
|
||||
const struct RECT *r;
|
||||
const struct POINT *p;
|
||||
int i;
|
||||
|
||||
if (screenclear)
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
/* --------------------- playfield --------------------- */
|
||||
glBindTexture(GL_TEXTURE_2D, texture[TEX_SCREEN]);
|
||||
glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, SCANLINE_LEN, SCREEN_HEIGHT, GL_RGB,
|
||||
GL_UNSIGNED_SHORT_5_6_5, hicolor_screen);
|
||||
r = &screenrect;
|
||||
glDrawTexiOES(r->l, r->t, 0, r->w, r->h);
|
||||
if (glGetError() != GL_NO_ERROR) Log_print("OpenGL error at playfield");
|
||||
|
||||
/* --------------------- overlays --------------------- */
|
||||
glEnable(GL_BLEND); /* enable blending */
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_BLEND);
|
||||
glBindTexture(GL_TEXTURE_2D, texture[TEX_OVL]);
|
||||
|
||||
if (!AndroidInput_JoyOvl.ovl_visible) goto ck; /*!*/
|
||||
|
||||
/* joystick area */
|
||||
glColor4f(1.0f, 1.0f, 1.0f, AndroidInput_JoyOvl.areaopacitycur);
|
||||
glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, crop_joy);
|
||||
r = &AndroidInput_JoyOvl.joyarea;
|
||||
glDrawTexiOES(r->l, Android_ScreenH - r->b, 0, r->r - r->l, r->b - r->t);
|
||||
if (glGetError() != GL_NO_ERROR) Log_print("OpenGL error at joy area");
|
||||
|
||||
/* stick */
|
||||
if (AndroidInput_JoyOvl.stickopacity >= OPACITY_CUTOFF) {
|
||||
glColor4f(1.0f, 1.0f, 1.0f, AndroidInput_JoyOvl.stickopacity);
|
||||
glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, crop_fire);
|
||||
p = &AndroidInput_JoyOvl.joystick;
|
||||
i = AndroidInput_JoyOvl.firewid;
|
||||
glDrawTexiOES(p->x - i, Android_ScreenH - (p->y + i), 0, i << 1, i << 1);
|
||||
if (glGetError() != GL_NO_ERROR) Log_print("OpenGL error at stick");
|
||||
}
|
||||
|
||||
/* fire */
|
||||
if (AndroidInput_JoyOvl.fireopacity >= OPACITY_CUTOFF) {
|
||||
glColor4f(1.0f, 1.0f, 1.0f, AndroidInput_JoyOvl.fireopacity);
|
||||
glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, crop_fire);
|
||||
p = &AndroidInput_JoyOvl.fire;
|
||||
i = AndroidInput_JoyOvl.firewid;
|
||||
glDrawTexiOES(p->x - i, Android_ScreenH - (p->y + i), 0, i << 1, i << 1);
|
||||
if (glGetError() != GL_NO_ERROR) Log_print("OpenGL error at fire");
|
||||
}
|
||||
/* glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, crop_all);
|
||||
glDrawTexiOES(0, 0, 0, 128, 64);
|
||||
*/
|
||||
|
||||
/* console keys */
|
||||
ck: if (AndroidInput_ConOvl.ovl_visible) {
|
||||
glDisable(GL_TEXTURE_2D); /* disable texturing */
|
||||
|
||||
glColor4f(0.0f, 0.0f, 0.0f, AndroidInput_ConOvl.opacity * 0.7);
|
||||
glVertexPointer(2, GL_SHORT, 0, conkey_shadow);
|
||||
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
|
||||
|
||||
glColor4f(0.5f, 0.9f, 1.0f, AndroidInput_ConOvl.opacity);
|
||||
glVertexPointer(2, GL_SHORT, 0, conkey_vrt);
|
||||
for (i = 0; i < (CONK_VERT_MAX >> 1); i += 4)
|
||||
glDrawArrays(GL_LINE_LOOP, i, 4);
|
||||
if (AndroidInput_ConOvl.hitkey >= 0) {
|
||||
glColor4f(0.34f, 0.67f, 1.0f, AndroidInput_ConOvl.opacity);
|
||||
glDrawArrays(GL_TRIANGLE_FAN, AndroidInput_ConOvl.hitkey << 2, 4);
|
||||
}
|
||||
glEnable(GL_TEXTURE_2D); /* enable texturing */
|
||||
|
||||
glColor4f(1.0f, 1.0f, 1.0f, AndroidInput_ConOvl.opacity);
|
||||
for (i = 0; i < CONK_VERT_MAX >> 2; i += 2) {
|
||||
glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, crop_lbl[i >> 1]);
|
||||
glDrawTexiOES(conkey_lbl[i], conkey_lbl[i + 1], 0, 40, 9);
|
||||
}
|
||||
if (glGetError() != GL_NO_ERROR) Log_print("OpenGL error at console overlay");
|
||||
}
|
||||
|
||||
glDisable(GL_BLEND); /* disable blending */
|
||||
}
|
||||
|
||||
void Update_Overlays(void)
|
||||
{
|
||||
struct joy_overlay_state *s;
|
||||
struct consolekey_overlay_state *c;
|
||||
|
||||
s = &AndroidInput_JoyOvl;
|
||||
c = &AndroidInput_ConOvl;
|
||||
|
||||
/* joy et co. */
|
||||
if (s->fireopacity > OPACITY_CUTOFF)
|
||||
s->fireopacity -= 0.05f;
|
||||
else
|
||||
s->fireopacity = 0.0f;
|
||||
if (s->stickopacity > OPACITY_CUTOFF) {
|
||||
s->stickopacity -= 0.05f;
|
||||
s->areaopacityfrm = 0;
|
||||
s->areaopacitycur = s->areaopacityset;
|
||||
} else {
|
||||
s->stickopacity = 0.0f;
|
||||
s->areaopacityfrm++;
|
||||
if (s->areaopacityfrm > OPACITY_FRMSTR) {
|
||||
if (s->areaopacitycur > OPACITY_CUTOFF)
|
||||
s->areaopacitycur -= OPACITY_STEP;
|
||||
else {
|
||||
s->areaopacitycur = OPACITY_CUTOFF;
|
||||
s->areaopacityfrm--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* console keys */
|
||||
switch (c->ovl_visible) {
|
||||
case COVL_READY:
|
||||
if (c->hitkey == CONK_NOKEY)
|
||||
if (!c->statecnt--)
|
||||
c->ovl_visible = COVL_FADEOUT;
|
||||
break;
|
||||
case COVL_FADEOUT:
|
||||
if (c->opacity > OPACITY_CUTOFF)
|
||||
c->opacity -= 2 * OPACITY_STEP;
|
||||
else {
|
||||
c->ovl_visible = COVL_HIDDEN;
|
||||
c->opacity = 0.0f;
|
||||
}
|
||||
break;
|
||||
case COVL_FADEIN:
|
||||
if (c->opacity < COVL_MAX_OPACITY)
|
||||
c->opacity += 4 * OPACITY_STEP;
|
||||
else {
|
||||
c->ovl_visible = COVL_READY;
|
||||
c->opacity = COVL_MAX_OPACITY;
|
||||
c->statecnt = COVL_HOLD_TIME;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (c->hitkey == CONK_RESET) {
|
||||
if (c->resetcnt >= RESET_HARD) {
|
||||
Atari800_Coldstart();
|
||||
} else if (c->resetcnt >= RESET_SOFT) {
|
||||
Atari800_Warmstart();
|
||||
CPU_cim_encountered = FALSE;
|
||||
}
|
||||
c->resetcnt++;
|
||||
} else {
|
||||
c->resetcnt = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void Android_ExitGraphics(void)
|
||||
{
|
||||
if (hicolor_screen)
|
||||
free(hicolor_screen);
|
||||
hicolor_screen = NULL;
|
||||
|
||||
if (palette)
|
||||
free(palette);
|
||||
palette = NULL;
|
||||
|
||||
glDeleteTextures(TEX_MAXNAMES, texture);
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
#define CONK_VERT_MAX (2 * 4 * 5)
|
||||
#define COVL_SHADOW_OFF 10
|
||||
|
||||
#define SCREEN_WIDTH 384
|
||||
#define SCREEN_HEIGHT 240
|
||||
#define DEAD_WIDTH 48
|
||||
#define SCANLINE_START (DEAD_WIDTH / 2)
|
||||
#define SCANLINE_END (SCREEN_WIDTH - DEAD_WIDTH / 2)
|
||||
#define SCANLINE_LEN (SCREEN_WIDTH - DEAD_WIDTH)
|
||||
|
||||
extern int Android_ScreenW;
|
||||
extern int Android_ScreenH;
|
||||
extern int Android_Aspect;
|
||||
extern int Android_Bilinear;
|
||||
extern int Android_CropScreen[];
|
||||
extern float Android_Joyscale;
|
||||
|
||||
int Android_InitGraphics(void);
|
||||
void Android_ExitGraphics(void);
|
||||
|
||||
void Android_ConvertScreen(void);
|
||||
void Android_PaletteUpdate(void);
|
||||
void Android_Render(void);
|
||||
void Update_Overlays(void);
|
||||
void Joyovl_Scale(void);
|
||||
@@ -0,0 +1,722 @@
|
||||
/*
|
||||
* jni.c - native functions exported to java
|
||||
*
|
||||
* Copyright (C) 2014 Kostas Nakos
|
||||
* Copyright (C) 2014 Atari800 development team (see DOC/CREDITS)
|
||||
*
|
||||
* This file is part of the Atari800 emulator project which emulates
|
||||
* the Atari 400, 800, 800XL, 130XE, and 5200 8-bit computers.
|
||||
*
|
||||
* Atari800 is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Atari800 is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Atari800; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <pthread.h>
|
||||
#include <jni.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "log.h"
|
||||
#include "atari.h"
|
||||
#include "input.h"
|
||||
#include "afile.h"
|
||||
#include "screen.h"
|
||||
#include "cpu.h"
|
||||
#include "antic.h"
|
||||
#include "../../memory.h" /* override system header */
|
||||
#include "sio.h"
|
||||
#include "sysrom.h"
|
||||
#include "akey.h"
|
||||
#include "devices.h"
|
||||
#include "cartridge.h"
|
||||
|
||||
#include "graphics.h"
|
||||
#include "androidinput.h"
|
||||
|
||||
#define PD2012_FNAME "PD2012.com"
|
||||
|
||||
/* exports/imports */
|
||||
int *ovl_texpix;
|
||||
int ovl_texw;
|
||||
int ovl_texh;
|
||||
extern void SoundThread_Update(void *buf, int offs, int len);
|
||||
extern void Android_SoundInit(int rate, int sizems, int bit16, int hq, int disableOSL);
|
||||
extern void Sound_Exit(void);
|
||||
extern void Sound_Pause(void);
|
||||
extern void Sound_Continue(void);
|
||||
extern int Android_osl_sound;
|
||||
|
||||
struct audiothread {
|
||||
UBYTE *sndbuf;
|
||||
jbyteArray sndarray;
|
||||
};
|
||||
static pthread_key_t audiothread_data;
|
||||
|
||||
static char devb_url[512];
|
||||
|
||||
|
||||
static void JNICALL NativeGetOverlays(JNIEnv *env, jobject this)
|
||||
{
|
||||
jclass cls;
|
||||
jfieldID fid;
|
||||
jintArray arr;
|
||||
jboolean cp;
|
||||
|
||||
cls = (*env)->GetObjectClass(env, this);
|
||||
|
||||
fid = (*env)->GetFieldID(env, cls, "OVL_TEXW", "I");
|
||||
ovl_texw = (*env)->GetIntField(env, this, fid);
|
||||
|
||||
fid = (*env)->GetFieldID(env, cls, "OVL_TEXH", "I");
|
||||
ovl_texh = (*env)->GetIntField(env, this, fid);
|
||||
|
||||
ovl_texpix = malloc(ovl_texw * ovl_texh * sizeof(int));
|
||||
if (ovl_texpix == NULL) Log_print("Cannot allocate memory for overlays");
|
||||
|
||||
fid = (*env)->GetFieldID(env, cls, "_pix", "[I");
|
||||
arr = (*env)->GetObjectField(env, this, fid);
|
||||
(*env)->GetIntArrayRegion(env, arr, 0, ovl_texw * ovl_texh, ovl_texpix);
|
||||
|
||||
Log_print("Overlays texture initialized: %dx%d", ovl_texw, ovl_texh);
|
||||
}
|
||||
|
||||
static void JNICALL NativeResize(JNIEnv *env, jobject this, jint w, jint h)
|
||||
{
|
||||
Log_print("Screen resize: %dx%d", w, h);
|
||||
Android_ScreenW = w;
|
||||
Android_ScreenH = h;
|
||||
Android_SplitCalc();
|
||||
Android_InitGraphics();
|
||||
}
|
||||
|
||||
static void JNICALL NativeClearDevB(JNIEnv *env, jobject this)
|
||||
{
|
||||
dev_b_status.ready = FALSE;
|
||||
memset(devb_url, 0, sizeof(devb_url));
|
||||
}
|
||||
|
||||
static jstring JNICALL NativeInit(JNIEnv *env, jobject this)
|
||||
{
|
||||
int ac = 1;
|
||||
char av = '\0';
|
||||
char *avp = &av;
|
||||
|
||||
pthread_key_create(&audiothread_data, NULL);
|
||||
pthread_setspecific(audiothread_data, NULL);
|
||||
|
||||
NativeClearDevB(env, this);
|
||||
|
||||
Atari800_Initialise(&ac, &avp);
|
||||
|
||||
return (*env)->NewStringUTF(env, Atari800_TITLE);
|
||||
}
|
||||
|
||||
static jobjectArray JNICALL NativeGetDrvFnames(JNIEnv *env, jobject this)
|
||||
{
|
||||
jobjectArray arr;
|
||||
int i;
|
||||
char tmp[FILENAME_MAX + 3], fname[FILENAME_MAX];
|
||||
jstring str;
|
||||
|
||||
arr = (*env)->NewObjectArray(env, 4, (*env)->FindClass(env, "java/lang/String"), NULL);
|
||||
for (i = 0; i < 4; i++) {
|
||||
Util_splitpath(SIO_filename[i], NULL, fname);
|
||||
sprintf(tmp, "D%d:%s", i + 1, fname);
|
||||
str = (*env)->NewStringUTF(env, tmp);
|
||||
(*env)->SetObjectArrayElement(env, arr, i, str);
|
||||
(*env)->DeleteLocalRef(env, str);
|
||||
}
|
||||
|
||||
return arr;
|
||||
}
|
||||
|
||||
static void JNICALL NativeUnmountAll(JNIEnv *env, jobject this)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 1; i <= 4; i++)
|
||||
SIO_DisableDrive(i);
|
||||
}
|
||||
|
||||
static jboolean JNICALL NativeIsDisk(JNIEnv *env, jobject this, jstring img)
|
||||
{
|
||||
const jbyte *img_utf = NULL;
|
||||
int type;
|
||||
|
||||
img_utf = (*env)->GetStringUTFChars(env, img, NULL);
|
||||
type = AFILE_DetectFileType(img_utf);
|
||||
(*env)->ReleaseStringUTFChars(env, img, img_utf);
|
||||
switch (type) {
|
||||
case AFILE_ATR:
|
||||
case AFILE_ATX:
|
||||
case AFILE_XFD:
|
||||
case AFILE_ATR_GZ:
|
||||
case AFILE_XFD_GZ:
|
||||
case AFILE_DCM:
|
||||
case AFILE_PRO:
|
||||
return JNI_TRUE;
|
||||
default:
|
||||
return JNI_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static jboolean JNICALL NativeSaveState(JNIEnv *env, jobject this, jstring fname)
|
||||
{
|
||||
const jbyte *fname_utf = NULL;
|
||||
int ret;
|
||||
|
||||
fname_utf = (*env)->GetStringUTFChars(env, fname, NULL);
|
||||
ret = StateSav_SaveAtariState(fname_utf, "wb", TRUE);
|
||||
Log_print("Saved state %s with return %d", fname_utf, ret);
|
||||
(*env)->ReleaseStringUTFChars(env, fname, fname_utf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static jint JNICALL NativeRunAtariProgram(JNIEnv *env, jobject this,
|
||||
jstring img, jint drv, jint reboot)
|
||||
{
|
||||
static char const * const cart_descriptions[CARTRIDGE_LAST_SUPPORTED + 1] = {
|
||||
NULL,
|
||||
CARTRIDGE_STD_8_DESC,
|
||||
CARTRIDGE_STD_16_DESC,
|
||||
CARTRIDGE_OSS_034M_16_DESC,
|
||||
CARTRIDGE_5200_32_DESC,
|
||||
CARTRIDGE_DB_32_DESC,
|
||||
CARTRIDGE_5200_EE_16_DESC,
|
||||
CARTRIDGE_5200_40_DESC,
|
||||
CARTRIDGE_WILL_64_DESC,
|
||||
CARTRIDGE_EXP_64_DESC,
|
||||
CARTRIDGE_DIAMOND_64_DESC,
|
||||
CARTRIDGE_SDX_64_DESC,
|
||||
CARTRIDGE_XEGS_32_DESC,
|
||||
CARTRIDGE_XEGS_07_64_DESC,
|
||||
CARTRIDGE_XEGS_128_DESC,
|
||||
CARTRIDGE_OSS_M091_16_DESC,
|
||||
CARTRIDGE_5200_NS_16_DESC,
|
||||
CARTRIDGE_ATRAX_128_DESC,
|
||||
CARTRIDGE_BBSB_40_DESC,
|
||||
CARTRIDGE_5200_8_DESC,
|
||||
CARTRIDGE_5200_4_DESC,
|
||||
CARTRIDGE_RIGHT_8_DESC,
|
||||
CARTRIDGE_WILL_32_DESC,
|
||||
CARTRIDGE_XEGS_256_DESC,
|
||||
CARTRIDGE_XEGS_512_DESC,
|
||||
CARTRIDGE_XEGS_1024_DESC,
|
||||
CARTRIDGE_MEGA_16_DESC,
|
||||
CARTRIDGE_MEGA_32_DESC,
|
||||
CARTRIDGE_MEGA_64_DESC,
|
||||
CARTRIDGE_MEGA_128_DESC,
|
||||
CARTRIDGE_MEGA_256_DESC,
|
||||
CARTRIDGE_MEGA_512_DESC,
|
||||
CARTRIDGE_MEGA_1024_DESC,
|
||||
CARTRIDGE_SWXEGS_32_DESC,
|
||||
CARTRIDGE_SWXEGS_64_DESC,
|
||||
CARTRIDGE_SWXEGS_128_DESC,
|
||||
CARTRIDGE_SWXEGS_256_DESC,
|
||||
CARTRIDGE_SWXEGS_512_DESC,
|
||||
CARTRIDGE_SWXEGS_1024_DESC,
|
||||
CARTRIDGE_PHOENIX_8_DESC,
|
||||
CARTRIDGE_BLIZZARD_16_DESC,
|
||||
CARTRIDGE_ATMAX_128_DESC,
|
||||
CARTRIDGE_ATMAX_1024_DESC,
|
||||
CARTRIDGE_SDX_128_DESC,
|
||||
CARTRIDGE_OSS_8_DESC,
|
||||
CARTRIDGE_OSS_043M_16_DESC,
|
||||
CARTRIDGE_BLIZZARD_4_DESC,
|
||||
CARTRIDGE_AST_32_DESC,
|
||||
CARTRIDGE_ATRAX_SDX_64_DESC,
|
||||
CARTRIDGE_ATRAX_SDX_128_DESC,
|
||||
CARTRIDGE_TURBOSOFT_64_DESC,
|
||||
CARTRIDGE_TURBOSOFT_128_DESC,
|
||||
CARTRIDGE_ULTRACART_32_DESC,
|
||||
CARTRIDGE_LOW_BANK_8_DESC,
|
||||
CARTRIDGE_SIC_128_DESC,
|
||||
CARTRIDGE_SIC_256_DESC,
|
||||
CARTRIDGE_SIC_512_DESC,
|
||||
CARTRIDGE_STD_2_DESC,
|
||||
CARTRIDGE_STD_4_DESC,
|
||||
CARTRIDGE_RIGHT_4_DESC,
|
||||
CARTRIDGE_BLIZZARD_32_DESC,
|
||||
CARTRIDGE_MEGAMAX_2048_DESC,
|
||||
CARTRIDGE_THECART_128M_DESC,
|
||||
CARTRIDGE_MEGA_4096_DESC,
|
||||
CARTRIDGE_MEGA_2048_DESC,
|
||||
CARTRIDGE_THECART_32M_DESC,
|
||||
CARTRIDGE_THECART_64M_DESC,
|
||||
CARTRIDGE_XEGS_8F_64_DESC
|
||||
};
|
||||
|
||||
const jbyte *img_utf = NULL;
|
||||
int ret = 0, r, kb, i, cnt = 0;
|
||||
jclass cls, scls;
|
||||
jfieldID fid;
|
||||
jobjectArray arr, xarr;
|
||||
jstring str;
|
||||
char tmp[128];
|
||||
|
||||
if (reboot) {
|
||||
NativeUnmountAll(env, this);
|
||||
CARTRIDGE_Remove();
|
||||
}
|
||||
|
||||
img_utf = (*env)->GetStringUTFChars(env, img, NULL);
|
||||
r = AFILE_OpenFile(img_utf, reboot, drv, FALSE);
|
||||
if ((r & 0xFF) == AFILE_ROM && (r >> 8) != 0) {
|
||||
kb = r >> 8;
|
||||
scls = (*env)->FindClass(env, "java/lang/String");
|
||||
cls = (*env)->GetObjectClass(env, this);
|
||||
fid = (*env)->GetFieldID(env, cls, "_cartTypes", "[[Ljava/lang/String;");
|
||||
for (i = 1; i <= CARTRIDGE_LAST_SUPPORTED; i++)
|
||||
if (CARTRIDGE_kb[i] == kb) cnt++;
|
||||
xarr = (*env)->NewObjectArray(env, 2, scls, NULL);
|
||||
arr = (*env)->NewObjectArray(env, cnt, (*env)->GetObjectClass(env, xarr), NULL);
|
||||
for (cnt = 0, i = 1; i <= CARTRIDGE_LAST_SUPPORTED; i++)
|
||||
if (CARTRIDGE_kb[i] == kb) {
|
||||
sprintf(tmp, "%d", i);
|
||||
str = (*env)->NewStringUTF(env, tmp);
|
||||
(*env)->SetObjectArrayElement(env, xarr, 0, str);
|
||||
(*env)->DeleteLocalRef(env, str);
|
||||
str = (*env)->NewStringUTF(env, cart_descriptions[i]);
|
||||
(*env)->SetObjectArrayElement(env, xarr, 1, str);
|
||||
(*env)->DeleteLocalRef(env, str);
|
||||
(*env)->SetObjectArrayElement(env, arr, cnt++, xarr);
|
||||
(*env)->DeleteLocalRef(env, xarr);
|
||||
xarr = (*env)->NewObjectArray(env, 2, scls, NULL);
|
||||
}
|
||||
(*env)->SetObjectField(env, this, fid, arr);
|
||||
ret = -2;
|
||||
} else if (r == AFILE_ERROR) {
|
||||
Log_print("Cannot start image: %s", img_utf);
|
||||
ret = -1;
|
||||
} else
|
||||
CPU_cim_encountered = FALSE;
|
||||
|
||||
(*env)->ReleaseStringUTFChars(env, img, img_utf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void JNICALL NativeBootCartType(JNIEnv *env, jobject this, jint kb)
|
||||
{
|
||||
CARTRIDGE_SetTypeAutoReboot(&CARTRIDGE_main, kb);
|
||||
Atari800_Coldstart();
|
||||
}
|
||||
|
||||
static void JNICALL NativeExit(JNIEnv *env, jobject this)
|
||||
{
|
||||
Atari800_Exit(FALSE);
|
||||
}
|
||||
|
||||
static jint JNICALL NativeRunFrame(JNIEnv *env, jobject this)
|
||||
{
|
||||
static int old_cim = FALSE;
|
||||
int ret = 0;
|
||||
|
||||
do {
|
||||
INPUT_key_code = PLATFORM_Keyboard();
|
||||
|
||||
if (!CPU_cim_encountered)
|
||||
Atari800_Frame();
|
||||
else
|
||||
Atari800_display_screen = TRUE;
|
||||
|
||||
if (Atari800_display_screen || CPU_cim_encountered)
|
||||
PLATFORM_DisplayScreen();
|
||||
|
||||
if (!old_cim && CPU_cim_encountered)
|
||||
ret = 1;
|
||||
|
||||
old_cim = CPU_cim_encountered;
|
||||
} while (!Atari800_display_screen);
|
||||
|
||||
if (dev_b_status.ready && devb_url[0] == '\0')
|
||||
if (strlen(dev_b_status.url)) {
|
||||
strncpy(devb_url, dev_b_status.url, sizeof(devb_url));
|
||||
Log_print("Received b: device URL: %s", devb_url);
|
||||
ret |= 2;
|
||||
} else
|
||||
Log_print("Device b: signalled with zero-length url");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void JNICALL NativeSoundInit(JNIEnv *env, jobject this, jint size)
|
||||
{
|
||||
jclass cls;
|
||||
jfieldID fid;
|
||||
jintArray arr;
|
||||
struct audiothread *at;
|
||||
|
||||
Log_print("Audio init with buffer size %d", size);
|
||||
|
||||
if (pthread_getspecific(audiothread_data))
|
||||
Log_print("Audiothread data already allocated for current thread");
|
||||
at = (struct audiothread *) malloc(sizeof(struct audiothread));
|
||||
|
||||
cls = (*env)->GetObjectClass(env, this);
|
||||
fid = (*env)->GetFieldID(env, cls, "_buffer", "[B");
|
||||
arr = (*env)->GetObjectField(env, this, fid);
|
||||
at->sndarray = (*env)->NewGlobalRef(env, arr);
|
||||
|
||||
at->sndbuf = malloc(size);
|
||||
if (!at->sndbuf) Log_print("Cannot allocate memory for sound buffer");
|
||||
|
||||
pthread_setspecific(audiothread_data, at);
|
||||
}
|
||||
|
||||
static void JNICALL NativeSoundUpdate(JNIEnv *env, jobject this, jint offset, jint length)
|
||||
{
|
||||
struct audiothread *at;
|
||||
|
||||
if ( !(at = (struct audiothread *) pthread_getspecific(audiothread_data)) )
|
||||
return;
|
||||
SoundThread_Update(at->sndbuf, offset, length);
|
||||
(*env)->SetByteArrayRegion(env, at->sndarray, offset, length, at->sndbuf + offset);
|
||||
}
|
||||
|
||||
static void JNICALL NativeSoundExit(JNIEnv *env, jobject this)
|
||||
{
|
||||
struct audiothread *at;
|
||||
|
||||
Log_print("Audio exit");
|
||||
|
||||
if ( !(at = (struct audiothread *) pthread_getspecific(audiothread_data)) )
|
||||
return;
|
||||
|
||||
(*env)->DeleteGlobalRef(env, at->sndarray);
|
||||
if (at->sndbuf)
|
||||
free(at->sndbuf);
|
||||
|
||||
free(at);
|
||||
pthread_setspecific(audiothread_data, NULL);
|
||||
}
|
||||
|
||||
static void JNICALL NativeKey(JNIEnv *env, jobject this, int k, int s)
|
||||
{
|
||||
Android_KeyEvent(k, s);
|
||||
}
|
||||
|
||||
static int JNICALL NativeTouch(JNIEnv *env, jobject this, int x1, int y1, int s1,
|
||||
int x2, int y2, int s2)
|
||||
{
|
||||
return Android_TouchEvent(x1, y1, s1, x2, y2, s2);
|
||||
}
|
||||
|
||||
|
||||
static void JNICALL NativePrefGfx(JNIEnv *env, jobject this, int aspect, jboolean bilinear,
|
||||
int artifact, int frameskip, jboolean collisions, int crophoriz,
|
||||
int cropvert)
|
||||
{
|
||||
Android_Aspect = aspect;
|
||||
Android_Bilinear = bilinear;
|
||||
ANTIC_artif_mode = artifact;
|
||||
ANTIC_UpdateArtifacting();
|
||||
if (frameskip == 0) {
|
||||
Atari800_refresh_rate = 1;
|
||||
Atari800_auto_frameskip = TRUE;
|
||||
} else {
|
||||
Atari800_auto_frameskip = FALSE;
|
||||
Atari800_refresh_rate = frameskip;
|
||||
}
|
||||
Atari800_collisions_in_skipped_frames = collisions;
|
||||
Android_CropScreen[0] = (SCANLINE_LEN - crophoriz) / 2;
|
||||
Android_CropScreen[2] = crophoriz;
|
||||
Android_CropScreen[1] = SCREEN_HEIGHT - (SCREEN_HEIGHT - cropvert) / 2;
|
||||
Android_CropScreen[3] = -cropvert;
|
||||
Screen_visible_x1 = SCANLINE_START + Android_CropScreen[0];
|
||||
Screen_visible_x2 = Screen_visible_x1 + crophoriz;
|
||||
Screen_visible_y1 = SCREEN_HEIGHT - Android_CropScreen[1];
|
||||
Screen_visible_y2 = Screen_visible_y1 + cropvert;
|
||||
}
|
||||
|
||||
static jboolean JNICALL NativePrefMachine(JNIEnv *env, jobject this, int nummac, jboolean ntsc)
|
||||
{
|
||||
struct tSysConfig {
|
||||
int type;
|
||||
int ram;
|
||||
};
|
||||
static const struct tSysConfig machine[] = {
|
||||
{ Atari800_MACHINE_800, 16 },
|
||||
{ Atari800_MACHINE_800, 48 },
|
||||
{ Atari800_MACHINE_800, 52 },
|
||||
{ Atari800_MACHINE_800, 16 },
|
||||
{ Atari800_MACHINE_800, 48 },
|
||||
{ Atari800_MACHINE_800, 52 },
|
||||
{ Atari800_MACHINE_XLXE, 16 },
|
||||
{ Atari800_MACHINE_XLXE, 64 },
|
||||
{ Atari800_MACHINE_XLXE, 128 },
|
||||
{ Atari800_MACHINE_XLXE, 192 },
|
||||
{ Atari800_MACHINE_XLXE, MEMORY_RAM_320_RAMBO },
|
||||
{ Atari800_MACHINE_XLXE, MEMORY_RAM_320_COMPY_SHOP },
|
||||
{ Atari800_MACHINE_XLXE, 576 },
|
||||
{ Atari800_MACHINE_XLXE, 1088 },
|
||||
{ Atari800_MACHINE_5200, 16 }
|
||||
};
|
||||
|
||||
Atari800_SetMachineType(machine[nummac].type);
|
||||
MEMORY_ram_size = machine[nummac].ram;
|
||||
/* Temporary hack to allow choosing OS rev. A/B and XL/XE features.
|
||||
Delete after adding proper support for choosing system settings. */
|
||||
if (nummac < 3)
|
||||
SYSROM_os_versions[Atari800_MACHINE_800] = ntsc ? SYSROM_A_NTSC : SYSROM_A_PAL;
|
||||
else if (nummac >= 3 && nummac < 6)
|
||||
/* If no OSB NTSC ROM present, try the "custom" 400/800 ROM. */
|
||||
SYSROM_os_versions[Atari800_MACHINE_800] =
|
||||
SYSROM_roms[SYSROM_B_NTSC].filename[0] == '\0' ?
|
||||
SYSROM_800_CUSTOM :
|
||||
SYSROM_B_NTSC;
|
||||
else if (Atari800_machine_type == Atari800_MACHINE_XLXE) {
|
||||
Atari800_builtin_basic = TRUE;
|
||||
Atari800_keyboard_leds = FALSE;
|
||||
Atari800_f_keys = FALSE;
|
||||
Atari800_jumper = FALSE;
|
||||
Atari800_builtin_game = FALSE;
|
||||
Atari800_keyboard_detached = FALSE;
|
||||
}
|
||||
/* End of hack */
|
||||
|
||||
Atari800_SetTVMode(ntsc ? Atari800_TV_NTSC : Atari800_TV_PAL);
|
||||
CPU_cim_encountered = FALSE;
|
||||
return Atari800_InitialiseMachine();
|
||||
}
|
||||
|
||||
static void JNICALL NativePrefEmulation(JNIEnv *env, jobject this, jboolean basic, jboolean speed,
|
||||
jboolean disk, jboolean sector, jboolean browser)
|
||||
{
|
||||
Atari800_disable_basic = basic;
|
||||
Screen_show_atari_speed = speed;
|
||||
Screen_show_disk_led = disk;
|
||||
Screen_show_sector_counter = sector;
|
||||
Devices_enable_b_patch = browser;
|
||||
Devices_UpdatePatches();
|
||||
}
|
||||
|
||||
static void JNICALL NativePrefSoftjoy(JNIEnv *env, jobject this, jboolean softjoy, int up, int down,
|
||||
int left, int right, int fire, int derotkeys, jobjectArray actions)
|
||||
{
|
||||
int i;
|
||||
jobject obj;
|
||||
const char *str;
|
||||
char *sep;
|
||||
UBYTE act, akey;
|
||||
|
||||
Android_SoftjoyEnable = softjoy;
|
||||
softjoymap[SOFTJOY_UP][0] = up;
|
||||
softjoymap[SOFTJOY_DOWN][0] = down;
|
||||
softjoymap[SOFTJOY_LEFT][0] = left;
|
||||
softjoymap[SOFTJOY_RIGHT][0] = right;
|
||||
softjoymap[SOFTJOY_FIRE][0] = fire;
|
||||
Android_DerotateKeys = derotkeys;
|
||||
|
||||
for (i = 0; i < SOFTJOY_MAXACTIONS; i++) {
|
||||
obj = (*env)->GetObjectArrayElement(env, actions, i);
|
||||
str = (*env)->GetStringUTFChars(env, obj, NULL);
|
||||
sep = strchr(str, ',');
|
||||
act = ACTION_NONE;
|
||||
akey = AKEY_NONE;
|
||||
if (sep) {
|
||||
act = atoi(str);
|
||||
akey = atoi(sep + 1);
|
||||
}
|
||||
softjoymap[SOFTJOY_ACTIONBASE + i][0] = act;
|
||||
softjoymap[SOFTJOY_ACTIONBASE + i][1] = akey;
|
||||
(*env)->ReleaseStringUTFChars(env, obj, str);
|
||||
(*env)->DeleteLocalRef(env, obj);
|
||||
}
|
||||
}
|
||||
|
||||
static void config_PD(void)
|
||||
{
|
||||
INPUT_mouse_mode = INPUT_MOUSE_PAD;
|
||||
Android_Splitpct = 1.0f;
|
||||
AndroidInput_JoyOvl.ovl_visible = FALSE;
|
||||
Android_PlanetaryDefense = TRUE;
|
||||
Android_Paddle = TRUE;
|
||||
Android_ReversePddle = 3;
|
||||
}
|
||||
|
||||
static void JNICALL NativePrefJoy(JNIEnv *env, jobject this, jboolean visible, int size, int opacity,
|
||||
jboolean righth, int deadband, jboolean midx, int anchor, int anchorx,
|
||||
int anchory, int grace, jboolean paddle, jboolean plandef)
|
||||
{
|
||||
AndroidInput_JoyOvl.ovl_visible = visible;
|
||||
AndroidInput_JoyOvl.areaopacityset = 0.01f * opacity;
|
||||
Android_Joyscale = 0.01f * size;
|
||||
Android_Joyleft = !righth;
|
||||
Android_Splitpct = 0.01f * midx;
|
||||
AndroidInput_JoyOvl.deadarea = 0.01f * deadband;
|
||||
AndroidInput_JoyOvl.gracearea = 0.02f * grace;
|
||||
AndroidInput_JoyOvl.anchor = anchor;
|
||||
if (anchor) {
|
||||
AndroidInput_JoyOvl.joyarea.l = anchorx;
|
||||
AndroidInput_JoyOvl.joyarea.t = anchory;
|
||||
}
|
||||
Android_Paddle = paddle;
|
||||
INPUT_mouse_mode = paddle ? INPUT_MOUSE_PAD : INPUT_MOUSE_OFF;
|
||||
Android_PlanetaryDefense = FALSE;
|
||||
Android_ReversePddle = 0;
|
||||
if (plandef)
|
||||
config_PD();
|
||||
|
||||
Android_SplitCalc();
|
||||
Joyovl_Scale();
|
||||
Joy_Reposition();
|
||||
}
|
||||
|
||||
static void JNICALL NativePrefSound(JNIEnv *env, jobject this, int mixrate, int bufsizems,
|
||||
jboolean sound16bit, jboolean hqpokey, jboolean disableOSL)
|
||||
{
|
||||
Android_SoundInit(mixrate, bufsizems, sound16bit, hqpokey, disableOSL);
|
||||
}
|
||||
|
||||
static jboolean JNICALL NativeSetROMPath(JNIEnv *env, jobject this, jstring path)
|
||||
{
|
||||
const jbyte *utf = NULL;
|
||||
jboolean ret = JNI_FALSE;
|
||||
|
||||
utf = (*env)->GetStringUTFChars(env, path, NULL);
|
||||
SYSROM_FindInDir(utf, FALSE);
|
||||
Log_print("sysrom %s %d", utf, SYSROM_FindInDir(utf, FALSE));
|
||||
ret |= chdir(utf);
|
||||
Log_print("sysrom %s %d", utf, SYSROM_FindInDir(utf, FALSE));
|
||||
ret |= Atari800_InitialiseMachine();
|
||||
(*env)->ReleaseStringUTFChars(env, path, utf);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static jstring JNICALL NativeGetJoypos(JNIEnv *env, jobject this)
|
||||
{
|
||||
char tmp[16];
|
||||
sprintf(tmp, "%d %d", AndroidInput_JoyOvl.joyarea.l, AndroidInput_JoyOvl.joyarea.t);
|
||||
return (*env)->NewStringUTF(env, tmp);
|
||||
}
|
||||
|
||||
static jstring JNICALL NativeGetURL(JNIEnv *env, jobject this)
|
||||
{
|
||||
return (*env)->NewStringUTF(env, dev_b_status.url);
|
||||
}
|
||||
|
||||
static jboolean JNICALL NativeBootPD(JNIEnv *env, jobject this, jobjectArray img, jint sz)
|
||||
{
|
||||
FILE *fp;
|
||||
void *src;
|
||||
|
||||
fp = fopen(PD2012_FNAME, "wb");
|
||||
if (!fp) {
|
||||
Log_print("ERROR: Cannot open PD2012 for write");
|
||||
return FALSE;
|
||||
}
|
||||
src = (*env)->GetByteArrayElements(env, img, NULL);
|
||||
fwrite(src, 1, sz, fp);
|
||||
fclose(fp);
|
||||
(*env)->ReleaseByteArrayElements(env, img, src, JNI_ABORT);
|
||||
|
||||
config_PD();
|
||||
NativeUnmountAll(env, this);
|
||||
CARTRIDGE_Remove();
|
||||
return AFILE_OpenFile(PD2012_FNAME, TRUE, 1, FALSE);
|
||||
}
|
||||
|
||||
static jboolean JNICALL NativeOSLSound(JNIEnv *env, jobject this)
|
||||
{
|
||||
return Android_osl_sound;
|
||||
}
|
||||
|
||||
static jboolean JNICALL NativeOSLSoundPause(JNIEnv *env, jobject this, jboolean pause)
|
||||
{
|
||||
if (pause)
|
||||
Sound_Pause();
|
||||
else
|
||||
Sound_Continue();
|
||||
}
|
||||
|
||||
static void JNICALL NativeOSLSoundInit(JNIEnv *env, jobject this)
|
||||
{
|
||||
Sound_Initialise(0, NULL);
|
||||
}
|
||||
|
||||
static void JNICALL NativeOSLSoundExit(JNIEnv *env, jobject this)
|
||||
{
|
||||
Sound_Exit();
|
||||
}
|
||||
|
||||
|
||||
jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved)
|
||||
{
|
||||
JNINativeMethod main_methods[] = {
|
||||
{ "NativeExit", "()V", NativeExit },
|
||||
{ "NativeRunAtariProgram", "(Ljava/lang/String;II)I", NativeRunAtariProgram },
|
||||
{ "NativePrefGfx", "(IZIIZII)V", NativePrefGfx },
|
||||
{ "NativePrefMachine", "(IZ)Z", NativePrefMachine },
|
||||
{ "NativePrefEmulation", "(ZZZZZ)V", NativePrefEmulation },
|
||||
{ "NativePrefSoftjoy", "(ZIIIIII[Ljava/lang/String;)V", NativePrefSoftjoy },
|
||||
{ "NativePrefJoy", "(ZIIZIIZIIIZZ)V", NativePrefJoy },
|
||||
{ "NativePrefSound", "(IIZZZ)V", NativePrefSound },
|
||||
{ "NativeSetROMPath", "(Ljava/lang/String;)Z", NativeSetROMPath },
|
||||
{ "NativeGetJoypos", "()Ljava/lang/String;", NativeGetJoypos },
|
||||
{ "NativeInit", "()Ljava/lang/String;", NativeInit },
|
||||
{ "NativeGetURL", "()Ljava/lang/String;", NativeGetURL },
|
||||
{ "NativeClearDevB", "()V", NativeClearDevB },
|
||||
{ "NativeBootCartType", "(I)V", NativeBootCartType },
|
||||
};
|
||||
JNINativeMethod view_methods[] = {
|
||||
{ "NativeTouch", "(IIIIII)I", NativeTouch },
|
||||
{ "NativeKey", "(II)V", NativeKey },
|
||||
};
|
||||
JNINativeMethod snd_methods[] = {
|
||||
{ "NativeSoundInit", "(I)V", NativeSoundInit },
|
||||
{ "NativeSoundUpdate", "(II)V", NativeSoundUpdate },
|
||||
{ "NativeSoundExit", "()V", NativeSoundExit },
|
||||
{ "NativeOSLSound", "()Z", NativeOSLSound },
|
||||
{ "NativeOSLSoundInit", "()V", NativeOSLSoundInit },
|
||||
{ "NativeOSLSoundExit", "()V", NativeOSLSoundExit },
|
||||
{ "NativeOSLSoundPause", "(Z)V", NativeOSLSoundPause },
|
||||
};
|
||||
JNINativeMethod render_methods[] = {
|
||||
{ "NativeRunFrame", "()I", NativeRunFrame },
|
||||
{ "NativeGetOverlays", "()V", NativeGetOverlays },
|
||||
{ "NativeResize", "(II)V", NativeResize },
|
||||
};
|
||||
JNINativeMethod fsel_methods[] = {
|
||||
{ "NativeIsDisk", "(Ljava/lang/String;)Z", NativeIsDisk },
|
||||
{ "NativeRunAtariProgram", "(Ljava/lang/String;II)I", NativeRunAtariProgram },
|
||||
{ "NativeGetDrvFnames", "()[Ljava/lang/String;", NativeGetDrvFnames },
|
||||
{ "NativeUnmountAll", "()V", NativeUnmountAll },
|
||||
};
|
||||
JNINativeMethod pref_methods[] = {
|
||||
{ "NativeSaveState", "(Ljava/lang/String;)Z", NativeSaveState },
|
||||
{ "NativeBootPD", "([BI)Z", NativeBootPD },
|
||||
{ "NativeOSLSound", "()Z", NativeOSLSound },
|
||||
};
|
||||
JNIEnv *env;
|
||||
jclass cls;
|
||||
|
||||
if ((*jvm)->GetEnv(jvm, (void **) &env, JNI_VERSION_1_2))
|
||||
return JNI_ERR;
|
||||
|
||||
cls = (*env)->FindClass(env, "name/nick/jubanka/colleen/MainActivity");
|
||||
(*env)->RegisterNatives(env, cls, main_methods, sizeof(main_methods)/sizeof(JNINativeMethod));
|
||||
cls = (*env)->FindClass(env, "name/nick/jubanka/colleen/A800view");
|
||||
(*env)->RegisterNatives(env, cls, view_methods, sizeof(view_methods)/sizeof(JNINativeMethod));
|
||||
cls = (*env)->FindClass(env, "name/nick/jubanka/colleen/AudioThread");
|
||||
(*env)->RegisterNatives(env, cls, snd_methods, sizeof(snd_methods)/sizeof(JNINativeMethod));
|
||||
cls = (*env)->FindClass(env, "name/nick/jubanka/colleen/A800Renderer");
|
||||
(*env)->RegisterNatives(env, cls, render_methods, sizeof(render_methods)/sizeof(JNINativeMethod));
|
||||
cls = (*env)->FindClass(env, "name/nick/jubanka/colleen/FileSelector");
|
||||
(*env)->RegisterNatives(env, cls, fsel_methods, sizeof(fsel_methods)/sizeof(JNINativeMethod));
|
||||
cls = (*env)->FindClass(env, "name/nick/jubanka/colleen/Preferences");
|
||||
(*env)->RegisterNatives(env, cls, pref_methods, sizeof(pref_methods)/sizeof(JNINativeMethod));
|
||||
|
||||
return JNI_VERSION_1_2;
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
#define STATIC_MAXKEYS 256
|
||||
|
||||
#define KEY_SHIFT 256
|
||||
#define KEY_CONTROL 257
|
||||
#define KEY_BACKSPACE 255
|
||||
#define KEY_UP 254
|
||||
#define KEY_DOWN 253
|
||||
#define KEY_LEFT 252
|
||||
#define KEY_RIGHT 251
|
||||
#define KEY_FIRE 250
|
||||
#define KEY_ENTER 249
|
||||
#define KEY_ESCAPE 248
|
||||
#define KEY_BREAK 242
|
||||
|
||||
static const SWORD skeyxlat[STATIC_MAXKEYS] = {
|
||||
[0 ... (STATIC_MAXKEYS - 1)] = AKEY_NONE,
|
||||
|
||||
['0'] = AKEY_0,
|
||||
['1'] = AKEY_1,
|
||||
['2'] = AKEY_2,
|
||||
['3'] = AKEY_3,
|
||||
['4'] = AKEY_4,
|
||||
['5'] = AKEY_5,
|
||||
['6'] = AKEY_6,
|
||||
['7'] = AKEY_7,
|
||||
['8'] = AKEY_8,
|
||||
['9'] = AKEY_9,
|
||||
|
||||
['a'] = AKEY_a,
|
||||
['b'] = AKEY_b,
|
||||
['c'] = AKEY_c,
|
||||
['d'] = AKEY_d,
|
||||
['e'] = AKEY_e,
|
||||
['f'] = AKEY_f,
|
||||
['g'] = AKEY_g,
|
||||
['h'] = AKEY_h,
|
||||
['i'] = AKEY_i,
|
||||
['j'] = AKEY_j,
|
||||
['k'] = AKEY_k,
|
||||
['l'] = AKEY_l,
|
||||
['m'] = AKEY_m,
|
||||
['n'] = AKEY_n,
|
||||
['o'] = AKEY_o,
|
||||
['p'] = AKEY_p,
|
||||
['q'] = AKEY_q,
|
||||
['r'] = AKEY_r,
|
||||
['s'] = AKEY_s,
|
||||
['t'] = AKEY_t,
|
||||
['u'] = AKEY_u,
|
||||
['v'] = AKEY_v,
|
||||
['w'] = AKEY_w,
|
||||
['x'] = AKEY_x,
|
||||
['y'] = AKEY_y,
|
||||
['z'] = AKEY_z,
|
||||
|
||||
['A'] = AKEY_A,
|
||||
['B'] = AKEY_B,
|
||||
['C'] = AKEY_C,
|
||||
['D'] = AKEY_D,
|
||||
['E'] = AKEY_E,
|
||||
['F'] = AKEY_F,
|
||||
['G'] = AKEY_G,
|
||||
['H'] = AKEY_H,
|
||||
['I'] = AKEY_I,
|
||||
['J'] = AKEY_J,
|
||||
['K'] = AKEY_K,
|
||||
['L'] = AKEY_L,
|
||||
['M'] = AKEY_M,
|
||||
['N'] = AKEY_N,
|
||||
['O'] = AKEY_O,
|
||||
['P'] = AKEY_P,
|
||||
['Q'] = AKEY_Q,
|
||||
['R'] = AKEY_R,
|
||||
['S'] = AKEY_S,
|
||||
['T'] = AKEY_T,
|
||||
['U'] = AKEY_U,
|
||||
['V'] = AKEY_V,
|
||||
['W'] = AKEY_W,
|
||||
['X'] = AKEY_X,
|
||||
['Y'] = AKEY_Y,
|
||||
['Z'] = AKEY_Z,
|
||||
|
||||
['\e'] = AKEY_ESCAPE,
|
||||
['~'] = AKEY_ESCAPE,
|
||||
['\t'] = AKEY_TAB,
|
||||
['\n'] = AKEY_RETURN,
|
||||
[' '] = AKEY_SPACE,
|
||||
['!'] = AKEY_EXCLAMATION,
|
||||
['\"'] = AKEY_DBLQUOTE,
|
||||
['#'] = AKEY_HASH,
|
||||
['$'] = AKEY_DOLLAR,
|
||||
['%'] = AKEY_PERCENT,
|
||||
['&'] = AKEY_AMPERSAND,
|
||||
['\''] = AKEY_QUOTE,
|
||||
['@'] = AKEY_AT,
|
||||
['('] = AKEY_PARENLEFT,
|
||||
[')'] = AKEY_PARENRIGHT,
|
||||
['<'] = AKEY_LESS,
|
||||
['>'] = AKEY_GREATER,
|
||||
['='] = AKEY_EQUAL,
|
||||
['?'] = AKEY_QUESTION,
|
||||
['-'] = AKEY_MINUS,
|
||||
['+'] = AKEY_PLUS,
|
||||
['*'] = AKEY_ASTERISK,
|
||||
['/'] = AKEY_SLASH,
|
||||
[':'] = AKEY_COLON,
|
||||
[';'] = AKEY_SEMICOLON,
|
||||
[','] = AKEY_COMMA,
|
||||
['.'] = AKEY_FULLSTOP,
|
||||
['_'] = AKEY_UNDERSCORE,
|
||||
['['] = AKEY_BRACKETLEFT,
|
||||
[']'] = AKEY_BRACKETRIGHT,
|
||||
['^'] = AKEY_CIRCUMFLEX,
|
||||
['\\'] = AKEY_BACKSLASH,
|
||||
['|'] = AKEY_BAR,
|
||||
['`'] = AKEY_CAPSTOGGLE,
|
||||
['{'] = AKEY_ATARI,
|
||||
|
||||
[KEY_BACKSPACE] = AKEY_BACKSPACE,
|
||||
[KEY_UP ] = AKEY_UP,
|
||||
[KEY_DOWN ] = AKEY_DOWN,
|
||||
[KEY_LEFT ] = AKEY_LEFT,
|
||||
[KEY_RIGHT ] = AKEY_RIGHT,
|
||||
[KEY_ENTER ] = AKEY_RETURN,
|
||||
[KEY_ESCAPE ] = AKEY_ESCAPE,
|
||||
[KEY_BREAK ] = AKEY_BREAK,
|
||||
|
||||
|
||||
|
||||
/* unmapped codes
|
||||
#define AKEY_HELP 0x11
|
||||
#define AKEY_DELETE_CHAR 0xb4
|
||||
#define AKEY_DELETE_LINE 0x74
|
||||
#define AKEY_INSERT_CHAR 0xb7
|
||||
#define AKEY_INSERT_LINE 0x77
|
||||
#define AKEY_SETTAB 0x6c
|
||||
#define AKEY_CLRTAB 0xac
|
||||
*/
|
||||
};
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* platform.c - platform interface implementation for android
|
||||
*
|
||||
* Copyright (C) 2010 Kostas Nakos
|
||||
* Copyright (C) 2010 Atari800 development team (see DOC/CREDITS)
|
||||
*
|
||||
* This file is part of the Atari800 emulator project which emulates
|
||||
* the Atari 400, 800, 800XL, 130XE, and 5200 8-bit computers.
|
||||
*
|
||||
* Atari800 is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Atari800 is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Atari800; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "atari.h"
|
||||
#include "cpu.h"
|
||||
#include "input.h"
|
||||
#include "devices.h"
|
||||
|
||||
#include "graphics.h"
|
||||
#include "androidinput.h"
|
||||
|
||||
int PLATFORM_Initialise(int *argc, char *argv[])
|
||||
{
|
||||
/* Android_InitGraphics() is deferred until GL surface is created */
|
||||
/* Sound_Initialise() not needed */
|
||||
Log_print("Core init");
|
||||
|
||||
Input_Initialize();
|
||||
|
||||
Devices_enable_h_patch = FALSE;
|
||||
INPUT_direct_mouse = TRUE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int PLATFORM_Exit(int run_monitor)
|
||||
{
|
||||
if (CPU_cim_encountered) {
|
||||
Log_print("CIM encountered");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
Log_print("Core_exit");
|
||||
|
||||
Android_ExitGraphics();
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
int PLATFORM_Keyboard(void)
|
||||
{
|
||||
return Keyboard_Dequeue();
|
||||
}
|
||||
|
||||
void PLATFORM_DisplayScreen(void)
|
||||
{
|
||||
Android_ConvertScreen();
|
||||
Android_Render();
|
||||
Update_Overlays();
|
||||
}
|
||||
|
||||
void PLATFORM_PaletteUpdate(void)
|
||||
{
|
||||
Android_PaletteUpdate();
|
||||
}
|
||||
|
||||
int PLATFORM_PORT(int num)
|
||||
{
|
||||
return (Android_PortStatus >> (num << 3)) & 0xFF;
|
||||
}
|
||||
|
||||
int PLATFORM_TRIG(int num)
|
||||
{
|
||||
return (Android_TrigStatus >> num) & 0x1;
|
||||
}
|
||||
@@ -0,0 +1,381 @@
|
||||
/*
|
||||
* sound.c - android sound
|
||||
*
|
||||
* Copyright (C) 2014 Kostas Nakos
|
||||
* Copyright (C) 2014 Atari800 development team (see DOC/CREDITS)
|
||||
*
|
||||
* This file is part of the Atari800 emulator project which emulates
|
||||
* the Atari 400, 800, 800XL, 130XE, and 5200 8-bit computers.
|
||||
*
|
||||
* Atari800 is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Atari800 is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Atari800; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <dlfcn.h>
|
||||
|
||||
#include <SLES/OpenSLES.h>
|
||||
#include <SLES/OpenSLES_Android.h>
|
||||
|
||||
#include "pokeysnd.h"
|
||||
#include "log.h"
|
||||
|
||||
static int at_sixteenbit = 0;
|
||||
static int snd_bufsizems = 0;
|
||||
static int snd_mixrate = 0;
|
||||
|
||||
#define OSL_BUFSIZE_MS 10
|
||||
static void *osl_handle;
|
||||
int Android_osl_sound;
|
||||
static int osl_disable = 0;
|
||||
static int osl_bufnum = 0;
|
||||
static int osl_bufszbytes = 0;
|
||||
static UBYTE *osl_soundbuf = NULL;
|
||||
static UBYTE **osl_soundbufptr = NULL;
|
||||
static ULONG osl_lastplayedindex = 0;
|
||||
static volatile ULONG osl_lastindex = 0;
|
||||
static int osl_nextbufindex = 0;
|
||||
|
||||
static SLObjectItf osl_engine = NULL,
|
||||
osl_mixer = NULL,
|
||||
osl_player = NULL;
|
||||
static SLEngineItf osl_engineif = NULL;
|
||||
static SLPlayItf osl_playif = NULL;
|
||||
static SLAndroidSimpleBufferQueueItf osl_bufqif = NULL;
|
||||
|
||||
static SLresult SLAPIENTRY (*osl_slCreateEngine) (
|
||||
SLObjectItf *pEngine,
|
||||
SLuint32 numOptions,
|
||||
const SLEngineOption *pEngineOptions,
|
||||
SLuint32 numInterfaces,
|
||||
const SLInterfaceID *pInterfaceIds,
|
||||
const SLboolean * pInterfaceRequired
|
||||
) = NULL;
|
||||
static SLAPIENTRY const SLInterfaceID *osl_SL_IID_VOLUME;
|
||||
static SLAPIENTRY const SLInterfaceID *osl_SL_IID_ANDROIDSIMPLEBUFFERQUEUE;
|
||||
static SLAPIENTRY const SLInterfaceID *osl_SL_IID_ENGINE;
|
||||
static SLAPIENTRY const SLInterfaceID *osl_SL_IID_PLAY;
|
||||
|
||||
#define CHECK_OSL(command, errmsg) \
|
||||
if ( (res = command) != SL_RESULT_SUCCESS ) { \
|
||||
Log_print("ERROR: OSL: Cannot " errmsg " (%08X)", res); \
|
||||
return FALSE; \
|
||||
} else \
|
||||
(void) 0
|
||||
#define GET_SYMBOL(var, sname) \
|
||||
var = dlsym(osl_handle, sname); \
|
||||
errstr = dlerror(); \
|
||||
if (errstr) { \
|
||||
Log_print("ERROR: Cannot resolve " sname ": %s", errstr); \
|
||||
return FALSE; \
|
||||
} else \
|
||||
(void) 0
|
||||
|
||||
void Sound_Continue(void);
|
||||
|
||||
/* Legacy AudioThread functions */
|
||||
|
||||
void Android_SoundInit(int rate, int bufsizems, int bit16, int hq, int disableOSL)
|
||||
{
|
||||
Log_print("SoundInit for android initializing with %dHz, %d bufsize, OSL %s",
|
||||
rate, bufsizems, (disableOSL) ? "off" : "on");
|
||||
POKEYSND_bienias_fix = 0;
|
||||
POKEYSND_enable_new_pokey = hq;
|
||||
at_sixteenbit = bit16;
|
||||
snd_bufsizems = bufsizems;
|
||||
snd_mixrate = rate;
|
||||
osl_bufnum = snd_bufsizems / OSL_BUFSIZE_MS;
|
||||
osl_bufszbytes = OSL_BUFSIZE_MS * (at_sixteenbit ? 2 : 1) * snd_mixrate / 1000;
|
||||
osl_disable = disableOSL;
|
||||
if (disableOSL)
|
||||
Android_osl_sound = FALSE;
|
||||
Log_print("Initializing POKEY");
|
||||
POKEYSND_Init(POKEYSND_FREQ_17_EXACT, rate, 1, bit16 ? POKEYSND_BIT16 : 0);
|
||||
Log_print("POKEY init done");
|
||||
}
|
||||
|
||||
void SoundThread_Update(void *buf, int offs, int len)
|
||||
{
|
||||
POKEYSND_Process(buf + offs, len >> at_sixteenbit);
|
||||
}
|
||||
|
||||
|
||||
/* Native sound helpers */
|
||||
|
||||
static int OSL_grab_functions(void)
|
||||
{
|
||||
const char *errstr;
|
||||
|
||||
dlerror();
|
||||
|
||||
GET_SYMBOL(osl_slCreateEngine, "slCreateEngine");
|
||||
GET_SYMBOL(osl_SL_IID_VOLUME, "SL_IID_VOLUME");
|
||||
GET_SYMBOL(osl_SL_IID_ANDROIDSIMPLEBUFFERQUEUE, "SL_IID_ANDROIDSIMPLEBUFFERQUEUE");
|
||||
GET_SYMBOL(osl_SL_IID_ENGINE, "SL_IID_ENGINE");
|
||||
GET_SYMBOL(osl_SL_IID_PLAY, "SL_IID_PLAY");
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static int OSL_load(void)
|
||||
{
|
||||
osl_handle = dlopen("libOpenSLES.so", RTLD_LAZY);
|
||||
if (! osl_handle) {
|
||||
Log_print("Cannot dlopen OSL");
|
||||
return FALSE;
|
||||
}
|
||||
if ( OSL_grab_functions() ) {
|
||||
Log_print("Open SL ES found and bound");
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void OSL_process(SLAndroidSimpleBufferQueueItf bufqif, void *ctx)
|
||||
{
|
||||
SLAndroidSimpleBufferQueueState st;
|
||||
|
||||
if ( (*bufqif)->GetState(bufqif, &st) != SL_RESULT_SUCCESS ) {
|
||||
Log_print("ERROR: Cannot get queue state");
|
||||
return;
|
||||
}
|
||||
osl_lastindex = st.index;
|
||||
}
|
||||
|
||||
static int OSL_init(void)
|
||||
{
|
||||
SLresult res;
|
||||
|
||||
const SLInterfaceID mixids[] = { *osl_SL_IID_VOLUME };
|
||||
const SLboolean mixreq[] = { SL_BOOLEAN_FALSE };
|
||||
|
||||
SLDataLocator_AndroidSimpleBufferQueue dlsbq = {
|
||||
.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE,
|
||||
.numBuffers = osl_bufnum
|
||||
};
|
||||
SLDataFormat_PCM dfpcm = {
|
||||
.formatType = SL_DATAFORMAT_PCM,
|
||||
.numChannels = 1,
|
||||
.samplesPerSec = (snd_mixrate == 44100) ? SL_SAMPLINGRATE_44_1 :
|
||||
(snd_mixrate == 22050) ? SL_SAMPLINGRATE_22_05 :
|
||||
(snd_mixrate == 11025) ? SL_SAMPLINGRATE_11_025:
|
||||
0,
|
||||
.bitsPerSample = (at_sixteenbit) ? SL_PCMSAMPLEFORMAT_FIXED_16 : SL_PCMSAMPLEFORMAT_FIXED_8,
|
||||
.containerSize = (at_sixteenbit) ? SL_PCMSAMPLEFORMAT_FIXED_16 : SL_PCMSAMPLEFORMAT_FIXED_8,
|
||||
.channelMask = SL_SPEAKER_FRONT_CENTER,
|
||||
.endianness = SL_BYTEORDER_LITTLEENDIAN
|
||||
};
|
||||
SLDataSource dsrc = {
|
||||
.pLocator = &dlsbq,
|
||||
.pFormat = &dfpcm
|
||||
};
|
||||
SLDataLocator_OutputMix dlmix = {
|
||||
.locatorType = SL_DATALOCATOR_OUTPUTMIX,
|
||||
.outputMix = NULL
|
||||
};
|
||||
SLDataSink dsnk = {
|
||||
.pLocator = &dlmix,
|
||||
.pFormat = NULL
|
||||
};
|
||||
const SLInterfaceID apids[] = { *osl_SL_IID_ANDROIDSIMPLEBUFFERQUEUE };
|
||||
const SLboolean apreq[] = { SL_BOOLEAN_TRUE };
|
||||
|
||||
if (dfpcm.samplesPerSec == 0) {
|
||||
Log_print("ERROR: Incorrect mixrate %d", snd_mixrate);
|
||||
return FALSE;
|
||||
}
|
||||
/* engine */
|
||||
CHECK_OSL( osl_slCreateEngine(&osl_engine, 0, NULL, 0, NULL, NULL), "create engine");
|
||||
CHECK_OSL( (*osl_engine)->Realize(osl_engine, SL_BOOLEAN_FALSE), "realize engine");
|
||||
CHECK_OSL( (*osl_engine)->GetInterface(osl_engine, *osl_SL_IID_ENGINE, &osl_engineif), "get engine i/f");
|
||||
|
||||
/* mixer */
|
||||
CHECK_OSL( (*osl_engineif)->CreateOutputMix(osl_engineif, &osl_mixer, 1, mixids, mixreq), "create mixer");
|
||||
CHECK_OSL( (*osl_mixer)->Realize(osl_mixer, SL_BOOLEAN_FALSE), "realize mixer");
|
||||
dlmix.outputMix = osl_mixer;
|
||||
|
||||
/* player */
|
||||
CHECK_OSL( (*osl_engineif)->CreateAudioPlayer(osl_engineif, &osl_player, &dsrc, &dsnk, 1, apids, apreq), "create player");
|
||||
CHECK_OSL( (*osl_player)->Realize(osl_player, SL_BOOLEAN_FALSE), "realize player");
|
||||
CHECK_OSL( (*osl_player)->GetInterface(osl_player, *osl_SL_IID_PLAY, &osl_playif), "get player i/f");
|
||||
CHECK_OSL( (*osl_player)->GetInterface(osl_player, *osl_SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &osl_bufqif), "get buffer queue i/f");
|
||||
|
||||
/* register callback */
|
||||
CHECK_OSL( (*osl_bufqif)->RegisterCallback(osl_bufqif, OSL_process, NULL), "register callback");
|
||||
|
||||
Log_print("OpenSL ES initialized successfully");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void OSL_stop_playback(void)
|
||||
{
|
||||
SLuint32 playstate;
|
||||
|
||||
(*osl_playif)->SetPlayState(osl_playif, SL_PLAYSTATE_STOPPED);
|
||||
Log_print("Waiting for OSL player to finish");
|
||||
do {
|
||||
usleep(50000);
|
||||
(*osl_playif)->GetPlayState(osl_playif, &playstate);
|
||||
} while (playstate != SL_PLAYSTATE_STOPPED);
|
||||
Log_print("Playback finished");
|
||||
}
|
||||
|
||||
static void OSL_teardown(void)
|
||||
{
|
||||
if (osl_playif) {
|
||||
OSL_stop_playback();
|
||||
(*osl_player)->Destroy(osl_player);
|
||||
osl_player = NULL;
|
||||
osl_playif = NULL;
|
||||
osl_bufqif = NULL;
|
||||
}
|
||||
|
||||
if (osl_mixer) {
|
||||
(*osl_mixer)->Destroy(osl_mixer);
|
||||
osl_mixer = NULL;
|
||||
}
|
||||
|
||||
if (osl_engine) {
|
||||
(*osl_engine)->Destroy(osl_engine);
|
||||
osl_engine = NULL;
|
||||
osl_engineif = NULL;
|
||||
}
|
||||
Log_print("OpenSL teardown complete");
|
||||
}
|
||||
|
||||
static void OSL_buf_free(void)
|
||||
{
|
||||
if (osl_soundbufptr) {
|
||||
free(osl_soundbufptr);
|
||||
osl_soundbufptr = NULL;
|
||||
}
|
||||
if (osl_soundbuf) {
|
||||
free(osl_soundbuf);
|
||||
osl_soundbuf = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int OSL_buf_alloc(void)
|
||||
{
|
||||
UBYTE *ptr;
|
||||
int i;
|
||||
|
||||
if (osl_soundbuf || osl_soundbufptr) {
|
||||
Log_print("WARNING: Sound buffers already allocated. Freeing.");
|
||||
OSL_buf_free();
|
||||
}
|
||||
if (! ( osl_soundbufptr = (UBYTE **) malloc(osl_bufnum * sizeof(void *)) ) ) {
|
||||
Log_print("ERROR: Cannot allocate sound buffer pointers (%d)", osl_bufnum);
|
||||
return FALSE;
|
||||
}
|
||||
if (! ( osl_soundbuf = (UBYTE *) malloc(osl_bufnum * osl_bufszbytes) ) ) {
|
||||
Log_print("ERROR: Cannot allocate sound buffer for %d buffers of %d bytes each", osl_bufnum, osl_bufszbytes);
|
||||
return FALSE;
|
||||
}
|
||||
memset(osl_soundbuf, 0, osl_bufnum * osl_bufszbytes);
|
||||
for (ptr = osl_soundbuf, i = 0; i < osl_bufnum; i++, ptr += osl_bufszbytes)
|
||||
osl_soundbufptr[i] = ptr;
|
||||
|
||||
Log_print("Allocated OK sound buffer for %d buffers %dms each, %d bits at %dHz",
|
||||
osl_bufnum, OSL_BUFSIZE_MS, (at_sixteenbit) ? 16 : 8, snd_mixrate);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static int OSL_start_playback(void)
|
||||
{
|
||||
SLresult res;
|
||||
int i;
|
||||
|
||||
Sound_Continue();
|
||||
|
||||
for (i = 0; i < osl_bufnum; i++) {
|
||||
if (! osl_bufqif) return FALSE;
|
||||
CHECK_OSL( (*osl_bufqif)->Enqueue(osl_bufqif, osl_soundbufptr[i], osl_bufszbytes), "enqueue init buffer");
|
||||
}
|
||||
|
||||
osl_nextbufindex = 0;
|
||||
osl_lastindex = osl_lastplayedindex = 0;
|
||||
|
||||
Log_print("Buffer queue bootstrap OK");
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* Platform interface. Used only with SL sound */
|
||||
|
||||
void Sound_Exit(void)
|
||||
{
|
||||
Log_print("Sound exit");
|
||||
|
||||
OSL_teardown();
|
||||
OSL_buf_free();
|
||||
if (osl_handle) {
|
||||
dlclose(osl_handle);
|
||||
osl_handle = NULL;
|
||||
}
|
||||
Android_osl_sound = FALSE;
|
||||
}
|
||||
|
||||
int Sound_Initialise(int *argc, char *argv[])
|
||||
{
|
||||
Android_osl_sound = TRUE;
|
||||
|
||||
if (
|
||||
osl_disable ||
|
||||
! OSL_load() ||
|
||||
! OSL_init() ||
|
||||
! OSL_buf_alloc() ||
|
||||
! OSL_start_playback()
|
||||
)
|
||||
{
|
||||
Android_osl_sound = FALSE;
|
||||
Sound_Exit();
|
||||
Log_print("Using legacy AudioThread");
|
||||
} else
|
||||
Log_print("Using OpenSL ES sound");
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void Sound_Update(void)
|
||||
{
|
||||
SLresult res;
|
||||
|
||||
while (osl_lastplayedindex < osl_lastindex) {
|
||||
if (! osl_soundbufptr) return;
|
||||
POKEYSND_Process(osl_soundbufptr[osl_nextbufindex], osl_bufszbytes >> at_sixteenbit);
|
||||
if (! osl_bufqif) return;
|
||||
res = (*osl_bufqif)->Enqueue(osl_bufqif, osl_soundbufptr[osl_nextbufindex], osl_bufszbytes);
|
||||
if (res != SL_RESULT_SUCCESS) {
|
||||
Log_print("Cannot enqueue buffer #%d (%08X)", osl_nextbufindex, res);
|
||||
return;
|
||||
}
|
||||
osl_nextbufindex = (osl_nextbufindex + 1) % osl_bufnum;
|
||||
osl_lastplayedindex++;
|
||||
}
|
||||
}
|
||||
|
||||
void Sound_Pause(void)
|
||||
{
|
||||
Log_print("OSL pausing sound");
|
||||
if (! osl_playif) return;
|
||||
(*osl_playif)->SetPlayState(osl_playif, SL_PLAYSTATE_PAUSED);
|
||||
}
|
||||
|
||||
void Sound_Continue(void)
|
||||
{
|
||||
Log_print("OSL resuming sound");
|
||||
if (! osl_playif) return;
|
||||
(*osl_playif)->SetPlayState(osl_playif, SL_PLAYSTATE_PLAYING);
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
# This file is automatically generated by Android Tools.
|
||||
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
|
||||
#
|
||||
# This file must be checked in Version Control Systems.
|
||||
#
|
||||
# To customize properties used by the Ant build system edit
|
||||
# "ant.properties", and override values to adapt the script to your
|
||||
# project structure.
|
||||
#
|
||||
# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
|
||||
#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
|
||||
|
||||
# Project target.
|
||||
target=android-20
|
||||
|
After Width: | Height: | Size: 3.4 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
|
||||
<solid android:color="#BB000000" />
|
||||
</shape>
|
||||
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 5.2 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 4.9 KiB |
|
After Width: | Height: | Size: 2.5 KiB |
@@ -0,0 +1,36 @@
|
||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent">
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:padding="10dp"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent">
|
||||
<TextView
|
||||
android:id="@+id/header"
|
||||
android:text="@string/pref_keymapmsg2"
|
||||
android:textAppearance="@android:style/TextAppearance.Medium"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"/>
|
||||
<EditText
|
||||
android:id="@+id/keyinput"
|
||||
android:hint="@string/pref_typeatarikey"
|
||||
android:lines="1"
|
||||
android:maxLines="1"
|
||||
android:maxLength="1"
|
||||
android:inputType="textNoSuggestions"
|
||||
android:imeOptions="actionDone"
|
||||
android:layout_marginLeft="20dp"
|
||||
android:layout_marginRight="20dp"
|
||||
android:layout_marginTop="6dp"
|
||||
android:layout_marginBottom="6dp"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"/>
|
||||
<TextView
|
||||
android:id="@+id/note"
|
||||
android:text="@string/pref_keymapmsg2note"
|
||||
android:textAppearance="@android:style/TextAppearance.Small"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"/>
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
@@ -0,0 +1,33 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
<ListView
|
||||
android:id="@id/android:list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_above="@+id/footer"/>
|
||||
<LinearLayout
|
||||
android:id="@+id/footer"
|
||||
android:background="@android:color/darker_gray"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:orientation="horizontal"
|
||||
android:padding="5dp">
|
||||
<Button
|
||||
android:id="@+id/fsel_ok"
|
||||
android:text="@string/selectdir"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"/>
|
||||
<Button
|
||||
android:id="@+id/fsel_cancel"
|
||||
android:text="@string/cancel"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"/>
|
||||
</LinearLayout>
|
||||
</RelativeLayout>
|
||||
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
<ImageView
|
||||
android:id="@+id/fsel_image"
|
||||
android:layout_height="30dp"
|
||||
android:layout_width="40dp"
|
||||
android:paddingLeft="10dp"/>
|
||||
<TextView
|
||||
android:id="@+id/fsel_text"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:paddingLeft="10dp"
|
||||
android:gravity="center_vertical"
|
||||
android:textSize="14sp"
|
||||
android:singleLine="true"
|
||||
android:ellipsize="end"/>
|
||||
</LinearLayout>
|
||||
@@ -0,0 +1,20 @@
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="horizontal"
|
||||
android:padding="10dp"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent">
|
||||
<SeekBar
|
||||
android:id="@+id/slider"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:layout_marginLeft="10dp"
|
||||
android:layout_marginRight="10dp"
|
||||
android:max="99"/>
|
||||
<TextView
|
||||
android:id="@+id/setting"
|
||||
android:text="99%"
|
||||
android:gravity="center_vertical"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="fill_parent"/>
|
||||
</LinearLayout>
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:id="@+id/menu_search"
|
||||
android:title="@string/fselmenu_search"
|
||||
android:icon="@drawable/ic_menu_search"
|
||||
android:showAsAction="ifRoom|collapseActionView"
|
||||
android:actionViewClass="android.widget.SearchView" />
|
||||
</menu>
|
||||
@@ -0,0 +1,20 @@
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:id="@+id/menu_open"
|
||||
android:title="@string/menu_open"
|
||||
android:icon="@drawable/ic_menu_archive"
|
||||
android:showAsAction="ifRoom"/>
|
||||
<item android:id="@+id/menu_nextdisk"
|
||||
android:title="@string/menu_nextdisk"
|
||||
android:icon="@drawable/ic_menu_rotate"
|
||||
android:showAsAction="ifRoom"/>
|
||||
<item android:id="@+id/menu_softkbd"
|
||||
android:title="@string/menu_softkbd"
|
||||
android:icon="@drawable/keyboard"
|
||||
android:showAsAction="ifRoom"/>
|
||||
<item android:id="@+id/menu_preferences"
|
||||
android:title="@string/menu_preferences"
|
||||
android:icon="@drawable/ic_menu_preferences"/>
|
||||
<item android:id="@+id/menu_quit"
|
||||
android:title="@string/menu_quit"
|
||||
android:icon="@drawable/ic_menu_close_clear_cancel"/>
|
||||
</menu>
|
||||
@@ -0,0 +1,5 @@
|
||||
<resources>
|
||||
<style name="MainTheme" parent="@android:style/Theme.Holo">
|
||||
<item name="android:windowActionBarOverlay">true</item>
|
||||
</style>
|
||||
</resources>
|
||||
@@ -0,0 +1,122 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string-array name="aspect">
|
||||
<item>@string/none</item>
|
||||
<item>@string/asp_portrait</item>
|
||||
<item>@string/asp_landscape</item>
|
||||
<item>@string/asp_both</item>
|
||||
</string-array>
|
||||
<string-array name="aspect_values">
|
||||
<item>0</item>
|
||||
<item>1</item>
|
||||
<item>2</item>
|
||||
<item>3</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="artifact">
|
||||
<item>@string/none</item>
|
||||
<item>@string/art_bluebrown1</item>
|
||||
<item>@string/art_bluebrown2</item>
|
||||
<item>@string/art_gtia</item>
|
||||
<item>@string/art_ctia</item>
|
||||
</string-array>
|
||||
<string-array name="artifact_values">
|
||||
<item>0</item>
|
||||
<item>1</item>
|
||||
<item>2</item>
|
||||
<item>3</item>
|
||||
<item>4</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="machine">
|
||||
<item>Atari OS/A (16 KB)</item>
|
||||
<item>Atari OS/A (48 KB)</item>
|
||||
<item>Atari OS/A (52 KB)</item>
|
||||
<item>Atari OS/B (16 KB)</item>
|
||||
<item>Atari OS/B (48 KB)</item>
|
||||
<item>Atari OS/B (52 KB)</item>
|
||||
<item>Atari 600XL (16 KB)</item>
|
||||
<item>Atari 800XL (64 KB)</item>
|
||||
<item>Atari 130XE (128 KB)</item>
|
||||
<item>Atari XL/XE (192 KB)</item>
|
||||
<item>Atari XL/XE (320 KB RAMBO)</item>
|
||||
<item>Atari XL/XE (320 KB COMPY SHOP)</item>
|
||||
<item>Atari XL/XE (576 KB)</item>
|
||||
<item>Atari XL/XE (1088 KB)</item>
|
||||
<item>Atari 5200 (16 KB)</item>
|
||||
</string-array>
|
||||
<string-array name="machine_values">
|
||||
<item>0</item>
|
||||
<item>1</item>
|
||||
<item>2</item>
|
||||
<item>3</item>
|
||||
<item>4</item>
|
||||
<item>5</item>
|
||||
<item>6</item>
|
||||
<item>7</item>
|
||||
<item>8</item>
|
||||
<item>9</item>
|
||||
<item>10</item>
|
||||
<item>11</item>
|
||||
<item>12</item>
|
||||
<item>13</item>
|
||||
<item>14</item>
|
||||
<item>15</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="frameskip">
|
||||
<item>@string/skipauto</item>
|
||||
<item>@string/skip0</item>
|
||||
<item>@string/skip1</item>
|
||||
<item>@string/skip2</item>
|
||||
<item>@string/skip3</item>
|
||||
<item>@string/skip4</item>
|
||||
<item>@string/skip5</item>
|
||||
</string-array>
|
||||
<string-array name="frameskip_values">
|
||||
<item>0</item>
|
||||
<item>1</item>
|
||||
<item>2</item>
|
||||
<item>3</item>
|
||||
<item>4</item>
|
||||
<item>5</item>
|
||||
<item>6</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="mixrate">
|
||||
<item>44100 Hz</item>
|
||||
<item>22050 Hz</item>
|
||||
<item>11025 Hz</item>
|
||||
</string-array>
|
||||
<string-array name="mixrate_values">
|
||||
<item>44100</item>
|
||||
<item>22050</item>
|
||||
<item>11025</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="derotkeys">
|
||||
<item>@string/none</item>
|
||||
<item>@string/left</item>
|
||||
<item>@string/right</item>
|
||||
</string-array>
|
||||
<string-array name="derotkeys_values">
|
||||
<item>0</item>
|
||||
<item>1</item>
|
||||
<item>2</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="changes_strings">
|
||||
<item>@string/changesv1_1a</item>
|
||||
<item>@string/changesv1_2</item>
|
||||
<item>@string/changesv1_3</item>
|
||||
<item>@string/changesv2_0</item>
|
||||
<item>@string/changesv3_0</item>
|
||||
</string-array>
|
||||
<integer-array name="changes_versions">
|
||||
<item>3</item>
|
||||
<item>10</item>
|
||||
<item>20</item>
|
||||
<item>200</item>
|
||||
<item>300</item>
|
||||
</integer-array>
|
||||
</resources>
|
||||
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<resources>
|
||||
<declare-styleable name="SliderPreference">
|
||||
<attr name="min" format="string"/>
|
||||
<attr name="max" format="string"/>
|
||||
<attr name="suffix" format="string"/>
|
||||
</declare-styleable>
|
||||
|
||||
<declare-styleable name="KeymapPreference">
|
||||
<attr name="ext" format="string"/>
|
||||
</declare-styleable>
|
||||
</resources>
|
||||
@@ -0,0 +1,289 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name">Colleen</string>
|
||||
<string name="menu_quit">Quit</string>
|
||||
<string name="menu_softkbd">Keyboard</string>
|
||||
<string name="menu_open">Open</string>
|
||||
<string name="menu_nextdisk">Next Side</string>
|
||||
<string name="fsel_openfile">Open File:</string>
|
||||
<string name="fsel_opendir">Open Dir:</string>
|
||||
<string name="fselmenu_search">Search</string>
|
||||
<string name="warning">Warning</string>
|
||||
<string name="mountnodisk">File %s is not a disk image.\nContinue?</string>
|
||||
<string name="yes">Yes</string>
|
||||
<string name="no">No</string>
|
||||
<string name="mountdisk">Mount Image to Drive</string>
|
||||
<string name="unmountall">Unmount all drives</string>
|
||||
<string name="mountnextdisk">Image %s inserted</string>
|
||||
<string name="mountnextdiskerror">Cannot insert image %s</string>
|
||||
<string name="mountinsertdisk">Image %1$s inserted into drive D%2$d:</string>
|
||||
<string name="savestateoverwrite">Save state file \'%s\' already exists. OK to overwrite?</string>
|
||||
<string name="savestateerror">Error: Cannot save state</string>
|
||||
<string name="savestateok">State saved</string>
|
||||
<string name="mountnonextdisk">No next disk image found</string>
|
||||
<string name="diskboot">Booting image %s</string>
|
||||
<string name="errorboot">Cannot boot image %s</string>
|
||||
<string name="none">None</string>
|
||||
<string name="left">Left</string>
|
||||
<string name="right">Right</string>
|
||||
<string name="menu_preferences">Preferences</string>
|
||||
<string name="cancel">Cancel</string>
|
||||
<string name="ok">OK</string>
|
||||
<string name="save">Save</string>
|
||||
<string name="cimcrash">The Atari computer has crashed.\n
|
||||
\nReset the machine or boot another image.</string>
|
||||
<string name="noromfoundrevert">Failed to initialize machine. Required ROM not found.\n
|
||||
\nYour machine change has been reverted.</string>
|
||||
<string name="noromfound">Failed to initialize machine. Required ROM not found.</string>
|
||||
<string name="pressback">Press \'Back\' once more to exit</string>
|
||||
<string name="welcome">Welcome to Colleen!</string>
|
||||
<string name="welcomenote">Hello,\n
|
||||
\nJust a few tips to get you going:
|
||||
\n1) Left hand joystick, right hand trigger (or search button).
|
||||
\n2) Tap the top-right corner for console keys.
|
||||
\n3) On Honeycomb (3.0)+ devices, tap near the screen\'s top for menu.
|
||||
\n4) On earlier devices, hit \'Menu\' for preferences and more.
|
||||
\n5) Long-press in the file selector to insert an image and not boot it.
|
||||
\n6) Use the WiiController or the keyboard for accuracy.\n
|
||||
\nFirst though, setup the Atari ROM path in the next dialog.\n
|
||||
\nHave fun! </string>
|
||||
<string name="pathsetup">Atari ROM Path</string>
|
||||
<string name="pathsetupmsg"><![CDATA[
|
||||
The emulator requires the original Atari ROM images.<br><br>
|
||||
Touch <b>OK</b> to use the file selector to point to the folder containing
|
||||
the ROM images.<br>
|
||||
Touch <b>Cancel</b> to quit to the OS.<br><br>
|
||||
Try the
|
||||
<a href="http://atari800.sourceforge.net/download.html">Atari800 site</a>
|
||||
for the XL ROMs.<br>
|
||||
Valid ROM filenames are:<br>
|
||||
atariosa.rom - OS/A,
|
||||
atariosb.rom - OS/B,
|
||||
atarixl.rom - XL,
|
||||
ataribas.rom - XL Basic,
|
||||
5200.rom - 5200
|
||||
]]></string>
|
||||
<string name="loadingdir">Loading directory. Please wait...</string>
|
||||
<string name="selectdir">Select directory</string>
|
||||
<string name="rompatherror">Failed to initialize machine.\n
|
||||
\nRequired ROM not found or path not accessible.</string>
|
||||
<string name="about">Colleen</string>
|
||||
<string name="aboutmsg"><![CDATA[
|
||||
<html><head></head><body>
|
||||
<p align="right"><font size="-1"><b>Package version: </b>%1$s<br>
|
||||
<b>Core version:</b>%2$s</font></p>
|
||||
<p align="center">Android port by <i>Kostas Nakos</i><br>
|
||||
<a href="http://pocketatari.atari.org/android">
|
||||
http://pocketatari.atari.org/android</a><br>
|
||||
Copyright © 1995-1998 <i>David Firth</i><br>
|
||||
Copyright © 1998-2014 <i>Atari800 Development Team</i><br>
|
||||
<a href="http://atari800.atari.org">http://atari800.atari.org</a></p>
|
||||
<p align="justify"><font size="-1">This program is not affiliated with Atari Inc.
|
||||
Atari and the Atari logo are trademarks owned by Atari Interactive, Inc.
|
||||
All other trademarks are the property of their respective owners.</font></p>
|
||||
<p align="justify"><font size="-1">This program is free software; you can
|
||||
redistribute it and/or modify it under the terms of the GNU General
|
||||
Public License as published by the Free Software Foundation; either
|
||||
version 2, or (at your option) any later version.</font></p>
|
||||
<p align="center">New Features? Translations? Bugs?<br>
|
||||
<a href="mailto:knakos@gmail.com>Email me</a></p>
|
||||
</body></html>
|
||||
]]></string>
|
||||
<string name="changesv1_1a"><![CDATA[
|
||||
<i>Changes for version 1.1a:</i>
|
||||
<br>This version is a quick fix for a regression to the <i>Anchor Joystick</i>
|
||||
preference, introduced in v1.1.<br><br>
|
||||
<i>Version 1.1 changelog follows:</i>
|
||||
<br><b>*</b> Fix a crash when sound was switched off
|
||||
<br><b>*</b> Added support for .com and .exe files
|
||||
<br><b>*</b> Added the auto frameskip option
|
||||
<br><b>*</b> Added the new <i>Joystick Grace Area</i> preference
|
||||
<br>Thanks to the bug/feature reporters!
|
||||
]]></string>
|
||||
<string name="changesv1_2"><![CDATA[
|
||||
<i>Changes for version 1.2:</i><br>
|
||||
<br><b>*</b> New disk management menu (long press in the file selector)
|
||||
<br><b>*</b> New screen cropping preferences
|
||||
<br><b>*</b> New arrow key derotation preference for external keyboards
|
||||
<br><b>*</b> DPAD-CENTER and tilde (~) now map to Escape key
|
||||
<br><b>*</b> Atari800 core version 2.2.0
|
||||
<br><b>*</b> Fix rare sound-related crashes
|
||||
<br><b>*</b> Various fixes and improvements
|
||||
<br><br>Thanks to the bug/feature reporters!
|
||||
]]></string>
|
||||
<string name="changesv1_3"><![CDATA[
|
||||
<i>Changes for version 1.3:</i><br>
|
||||
<br><b>*</b> New NTSC/PAL preference
|
||||
<br><b>*</b> New keyboard action mapping preferences
|
||||
<br><b>*</b> Added support for Xperia Play fire buttons
|
||||
<br><b>*</b> DPAD-CENTER now mapped to Break key
|
||||
<br><b>*</b> Atari800 core version 2.2.1
|
||||
<br><b>*</b> Several bug fixes and improvements
|
||||
<br><br>Thank you for your continued support and bug/feature reports :-)
|
||||
]]></string>
|
||||
<string name="changesv2_0"><![CDATA[
|
||||
<i>Changes for version 2.0:</i><br>
|
||||
<br><b>*</b> Free game included: Planetary Defense 2012. Find it in the Preferences.
|
||||
<br><b>*</b> UI overhaul for Honeycomb+ devices
|
||||
<br><b>*</b> When aspect correction is portrait, make room for the soft keyboard
|
||||
<br><b>*</b> Bugfix: Crashes while loading ROMs
|
||||
<br><b>*</b> Bugfix: Won\'t load OSB ROMs
|
||||
<br><b>*</b> Bugfix: Crashes in keyboard-joystick remapping
|
||||
<br><b>*</b> Warning: State saves from previous version are incompatible (sorry)
|
||||
<br><b>*</b> Numerous bug fixes and improvements
|
||||
<br><br>Thank you for your continued support and bug/feature reports
|
||||
]]></string>
|
||||
<string name="changesv3_0"><![CDATA[
|
||||
<i>Changes for version 3.0:</i><br>
|
||||
<br><b>*</b> Native Open SL ES sound for perfect playback for > 2.3 devices.
|
||||
<br><b>*</b> Fix for no sound for NTSC machines under certain circumstances.
|
||||
<br><b>*</b> Various other bugfixes.
|
||||
<br><b>*</b> Upgrade to atari800 core v. 3.1.0.
|
||||
<br><br>Thank you for your continued support and bug/feature reports
|
||||
]]></string>
|
||||
<string name="atariupdate">Colleen Update</string>
|
||||
<string name="confirmurl">The current Atari program is requesting browser access to the
|
||||
following URL:\n%s\nAllow access?</string>
|
||||
<string name="browserreqdenied">Denied Atari program browser request (malformed URL)</string>
|
||||
<string name="actionbarhelptoast">Touch near the top of the screen for menu\nTouch
|
||||
top-right for console keys</string>
|
||||
<string name="noactionbarhelptoast">Touch top-right for console keys</string>
|
||||
<string name="selectcarttype">Select cartridge type</string>
|
||||
<string name="pdbooterror">Cannot prepare Planetary Defense 2012 for boot :-(</string>
|
||||
<string name="pdreminder">Reminder: \"Touchscreen Mode\" enabled. Please remember to uncheck
|
||||
the \"Touchscreen Mode\" preference when done playing</string>
|
||||
|
||||
<!-- Preferences -->
|
||||
<string name="preftitle_graphics">Graphics</string>
|
||||
<string name="preftitle_emulation">Emulation</string>
|
||||
<string name="preftitle_input">Input</string>
|
||||
<string name="preftitle_sound">Sound</string>
|
||||
<string name="preftitle_about">About</string>
|
||||
<string name="preftitle_extras">Extras</string>
|
||||
<string name="pref_aspect">Aspect</string>
|
||||
<string name="pref_aspect_sum">Maintain aspect for scaling the screen</string>
|
||||
<string name="asp_portrait">Portrait</string>
|
||||
<string name="asp_landscape">Landscape</string>
|
||||
<string name="asp_both">Both</string>
|
||||
<string name="pref_bilinear">Bilinear Scaling</string>
|
||||
<string name="pref_bilinear_sum">Filter scaled screen</string>
|
||||
<string name="pref_artifact">Artifacting</string>
|
||||
<string name="pref_artifact_sum">Emulate TV artifacts</string>
|
||||
<string name="art_brownblue">Brown/Blue</string>
|
||||
<string name="art_bluebrown1">Blue/Brown 1</string>
|
||||
<string name="art_bluebrown2">Blue/Brown 2</string>
|
||||
<string name="art_gtia">GTIA</string>
|
||||
<string name="art_ctia">CTIA</string>
|
||||
<string name="pref_machine">Machine Type</string>
|
||||
<string name="pref_machine_sum">Select machine to emulate</string>
|
||||
<string name="pref_speed">Show Atari Speed</string>
|
||||
<string name="pref_speed_sum">Percentage of real Atari speed</string>
|
||||
<string name="pref_disk">Show Drive Access</string>
|
||||
<string name="pref_disk_sum">Display disk drive number for ongoing accesses</string>
|
||||
<string name="pref_sector">Show Sector Access</string>
|
||||
<string name="pref_sector_sum">Display disk sector number for ongoing accesses</string>
|
||||
<string name="pref_frameskip">Frameskip</string>
|
||||
<string name="pref_frameskip_sum">Screen repaint frequency</string>
|
||||
<string name="skipauto">Auto</string>
|
||||
<string name="skip0">Each frame (Skip 0)</string>
|
||||
<string name="skip1">Alternate frames (Skip 1)</string>
|
||||
<string name="skip2">Skip 2 frames</string>
|
||||
<string name="skip3">Skip 3 frames</string>
|
||||
<string name="skip4">Skip 4 frames</string>
|
||||
<string name="skip5">Skip 5 frames</string>
|
||||
<string name="pref_collisions">Accurate Collisions</string>
|
||||
<string name="pref_collisions_sum">Calculate collisions in skipped frames</string>
|
||||
<string name="pref_basic">Disable BASIC</string>
|
||||
<string name="pref_basic_sum">Simulate holding OPTION key down when booting</string>
|
||||
<string name="pref_softjoy">Keyboard Joystick</string>
|
||||
<string name="pref_softjoy_sum">Emulate joystick w/ hardware keyboard/Wii Remote</string>
|
||||
<string name="pref_keydefscr">Configure Keyboard</string>
|
||||
<string name="pref_keydefscr_sum">Define keyboard joystick keys</string>
|
||||
<string name="pref_up">Up</string>
|
||||
<string name="pref_down">Down</string>
|
||||
<string name="pref_left">Left</string>
|
||||
<string name="pref_right">Right</string>
|
||||
<string name="pref_fire">Fire</string>
|
||||
<string name="pref_actiona">Action A</string>
|
||||
<string name="pref_actionb">Action B</string>
|
||||
<string name="pref_actionc">Action C</string>
|
||||
<string name="preftitle_keymap_joystick">Joystick Keymap</string>
|
||||
<string name="preftitle_keymap_actions">Generic Mappable Keys</string>
|
||||
<string name="pref_joysize">Joystick Area Size</string>
|
||||
<string name="pref_joysize_sum">Enlarge/Shrink on-screen joystick</string>
|
||||
<string name="pref_joyvisible">Joystick Visible</string>
|
||||
<string name="pref_joyvisible_sum">Show the on-screen joystick</string>
|
||||
<string name="pref_joyrighth">Right-Handed Joystick</string>
|
||||
<string name="pref_joyrighth_sum">Exchange on-screen joystick/fire button areas</string>
|
||||
<string name="pref_joydeadband">Joystick Deadband</string>
|
||||
<string name="pref_joydeadband_sum">Joystick deflections lower than this will not register</string>
|
||||
<string name="pref_joymidx">Joystick Screen Split</string>
|
||||
<string name="pref_joymidx_sum">Horizontal percentage of screen allocated to on-screen joystick</string>
|
||||
<string name="pref_joyopacity">Joystick Opacity</string>
|
||||
<string name="pref_joyopacity_sum">Alpha blend factor for the on-screen joystick</string>
|
||||
<string name="pref_sound">Sound</string>
|
||||
<string name="pref_sound_sum">Enable sound emulation</string>
|
||||
<string name="pref_mixrate">Mix Rate</string>
|
||||
<string name="pref_mixrate_sum">Sound generation frequency</string>
|
||||
<string name="pref_sound16bit">16bit Sound</string>
|
||||
<string name="pref_sound16bit_sum">Toggle between 8bit/16bit sound generation</string>
|
||||
<string name="pref_hqpokey">High Quality Sound</string>
|
||||
<string name="pref_hqpokey_sum">Use high quality POKEY emulation</string>
|
||||
<string name="pref_romdir">ROMs directory</string>
|
||||
<string name="pref_romdir_sum">Set directory where Atari ROMs reside</string>
|
||||
<string name="pref_keymapdupmsg">This key is already used for another action.\n\nWould you like to swap
|
||||
the mapping between these two keys? (\'No\' will keep current
|
||||
assignments)</string>
|
||||
<string name="pref_warnresetactions">Are you sure you want to clear the current action mappings?</string>
|
||||
<string name="pref_keymapmsg">Press the key on the controller to assign to this action</string>
|
||||
<string name="pref_keymapmsg1">Step 1:\nPress the key on the CONTROLLER to assign to this action</string>
|
||||
<string name="pref_keymapmsg2">Step 2:\nType the key of the Atari to assign to this action in
|
||||
the next box (1 character only)</string>
|
||||
<string name="pref_keymapmsg2note">NOTE:\nUse the hardware keyboard to type in the box. If you have
|
||||
enabled the Wiicontroller IME, please temporarily disable it by long
|
||||
pressing in the box and select the standard IME. The virtual keyboard
|
||||
subsequently will show up when you select the text box above. Please
|
||||
remember to re-set the WiiController IME when finished.</string>
|
||||
<string name="pref_typeatarikey">Type Atari key</string>
|
||||
<string name="pref_keymap_current">Current:</string>
|
||||
<string name="pref_keymap_controller">Controller:</string>
|
||||
<string name="pref_keymap_mappedto">is mapped to:</string>
|
||||
<string name="pref_resetactions">Reset Action Mappings</string>
|
||||
<string name="pref_resetactions_sum">Clear current action mappings</string>
|
||||
<string name="pref_mixbufsize">Sound Buffer Size</string>
|
||||
<string name="pref_mixbufsize_sum">Lower for lower latency, higher to get rid of clicks</string>
|
||||
<string name="pref_rompath">Atari ROM Path</string>
|
||||
<string name="pref_rompath_sum">Set directory containing the ROM images</string>
|
||||
<string name="pref_about">About</string>
|
||||
<string name="pref_about_sum">Colleen credits</string>
|
||||
<string name="pref_help">Help</string>
|
||||
<string name="pref_help_sum">Android port instructions</string>
|
||||
<string name="pref_anchor">Anchor Joystick</string>
|
||||
<string name="pref_anchor_sum">Fix on-screen joystick to current position</string>
|
||||
<string name="pref_joygrace">Joystick Grace Area</string>
|
||||
<string name="pref_joygrace_sum">Area extension around the joystick which results in no recentering</string>
|
||||
<string name="pref_crophoriz">Crop Horizontally</string>
|
||||
<string name="pref_crophoriz_sum">Width in pixels of original screen to show</string>
|
||||
<string name="pref_cropvert">Crop Vertically</string>
|
||||
<string name="pref_cropvert_sum">Height in pixels of original screen to show</string>
|
||||
<string name="pref_derotkeys">Derotate Arrow Keys</string>
|
||||
<string name="pref_derotkeys_sum">Fix external keyboard arrow keys when display is rotated</string>
|
||||
<string name="pref_ntsc">NTSC TV Mode</string>
|
||||
<string name="pref_ntsc_sum">Toggle between NTSC (60fps) and PAL (50fps) tv modes</string>
|
||||
<string name="pref_paddle">Paddle Mode</string>
|
||||
<string name="pref_paddle_sum">Enable paddle emulation instead of joystick</string>
|
||||
<string name="pref_savestate">Save Current State</string>
|
||||
<string name="pref_savestate_sum_dis">Disabled! Set a state save path first!</string>
|
||||
<string name="pref_savestate_sum_ena">Save current machine state</string>
|
||||
<string name="pref_savestate_msg">Enter the file name:</string>
|
||||
<string name="pref_statepath">State Save Path</string>
|
||||
<string name="pref_statepath_sum">Where atari states will be saved</string>
|
||||
<string name="pref_plandef">Touchscreen Mode</string>
|
||||
<string name="pref_plandef_sum">A Koala Pad-like input mode for games like Planetary Defense</string>
|
||||
<string name="pref_browser">Allow Web Browser</string>
|
||||
<string name="pref_browser_sum">Provide the B: device to the emulated Atari</string>
|
||||
<string name="pref_launchpd">Planetary Defense 2012</string>
|
||||
<string name="pref_launchpd_sum">A game revamped for touchscreen and Colleen by Tom Hudson</string>
|
||||
<string name="pref_forceAT">Force Legacy Playback</string>
|
||||
<string name="pref_forceAT_sum">Disable OpenSL ES sound if checked</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,4 @@
|
||||
<resources>
|
||||
<style name="MainTheme" parent="@android:style/Theme.NoTitleBar.Fullscreen">
|
||||
</style>
|
||||
</resources>
|
||||
@@ -0,0 +1,322 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<PreferenceScreen
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:jub="http://schemas.android.com/apk/res/name.nick.jubanka.colleen">
|
||||
|
||||
<PreferenceCategory android:title="@string/preftitle_graphics">
|
||||
<CheckBoxPreference
|
||||
android:key="ntsc"
|
||||
android:title="@string/pref_ntsc"
|
||||
android:summary="@string/pref_ntsc_sum"
|
||||
android:defaultValue="false"/>
|
||||
<ListPreference
|
||||
android:key="aspect"
|
||||
android:title="@string/pref_aspect"
|
||||
android:summary="@string/pref_aspect_sum"
|
||||
android:entries="@array/aspect"
|
||||
android:entryValues="@array/aspect_values"
|
||||
android:defaultValue="0"/>
|
||||
<CheckBoxPreference
|
||||
android:key="bilinear"
|
||||
android:title="@string/pref_bilinear"
|
||||
android:summary="@string/pref_bilinear_sum"
|
||||
android:defaultValue="false"/>
|
||||
<ListPreference
|
||||
android:key="artifact"
|
||||
android:title="@string/pref_artifact"
|
||||
android:summary="@string/pref_artifact_sum"
|
||||
android:entries="@array/artifact"
|
||||
android:entryValues="@array/artifact_values"
|
||||
android:defaultValue="0"/>
|
||||
<ListPreference
|
||||
android:key="frameskip"
|
||||
android:title="@string/pref_frameskip"
|
||||
android:summary="@string/pref_frameskip_sum"
|
||||
android:entries="@array/frameskip"
|
||||
android:entryValues="@array/frameskip_values"
|
||||
android:defaultValue="0"/>
|
||||
<CheckBoxPreference
|
||||
android:key="collisions"
|
||||
android:title="@string/pref_collisions"
|
||||
android:summary="@string/pref_collisions_sum"
|
||||
android:defaultValue="true"/>
|
||||
<name.nick.jubanka.colleen.SliderPreference
|
||||
android:key="crophoriz"
|
||||
android:title="@string/pref_crophoriz"
|
||||
android:summary="@string/pref_crophoriz_sum"
|
||||
android:defaultValue="336"
|
||||
jub:suffix=""
|
||||
jub:min="320"
|
||||
jub:max="336"/>
|
||||
<name.nick.jubanka.colleen.SliderPreference
|
||||
android:key="cropvert"
|
||||
android:title="@string/pref_cropvert"
|
||||
android:summary="@string/pref_cropvert_sum"
|
||||
android:defaultValue="240"
|
||||
jub:suffix=""
|
||||
jub:min="192"
|
||||
jub:max="240"/>
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory android:title="@string/preftitle_emulation">
|
||||
<ListPreference
|
||||
android:key="machine"
|
||||
android:title="@string/pref_machine"
|
||||
android:summary="@string/pref_machine_sum"
|
||||
android:entries="@array/machine"
|
||||
android:entryValues="@array/machine_values"
|
||||
android:defaultValue="7"/>
|
||||
<Preference
|
||||
android:key="rompath"
|
||||
android:title="@string/pref_rompath"
|
||||
android:summary="@string/pref_rompath_sum"/>
|
||||
<Preference
|
||||
android:key="statepath"
|
||||
android:title="@string/pref_statepath"
|
||||
android:summary="@string/pref_statepath_sum"/>
|
||||
<EditTextPreference
|
||||
android:key="savestate"
|
||||
android:title="@string/pref_savestate"
|
||||
android:summary="@string/pref_savestate_sum_dis"
|
||||
android:dialogMessage="@string/pref_savestate_msg"
|
||||
android:positiveButtonText="@string/save"
|
||||
android:negativeButtonText="@string/cancel"
|
||||
android:lines="1"
|
||||
android:maxLines="1"
|
||||
android:inputType="textNoSuggestions"
|
||||
android:imeOptions="actionDone"
|
||||
android:enabled="false"/>
|
||||
<CheckBoxPreference
|
||||
android:key="basic"
|
||||
android:title="@string/pref_basic"
|
||||
android:summary="@string/pref_basic_sum"
|
||||
android:defaultValue="true"/>
|
||||
<CheckBoxPreference
|
||||
android:key="speed"
|
||||
android:title="@string/pref_speed"
|
||||
android:summary="@string/pref_speed_sum"
|
||||
android:defaultValue="false"/>
|
||||
<CheckBoxPreference
|
||||
android:key="disk"
|
||||
android:title="@string/pref_disk"
|
||||
android:summary="@string/pref_disk_sum"
|
||||
android:defaultValue="true"/>
|
||||
<CheckBoxPreference
|
||||
android:key="sector"
|
||||
android:title="@string/pref_sector"
|
||||
android:summary="@string/pref_sector_sum"
|
||||
android:defaultValue="false"/>
|
||||
<CheckBoxPreference
|
||||
android:key="browser"
|
||||
android:title="@string/pref_browser"
|
||||
android:summary="@string/pref_browser_sum"
|
||||
android:defaultValue="true"/>
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory android:title="@string/preftitle_input">
|
||||
<CheckBoxPreference
|
||||
android:key="softjoy"
|
||||
android:title="@string/pref_softjoy"
|
||||
android:summary="@string/pref_softjoy_sum"
|
||||
android:defaultValue="false"/>
|
||||
<PreferenceScreen
|
||||
android:key="keydefscr"
|
||||
android:title="@string/pref_keydefscr"
|
||||
android:summary="@string/pref_keydefscr_sum"
|
||||
android:dependency="softjoy">
|
||||
<PreferenceCategory android:title="@string/preftitle_keymap_joystick">
|
||||
<name.nick.jubanka.colleen.KeymapPreference
|
||||
android:key="up"
|
||||
android:title="@string/pref_up"
|
||||
android:defaultValue="254"
|
||||
jub:ext="false"/>
|
||||
<name.nick.jubanka.colleen.KeymapPreference
|
||||
android:key="down"
|
||||
android:title="@string/pref_down"
|
||||
android:defaultValue="253"
|
||||
jub:ext="false"/>
|
||||
<name.nick.jubanka.colleen.KeymapPreference
|
||||
android:key="left"
|
||||
android:title="@string/pref_left"
|
||||
android:defaultValue="252"
|
||||
jub:ext="false"/>
|
||||
<name.nick.jubanka.colleen.KeymapPreference
|
||||
android:key="right"
|
||||
android:title="@string/pref_right"
|
||||
android:defaultValue="251"
|
||||
jub:ext="false"/>
|
||||
<name.nick.jubanka.colleen.KeymapPreference
|
||||
android:key="fire"
|
||||
android:title="@string/pref_fire"
|
||||
android:defaultValue="50"
|
||||
jub:ext="false"/>
|
||||
</PreferenceCategory>
|
||||
<PreferenceCategory android:title="@string/preftitle_keymap_actions">
|
||||
<name.nick.jubanka.colleen.KeymapPreference
|
||||
android:key="actiona"
|
||||
android:title="@string/pref_actiona"
|
||||
android:defaultValue="-1,-1"
|
||||
jub:ext="true"/>
|
||||
<name.nick.jubanka.colleen.KeymapPreference
|
||||
android:key="actionb"
|
||||
android:title="@string/pref_actionb"
|
||||
android:defaultValue="-1,-1"
|
||||
jub:ext="true"/>
|
||||
<name.nick.jubanka.colleen.KeymapPreference
|
||||
android:key="actionc"
|
||||
android:title="@string/pref_actionc"
|
||||
android:defaultValue="-1,-1"
|
||||
jub:ext="true"/>
|
||||
<Preference
|
||||
android:key="resetactions"
|
||||
android:title="@string/pref_resetactions"
|
||||
android:summary="@string/pref_resetactions_sum"/>
|
||||
</PreferenceCategory>
|
||||
</PreferenceScreen>
|
||||
<ListPreference
|
||||
android:key="derotkeys"
|
||||
android:title="@string/pref_derotkeys"
|
||||
android:summary="@string/pref_derotkeys_sum"
|
||||
android:entries="@array/derotkeys"
|
||||
android:entryValues="@array/derotkeys_values"
|
||||
android:defaultValue="0"/>
|
||||
<CheckBoxPreference
|
||||
android:key="joyvisible"
|
||||
android:title="@string/pref_joyvisible"
|
||||
android:summary="@string/pref_joyvisible_sum"
|
||||
android:dependency="plandef"
|
||||
android:defaultValue="true"/>
|
||||
<name.nick.jubanka.colleen.SliderPreference
|
||||
android:key="joyopacity"
|
||||
android:title="@string/pref_joyopacity"
|
||||
android:summary="@string/pref_joyopacity_sum"
|
||||
android:dependency="joyvisible"
|
||||
android:defaultValue="25"
|
||||
jub:min="10"
|
||||
jub:max="90"/>
|
||||
<name.nick.jubanka.colleen.SliderPreference
|
||||
android:key="joysize"
|
||||
android:title="@string/pref_joysize"
|
||||
android:summary="@string/pref_joysize_sum"
|
||||
android:dependency="plandef"
|
||||
android:defaultValue="15"
|
||||
jub:min="10"
|
||||
jub:max="32"/>
|
||||
<CheckBoxPreference
|
||||
android:key="joyrighth"
|
||||
android:title="@string/pref_joyrighth"
|
||||
android:summary="@string/pref_joyrighth_sum"
|
||||
android:dependency="plandef"
|
||||
android:defaultValue="false"/>
|
||||
<CheckBoxPreference
|
||||
android:key="anchor"
|
||||
android:title="@string/pref_anchor"
|
||||
android:summary="@string/pref_anchor_sum"
|
||||
android:dependency="plandef"
|
||||
android:defaultValue="false"/>
|
||||
<name.nick.jubanka.colleen.SliderPreference
|
||||
android:key="joymidx"
|
||||
android:title="@string/pref_joymidx"
|
||||
android:summary="@string/pref_joymidx_sum"
|
||||
android:dependency="plandef"
|
||||
android:defaultValue="65"
|
||||
jub:min="50"
|
||||
jub:max="80"/>
|
||||
<name.nick.jubanka.colleen.SliderPreference
|
||||
android:key="joydeadband"
|
||||
android:title="@string/pref_joydeadband"
|
||||
android:summary="@string/pref_joydeadband_sum"
|
||||
android:dependency="plandef"
|
||||
android:defaultValue="35"
|
||||
jub:min="20"
|
||||
jub:max="60"/>
|
||||
<name.nick.jubanka.colleen.SliderPreference
|
||||
android:key="joygrace"
|
||||
android:title="@string/pref_joygrace"
|
||||
android:summary="@string/pref_joygrace_sum"
|
||||
android:dependency="plandef"
|
||||
android:defaultValue="50"
|
||||
jub:min="10"
|
||||
jub:max="99"/>
|
||||
<CheckBoxPreference
|
||||
android:key="paddle"
|
||||
android:title="@string/pref_paddle"
|
||||
android:summary="@string/pref_paddle_sum"
|
||||
android:disableDependentsState="true"
|
||||
android:dependency="plandef"
|
||||
android:defaultValue="false"/>
|
||||
<CheckBoxPreference
|
||||
android:key="plandef"
|
||||
android:title="@string/pref_plandef"
|
||||
android:summary="@string/pref_plandef_sum"
|
||||
android:disableDependentsState="true"
|
||||
android:defaultValue="false"/>
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory android:title="@string/preftitle_sound">
|
||||
<CheckBoxPreference
|
||||
android:key="sound"
|
||||
android:title="@string/pref_sound"
|
||||
android:summary="@string/pref_sound_sum"
|
||||
android:defaultValue="true"/>
|
||||
<ListPreference
|
||||
android:key="mixrate"
|
||||
android:title="@string/pref_mixrate"
|
||||
android:summary="@string/pref_mixrate_sum"
|
||||
android:entries="@array/mixrate"
|
||||
android:entryValues="@array/mixrate_values"
|
||||
android:defaultValue="44100"
|
||||
android:dependency="sound"/>
|
||||
<name.nick.jubanka.colleen.SliderPreference
|
||||
android:key="mixbufsize"
|
||||
android:title="@string/pref_mixbufsize"
|
||||
android:summary="@string/pref_mixbufsize_sum"
|
||||
android:defaultValue="10"
|
||||
jub:suffix="0ms"
|
||||
jub:min="10"
|
||||
jub:max="40"/>
|
||||
<CheckBoxPreference
|
||||
android:key="sound16bit"
|
||||
android:title="@string/pref_sound16bit"
|
||||
android:summary="@string/pref_sound16bit_sum"
|
||||
android:defaultValue="true"
|
||||
android:dependency="sound"/>
|
||||
<CheckBoxPreference
|
||||
android:key="hqpokey"
|
||||
android:title="@string/pref_hqpokey"
|
||||
android:summary="@string/pref_hqpokey_sum"
|
||||
android:defaultValue="false"
|
||||
android:dependency="sound"/>
|
||||
<CheckBoxPreference
|
||||
android:key="forceAT"
|
||||
android:title="@string/pref_forceAT"
|
||||
android:summary="@string/pref_forceAT_sum"
|
||||
android:defaultValue="false"
|
||||
android:dependency="sound"
|
||||
android:enabled="false"/>
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory android:title="@string/preftitle_extras">
|
||||
<Preference
|
||||
android:key="launchpd"
|
||||
android:title="@string/pref_launchpd"
|
||||
android:summary="@string/pref_launchpd_sum"/>
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory android:title="@string/preftitle_about">
|
||||
<Preference
|
||||
android:key="about"
|
||||
android:title="@string/pref_about"
|
||||
android:summary="@string/pref_about_sum"/>
|
||||
<Preference
|
||||
android:key="help"
|
||||
android:title="@string/pref_help"
|
||||
android:summary="@string/pref_help_sum"/>
|
||||
</PreferenceCategory>
|
||||
|
||||
<!--
|
||||
reset to default?
|
||||
save state on exit (state/disk/none)?
|
||||
mouse?
|
||||
-->
|
||||
</PreferenceScreen>
|
||||
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
* A800Renderer.java - opengl graphics frontend to android
|
||||
*
|
||||
* Copyright (C) 2010 Kostas Nakos
|
||||
* Copyright (C) 2010 Atari800 development team (see DOC/CREDITS)
|
||||
*
|
||||
* This file is part of the Atari800 emulator project which emulates
|
||||
* the Atari 400, 800, 800XL, 130XE, and 5200 8-bit computers.
|
||||
*
|
||||
* Atari800 is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Atari800 is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Atari800; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
package name.nick.jubanka.colleen;
|
||||
|
||||
import javax.microedition.khronos.egl.EGLConfig;
|
||||
import javax.microedition.khronos.opengles.GL10;
|
||||
|
||||
import android.opengl.GLSurfaceView;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Bitmap.Config;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.RectF;
|
||||
import android.graphics.Region;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
import android.content.Context;
|
||||
import android.os.Message;
|
||||
import android.os.Handler;
|
||||
|
||||
public final class A800Renderer implements GLSurfaceView.Renderer
|
||||
{
|
||||
public static final int REQ_BROWSER = 1;
|
||||
private static final String TAG = "A800Renderer";
|
||||
private final int OVL_TEXW = 128;
|
||||
private final int OVL_TEXH = 64;
|
||||
private int[] _pix;
|
||||
private Toast _crashtoast;
|
||||
private int _frameret;
|
||||
private Handler _handler = null;
|
||||
|
||||
@Override
|
||||
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
|
||||
_pix = new int[OVL_TEXW * OVL_TEXH];
|
||||
generateOverlays();
|
||||
NativeGetOverlays();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSurfaceChanged(GL10 gl, int w, int h) {
|
||||
gl.glViewport(0, 0, w, h);
|
||||
NativeResize(w, h);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDrawFrame(GL10 gl) {
|
||||
_frameret = NativeRunFrame();
|
||||
if ((_frameret & 1) != 0)
|
||||
_crashtoast.show();
|
||||
else if ((_frameret & 2) != 0) {
|
||||
Log.d(TAG, "Browser request");
|
||||
if (_handler != null)
|
||||
_handler.dispatchMessage(Message.obtain(_handler, REQ_BROWSER));
|
||||
}
|
||||
}
|
||||
|
||||
public void prepareToast(Context c) {
|
||||
_crashtoast = Toast.makeText(c, R.string.cimcrash, Toast.LENGTH_LONG);
|
||||
}
|
||||
|
||||
public void setHandler(Handler h) {
|
||||
_handler = h;
|
||||
}
|
||||
|
||||
private void generateOverlays() {
|
||||
RectF r;
|
||||
|
||||
Paint fill = new Paint(0);
|
||||
fill.setStyle(Paint.Style.FILL);
|
||||
fill.setColor(0xFF0054A8);
|
||||
Paint stroke = new Paint(0);
|
||||
stroke.setStyle(Paint.Style.STROKE);
|
||||
stroke.setStrokeWidth(1);
|
||||
stroke.setColor(0xFF001976);
|
||||
|
||||
Bitmap bmp = Bitmap.createBitmap(OVL_TEXW, OVL_TEXH, Bitmap.Config.ARGB_8888);
|
||||
bmp.eraseColor(0);
|
||||
Canvas can = new Canvas(bmp);
|
||||
|
||||
// Joystick area
|
||||
r = new RectF(0, 0, 63, 63);
|
||||
can.clipRect(0, 0, 64, 64, Region.Op.REPLACE);
|
||||
can.drawRoundRect(r, 6, 6, fill);
|
||||
can.drawRoundRect(r, 6, 6, stroke);
|
||||
|
||||
// Fire/Joy point
|
||||
r = new RectF(67, 3, 77, 13);
|
||||
can.clipRect(64, 0, 79, 15, Region.Op.REPLACE);
|
||||
can.drawOval(r, fill);
|
||||
|
||||
// El texto
|
||||
can.clipRect(64, 16, 128, 64, Region.Op.REPLACE);
|
||||
Paint t = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
t.setColor(0xFF001976);
|
||||
t.setTextSize(10);
|
||||
can.drawColor(0x00ffffff);
|
||||
can.drawText("START", 65, 24, t);
|
||||
can.drawText("SELECT", 65, 34, t);
|
||||
can.drawText("OPTION", 65, 44, t);
|
||||
can.drawText("RESET", 65, 54, t);
|
||||
can.drawText("HELP", 65, 64, t);
|
||||
|
||||
bmp.getPixels(_pix, 0, OVL_TEXW, 0, 0, OVL_TEXW, OVL_TEXH);
|
||||
bmp.recycle();
|
||||
}
|
||||
|
||||
// Native function declarations
|
||||
private native void NativeGetOverlays();
|
||||
private native int NativeRunFrame();
|
||||
private native void NativeResize(int w, int h);
|
||||
}
|
||||
@@ -0,0 +1,265 @@
|
||||
/*
|
||||
* A800view.java - atari screen view
|
||||
*
|
||||
* Copyright (C) 2014 Kostas Nakos
|
||||
* Copyright (C) 2014 Atari800 development team (see DOC/CREDITS)
|
||||
*
|
||||
* This file is part of the Atari800 emulator project which emulates
|
||||
* the Atari 400, 800, 800XL, 130XE, and 5200 8-bit computers.
|
||||
*
|
||||
* Atari800 is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Atari800 is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Atari800; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
package name.nick.jubanka.colleen;
|
||||
|
||||
import android.content.Context;
|
||||
import android.opengl.GLSurfaceView;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.KeyEvent;
|
||||
import android.util.Log;
|
||||
import android.view.KeyCharacterMap;
|
||||
import android.os.Build;
|
||||
import android.widget.Toast;
|
||||
import android.view.View;
|
||||
import android.util.SparseArray;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.view.inputmethod.InputConnection;
|
||||
import android.view.inputmethod.BaseInputConnection;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import static android.view.KeyEvent.*;
|
||||
|
||||
|
||||
public final class A800view extends GLSurfaceView
|
||||
{
|
||||
public static final int KEY_SHIFT = 256;
|
||||
public static final int KEY_CONTROL = 257;
|
||||
public static final int KEY_BACKSPACE = 255;
|
||||
public static final int KEY_UP = 254;
|
||||
public static final int KEY_DOWN = 253;
|
||||
public static final int KEY_LEFT = 252;
|
||||
public static final int KEY_RIGHT = 251;
|
||||
public static final int KEY_FIRE = 250;
|
||||
public static final int KEY_ENTER = 249;
|
||||
public static final int KEY_ESCAPE = 248;
|
||||
public static final int KEY_CENTER = 247;
|
||||
public static final int KEY_BT_X = 246;
|
||||
public static final int KEY_BT_Y = 245;
|
||||
public static final int KEY_BT_L1 = 244;
|
||||
public static final int KEY_BT_R1 = 243;
|
||||
public static final int KEY_BREAK = 242;
|
||||
// keycodes from newer sdks
|
||||
public static final int KC_BUTTON_X = 307;
|
||||
public static final int KC_BUTTON_Y = 308;
|
||||
public static final int KC_BUTTON_L1 = 310;
|
||||
public static final int KC_BUTTON_R1 = 311;
|
||||
|
||||
|
||||
private static final String TAG = "A800View";
|
||||
private A800Renderer _renderer;
|
||||
private KeyCharacterMap _keymap;
|
||||
private int _key, _meta, _hit;
|
||||
private TouchFactory _touchHandler = null;
|
||||
private Toast _toastquit;
|
||||
private Integer _xkey;
|
||||
|
||||
public A800view(Context context) {
|
||||
super(context);
|
||||
|
||||
_renderer = new A800Renderer();
|
||||
setRenderer(_renderer);
|
||||
_renderer.prepareToast(context);
|
||||
_renderer.setHandler(new Handler() {
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
((MainActivity) getContext()).message(msg.what);
|
||||
}
|
||||
});
|
||||
|
||||
setFocusable(true);
|
||||
setFocusableInTouchMode(true);
|
||||
requestFocus();
|
||||
|
||||
_keymap = KeyCharacterMap.load(KeyCharacterMap.BUILT_IN_KEYBOARD);
|
||||
|
||||
if (Integer.parseInt(Build.VERSION.SDK) < Build.VERSION_CODES.ECLAIR)
|
||||
_touchHandler = new SingleTouch();
|
||||
else
|
||||
_touchHandler = new MultiTouch();
|
||||
|
||||
_toastquit = Toast.makeText(context, R.string.pressback, Toast.LENGTH_SHORT);
|
||||
}
|
||||
|
||||
public void pause(boolean p) {
|
||||
setRenderMode(p ? GLSurfaceView.RENDERMODE_WHEN_DIRTY :
|
||||
GLSurfaceView.RENDERMODE_CONTINUOUSLY);
|
||||
}
|
||||
|
||||
// Touch input
|
||||
@Override
|
||||
public boolean onTouchEvent(final MotionEvent ev) {
|
||||
int ret = _touchHandler.onTouchEvent(ev);
|
||||
|
||||
if (Integer.parseInt(Build.VERSION.SDK) >= Build.VERSION_CODES.HONEYCOMB) {
|
||||
MainActivity m = (MainActivity) getContext();
|
||||
if (ret == 2)
|
||||
m._aBar.show(m);
|
||||
else if (ret == 1)
|
||||
m._aBar.hide(m);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
abstract static class TouchFactory {
|
||||
public abstract int onTouchEvent(MotionEvent ev);
|
||||
};
|
||||
|
||||
private static final class SingleTouch extends TouchFactory {
|
||||
private int _x1, _y1, _s1;
|
||||
private int _action, _actioncode;
|
||||
|
||||
|
||||
@Override
|
||||
public int onTouchEvent(final MotionEvent ev) {
|
||||
_action = ev.getAction();
|
||||
_actioncode = _action & MotionEvent.ACTION_MASK;
|
||||
_x1 = (int) ev.getX();
|
||||
_y1 = (int) ev.getY();
|
||||
_s1 = 1;
|
||||
if (_actioncode == MotionEvent.ACTION_UP)
|
||||
_s1 = 0;
|
||||
|
||||
return NativeTouch(_x1, _y1, _s1, -1000, -1000, 0);
|
||||
}
|
||||
}
|
||||
|
||||
private static final class MultiTouch extends TouchFactory {
|
||||
private int _x1, _y1, _s1, _x2, _y2, _s2;
|
||||
private int _action, _actioncode, _ptrcnt;
|
||||
|
||||
@Override
|
||||
public int onTouchEvent(final MotionEvent ev) {
|
||||
_action = ev.getAction();
|
||||
_actioncode = _action & MotionEvent.ACTION_MASK;
|
||||
_ptrcnt = ev.getPointerCount();
|
||||
_x1 = (int) ev.getX(0);
|
||||
_y1 = (int) ev.getY(0);
|
||||
_s1 = 1;
|
||||
if (_ptrcnt > 1) {
|
||||
_x2 = (int) ev.getX(1);
|
||||
_y2 = (int) ev.getY(1);
|
||||
_s2 = 1;
|
||||
} else {
|
||||
_x2 = -1000;
|
||||
_y2 = -1000;
|
||||
_s2 = 0;
|
||||
}
|
||||
if (_actioncode == MotionEvent.ACTION_UP) {
|
||||
_s1 = _s2 = 0;
|
||||
} else if (_actioncode == MotionEvent.ACTION_POINTER_UP) {
|
||||
if ( (_action >> MotionEvent.ACTION_POINTER_ID_SHIFT) == 0)
|
||||
_s1 = 0;
|
||||
else
|
||||
_s2 = 0;
|
||||
}
|
||||
|
||||
return NativeTouch(_x1, _y1, _s1, _x2, _y2, _s2);
|
||||
}
|
||||
}
|
||||
|
||||
// Key input
|
||||
@Override
|
||||
public boolean onKeyDown(int kc, final KeyEvent ev) {
|
||||
return doKey(kc, ev);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onKeyUp(int kc, final KeyEvent ev) {
|
||||
return doKey(kc, ev);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCheckIsTextEditor() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
|
||||
return new BaseInputConnection(this, false) {
|
||||
@Override
|
||||
public boolean deleteSurroundingText(int leftLength, int rightLength) {
|
||||
//Log.d(TAG, "Synthetic del");
|
||||
this.sendKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL));
|
||||
this.sendKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DEL));
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private boolean doKey(int kc, final KeyEvent ev) {
|
||||
_hit =(ev.getAction() == ACTION_DOWN) ? 1 : 0;
|
||||
|
||||
if (kc == KEYCODE_BACK && _hit == 1) {
|
||||
MainActivity m = (MainActivity) getContext();
|
||||
if (_toastquit.getView().getWindowVisibility() == View.VISIBLE) {
|
||||
_toastquit.cancel();
|
||||
m.finish();
|
||||
} else if (m._aBar.isShowing(m))
|
||||
m._aBar.hide(m);
|
||||
else
|
||||
_toastquit.show();
|
||||
return true;
|
||||
}
|
||||
|
||||
_xkey = XLATKEYS.get(kc);
|
||||
if (_xkey != null)
|
||||
_key = _xkey.intValue();
|
||||
else {
|
||||
_meta = ev.getMetaState();
|
||||
if ((_meta & KeyEvent.META_SHIFT_RIGHT_ON) == KeyEvent.META_SHIFT_RIGHT_ON)
|
||||
_meta &= ~(KeyEvent.META_SHIFT_RIGHT_ON | KeyEvent.META_SHIFT_ON);
|
||||
_key = _keymap.get(kc, _meta);
|
||||
if (_key == 0)
|
||||
return false;
|
||||
}
|
||||
|
||||
//Log.d(TAG, String.format("key %d %d -> %d", ev.getAction(), kc, _key));
|
||||
|
||||
NativeKey(_key, _hit);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private native static int NativeTouch(int x1, int y1, int s1, int x2, int y2, int s2);
|
||||
private native void NativeKey(int keycode, int status);
|
||||
|
||||
public static final SparseArray<Integer> XLATKEYS = new SparseArray<Integer>(14);
|
||||
static {
|
||||
XLATKEYS.put(KEYCODE_DPAD_UP, KEY_UP);
|
||||
XLATKEYS.put(KEYCODE_DPAD_DOWN, KEY_DOWN);
|
||||
XLATKEYS.put(KEYCODE_DPAD_LEFT, KEY_LEFT);
|
||||
XLATKEYS.put(KEYCODE_DPAD_RIGHT, KEY_RIGHT);
|
||||
XLATKEYS.put(KEYCODE_DPAD_CENTER, KEY_BREAK);
|
||||
XLATKEYS.put(KEYCODE_SEARCH, KEY_FIRE);
|
||||
XLATKEYS.put(KEYCODE_SHIFT_LEFT, KEY_SHIFT);
|
||||
XLATKEYS.put(KEYCODE_SHIFT_RIGHT, KEY_CONTROL);
|
||||
XLATKEYS.put(KEYCODE_DEL, KEY_BACKSPACE);
|
||||
XLATKEYS.put(KEYCODE_ENTER, KEY_ENTER);
|
||||
XLATKEYS.put(KC_BUTTON_X, KEY_BT_X);
|
||||
XLATKEYS.put(KC_BUTTON_Y, KEY_BT_Y);
|
||||
XLATKEYS.put(KC_BUTTON_L1, KEY_BT_L1);
|
||||
XLATKEYS.put(KC_BUTTON_R1, KEY_BT_R1);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
/*
|
||||
* AudioThread.java - pushes audio to android
|
||||
*
|
||||
* Copyright (C) 2014 Kostas Nakos
|
||||
* Copyright (C) 2014 Atari800 development team (see DOC/CREDITS)
|
||||
*
|
||||
* This file is part of the Atari800 emulator project which emulates
|
||||
* the Atari 400, 800, 800XL, 130XE, and 5200 8-bit computers.
|
||||
*
|
||||
* Atari800 is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Atari800 is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Atari800; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
package name.nick.jubanka.colleen;
|
||||
|
||||
import android.media.AudioTrack;
|
||||
import android.media.AudioFormat;
|
||||
import android.media.AudioManager;
|
||||
import android.util.Log;
|
||||
|
||||
|
||||
public final class AudioThread extends Thread
|
||||
{
|
||||
private static final String TAG = "A800AudioThread";
|
||||
|
||||
private AudioTrack _at;
|
||||
private int _bufsize;
|
||||
private byte[] _buffer;
|
||||
private boolean _quit;
|
||||
private int _chunk;
|
||||
private boolean _initok;
|
||||
private boolean _pause;
|
||||
private boolean _useOSL;
|
||||
|
||||
public AudioThread(int rate, int bytes, int bufsizems, boolean ntsc) {
|
||||
NativeOSLSoundInit();
|
||||
_useOSL = NativeOSLSound();
|
||||
if (_useOSL) {
|
||||
Log.d(TAG, "Disabling AudioThread. Using OSL instead");
|
||||
return;
|
||||
}
|
||||
|
||||
int format = bytes == 1 ? AudioFormat.ENCODING_PCM_8BIT : AudioFormat.ENCODING_PCM_16BIT;
|
||||
int minbuf = AudioTrack.getMinBufferSize(rate, AudioFormat.CHANNEL_OUT_MONO, format);
|
||||
int hardmin = (int) ( ((float) rate * bytes) / ((float) (1000.0f / bufsizems)) );
|
||||
_chunk = (rate * bytes / (ntsc ? 60 : 50) + 3) / 4 * 4;
|
||||
_bufsize = (hardmin > minbuf) ? hardmin : minbuf;
|
||||
_bufsize = ((_bufsize + _chunk - 1) / _chunk * _chunk + 3) / 4 * 4;
|
||||
_at = new AudioTrack(AudioManager.STREAM_MUSIC, rate, AudioFormat.CHANNEL_CONFIGURATION_MONO,
|
||||
format, _bufsize, AudioTrack.MODE_STREAM);
|
||||
_buffer = new byte[_bufsize];
|
||||
Log.d( TAG, String.format(
|
||||
"Mixing audio at %dHz, %dbit, buffer size %d (%d ms) [requested=%d(%dms), minbuf=%d(%dms)], chunk %d",
|
||||
rate, 8 * bytes, _bufsize, (int) (((float) _bufsize)/((float) rate * bytes) * 1000),
|
||||
hardmin, (int) (((float) hardmin)/((float) rate * bytes) * 1000),
|
||||
minbuf, (int) (((float) minbuf)/((float) rate * bytes) * 1000),
|
||||
_chunk
|
||||
) );
|
||||
_quit = false;
|
||||
_pause = false;
|
||||
this.setDaemon(true);
|
||||
if (_at.getState() != AudioTrack.STATE_INITIALIZED) {
|
||||
Log.e(TAG, "Cannot initialize audio");
|
||||
_initok = false;
|
||||
} else
|
||||
_initok = true;
|
||||
}
|
||||
|
||||
public void pause(boolean p) {
|
||||
if (_useOSL) {
|
||||
NativeOSLSoundPause(p);
|
||||
return;
|
||||
}
|
||||
|
||||
_pause = p;
|
||||
if (!_initok) return;
|
||||
if (p) {
|
||||
_at.pause();
|
||||
Log.d(TAG, "Audio paused");
|
||||
} else {
|
||||
_at.play();
|
||||
Log.d(TAG, "Audio resumed");
|
||||
}
|
||||
}
|
||||
|
||||
public void run() {
|
||||
int offset = 0;
|
||||
int len, w, chunk;
|
||||
|
||||
if (_useOSL) {
|
||||
NativeOSLSoundPause(false);
|
||||
return;
|
||||
}
|
||||
if (!_initok) return;
|
||||
|
||||
Log.d(TAG, "Running");
|
||||
NativeSoundInit(_bufsize);
|
||||
_at.play();
|
||||
chunk = _chunk / 2;
|
||||
|
||||
try {
|
||||
while (!_quit) {
|
||||
if (_pause) {
|
||||
sleep(100);
|
||||
continue;
|
||||
}
|
||||
|
||||
len = _bufsize - offset;
|
||||
if (len > chunk)
|
||||
len = chunk;
|
||||
else if (len <= 0) {
|
||||
len = chunk;
|
||||
offset = 0;
|
||||
}
|
||||
NativeSoundUpdate(offset, len);
|
||||
w = 0;
|
||||
while (w < len)
|
||||
w += _at.write(_buffer, offset + w, len - w);
|
||||
offset += w;
|
||||
}
|
||||
} catch (InterruptedException ex) {
|
||||
}
|
||||
|
||||
Log.d(TAG, "Exit");
|
||||
NativeSoundExit();
|
||||
_at.stop();
|
||||
_at.release();
|
||||
}
|
||||
|
||||
public void interrupt() {
|
||||
if (_useOSL) {
|
||||
NativeOSLSoundExit();
|
||||
return;
|
||||
}
|
||||
|
||||
Log.d(TAG, "Audio thread exit via interrupt");
|
||||
_quit = true;
|
||||
}
|
||||
|
||||
// Native function declarations
|
||||
private native void NativeSoundInit(int size);
|
||||
private native void NativeSoundUpdate(int offset, int length);
|
||||
private native void NativeSoundExit();
|
||||
private native boolean NativeOSLSound();
|
||||
private native void NativeOSLSoundInit();
|
||||
private native void NativeOSLSoundExit();
|
||||
private native void NativeOSLSoundPause(boolean paused);
|
||||
}
|
||||
@@ -0,0 +1,449 @@
|
||||
/*
|
||||
* FileSelector.java - the file selector activity
|
||||
*
|
||||
* Copyright (C) 2010 Kostas Nakos
|
||||
* Copyright (C) 2010 Atari800 development team (see DOC/CREDITS)
|
||||
*
|
||||
* This file is part of the Atari800 emulator project which emulates
|
||||
* the Atari 400, 800, 800XL, 130XE, and 5200 8-bit computers.
|
||||
*
|
||||
* Atari800 is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Atari800 is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Atari800; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
package name.nick.jubanka.colleen;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
import java.util.Comparator;
|
||||
import java.util.Set;
|
||||
import java.util.HashSet;
|
||||
|
||||
import android.app.ListActivity;
|
||||
import android.os.Bundle;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.os.Environment;
|
||||
import android.view.View;
|
||||
import android.widget.ListView;
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.view.LayoutInflater;
|
||||
import android.content.Context;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
import android.widget.ImageView;
|
||||
import android.content.SharedPreferences;
|
||||
import android.widget.AdapterView;
|
||||
import android.content.DialogInterface;
|
||||
import android.app.AlertDialog;
|
||||
import android.util.Log;
|
||||
import android.app.ProgressDialog;
|
||||
import android.os.AsyncTask;
|
||||
import android.app.Dialog;
|
||||
import android.widget.Toast;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.widget.SearchView;
|
||||
import android.text.TextUtils;
|
||||
import android.os.Build;
|
||||
|
||||
|
||||
public final class FileSelector extends ListActivity implements AdapterView.OnItemLongClickListener,
|
||||
View.OnClickListener
|
||||
{
|
||||
public static final String ACTION_OPEN_FILE = "jubanka.intent.OPENFILE";
|
||||
public static final String ACTION_OPEN_PATH = "jubanka.intent.OPENPATH";
|
||||
|
||||
private static final String TAG = "FileSelector";
|
||||
private static final String SAVED_PATH = "SavedPath";
|
||||
private static final String SAVED_POS = "SavedPos";
|
||||
|
||||
private static final int DLG_MOUNT = 0;
|
||||
private static final int DLG_WARNING = 1;
|
||||
|
||||
private IconArrayAdapter _ad = null;
|
||||
private File _curdir;
|
||||
private ListDirTask _task = null;
|
||||
private boolean _pathsel = false;
|
||||
private static String _mntfname = null;
|
||||
private static String _drive1fname = null;
|
||||
private SearchNull _srchView;
|
||||
|
||||
private final class IconArrayAdapter extends ArrayAdapter<String> {
|
||||
LayoutInflater _inf = null;
|
||||
|
||||
public IconArrayAdapter(Context context, int textViewResourceId) {
|
||||
super(context, textViewResourceId);
|
||||
_inf = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int pos, View view, ViewGroup par) {
|
||||
if (view == null)
|
||||
view = _inf.inflate(R.layout.file_selector_row, null);
|
||||
String itm = getItem(pos);
|
||||
((TextView) view.findViewById(R.id.fsel_text)).setText(itm);
|
||||
((ImageView) view.findViewById(R.id.fsel_image)).setImageResource(
|
||||
itm.endsWith("/") ? R.drawable.folder : 0 );
|
||||
|
||||
return view;
|
||||
}
|
||||
}
|
||||
|
||||
private static class SearchNull {
|
||||
public boolean onCreateOptionsMenu(Menu menu, ListActivity a) { return false; };
|
||||
public void reset(ListActivity a) {};
|
||||
}
|
||||
|
||||
private static final class SearchHelp extends SearchNull implements SearchView.OnQueryTextListener {
|
||||
ListActivity _actv;
|
||||
private MenuItem _msrch = null;
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu, ListActivity a) {
|
||||
_actv = a;
|
||||
MenuInflater inf = a.getMenuInflater();
|
||||
inf.inflate(R.menu.fsel_menu, menu);
|
||||
_msrch = menu.findItem(R.id.menu_search);
|
||||
((SearchView) _msrch.getActionView()).setOnQueryTextListener(this);
|
||||
return true;
|
||||
};
|
||||
|
||||
@Override
|
||||
public boolean onQueryTextChange(String newText) {
|
||||
if (TextUtils.isEmpty(newText))
|
||||
_actv.getListView().clearTextFilter();
|
||||
else
|
||||
_actv.getListView().setFilterText(newText.toString());
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onQueryTextSubmit(String query) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset(ListActivity a) {
|
||||
a.getListView().clearTextFilter();
|
||||
if (_msrch != null)
|
||||
if (Integer.parseInt(Build.VERSION.SDK) >= Build.VERSION_CODES.ICE_CREAM_SANDWICH)
|
||||
_msrch.collapseActionView();
|
||||
else
|
||||
((SearchView) _msrch.getActionView()).setIconified(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
if (Integer.parseInt(Build.VERSION.SDK) >= Build.VERSION_CODES.HONEYCOMB)
|
||||
_srchView = new SearchHelp();
|
||||
else
|
||||
_srchView = new SearchNull();
|
||||
|
||||
ListView lv = getListView();
|
||||
_pathsel = getIntent().getAction().equals(ACTION_OPEN_PATH);
|
||||
|
||||
if (_pathsel) {
|
||||
LayoutInflater inf = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
View v = inf.inflate(R.layout.file_selector_footer, null);
|
||||
setContentView(v);
|
||||
findViewById(R.id.fsel_ok).setOnClickListener(this);
|
||||
findViewById(R.id.fsel_cancel).setOnClickListener(this);
|
||||
} else
|
||||
lv.setOnItemLongClickListener(this);
|
||||
|
||||
lv.setTextFilterEnabled(true);
|
||||
lv.setFastScrollEnabled(true);
|
||||
|
||||
SharedPreferences prefs = getPreferences(Context.MODE_PRIVATE);
|
||||
String oldpath = _pathsel ? (getIntent().getData() != null ? getIntent().getData().getPath() : null)
|
||||
: prefs.getString(SAVED_PATH, null);
|
||||
listDirectory((oldpath != null) ? new File(oldpath) : Environment.getExternalStorageDirectory(),
|
||||
_pathsel ? 0 : prefs.getInt(SAVED_POS, 0), getLastNonConfigurationInstance());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
if (!_pathsel) {
|
||||
SharedPreferences prefs = getPreferences(Context.MODE_PRIVATE);
|
||||
SharedPreferences.Editor edit = prefs.edit();
|
||||
edit.putString(SAVED_PATH, _curdir.getAbsolutePath());
|
||||
edit.putInt(SAVED_POS, getListView().getFirstVisiblePosition());
|
||||
edit.commit();
|
||||
}
|
||||
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
if (_task != null) _task.cancel(true); // you rotate, you lose
|
||||
_task = null;
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object onRetainNonConfigurationInstance() {
|
||||
String[] ret = null;
|
||||
if (_task == null && _ad != null && _ad.getCount() > 0) {
|
||||
int cnt = _ad.getCount();
|
||||
ret = new String[cnt];
|
||||
for (int i = 0; i < cnt; i++)
|
||||
ret[i] = _ad.getItem(i);
|
||||
}
|
||||
return (Object) ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
switch (v.getId()) {
|
||||
case R.id.fsel_ok:
|
||||
setResult(Activity.RESULT_OK, new Intent(MainActivity.ACTION_SET_ROMPATH,
|
||||
Uri.fromFile(_curdir)));
|
||||
finish();
|
||||
break;
|
||||
case R.id.fsel_cancel:
|
||||
setResult(Activity.RESULT_CANCELED);
|
||||
finish();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onListItemClick(ListView l, View v, int pos, long id) {
|
||||
String fname = _ad.getItem(pos);
|
||||
if (fname.startsWith("../"))
|
||||
listDirectory(_curdir.getParentFile(), 0, null);
|
||||
else if (fname.endsWith("/"))
|
||||
listDirectory(new File(_curdir, fname), 0, null);
|
||||
else if (!_pathsel) {
|
||||
_drive1fname = null;
|
||||
setResult(Activity.RESULT_OK, new Intent(MainActivity.ACTION_INSERT_REBOOT,
|
||||
Uri.fromFile(new File(_curdir, fname))));
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onItemLongClick(AdapterView<?> l, View v, final int pos, long id) {
|
||||
_mntfname = _ad.getItem(pos);
|
||||
if (_mntfname.endsWith("/"))
|
||||
return true;
|
||||
if (!NativeIsDisk(_curdir + "/" + _mntfname)) {
|
||||
showDialog(DLG_WARNING);
|
||||
return true;
|
||||
}
|
||||
showDialog(DLG_MOUNT);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Dialog onCreateDialog(int id) {
|
||||
Dialog d;
|
||||
|
||||
switch (id) {
|
||||
case DLG_MOUNT:
|
||||
CharSequence[] items = new CharSequence[5];
|
||||
String[] drives = NativeGetDrvFnames();
|
||||
for (int i = 0; i < 4; i++)
|
||||
items[i] = new StringBuilder(drives[i]);
|
||||
items[4] = getString(R.string.unmountall);
|
||||
d = new AlertDialog.Builder(this)
|
||||
.setTitle(R.string.mountdisk)
|
||||
.setCancelable(true)
|
||||
.setItems(items, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface d, int i) {
|
||||
if (i < 4) {
|
||||
NativeRunAtariProgram(_curdir + "/" + _mntfname, i + 1, 0);
|
||||
if (i == 0)
|
||||
_drive1fname = _curdir + "/" + _mntfname;
|
||||
Toast.makeText(FileSelector.this,
|
||||
String.format(getString(R.string.mountinsertdisk), _mntfname, i + 1),
|
||||
Toast.LENGTH_SHORT)
|
||||
.show();
|
||||
} else {
|
||||
NativeUnmountAll();
|
||||
_drive1fname = null;
|
||||
}
|
||||
_mntfname = null;
|
||||
dismissDialog(DLG_MOUNT);
|
||||
}
|
||||
})
|
||||
.create();
|
||||
break;
|
||||
|
||||
case DLG_WARNING:
|
||||
d = new AlertDialog.Builder(FileSelector.this)
|
||||
.setTitle(R.string.warning)
|
||||
.setIcon(android.R.drawable.ic_dialog_alert)
|
||||
.setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface d, int i) {
|
||||
dismissDialog(DLG_WARNING);
|
||||
showDialog(DLG_MOUNT);
|
||||
}
|
||||
})
|
||||
.setNegativeButton(R.string.no, null)
|
||||
.setMessage(String.format(getString(R.string.mountnodisk), _mntfname))
|
||||
.create();
|
||||
break;
|
||||
|
||||
default:
|
||||
d = null;
|
||||
}
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPrepareDialog(int id, Dialog d) {
|
||||
switch (id) {
|
||||
case DLG_MOUNT:
|
||||
String[] drives = NativeGetDrvFnames();
|
||||
StringBuilder itemtxt;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
itemtxt = (StringBuilder) ((AlertDialog) d).getListView().getAdapter().getItem(i);
|
||||
itemtxt.delete(0, itemtxt.length());
|
||||
itemtxt.append(drives[i]);
|
||||
}
|
||||
((AlertDialog) d).getListView().invalidateViews();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
return _srchView.onCreateOptionsMenu(menu, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void finish() {
|
||||
if (!_pathsel) {
|
||||
if (_drive1fname != null) {
|
||||
setResult(Activity.RESULT_OK, new Intent(MainActivity.ACTION_INSERT_ONLY,
|
||||
Uri.fromFile(new File(_drive1fname))));
|
||||
}
|
||||
}
|
||||
super.finish();
|
||||
}
|
||||
|
||||
private void listDirectory(File dir, int pos, Object retain) {
|
||||
if (_ad != null) _ad.clear();
|
||||
_srchView.reset(this);
|
||||
setTitle(getString(_pathsel ? R.string.fsel_opendir : R.string.fsel_openfile)
|
||||
+ " " + dir.getAbsolutePath());
|
||||
_curdir = dir;
|
||||
|
||||
if (retain == null)
|
||||
_task = (ListDirTask) new ListDirTask(pos).execute(dir);
|
||||
else {
|
||||
_ad = new IconArrayAdapter(this, R.layout.file_selector_row);
|
||||
for (String str: (String[]) retain)
|
||||
_ad.add(str);
|
||||
|
||||
setListAdapter(_ad);
|
||||
if (pos > _ad.getCount()) pos = _ad.getCount();
|
||||
setSelection(pos);
|
||||
}
|
||||
}
|
||||
|
||||
private final class ListDirTask extends AsyncTask<File, Void, IconArrayAdapter>
|
||||
{
|
||||
ProgressDialog _pdlg;
|
||||
int _position;
|
||||
|
||||
public ListDirTask(int pos) {
|
||||
_position = pos;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPreExecute() {
|
||||
_pdlg = ProgressDialog.show(FileSelector.this, "", getString(R.string.loadingdir), true);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(IconArrayAdapter res) {
|
||||
_ad = res;
|
||||
setListAdapter(_ad);
|
||||
if (_position > res.getCount()) _position = res.getCount();
|
||||
setSelection(_position);
|
||||
|
||||
try {
|
||||
_pdlg.dismiss();
|
||||
} catch (Exception e) {
|
||||
Log.d(TAG, "Leaked pdialog handle");
|
||||
}
|
||||
_task = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IconArrayAdapter doInBackground(File... files) {
|
||||
IconArrayAdapter flst = new IconArrayAdapter(FileSelector.this, R.layout.file_selector_row);
|
||||
File dir = files[0];
|
||||
if (!dir.toString().equals("/"))
|
||||
flst.add("../");
|
||||
|
||||
File[] lst = dir.listFiles(new FileFilter() {
|
||||
@Override
|
||||
public boolean accept(File file) {
|
||||
String f = file.getName().toLowerCase();
|
||||
int l = f.length();
|
||||
return file.isDirectory() || (l > 3 && EXTENSIONS.contains(f.substring(l - 3)));
|
||||
}
|
||||
});
|
||||
if (lst == null) return flst;
|
||||
|
||||
for (File f: lst) {
|
||||
if (f.isDirectory())
|
||||
flst.add(f.getName() + "/");
|
||||
else
|
||||
flst.add(f.getName());
|
||||
}
|
||||
|
||||
flst.sort(new Comparator<String>() {
|
||||
@Override
|
||||
public int compare(String s1, String s2) {
|
||||
boolean s1b = s1.endsWith("/");
|
||||
boolean s2b = s2.endsWith("/");
|
||||
if (s1b && !s2b) return -1;
|
||||
if (s2b && !s1b) return 1;
|
||||
return s1.compareToIgnoreCase(s2);
|
||||
}
|
||||
});
|
||||
return flst;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private native boolean NativeIsDisk(String img);
|
||||
private native int NativeRunAtariProgram(String img, int drive, int reboot);
|
||||
private native String[] NativeGetDrvFnames();
|
||||
private native void NativeUnmountAll();
|
||||
|
||||
private static final Set<String> EXTENSIONS = new HashSet<String>(13);
|
||||
static {
|
||||
EXTENSIONS.add("atr"); EXTENSIONS.add("atz"); EXTENSIONS.add("xfd");
|
||||
EXTENSIONS.add("dcm"); EXTENSIONS.add("xfz"); EXTENSIONS.add("xex");
|
||||
EXTENSIONS.add("cas"); EXTENSIONS.add("rom"); EXTENSIONS.add("bin");
|
||||
EXTENSIONS.add("car"); EXTENSIONS.add("a8s"); EXTENSIONS.add("com");
|
||||
EXTENSIONS.add("exe");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,308 @@
|
||||
/*
|
||||
* KeymapPreference.java - Even simpler preference for mapping keys
|
||||
*
|
||||
* Copyright (C) 2010 Kostas Nakos
|
||||
* Copyright (C) 2010 Atari800 development team (see DOC/CREDITS)
|
||||
*
|
||||
* This file is part of the Atari800 emulator project which emulates
|
||||
* the Atari 400, 800, 800XL, 130XE, and 5200 8-bit computers.
|
||||
*
|
||||
* Atari800 is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Atari800 is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Atari800; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
package name.nick.jubanka.colleen;
|
||||
|
||||
import android.preference.DialogPreference;
|
||||
import android.content.Context;
|
||||
import android.view.KeyCharacterMap;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.KeyEvent;
|
||||
import android.content.res.TypedArray;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.content.DialogInterface;
|
||||
import android.os.Bundle;
|
||||
import android.app.Dialog;
|
||||
import android.app.AlertDialog;
|
||||
import android.util.SparseArray;
|
||||
import android.widget.TextView;
|
||||
import android.R.style;
|
||||
import android.util.TypedValue;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.inputmethod.InputConnection;
|
||||
import android.view.inputmethod.BaseInputConnection;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import static android.view.KeyEvent.*;
|
||||
import static name.nick.jubanka.colleen.A800view.*;
|
||||
|
||||
|
||||
public final class KeymapPreference extends DialogPreference
|
||||
{
|
||||
private static final String TAG = "KeyPreference";
|
||||
|
||||
private static final int DEFKEY = 'a';
|
||||
private static final String DEFKEYEXT = "-1,-1";
|
||||
private static final int EXTSTR_ACTION = 0;
|
||||
private static final int EXTSTR_KEY = 1;
|
||||
|
||||
private KeyCharacterMap _keymap;
|
||||
private int _def;
|
||||
private String _defext = null;
|
||||
private boolean _extended = false;
|
||||
|
||||
public KeymapPreference(Context c, AttributeSet a) {
|
||||
super(c, a);
|
||||
_keymap = KeyCharacterMap.load(KeyCharacterMap.BUILT_IN_KEYBOARD);
|
||||
|
||||
_extended = Boolean.parseBoolean( c.obtainStyledAttributes(a, R.styleable.KeymapPreference)
|
||||
.getString(R.styleable.KeymapPreference_ext) );
|
||||
|
||||
setNegativeButtonText(R.string.cancel);
|
||||
setDialogTitle(getTitle());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object onGetDefaultValue(TypedArray a, int i) {
|
||||
try {
|
||||
_def = a.getInt(i, DEFKEY);
|
||||
return _def;
|
||||
} catch (NumberFormatException e) {
|
||||
_defext = a.getString(i);
|
||||
if (_defext == null)
|
||||
_defext = DEFKEYEXT;
|
||||
return _defext;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSetInitialValue(boolean restore, Object def) {
|
||||
if (!restore)
|
||||
if (!_extended)
|
||||
persistInt((Integer) def);
|
||||
else
|
||||
persistString((String) def);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void showDialog(Bundle state) {
|
||||
super.showDialog(state);
|
||||
Dialog d = getDialog();
|
||||
d.takeKeyEvents(true);
|
||||
((AlertDialog) d).getButton(DialogInterface.BUTTON_POSITIVE).setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected View onCreateDialogView() {
|
||||
View v = new SnoopTextView(getContext());
|
||||
return v;
|
||||
}
|
||||
|
||||
private final class SnoopTextView extends TextView
|
||||
{
|
||||
public SnoopTextView(Context c) {
|
||||
super(c);
|
||||
setText( (!_extended) ? R.string.pref_keymapmsg : R.string.pref_keymapmsg1);
|
||||
setCursorVisible(false);
|
||||
int pad = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
|
||||
(float) 10,
|
||||
c.getResources().getDisplayMetrics());
|
||||
setPadding(pad, pad, pad, pad);
|
||||
setFocusable(true);
|
||||
setFocusableInTouchMode(true);
|
||||
requestFocus();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCheckIsTextEditor() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputConnection onCreateInputConnection (EditorInfo outAttrs) {
|
||||
return new BaseInputConnection(this, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onKeyDown(int kc, KeyEvent ev) {
|
||||
Log.d(TAG, "key " + kc);
|
||||
|
||||
for (int res: RESKEYS)
|
||||
if (res == kc)
|
||||
return false;
|
||||
|
||||
final int k = xlatKey(kc);
|
||||
|
||||
if (k == 0)
|
||||
return false;
|
||||
|
||||
if (k == getKeymap() && !_extended) {
|
||||
getDialog().dismiss();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (callChangeListener(new Integer(k)) == false) {
|
||||
new AlertDialog.Builder(getContext())
|
||||
.setTitle(R.string.warning)
|
||||
.setIcon(android.R.drawable.ic_dialog_alert)
|
||||
.setMessage(R.string.pref_keymapdupmsg)
|
||||
.setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface d, int i) {
|
||||
callChangeListener(new Integer(-k));
|
||||
setKeymap(k);
|
||||
d.dismiss();
|
||||
if (!_extended) {
|
||||
Dialog d1 = getDialog();
|
||||
if (d1 != null)
|
||||
d1.dismiss();
|
||||
} else
|
||||
showExtDialog();
|
||||
}
|
||||
})
|
||||
.setNegativeButton(R.string.no, new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface d, int i) {
|
||||
Dialog d1 = getDialog();
|
||||
if (d1 != null)
|
||||
d1.dismiss();
|
||||
}
|
||||
})
|
||||
.show();
|
||||
return true;
|
||||
}
|
||||
|
||||
setKeymap(k);
|
||||
if (!_extended)
|
||||
getDialog().dismiss();
|
||||
else
|
||||
showExtDialog();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private void showExtDialog()
|
||||
{
|
||||
LayoutInflater inf = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
new AlertDialog.Builder(getContext())
|
||||
.setTitle(getTitle())
|
||||
.setView(inf.inflate(R.layout.extended_keymap, null))
|
||||
.setCancelable(false)
|
||||
.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface d, int i) {
|
||||
CharSequence txt = ((TextView) ((Dialog) d).findViewById(R.id.keyinput)).getText();
|
||||
if (txt == null || txt.length() != 1) {
|
||||
getDialog().dismiss();
|
||||
return;
|
||||
}
|
||||
persistString( buildExtPref(parseExtPref(EXTSTR_ACTION), (int) txt.charAt(0)) );
|
||||
updateSum();
|
||||
d.dismiss();
|
||||
getDialog().dismiss();
|
||||
}
|
||||
})
|
||||
.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface d, int i) {
|
||||
getDialog().dismiss();
|
||||
}
|
||||
})
|
||||
.show();
|
||||
}
|
||||
|
||||
public void updateSum() {
|
||||
StringBuilder str = new StringBuilder();
|
||||
str.append( getContext()
|
||||
.getString(_extended ? R.string.pref_keymap_controller : R.string.pref_keymap_current) );
|
||||
str.append(" ");
|
||||
str.append( getKeyname(getKeymap()) );
|
||||
if (_extended) {
|
||||
str.append(" ");
|
||||
str.append( getContext().getString(R.string.pref_keymap_mappedto) );
|
||||
str.append(" ");
|
||||
str.append( getKeyname(parseExtPref(EXTSTR_KEY)) );
|
||||
}
|
||||
setSummary(str);
|
||||
}
|
||||
|
||||
private int xlatKey(int kc) {
|
||||
int k;
|
||||
|
||||
Integer xlat = A800view.XLATKEYS.get(kc);
|
||||
if (xlat != null)
|
||||
k = xlat.intValue();
|
||||
else
|
||||
k = _keymap.get(kc, 0);
|
||||
return k;
|
||||
}
|
||||
|
||||
public void setKeymap(int k) {
|
||||
if (!_extended)
|
||||
persistInt(k);
|
||||
else
|
||||
persistString( buildExtPref(k, parseExtPref(EXTSTR_KEY)) );
|
||||
updateSum();
|
||||
}
|
||||
|
||||
public void setDefaultKeymap() {
|
||||
if (!_extended) return;
|
||||
persistString(DEFKEYEXT);
|
||||
updateSum();
|
||||
}
|
||||
|
||||
public int getKeymap() {
|
||||
if (!_extended)
|
||||
return getPersistedInt(-128) == -128 ? _def : getPersistedInt(-1);
|
||||
else
|
||||
return parseExtPref(EXTSTR_ACTION);
|
||||
}
|
||||
|
||||
private int parseExtPref(int part) {
|
||||
String str = getPersistedString(null);
|
||||
return Integer.parseInt( ((str != null) ? str : _defext).split(",")[part] );
|
||||
}
|
||||
|
||||
private String buildExtPref(int k1, int k2) {
|
||||
return Integer.toString(k1) + "," + Integer.toString(k2);
|
||||
}
|
||||
|
||||
private String getKeyname(int k) {
|
||||
String name = null;
|
||||
name = KEYNAMES.get(k);
|
||||
if (name != null) return name;
|
||||
if (k > 31 && k < 127) return Character.toString((char) k);
|
||||
return "ASCII " + k;
|
||||
}
|
||||
|
||||
// Real programmers *hate* data entry ;-)
|
||||
private static final SparseArray<String> KEYNAMES = new SparseArray<String>(13);
|
||||
static {
|
||||
KEYNAMES.put(-1, "None");
|
||||
KEYNAMES.put(' ', "Space");
|
||||
KEYNAMES.put(KEY_DOWN, "Down arrow");
|
||||
KEYNAMES.put(KEY_LEFT, "Left arrow");
|
||||
KEYNAMES.put(KEY_RIGHT, "Right arrow");
|
||||
KEYNAMES.put(KEY_UP, "Up arrow");
|
||||
KEYNAMES.put(KEY_ENTER, "Enter");
|
||||
KEYNAMES.put(KEY_BACKSPACE, "Del");
|
||||
KEYNAMES.put(KEY_BT_X, "Button X");
|
||||
KEYNAMES.put(KEY_BT_Y, "Button Y");
|
||||
KEYNAMES.put(KEY_BT_L1, "Button L1");
|
||||
KEYNAMES.put(KEY_BT_R1, "Button R1");
|
||||
KEYNAMES.put(KEY_BREAK, "DPAD Enter");
|
||||
}
|
||||
private static final int[] RESKEYS = {
|
||||
KEYCODE_SHIFT_LEFT, KEYCODE_SHIFT_RIGHT, KEYCODE_VOLUME_UP, KEYCODE_VOLUME_DOWN,
|
||||
KEYCODE_MENU, KEYCODE_SEARCH, KEYCODE_BACK, KEYCODE_HOME, KEYCODE_POWER,
|
||||
KEYCODE_CALL, KEYCODE_ENDCALL
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,870 @@
|
||||
/*
|
||||
* MainActivity.java - activity entry point for atari800
|
||||
*
|
||||
* Copyright (C) 2014 Kostas Nakos
|
||||
* Copyright (C) 2014 Atari800 development team (see DOC/CREDITS)
|
||||
*
|
||||
* This file is part of the Atari800 emulator project which emulates
|
||||
* the Atari 400, 800, 800XL, 130XE, and 5200 8-bit computers.
|
||||
*
|
||||
* Atari800 is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Atari800 is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Atari800; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
package name.nick.jubanka.colleen;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.regex.Matcher;
|
||||
import java.io.File;
|
||||
import java.util.Map;
|
||||
import java.util.EnumMap;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.os.Bundle;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
import android.content.SharedPreferences;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.app.Dialog;
|
||||
import android.app.AlertDialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.widget.TextView;
|
||||
import android.R.style;
|
||||
import android.widget.ScrollView;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.net.Uri;
|
||||
import android.text.Html;
|
||||
import android.text.method.LinkMovementMethod;
|
||||
import android.app.ActionBar;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
import android.os.Build;
|
||||
import android.view.View;
|
||||
|
||||
|
||||
public final class MainActivity extends Activity
|
||||
{
|
||||
public static final String ACTION_INSERT_REBOOT = "name.nick.jubanka.colleen.FileSelector.INSERTREBOOT";
|
||||
public static final String ACTION_INSERT_ONLY = "name.nick.jubanka.colleen.FileSelector.INSERTONLY";
|
||||
public static final String ACTION_SET_ROMPATH = "name.nick.jubanka.colleen.FileSelector.SETROMPATH";
|
||||
|
||||
private static final String TAG = "MainActivity";
|
||||
private static final int ACTIVITY_FSEL = 1;
|
||||
private static final int ACTIVITY_PREFS = 2;
|
||||
private static final int DLG_WELCOME = 0;
|
||||
private static final int DLG_PATHSETUP = 1;
|
||||
private static final int DLG_CHANGES = 2;
|
||||
private static final int DLG_BRWSCONFRM = 3;
|
||||
private static final int DLG_SELCARTTYPE = 4;
|
||||
|
||||
public static String _pkgversion;
|
||||
public static String _coreversion;
|
||||
public ActionBarNull _aBar = null;
|
||||
private static boolean _initialized = false;
|
||||
private static String _curDiskFname = null;
|
||||
private A800view _view = null;
|
||||
private AudioThread _audio = null;
|
||||
private InputMethodManager _imng;
|
||||
private Settings _settings = null;
|
||||
private boolean _bootupconfig = false;
|
||||
private String _cartTypes[][] = null;
|
||||
|
||||
public static class ActionBarNull {
|
||||
public ActionBarNull(Activity a) {};
|
||||
public void hide(Activity a) {};
|
||||
public void hide(Activity a, boolean p) {};
|
||||
public void hide(Activity a, boolean p, boolean f) {};
|
||||
public void show(Activity a) {};
|
||||
public boolean isShowing(Activity a) { return false; }
|
||||
public boolean isReal() { return false; }
|
||||
public void init(Activity a) {};
|
||||
}
|
||||
|
||||
public static final class ActionBarHelp extends ActionBarNull {
|
||||
public ActionBarHelp(Activity a) {
|
||||
super(a);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hide(Activity a) {
|
||||
hide(a, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hide(Activity a, boolean p) {
|
||||
hide(a, p, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hide(Activity a, boolean p, boolean f) {
|
||||
ActionBar ab = a.getActionBar();
|
||||
View v = ((MainActivity) a)._view;
|
||||
if ( !f && !ab.isShowing() &&
|
||||
(v.getSystemUiVisibility() & View.STATUS_BAR_HIDDEN) == View.STATUS_BAR_HIDDEN )
|
||||
return;
|
||||
|
||||
if (Integer.parseInt(Build.VERSION.SDK) < Build.VERSION_CODES.JELLY_BEAN) {
|
||||
a.getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
||||
a.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
|
||||
}
|
||||
if (v != null) {
|
||||
int flags = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
|
||||
View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
|
||||
View.SYSTEM_UI_FLAG_FULLSCREEN |
|
||||
View.STATUS_BAR_HIDDEN;
|
||||
if (p == true)
|
||||
flags |= View.SYSTEM_UI_FLAG_LOW_PROFILE;
|
||||
v.setSystemUiVisibility(flags);
|
||||
}
|
||||
ab.hide();
|
||||
((MainActivity) a).pauseEmulation(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void show(Activity a) {
|
||||
ActionBar ab = a.getActionBar();
|
||||
if (ab.isShowing()) return;
|
||||
|
||||
((MainActivity) a).pauseEmulation(true);
|
||||
if (Integer.parseInt(Build.VERSION.SDK) < Build.VERSION_CODES.JELLY_BEAN) {
|
||||
a.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
||||
a.getWindow().addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
|
||||
}
|
||||
View v = ((MainActivity) a)._view;
|
||||
if (v != null) v.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
|
||||
View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
|
||||
View.STATUS_BAR_VISIBLE);
|
||||
ab.show();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isShowing(Activity a) {
|
||||
return a.getActionBar().isShowing();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isReal() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(Activity a) {
|
||||
a.getActionBar().setBackgroundDrawable(a.getResources().getDrawable(R.drawable.actionbar_bg));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static {
|
||||
System.loadLibrary("atari800");
|
||||
_coreversion = NativeInit();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
if (Integer.parseInt(Build.VERSION.SDK) >= Build.VERSION_CODES.HONEYCOMB)
|
||||
_aBar = new ActionBarHelp(this);
|
||||
else
|
||||
_aBar = new ActionBarNull(this);
|
||||
|
||||
_view = new A800view(this);
|
||||
setContentView(_view);
|
||||
_view.setKeepScreenOn(true);
|
||||
|
||||
_aBar.init(this);
|
||||
_aBar.hide(this);
|
||||
|
||||
_imng = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
|
||||
PreferenceManager.setDefaultValues(this, R.xml.preferences, true);
|
||||
Object obj = getLastNonConfigurationInstance();
|
||||
_settings = new Settings(PreferenceManager.getDefaultSharedPreferences(this), this, obj);
|
||||
_pkgversion = getPInfo().versionName;
|
||||
|
||||
if (!_initialized) {
|
||||
_settings.putBoolean("plandef", false);
|
||||
_settings.fetchApplySettings();
|
||||
_initialized = true;
|
||||
bootupMsgs();
|
||||
} else {
|
||||
_settings.fetch();
|
||||
if (obj != null) _settings.testApply();
|
||||
_settings.commit();
|
||||
soundInit(false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object onRetainNonConfigurationInstance() {
|
||||
return (Object) _settings.serialize();
|
||||
}
|
||||
|
||||
private void bootupMsgs() {
|
||||
String instver = _settings.get(false, "version");
|
||||
if (instver == null || instver.equals("false")) {
|
||||
_bootupconfig = true;
|
||||
pauseEmulation(true);
|
||||
showDialog(DLG_WELCOME);
|
||||
return;
|
||||
}
|
||||
|
||||
String rompath = _settings.get(false, "rompath");
|
||||
if (rompath == null || rompath.equals("false")) {
|
||||
pauseEmulation(true);
|
||||
_bootupconfig = true;
|
||||
showDialog(DLG_PATHSETUP);
|
||||
return;
|
||||
}
|
||||
|
||||
if (Integer.parseInt(instver) != getPInfo().versionCode) {
|
||||
_bootupconfig = true;
|
||||
pauseEmulation(true);
|
||||
showDialog(DLG_CHANGES);
|
||||
return;
|
||||
}
|
||||
Toast.makeText(this,
|
||||
_aBar.isReal() ? R.string.actionbarhelptoast : R.string.noactionbarhelptoast,
|
||||
Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
|
||||
public void message(int msg) {
|
||||
switch (msg) {
|
||||
case A800Renderer.REQ_BROWSER:
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
showDialog(DLG_BRWSCONFRM);
|
||||
}
|
||||
});
|
||||
break;
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Dialog onCreateDialog(int id) {
|
||||
Dialog d;
|
||||
TextView t;
|
||||
ScrollView s;
|
||||
|
||||
switch (id) {
|
||||
case DLG_WELCOME:
|
||||
t = new TextView(this);
|
||||
t.setText(R.string.welcomenote);
|
||||
t.setTextAppearance(this, android.R.style.TextAppearance_Small_Inverse);
|
||||
t.setBackgroundResource(android.R.color.background_light);
|
||||
s = new ScrollView(this);
|
||||
s.addView(t);
|
||||
d = new AlertDialog.Builder(this)
|
||||
.setTitle(R.string.welcome)
|
||||
.setView(s)
|
||||
.setInverseBackgroundForced(true)
|
||||
.setCancelable(false)
|
||||
.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface d, int i) {
|
||||
dismissDialog(DLG_WELCOME);
|
||||
_settings.putInt("version", getPInfo().versionCode);
|
||||
showDialog(DLG_PATHSETUP);
|
||||
}
|
||||
})
|
||||
.create();
|
||||
break;
|
||||
|
||||
case DLG_PATHSETUP:
|
||||
t = new TextView(this);
|
||||
t.setText(Html.fromHtml(getString(R.string.pathsetupmsg)));
|
||||
t.setTextAppearance(this, android.R.style.TextAppearance_Small_Inverse);
|
||||
t.setBackgroundResource(android.R.color.background_light);
|
||||
t.setMovementMethod(LinkMovementMethod.getInstance());
|
||||
s = new ScrollView(this);
|
||||
s.addView(t);
|
||||
d = new AlertDialog.Builder(this)
|
||||
.setTitle(R.string.pathsetup)
|
||||
.setView(s)
|
||||
.setInverseBackgroundForced(true)
|
||||
.setCancelable(false)
|
||||
.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface d, int i) {
|
||||
dismissDialog(DLG_PATHSETUP);
|
||||
startActivityForResult(new Intent(FileSelector.ACTION_OPEN_PATH,
|
||||
null, MainActivity.this, FileSelector.class), ACTIVITY_FSEL);
|
||||
}
|
||||
})
|
||||
.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface d, int i) {
|
||||
dismissDialog(DLG_PATHSETUP);
|
||||
finish();
|
||||
}
|
||||
})
|
||||
.create();
|
||||
break;
|
||||
|
||||
case DLG_CHANGES:
|
||||
t = new TextView(this);
|
||||
int[] vs = getResources().getIntArray(R.array.changes_versions);
|
||||
int instver = getPInfo().versionCode;
|
||||
for (int i = 0; i < vs.length; i++)
|
||||
if (vs[i] == instver) {
|
||||
t.setText(Html.fromHtml(getResources().getStringArray(R.array.changes_strings)[i]));
|
||||
break;
|
||||
}
|
||||
t.setTextAppearance(this, android.R.style.TextAppearance_Small_Inverse);
|
||||
t.setBackgroundResource(android.R.color.background_light);
|
||||
t.setMovementMethod(LinkMovementMethod.getInstance());
|
||||
s = new ScrollView(this);
|
||||
s.addView(t);
|
||||
d = new AlertDialog.Builder(this)
|
||||
.setTitle(R.string.atariupdate)
|
||||
.setView(s)
|
||||
.setInverseBackgroundForced(true)
|
||||
.setCancelable(false)
|
||||
.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface d, int i) {
|
||||
_settings.putInt("version", getPInfo().versionCode);
|
||||
_bootupconfig = false;
|
||||
pauseEmulation(false);
|
||||
dismissDialog(DLG_CHANGES);
|
||||
Toast.makeText(MainActivity.this, _aBar.isReal() ?
|
||||
R.string.actionbarhelptoast :
|
||||
R.string.noactionbarhelptoast,
|
||||
Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
})
|
||||
.create();
|
||||
break;
|
||||
|
||||
case DLG_BRWSCONFRM:
|
||||
String url = NativeGetURL();
|
||||
if (url.length() == 0) {
|
||||
d = null;
|
||||
break;
|
||||
}
|
||||
if (! validateURL(url)) {
|
||||
Log.d(TAG, "Browser request denied for improper url " + url);
|
||||
d = null;
|
||||
NativeClearDevB();
|
||||
Toast.makeText(this, R.string.browserreqdenied, Toast.LENGTH_SHORT).show();
|
||||
break;
|
||||
}
|
||||
pauseEmulation(true);
|
||||
d = new AlertDialog.Builder(this)
|
||||
.setTitle(R.string.warning)
|
||||
.setIcon(android.R.drawable.ic_dialog_alert)
|
||||
.setCancelable(false)
|
||||
.setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface d, int i) {
|
||||
String u = NativeGetURL().trim();
|
||||
Log.d(TAG, "Spawning browser for " + u);
|
||||
pauseEmulation(false);
|
||||
try {
|
||||
startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(u)));
|
||||
} catch (Exception e1) {
|
||||
Log.d(TAG, "Exception, trying with lower case");
|
||||
try {
|
||||
startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(u.toLowerCase())));
|
||||
} catch (Exception e2) {
|
||||
Log.d(TAG, "Exception, failed, giving up");
|
||||
}
|
||||
}
|
||||
NativeClearDevB();
|
||||
}
|
||||
})
|
||||
.setNegativeButton(R.string.no, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface d, int i) {
|
||||
pauseEmulation(false);
|
||||
NativeClearDevB();
|
||||
}
|
||||
})
|
||||
.setMessage("")
|
||||
.create();
|
||||
break;
|
||||
|
||||
case DLG_SELCARTTYPE:
|
||||
if (_cartTypes == null || _cartTypes.length == 0) {
|
||||
Log.d(TAG, "0 cart types passed");
|
||||
d = null;
|
||||
break;
|
||||
}
|
||||
pauseEmulation(true);
|
||||
String itm[] = new String[_cartTypes.length];
|
||||
for (int i = 0; i < _cartTypes.length; itm[i] = _cartTypes[i][1], i++);
|
||||
d = new AlertDialog.Builder(this)
|
||||
.setTitle(R.string.selectcarttype)
|
||||
.setCancelable(false)
|
||||
.setItems(itm, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface d, int i) {
|
||||
NativeBootCartType(Integer.parseInt(_cartTypes[i][0]));
|
||||
pauseEmulation(false);
|
||||
removeDialog(DLG_SELCARTTYPE);
|
||||
}
|
||||
})
|
||||
.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface d, int i) {
|
||||
pauseEmulation(false);
|
||||
removeDialog(DLG_SELCARTTYPE);
|
||||
}
|
||||
})
|
||||
.create();
|
||||
break;
|
||||
|
||||
default:
|
||||
d = null;
|
||||
}
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPrepareDialog(int id, Dialog d) {
|
||||
switch (id) {
|
||||
case DLG_BRWSCONFRM:
|
||||
((AlertDialog) d).setMessage(String.format(getString(R.string.confirmurl), NativeGetURL().trim()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean validateURL(String u) {
|
||||
if (u.trim().toLowerCase().startsWith("http://"))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
private PackageInfo getPInfo() {
|
||||
PackageInfo p;
|
||||
try {
|
||||
p = getPackageManager().getPackageInfo("name.nick.jubanka.colleen", 0);
|
||||
} catch (Exception e) {
|
||||
Log.d(TAG, "Package not found");
|
||||
p = null;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
private void soundInit(boolean n) {
|
||||
if (Boolean.parseBoolean(_settings.get(n, "sound"))) {
|
||||
if (_audio != null) _audio.interrupt();
|
||||
_audio = new AudioThread(Integer.parseInt(_settings.get(n, "mixrate")),
|
||||
Boolean.parseBoolean(_settings.get(n, "sound16bit")) ? 2 : 1,
|
||||
Integer.parseInt(_settings.get(n, "mixbufsize")) * 10,
|
||||
Boolean.parseBoolean(_settings.get(n, "ntsc")));
|
||||
_audio.start();
|
||||
} else {
|
||||
if (_audio != null) _audio.interrupt();
|
||||
_audio = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void pauseEmulation(boolean pause) {
|
||||
if (pause) {
|
||||
if (_audio != null) _audio.pause(pause);
|
||||
if (_view != null) _view.pause(pause);
|
||||
} else {
|
||||
if (_view != null) _view.pause(pause);
|
||||
if (_audio != null) _audio.pause(pause);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
_imng.hideSoftInputFromWindow(_view.getWindowToken(), 0);
|
||||
pauseEmulation(true);
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
_aBar.hide(this, true, true);
|
||||
if (!_bootupconfig) pauseEmulation(false);
|
||||
super.onResume();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
if(_audio != null) _audio.interrupt();
|
||||
super.onDestroy();
|
||||
if (isFinishing()) {
|
||||
Log.d(TAG, "Exiting with finishing flag up");
|
||||
NativeExit();
|
||||
android.os.Process.killProcess(android.os.Process.myPid());
|
||||
}
|
||||
}
|
||||
|
||||
// Menu stuff
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
MenuInflater inf = getMenuInflater();
|
||||
inf.inflate(R.menu.menu, menu);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOptionsMenuClosed(Menu m) {
|
||||
_aBar.hide(this);
|
||||
pauseEmulation(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPrepareOptionsMenu(Menu menu) {
|
||||
if (!_aBar.isReal())
|
||||
pauseEmulation(true); // menu is always shown on > honeycomb
|
||||
_imng.hideSoftInputFromWindow(_view.getWindowToken(), 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case R.id.menu_quit:
|
||||
finish();
|
||||
return true;
|
||||
case R.id.menu_softkbd:
|
||||
_imng.showSoftInput(_view, InputMethodManager.SHOW_FORCED);
|
||||
_aBar.hide(this, false);
|
||||
return true;
|
||||
case R.id.menu_open:
|
||||
startActivityForResult(new Intent(FileSelector.ACTION_OPEN_FILE, null, this, FileSelector.class),
|
||||
ACTIVITY_FSEL);
|
||||
return true;
|
||||
case R.id.menu_nextdisk:
|
||||
insertNextDisk();
|
||||
_aBar.hide(this);
|
||||
return true;
|
||||
case R.id.menu_preferences:
|
||||
startActivityForResult(new Intent(this, Preferences.class), ACTIVITY_PREFS);
|
||||
return true;
|
||||
default:
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int reqc, int resc, Intent data) {
|
||||
_aBar.hide(this);
|
||||
|
||||
switch (reqc) {
|
||||
case ACTIVITY_FSEL:
|
||||
if (resc != RESULT_OK) {
|
||||
if (_bootupconfig) finish();
|
||||
break;
|
||||
}
|
||||
if (data.getAction().equals(ACTION_SET_ROMPATH)) {
|
||||
String p = data.getData().getPath();
|
||||
_settings.putString("rompath", p);
|
||||
_settings.simulateChanged("rompath");
|
||||
_bootupconfig = false;
|
||||
pauseEmulation(false);
|
||||
break;
|
||||
}
|
||||
_curDiskFname = data.getData().getPath();
|
||||
if (data.getAction().equals(ACTION_INSERT_REBOOT)) {
|
||||
int r = NativeRunAtariProgram(_curDiskFname, 1, 1);
|
||||
if (r == -2)
|
||||
showDialog(DLG_SELCARTTYPE);
|
||||
else
|
||||
Toast.makeText(this, String.format(getString(r < 0 ? R.string.errorboot : R.string.diskboot),
|
||||
_curDiskFname.substring(_curDiskFname.lastIndexOf("/") + 1)),
|
||||
Toast.LENGTH_SHORT)
|
||||
.show();
|
||||
}
|
||||
break;
|
||||
case ACTIVITY_PREFS:
|
||||
_settings.fetchApplySettings();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void insertNextDisk() {
|
||||
final String[] pats = { "[\\s(,_]+s\\d" };
|
||||
Pattern p;
|
||||
Matcher m;
|
||||
File f;
|
||||
|
||||
if (_curDiskFname == null)
|
||||
return;
|
||||
String path = _curDiskFname.substring(0, _curDiskFname.lastIndexOf("/") + 1);
|
||||
String fname = _curDiskFname.substring(_curDiskFname.lastIndexOf("/") + 1,
|
||||
_curDiskFname.lastIndexOf("."));
|
||||
String ext = _curDiskFname.substring(_curDiskFname.lastIndexOf("."));
|
||||
Log.d(TAG, "Enter **" + path + "**" + fname + "**" + ext + "**");
|
||||
|
||||
for (String s: pats) {
|
||||
p = Pattern.compile(s);
|
||||
m = p.matcher(fname);
|
||||
if (m.find()) {
|
||||
Log.d(TAG, "Match **" + m.group() + "**");
|
||||
char side = (char)(fname.charAt(m.end() - 1) + 1);
|
||||
char end = side;
|
||||
do {
|
||||
f = new File(path + fname.substring(0, m.end() - 1) + side +
|
||||
fname.substring(m.end()) + ext);
|
||||
Log.d(TAG, "Trying loop " + f.getName());
|
||||
if (f.exists()) {
|
||||
_curDiskFname = f.getPath();
|
||||
int r = NativeRunAtariProgram(_curDiskFname, 1, 0);
|
||||
Toast.makeText(this,
|
||||
String.format(getString(
|
||||
r >= 0 ? R.string.mountnextdisk : R.string.mountnextdiskerror),
|
||||
f.getName()),
|
||||
Toast.LENGTH_SHORT)
|
||||
.show();
|
||||
return;
|
||||
}
|
||||
if (side != '9')
|
||||
side++;
|
||||
else
|
||||
side = '0';
|
||||
} while (side != end);
|
||||
}
|
||||
}
|
||||
Toast.makeText(this, R.string.mountnonextdisk, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
|
||||
private native int NativeRunAtariProgram(String img, int drive, int reboot);
|
||||
private native void NativeBootCartType(int kb);
|
||||
private native void NativeExit();
|
||||
private static native String NativeInit();
|
||||
|
||||
// ----------------- Preferences -------------------
|
||||
|
||||
private final static class Settings
|
||||
{
|
||||
enum PreferenceName {
|
||||
aspect, bilinear, artifact, frameskip, collisions, machine, basic, speed,
|
||||
disk, sector, softjoy, up, down, left, right, fire, joyvisible, joysize,
|
||||
joyopacity, joyrighth, joydeadband, joymidx, sound, mixrate, sound16bit,
|
||||
hqpokey, mixbufsize, version, rompath, anchor, anchorstr, joygrace,
|
||||
crophoriz, cropvert, derotkeys, actiona, actionb, actionc, ntsc, paddle,
|
||||
plandef, browser, forceAT
|
||||
};
|
||||
private SharedPreferences _sharedprefs;
|
||||
private Map<PreferenceName, String> _values, _newvalues;
|
||||
private Context _context;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public Settings(SharedPreferences s, Context c, Object retain) {
|
||||
_sharedprefs = s;
|
||||
_context = c;
|
||||
if (retain == null)
|
||||
_values = new EnumMap<PreferenceName, String>(PreferenceName.class);
|
||||
else
|
||||
_values = (EnumMap<PreferenceName, String>) retain;
|
||||
_newvalues = new EnumMap<PreferenceName, String>(PreferenceName.class);
|
||||
}
|
||||
|
||||
public void fetch() {
|
||||
String v = null;
|
||||
for (PreferenceName n: PreferenceName.values()) {
|
||||
// nice, efficient coerce to string follows
|
||||
try { v = Boolean.toString(_sharedprefs.getBoolean(n.toString(), false)); } catch(Exception e1) {
|
||||
try { v = Integer.toString(_sharedprefs.getInt(n.toString(), -1)); } catch(Exception e2) {
|
||||
try { v = _sharedprefs.getString(n.toString(), null); } catch(Exception e3) {
|
||||
throw new ClassCastException(); }}};
|
||||
_newvalues.put(n, v);
|
||||
}
|
||||
}
|
||||
|
||||
private void apply() {
|
||||
NativePrefGfx( Integer.parseInt(_newvalues.get(PreferenceName.aspect)),
|
||||
Boolean.parseBoolean(_newvalues.get(PreferenceName.bilinear)),
|
||||
Integer.parseInt(_newvalues.get(PreferenceName.artifact)),
|
||||
Integer.parseInt(_newvalues.get(PreferenceName.frameskip)),
|
||||
Boolean.parseBoolean(_newvalues.get(PreferenceName.collisions)),
|
||||
Integer.parseInt(_newvalues.get(PreferenceName.crophoriz)),
|
||||
Integer.parseInt(_newvalues.get(PreferenceName.cropvert)) );
|
||||
|
||||
if ( changed(PreferenceName.machine) || changed(PreferenceName.ntsc) ) {
|
||||
if ( !NativePrefMachine(Integer.parseInt(_newvalues.get(PreferenceName.machine)),
|
||||
Boolean.parseBoolean(_newvalues.get(PreferenceName.ntsc))) ) {
|
||||
Log.d(TAG, "OS rom not found");
|
||||
if ( _values.get(PreferenceName.machine) != null &&
|
||||
!_values.get(PreferenceName.machine).equals("false") ) {
|
||||
Toast.makeText(_context, R.string.noromfoundrevert, Toast.LENGTH_LONG).show();
|
||||
revertString(PreferenceName.machine);
|
||||
NativePrefMachine(Integer.parseInt(_newvalues.get(PreferenceName.machine)),
|
||||
Boolean.parseBoolean(_newvalues.get(PreferenceName.ntsc)));
|
||||
} else {
|
||||
Toast.makeText(_context, R.string.noromfound, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NativePrefEmulation( Boolean.parseBoolean(_newvalues.get(PreferenceName.basic)),
|
||||
Boolean.parseBoolean(_newvalues.get(PreferenceName.speed)),
|
||||
Boolean.parseBoolean(_newvalues.get(PreferenceName.disk)),
|
||||
Boolean.parseBoolean(_newvalues.get(PreferenceName.sector)),
|
||||
Boolean.parseBoolean(_newvalues.get(PreferenceName.browser)) );
|
||||
|
||||
NativePrefSoftjoy( Boolean.parseBoolean(_newvalues.get(PreferenceName.softjoy)),
|
||||
Integer.parseInt(_newvalues.get(PreferenceName.up)),
|
||||
Integer.parseInt(_newvalues.get(PreferenceName.down)),
|
||||
Integer.parseInt(_newvalues.get(PreferenceName.left)),
|
||||
Integer.parseInt(_newvalues.get(PreferenceName.right)),
|
||||
Integer.parseInt(_newvalues.get(PreferenceName.fire)),
|
||||
Integer.parseInt(_newvalues.get(PreferenceName.derotkeys)),
|
||||
new String[] { _newvalues.get(PreferenceName.actiona),
|
||||
_newvalues.get(PreferenceName.actionb),
|
||||
_newvalues.get(PreferenceName.actionc) } );
|
||||
|
||||
int x = 0, y = 0;
|
||||
if (Boolean.parseBoolean(_newvalues.get(PreferenceName.anchor))) {
|
||||
String astr = _newvalues.get(PreferenceName.anchorstr);
|
||||
if (astr == null || astr.equals("false")) {
|
||||
astr = NativeGetJoypos();
|
||||
putString("anchorstr", astr);
|
||||
_newvalues.put(PreferenceName.anchorstr, astr);
|
||||
}
|
||||
String[] tok = astr.split(" ");
|
||||
x = Integer.parseInt(tok[0]);
|
||||
y = Integer.parseInt(tok[1]);
|
||||
} else
|
||||
putString("anchorstr", "false");
|
||||
NativePrefJoy( Boolean.parseBoolean(_newvalues.get(PreferenceName.joyvisible)),
|
||||
Integer.parseInt(_newvalues.get(PreferenceName.joysize)),
|
||||
Integer.parseInt(_newvalues.get(PreferenceName.joyopacity)),
|
||||
Boolean.parseBoolean(_newvalues.get(PreferenceName.joyrighth)),
|
||||
Integer.parseInt(_newvalues.get(PreferenceName.joydeadband)),
|
||||
Integer.parseInt(_newvalues.get(PreferenceName.joymidx)),
|
||||
Boolean.parseBoolean(_newvalues.get(PreferenceName.anchor)),
|
||||
x, y,
|
||||
Integer.parseInt(_newvalues.get(PreferenceName.joygrace)),
|
||||
Boolean.parseBoolean(_newvalues.get(PreferenceName.paddle)),
|
||||
Boolean.parseBoolean(_newvalues.get(PreferenceName.plandef)) );
|
||||
|
||||
if ( changed(PreferenceName.mixrate) || changed(PreferenceName.sound16bit) ||
|
||||
changed(PreferenceName.hqpokey) || changed(PreferenceName.mixbufsize) ||
|
||||
changed(PreferenceName.forceAT) )
|
||||
NativePrefSound( Integer.parseInt(_newvalues.get(PreferenceName.mixrate)),
|
||||
Integer.parseInt(_newvalues.get(PreferenceName.mixbufsize)) * 10,
|
||||
Boolean.parseBoolean(_newvalues.get(PreferenceName.sound16bit)),
|
||||
Boolean.parseBoolean(_newvalues.get(PreferenceName.hqpokey)),
|
||||
Boolean.parseBoolean(_newvalues.get(PreferenceName.forceAT)) );
|
||||
if ( changed(PreferenceName.sound) || changed(PreferenceName.mixrate) ||
|
||||
changed(PreferenceName.sound16bit) || changed(PreferenceName.mixbufsize) ||
|
||||
changed(PreferenceName.forceAT) )
|
||||
((MainActivity) _context).soundInit(true);
|
||||
|
||||
if (changed(PreferenceName.rompath))
|
||||
if (!NativeSetROMPath(_newvalues.get(PreferenceName.rompath)))
|
||||
Toast.makeText(_context, R.string.rompatherror, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
|
||||
public void simulateChanged(String key) {
|
||||
for (PreferenceName n: PreferenceName.values())
|
||||
_newvalues.put(n, _values.get(n));
|
||||
_values.put(PreferenceName.valueOf(key), null);
|
||||
apply();
|
||||
commit();
|
||||
}
|
||||
|
||||
public String get(boolean newval, String what) {
|
||||
return (newval ? _newvalues : _values).get(PreferenceName.valueOf(what));
|
||||
}
|
||||
|
||||
public void putInt(String key, int val) {
|
||||
_values.put(PreferenceName.valueOf(key), Integer.toString(val));
|
||||
SharedPreferences.Editor e = _sharedprefs.edit();
|
||||
e.putInt(key, val);
|
||||
e.commit();
|
||||
}
|
||||
|
||||
public void putString(String key, String val) {
|
||||
_values.put(PreferenceName.valueOf(key), val);
|
||||
SharedPreferences.Editor e = _sharedprefs.edit();
|
||||
e.putString(key, val);
|
||||
e.commit();
|
||||
}
|
||||
|
||||
public void putBoolean(String key, boolean val) {
|
||||
_values.put(PreferenceName.valueOf(key), Boolean.toString(val));
|
||||
SharedPreferences.Editor e = _sharedprefs.edit();
|
||||
e.putBoolean(key, val);
|
||||
e.commit();
|
||||
}
|
||||
|
||||
private void revertString(PreferenceName p) {
|
||||
String oldval = _values.get(p);
|
||||
_newvalues.put(p, oldval);
|
||||
SharedPreferences.Editor e = _sharedprefs.edit();
|
||||
e.putString(p.toString(), oldval);
|
||||
e.commit();
|
||||
}
|
||||
|
||||
public void fetchApplySettings() {
|
||||
fetch();
|
||||
apply();
|
||||
commit();
|
||||
}
|
||||
|
||||
public void testApply() {
|
||||
boolean changed = false;
|
||||
for (PreferenceName n: PreferenceName.values())
|
||||
changed |= changed(n);
|
||||
if (changed)
|
||||
apply();
|
||||
}
|
||||
|
||||
private boolean changed(PreferenceName p) {
|
||||
String s1 = _values.get(p);
|
||||
String s2 = _newvalues.get(p);
|
||||
if (s1 == null || s2 == null) return true;
|
||||
return !s1.equals(s2);
|
||||
}
|
||||
|
||||
public void commit() {
|
||||
for (PreferenceName n: PreferenceName.values()) {
|
||||
_values.put(n, _newvalues.get(n));
|
||||
_newvalues.put(n, null);
|
||||
}
|
||||
}
|
||||
|
||||
public void print() {
|
||||
for (PreferenceName n: PreferenceName.values())
|
||||
Log.d(TAG, n.toString() + "=" + _values.get(n));
|
||||
}
|
||||
|
||||
public Map<PreferenceName, String> serialize() {
|
||||
return _values;
|
||||
}
|
||||
}
|
||||
private static native void NativePrefGfx(int aspect, boolean bilinear, int artifact,
|
||||
int frameskip, boolean collisions, int crophoriz, int cropvert);
|
||||
private static native boolean NativePrefMachine(int machine, boolean ntsc);
|
||||
private static native void NativePrefEmulation(boolean basic, boolean speed, boolean disk,
|
||||
boolean sector, boolean browser);
|
||||
private static native void NativePrefSoftjoy(boolean softjoy, int up, int down, int left, int right,
|
||||
int fire, int derotkeys, String[] actions);
|
||||
private static native void NativePrefJoy(boolean visible, int size, int opacity, boolean righth,
|
||||
int deadband, int midx, boolean anchor, int anchorx, int anchory,
|
||||
int grace, boolean paddle, boolean plandef);
|
||||
private static native void NativePrefSound(int mixrate, int mixbufsizems, boolean sound16bit, boolean hqpokey,
|
||||
boolean disableOSL);
|
||||
private static native boolean NativeSetROMPath(String path);
|
||||
private static native String NativeGetJoypos();
|
||||
private static native String NativeGetURL();
|
||||
private static native void NativeClearDevB();
|
||||
}
|
||||
@@ -0,0 +1,291 @@
|
||||
/*
|
||||
* Preferences.java - Preference activity for the emulator
|
||||
*
|
||||
* Copyright (C) 2014 Kostas Nakos
|
||||
* Copyright (C) 2014 Atari800 development team (see DOC/CREDITS)
|
||||
*
|
||||
* This file is part of the Atari800 emulator project which emulates
|
||||
* the Atari 400, 800, 800XL, 130XE, and 5200 8-bit computers.
|
||||
*
|
||||
* Atari800 is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Atari800 is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Atari800; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
package name.nick.jubanka.colleen;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import android.preference.PreferenceActivity;
|
||||
import android.os.Bundle;
|
||||
import android.preference.Preference;
|
||||
import android.util.Log;
|
||||
import android.content.SharedPreferences;
|
||||
import android.preference.Preference.OnPreferenceClickListener;
|
||||
import android.net.Uri;
|
||||
import android.content.Intent;
|
||||
import android.app.AlertDialog;
|
||||
import android.webkit.WebView;
|
||||
import android.app.Dialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.preference.EditTextPreference;
|
||||
import android.widget.Toast;
|
||||
import android.content.res.Resources;
|
||||
import android.preference.CheckBoxPreference;
|
||||
|
||||
|
||||
public final class Preferences extends PreferenceActivity implements Preference.OnPreferenceChangeListener
|
||||
{
|
||||
private static final String TAG = "Preferences";
|
||||
private static final String[] PREF_KEYS = { "up", "down", "left", "right", "fire",
|
||||
"actiona", "actionb", "actionc" };
|
||||
private static final String PD_RESNAME = "pd2012";
|
||||
private static final int ACTIVITY_FSEL_ROMPATH = 1;
|
||||
private static final int ACTIVITY_FSEL_STATEPATH = 2;
|
||||
private static final int DLG_ABOUT = 1;
|
||||
private static final int DLG_RESET = 2;
|
||||
private static final int DLG_OVRWR = 3;
|
||||
private SharedPreferences _sp;
|
||||
private String _svstfname = null;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
KeymapPreference kp;
|
||||
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
addPreferencesFromResource(R.xml.preferences);
|
||||
_sp = getPreferenceManager().getSharedPreferences();
|
||||
|
||||
for (String s: PREF_KEYS) {
|
||||
kp = (KeymapPreference) findPreference(s);
|
||||
kp.setOnPreferenceChangeListener(this);
|
||||
kp.updateSum();
|
||||
}
|
||||
|
||||
for (final String pref: new String[] {"rompath", "statepath"}) {
|
||||
findPreference(pref).setOnPreferenceClickListener(new OnPreferenceClickListener() {
|
||||
@Override
|
||||
public boolean onPreferenceClick(Preference p) {
|
||||
String val = _sp.getString(pref, null);
|
||||
Uri u = (val == null) ? null : Uri.fromFile(new File(val));
|
||||
startActivityForResult(new Intent(FileSelector.ACTION_OPEN_PATH, u,
|
||||
Preferences.this, FileSelector.class),
|
||||
pref.equals("rompath") ? ACTIVITY_FSEL_ROMPATH :
|
||||
ACTIVITY_FSEL_STATEPATH);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
findPreference("about").setOnPreferenceClickListener(new OnPreferenceClickListener() {
|
||||
@Override
|
||||
public boolean onPreferenceClick(Preference p) {
|
||||
showDialog(DLG_ABOUT);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
findPreference("help").setOnPreferenceClickListener(new OnPreferenceClickListener() {
|
||||
@Override
|
||||
public boolean onPreferenceClick(Preference p) {
|
||||
startActivity(new Intent(Intent.ACTION_VIEW,
|
||||
Uri.parse("http://pocketatari.atari.org/android/index.html#manual")));
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
findPreference("resetactions").setOnPreferenceClickListener(new OnPreferenceClickListener() {
|
||||
@Override
|
||||
public boolean onPreferenceClick(Preference p) {
|
||||
showDialog(DLG_RESET);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
if (_sp.getString("statepath", null) != null)
|
||||
enableStateSave();
|
||||
findPreference("savestate").setOnPreferenceChangeListener(this);
|
||||
|
||||
Preference p = findPreference("launchpd");
|
||||
if (p != null) {
|
||||
p.setOnPreferenceClickListener(new OnPreferenceClickListener() {
|
||||
@Override
|
||||
public boolean onPreferenceClick(Preference p) {
|
||||
Resources res = Preferences.this.getResources();
|
||||
int id = res.getIdentifier(PD_RESNAME, "raw", Preferences.this.getPackageName());
|
||||
if (id != 0) {
|
||||
InputStream is = res.openRawResource(id);
|
||||
byte pddata[];
|
||||
try {
|
||||
pddata = new byte[is.available()];
|
||||
is.read(pddata);
|
||||
is.close();
|
||||
} catch (IOException e) {
|
||||
Log.d(TAG, "IO exception while reading resouce");
|
||||
return true;
|
||||
}
|
||||
if (! NativeBootPD(pddata, pddata.length))
|
||||
Toast.makeText(Preferences.this, R.string.pdbooterror, Toast.LENGTH_LONG).show();
|
||||
else {
|
||||
((CheckBoxPreference) Preferences.this.findPreference("plandef")).setChecked(true);
|
||||
Toast.makeText(Preferences.this, R.string.pdreminder, Toast.LENGTH_LONG).show();
|
||||
Preferences.this.finish();
|
||||
}
|
||||
} else {
|
||||
Log.d(TAG, "PD2012 resource not found");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
if (getResources().getIdentifier(PD_RESNAME, "raw", getPackageName()) == 0)
|
||||
p.setEnabled(false);
|
||||
}
|
||||
|
||||
findPreference("forceAT").setEnabled(NativeOSLSound() || ((CheckBoxPreference) findPreference("forceAT")).isChecked());
|
||||
}
|
||||
|
||||
private void enableStateSave() {
|
||||
Preference p = findPreference("savestate");
|
||||
p.setEnabled(true);
|
||||
p.setSummary(getString(R.string.pref_savestate_sum_ena));
|
||||
}
|
||||
|
||||
private boolean saveState(boolean force) {
|
||||
String path = _sp.getString("statepath", null);
|
||||
if (path == null) {
|
||||
Log.d(TAG, "state save path is null");
|
||||
Toast.makeText(this, R.string.savestateerror, Toast.LENGTH_LONG).show();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!force && new File(path, _svstfname).exists())
|
||||
return false;
|
||||
|
||||
if (!NativeSaveState(path + '/' + _svstfname)) {
|
||||
Toast.makeText(this, R.string.savestateerror, Toast.LENGTH_LONG).show();
|
||||
return true;
|
||||
}
|
||||
|
||||
Toast.makeText(this, R.string.savestateok, Toast.LENGTH_LONG).show();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference p, Object v) {
|
||||
if (p.getKey().equals("savestate")) {
|
||||
_svstfname = (String) v + ".a8s";
|
||||
if (!saveState(false))
|
||||
showDialog(DLG_OVRWR);
|
||||
return true;
|
||||
} else {
|
||||
int k = (Integer) v;
|
||||
KeymapPreference pref;
|
||||
|
||||
Log.d(TAG, "Change " + k);
|
||||
for (String key: PREF_KEYS) {
|
||||
if (key.equals(p.getKey())) continue;
|
||||
pref = (KeymapPreference) findPreference(key);
|
||||
if (k >= 0) { // check mappings
|
||||
if (pref.getKeymap() == k)
|
||||
return false;
|
||||
} else { // swap mappings
|
||||
if (pref.getKeymap() == -k) {
|
||||
pref.setKeymap( ((KeymapPreference) p).getKeymap() );
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int reqc, int resc, Intent data) {
|
||||
String pref = "rompath";
|
||||
if (resc != RESULT_OK) return;
|
||||
switch (reqc) {
|
||||
case ACTIVITY_FSEL_STATEPATH:
|
||||
pref = "statepath";
|
||||
enableStateSave();
|
||||
// fallthrough
|
||||
case ACTIVITY_FSEL_ROMPATH:
|
||||
SharedPreferences.Editor e = _sp.edit();
|
||||
e.putString(pref, data.getData().getPath());
|
||||
e.commit();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Dialog onCreateDialog(int id) {
|
||||
Dialog d;
|
||||
|
||||
switch (id) {
|
||||
case DLG_ABOUT:
|
||||
WebView v = new WebView(this);
|
||||
v.loadData(String.format(getString(R.string.aboutmsg),
|
||||
MainActivity._pkgversion, MainActivity._coreversion), "text/html", "utf-8");
|
||||
v.setVerticalScrollBarEnabled(true);
|
||||
d = new AlertDialog.Builder(this)
|
||||
.setTitle(R.string.about)
|
||||
.setIcon(R.drawable.icon)
|
||||
.setView(v)
|
||||
.setInverseBackgroundForced(true)
|
||||
.setPositiveButton(R.string.ok, null)
|
||||
.create();
|
||||
break;
|
||||
|
||||
case DLG_RESET:
|
||||
d = new AlertDialog.Builder(this)
|
||||
.setTitle(R.string.warning)
|
||||
.setIcon(android.R.drawable.ic_dialog_alert)
|
||||
.setMessage(R.string.pref_warnresetactions)
|
||||
.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface d, int i) {
|
||||
for (String str: new String[] {"actiona", "actionb", "actionc"})
|
||||
((KeymapPreference) findPreference(str)).setDefaultKeymap();
|
||||
}
|
||||
})
|
||||
.setNegativeButton(R.string.cancel, null)
|
||||
.create();
|
||||
break;
|
||||
|
||||
case DLG_OVRWR:
|
||||
d = new AlertDialog.Builder(this)
|
||||
.setTitle(R.string.warning)
|
||||
.setIcon(android.R.drawable.ic_dialog_alert)
|
||||
.setMessage(String.format(getString(R.string.savestateoverwrite), _svstfname))
|
||||
.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface d, int i) {
|
||||
saveState(true);
|
||||
}
|
||||
})
|
||||
.setNegativeButton(R.string.cancel, null)
|
||||
.create();
|
||||
break;
|
||||
|
||||
default:
|
||||
d = null;
|
||||
}
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
|
||||
private native boolean NativeSaveState(String fname);
|
||||
private native boolean NativeBootPD(byte data[], int len);
|
||||
private native boolean NativeOSLSound();
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* SliderPreference.java - Simple custom preference dialog with a slider
|
||||
*
|
||||
* Copyright (C) 2010 Kostas Nakos
|
||||
* Copyright (C) 2010 Atari800 development team (see DOC/CREDITS)
|
||||
*
|
||||
* This file is part of the Atari800 emulator project which emulates
|
||||
* the Atari 400, 800, 800XL, 130XE, and 5200 8-bit computers.
|
||||
*
|
||||
* Atari800 is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Atari800 is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Atari800; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
package name.nick.jubanka.colleen;
|
||||
|
||||
import android.preference.DialogPreference;
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.content.res.TypedArray;
|
||||
import android.widget.SeekBar;
|
||||
import android.view.LayoutInflater;
|
||||
import android.widget.TextView;
|
||||
import android.view.View;
|
||||
import android.content.DialogInterface;
|
||||
import android.util.Log;
|
||||
|
||||
|
||||
public final class SliderPreference extends DialogPreference implements SeekBar.OnSeekBarChangeListener
|
||||
{
|
||||
private int _min, _max, _def;
|
||||
private String _suffix;
|
||||
private TextView _txtSetting = null;
|
||||
private int _sliderValue;
|
||||
|
||||
public SliderPreference(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
|
||||
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SliderPreference);
|
||||
_min = a.getInt(R.styleable.SliderPreference_min, 10);
|
||||
_max = a.getInt(R.styleable.SliderPreference_max, 99);
|
||||
_suffix = a.getString(R.styleable.SliderPreference_suffix);
|
||||
if (_suffix == null) _suffix = "%";
|
||||
a.recycle();
|
||||
|
||||
setPositiveButtonText(R.string.ok);
|
||||
setNegativeButtonText(R.string.cancel);
|
||||
setDialogTitle(getTitle());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object onGetDefaultValue(TypedArray a, int i) {
|
||||
_def = a.getInt(i, 10);
|
||||
return _def;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSetInitialValue(boolean restore, Object def) {
|
||||
if (restore == false)
|
||||
persistInt((Integer) def);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected View onCreateDialogView() {
|
||||
LayoutInflater inf = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
View v = inf.inflate(R.layout.slider_dialog, null);
|
||||
_txtSetting = (TextView) v.findViewById(R.id.setting);
|
||||
SeekBar s = (SeekBar) v.findViewById(R.id.slider);
|
||||
s.setOnSeekBarChangeListener(this);
|
||||
s.setMax(_max - _min);
|
||||
s.setProgress(getPersistedInt(_def) - _min);
|
||||
onProgressChanged(s, getPersistedInt(_def) - _min, false);
|
||||
return v;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(DialogInterface d, int w) {
|
||||
if (w == DialogInterface.BUTTON_POSITIVE && callChangeListener(_sliderValue))
|
||||
persistInt(_sliderValue);
|
||||
_txtSetting = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProgressChanged(SeekBar s, int p, boolean u) {
|
||||
_sliderValue = p + _min;
|
||||
_txtSetting.setText(_sliderValue + _suffix);
|
||||
}
|
||||
|
||||
@Override public void onStartTrackingTouch(SeekBar seekBar) {}
|
||||
@Override public void onStopTrackingTouch(SeekBar seekBar) {}
|
||||
}
|
||||