initial commit

This commit is contained in:
R-type
2015-12-14 14:00:35 +01:00
commit 5a96c0ca66
377 changed files with 149124 additions and 0 deletions
+27
View File
@@ -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>
+92
View File
@@ -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>
+47
View File
@@ -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)
+1
View File
@@ -0,0 +1 @@
APP_PLATFORM := android-9
+547
View File
@@ -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;
}
+111
View File
@@ -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);
+480
View File
@@ -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);
}
+25
View File
@@ -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);
+722
View File
@@ -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;
}
+139
View File
@@ -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
*/
};
+86
View File
@@ -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;
}
+381
View File
@@ -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);
}
+14
View File
@@ -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
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

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>
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

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>
+20
View File
@@ -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>
+122
View File
@@ -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>
+13
View File
@@ -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>
+289
View File
@@ -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 &copy; 1995-1998 <i>David Firth</i><br>
Copyright &copy; 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>
+133
View File
@@ -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);
}
+265
View File
@@ -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);
}
}
+159
View File
@@ -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);
}
+449
View File
@@ -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
};
}
+870
View File
@@ -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();
}
+291
View File
@@ -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) {}
}