Compare commits

...

88 Commits

Author SHA1 Message Date
Aaron Culliney
2739a6b084 Bumped versionCode for next RC build 2020-01-26 09:13:16 -08:00
Aaron Culliney
ead2ac32f5 Fix testdisk on Android 2020-01-11 15:13:20 -10:00
Aaron Culliney
76f98557b7 Fix/improve test broken by 82813b3ac 2020-01-10 16:47:24 -10:00
Aaron Culliney
294c6ca790 Update Android Studio thingz 2019-12-02 10:54:34 -08:00
Aaron Culliney
21fff2cc15 Build ABI specific APKs 2019-12-01 15:18:00 -08:00
Aaron Culliney
2bda54d342 Various project changes 2019-12-01 15:17:51 -08:00
Aaron Culliney
a5a4a50983 HACK: Experimental fix for speaker-getting-wedged issue
- Repro: GDB breakpoint while cpu_thread() is nanosleeping() seems to produce maximum cycles_speaker_feedback, and it never clears up
2019-12-01 15:12:10 -08:00
Aaron Culliney
d63ba9538d Refactor email-logs and crash handler codepaths
- Use a new activity and change to Intent.ACTION_SEND handler
    - Fixes an issue where email data was previously lost on a re-orientation event
2019-12-01 11:38:30 -08:00
Aaron Culliney
2728d44f8c Fix bug in system disk chooser where orientation event prevented actual selection 2019-12-01 11:25:24 -08:00
Aaron Culliney
ad0a8400ef Eliminate a warning 2019-12-01 11:25:05 -08:00
Aaron Culliney
4616a41326 Correctly symbolicate for arm64-v8a and x86_64 2019-11-30 09:28:26 -08:00
Aaron Culliney
a470329143 Bumped versionCode for next RC build 2019-11-30 09:19:06 -08:00
Aaron Culliney
ab482fc3dc Fix invisible menu checkboxes on older Droids 2019-11-30 08:35:20 -08:00
Aaron Culliney
2ef5abba2c Add an email logs menu item that sends app data 2019-11-30 07:59:14 -08:00
Aaron Culliney
68e6fb768f Avoid UI race when copying assets to storage 2019-11-29 14:03:09 -08:00
Aaron Culliney
296bd12029 Fix release notes and allow internal/external storage disk selection to work on Android 10 2019-11-29 14:02:10 -08:00
Aaron Culliney
f5899e74d6 Zip up crash and log files on Droid to email to developer 2019-11-29 12:41:34 -08:00
Aaron Culliney
310825e2cc Route Android logging to native side and timestamp logs 2019-11-29 08:53:16 -08:00
Aaron Culliney
46b74f65fa Update release notes 2019-11-10 16:22:09 -08:00
Aaron Culliney
397b43b3e7 Improve joystick button reset procedure 2019-11-10 16:10:08 -08:00
Aaron Culliney
5aa691815b Support open/closed apple tap events from touch keyboard 2019-11-10 15:30:58 -08:00
Aaron Culliney
c0fd258c40 Shuffle some Android settings around 2019-11-10 12:34:09 -08:00
Aaron Culliney
acade0076b Fix Android Studio lint warning 2019-11-10 08:31:33 -08:00
Aaron Culliney
cff22f6b34 Correctly show chosen file name in disks menu 2019-11-10 08:30:00 -08:00
Aaron Culliney
bf4f76a142 Use origin value for touch joysticks if pythagorean result is 0 2019-11-10 06:58:27 -08:00
Aaron Culliney
f654fb0825 Improve calibration routine for touch keypad joystick 2019-11-10 06:58:16 -08:00
Aaron Culliney
9a1d305975 Remove more unnecessary negative buttons 2019-11-09 16:10:46 -08:00
Aaron Culliney
e224912f8b Correctly set cpu65_vmem_X function table sizes :P 2019-10-31 17:37:26 -07:00
Aaron Culliney
1e986d73ef Improve button positioning
- Keeps similar consistent locations as prior menus
    - Remove cancel no-op buttons
2019-10-31 17:36:25 -07:00
Aaron Culliney
dfc09e87bb Correctly set the filename from chooser activity 2019-10-31 17:34:28 -07:00
Aaron Culliney
57d1e95cce Improve background color and layout 2019-10-31 17:33:41 -07:00
Aaron Culliney
d6e21fd967 Fix a crash in disk selection 2019-10-31 17:33:14 -07:00
Aaron Culliney
37df4796c9 Updated Android build tools and first RC 2019-10-28 07:27:41 -07:00
Aaron Culliney
76c9daeb4c New style for Android app 2019-10-28 07:25:57 -07:00
Aaron Culliney
e70051d6a4 Migrate to AndroidX 2019-10-27 17:40:01 -07:00
Aaron Culliney
7ac274261a Migrate to AppCompat 2019-10-27 17:36:36 -07:00
Aaron Culliney
cf01bb7985 Ensure proper setting of Android 64bit flags 2019-10-27 16:46:48 -07:00
Aaron Culliney
8f2ab2f072 Compress cpu65_vmem_* function tables to save space 2019-10-27 16:26:35 -07:00
Aaron Culliney
00984b7ab9 Compile on Android with more warnings 2019-10-27 13:10:29 -07:00
Aaron Culliney
1491d3f88d Streamline 65c02 cpu on ARM 2019-10-21 07:35:54 -07:00
Aaron Culliney
2615351d46 Optimize and de-macroize BIT computation on ARM 2019-10-20 18:44:06 -07:00
Aaron Culliney
f608de450e Allow super twitchy touch joystick 2019-10-20 18:40:59 -07:00
Aaron Culliney
c59b18d176 Inscrutible changes insisted upon by Android Studio 2019-10-06 17:05:51 -07:00
Aaron Culliney
1c022c9587 Get our copy of breakpad, such as it is, building against NDK R15C
- Likely we should "soon" upgrade breakpad/crashpad and the NDK to a mutually agreed-upon version
2019-10-06 17:05:16 -07:00
Aaron Culliney
43d99ec9f4 First cut at 65c02 implemented on aarch64 2019-10-06 17:05:08 -07:00
Aaron Culliney
4e27c1a322 Rename internal _debugger_go() to avoid name collision with main debugger_go() 2019-10-06 16:28:44 -07:00
Aaron Culliney
82813b3ac8 Use correct initial pointers 2019-10-06 16:25:59 -07:00
Aaron Culliney
ad95368a8c Can debug again on Pixely devices ... 2019-10-06 16:22:38 -07:00
Aaron Culliney
135ccb6b2d Fix build break in testprefs 2019-10-06 16:18:39 -07:00
Aaron Culliney
7591715357 Fixes to get CPU tracing building again 2019-10-06 16:18:30 -07:00
Aaron Culliney
317f83a44b Inscrutable changes insisted upon by IB 2019-08-09 12:10:44 +05:30
Aaron Culliney
3bad383cbe Fix iOS build 2019-08-09 11:40:15 +05:30
Aaron Culliney
96e75f50ff Avoid doing stderr logging, period 2019-06-28 21:10:07 -07:00
Aaron Culliney
17147ce662 LOG strerror(errno) for functions that may fail 2019-06-28 20:17:37 -07:00
Aaron Culliney
348eeb1f09 Avoid strerror(errno) spam in logs 2019-06-28 20:06:34 -07:00
Aaron Culliney
e8e3110d18 Remove an aggressive assert for overtaxed audio systems 2019-06-28 19:21:07 -07:00
Aaron Culliney
412fb06011 Slowpath debugging interface 2019-06-28 18:31:08 -07:00
Aaron Culliney
fe714af37c Kill some unnecessary uses of volatile 2019-06-28 18:30:45 -07:00
Aaron Culliney
45cc3332fb Fixes for Linux build 2019-06-27 16:22:05 -07:00
Aaron Culliney
7eacfb4564 Provide more visual feedback when calibrating joystick variants 2019-06-09 16:58:53 -07:00
Aaron Culliney
e643b1c855 Remove misguided justTapConfigured code path 2019-06-09 14:04:14 -07:00
Aaron Culliney
4515e5d7b7 Support touch joystick button fire when swiping left/right 2019-06-09 13:02:27 -07:00
Aaron Culliney
713d9a99e2 More complete feature changes 2019-06-09 11:51:15 -07:00
Aaron Culliney
fcfd32b843 Allow Android device sensor portrait mode 2019-06-09 11:14:28 -07:00
Aaron Culliney
c98777c6ae Correctly handle shifted keys in JSON touch keypad joystick and simplify associated JSON preferences 2019-06-09 10:53:31 -07:00
Aaron Culliney
2878c5adde Partial Revert "Fastpath no-logging, don't do normal logging in release but provide a means to RELEASE_LOG()"
- Logging to file may still be useful in release, and we already don't do logging to logcat/stdout
    - TODO: verify no hotpath logging in release ...
2019-06-02 13:12:10 -07:00
Aaron Culliney
788c6cb172 Refactor touch joystick variants
- Use end-of-video-frame callbacks for more conformant delay timing
    - Allow full octant for button side in touch keypad joystick
    - Improved response upon unambiguous touch event (e.g., octant change) to immediately press key
2019-06-02 13:08:16 -07:00
Aaron Culliney
a25d68a1d2 Remove old preference migration codepath 2019-06-02 08:25:08 -07:00
Aaron Culliney
44e7bda8ac Kill some build warnings 2019-06-01 17:47:35 -07:00
Aaron Culliney
5efd1099bc Build x86_64 for Android 2019-06-01 17:17:53 -07:00
Aaron Culliney
3579423890 Upgraded Android Studio 2019-06-01 17:17:20 -07:00
Aaron Culliney
20852bf737 Lite refactor keys API 2019-06-01 07:27:54 -07:00
Aaron Culliney
1a32756bd4 Fastpath no-logging, don't do normal logging in release but provide a means to RELEASE_LOG() 2019-06-01 07:24:30 -07:00
Aaron Culliney
5a8aa065a9 Refactor/simplify spinlocking 2019-06-01 07:13:59 -07:00
Aaron Culliney
ef3944a4dd Provide callback API for end-of-video-frame 2019-06-01 07:01:42 -07:00
Aaron Culliney
9e8a5e2134 Rename some debugger functions and mark as TESTING 2019-03-24 15:50:16 -07:00
Aaron Culliney
1c61071a11 Lite refactor debugger interface 2019-03-24 15:19:58 -07:00
Aaron Culliney
3ca0d9b618 Add some new keyboard variants for the given RPGs 2019-03-10 12:18:27 -07:00
Aaron Culliney
9b110224a0 Lite refactor/rename keys internal API 2019-03-10 12:17:06 -07:00
Aaron Culliney
faf7d707ed Unbreak INTERFACE_CLASSIC rendering 2019-03-09 06:56:49 -08:00
Aaron Culliney
5bd8e25739 Improved two-thumb support for touch keyboard 2019-03-02 17:23:50 -08:00
Aaron Culliney
75be89d1ab Promote alt keyboard selection to a higher menu order 2019-03-02 16:23:16 -08:00
Aaron Culliney
f038ef0346 Unify and brace thread creation with TEMP_FAILURE_RETRY() 2019-02-24 09:24:12 -08:00
Aaron Culliney
d90e12b5dc Rename touch joystick variants for code clarity 2019-02-17 08:12:23 -08:00
Aaron Culliney
c22ad4c1e9 Remove ancient text files 2018-12-08 07:57:09 -08:00
Aaron Culliney
1bf328795a Silence some Android build warnings 2018-12-02 09:10:11 -08:00
Aaron Culliney
aa74763d28 Bump Android version to 2.0.1 2018-11-25 19:01:45 -08:00
Aaron Culliney
36c83c62e3 Make sure data_dir and logging are initialized for all platforms 2018-11-25 18:57:26 -08:00
147 changed files with 7241 additions and 8420 deletions

1
.gitignore vendored
View File

@ -79,6 +79,7 @@ Android/local.properties
Android/.idea/workspace.xml
Android/.idea/libraries
Android/.idea/dictionaries
Android/.idea/caches
Android/build
Android/jni/obj
Android/obj

14
ASM
View File

@ -1,14 +0,0 @@
Begin3
Title: Apple //ix
Author: alexb@csd.uu.se (Alexander Jean-Claude Bottema)
sl14@cornell.edu (Stephen Lee)
michael@talamasca.ocis.net (Michael Deutschmann)
ASC _at_ BITR0T (Aaron Culliney)
Version: 0.8
Entered-date: 2014-02-27
Description: Apple //e emulator for POSIX systems
Keywords: emulator, linux, posix
Uploader: ASC _at_ BITR0T (Aaron Culliney)
Primary-site: https://github.com/mauiaaron/apple2
Platform: Linux i386
End

View File

@ -1,29 +1,116 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<Objective-C-extensions>
<file>
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Import" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Macro" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Typedef" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Enum" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Constant" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Global" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Struct" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="FunctionPredecl" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Function" />
</file>
<class>
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Property" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Synthesize" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InitMethod" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="StaticMethod" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InstanceMethod" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="DeallocMethod" />
</class>
<extensions>
<pair source="cpp" header="h" fileNamingConvention="NONE" />
<pair source="c" header="h" fileNamingConvention="NONE" />
</extensions>
</Objective-C-extensions>
<codeStyleSettings language="XML">
<indentOptions>
<option name="CONTINUATION_INDENT_SIZE" value="4" />
</indentOptions>
<arrangement>
<rules>
<section>
<rule>
<match>
<AND>
<NAME>xmlns:android</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>xmlns:.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:id</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:name</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>name</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>style</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
<order>ANDROID_ATTRIBUTE_ORDER</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>.*</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
</rules>
</arrangement>
</codeStyleSettings>
</code_scheme>
</component>

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding" addBOMForNewFiles="with NO BOM" />
</project>

View File

@ -3,6 +3,9 @@
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
<compositeConfiguration>
<compositeBuild compositeDefinitionSource="SCRIPT" />
</compositeConfiguration>
<option name="distributionType" value="DEFAULT_WRAPPED" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="modules">

View File

@ -5,22 +5,32 @@
<option name="myDefaultNotNull" value="android.support.annotation.NonNull" />
<option name="myNullables">
<value>
<list size="5">
<list size="10">
<item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.Nullable" />
<item index="1" class="java.lang.String" itemvalue="javax.annotation.Nullable" />
<item index="2" class="java.lang.String" itemvalue="javax.annotation.CheckForNull" />
<item index="3" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.Nullable" />
<item index="4" class="java.lang.String" itemvalue="android.support.annotation.Nullable" />
<item index="5" class="java.lang.String" itemvalue="androidx.annotation.Nullable" />
<item index="6" class="java.lang.String" itemvalue="androidx.annotation.RecentlyNullable" />
<item index="7" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.qual.Nullable" />
<item index="8" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NullableDecl" />
<item index="9" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NullableType" />
</list>
</value>
</option>
<option name="myNotNulls">
<value>
<list size="4">
<list size="9">
<item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.NotNull" />
<item index="1" class="java.lang.String" itemvalue="javax.annotation.Nonnull" />
<item index="2" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.NonNull" />
<item index="3" class="java.lang.String" itemvalue="android.support.annotation.NonNull" />
<item index="4" class="java.lang.String" itemvalue="androidx.annotation.NonNull" />
<item index="5" class="java.lang.String" itemvalue="androidx.annotation.RecentlyNonNull" />
<item index="6" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.qual.NonNull" />
<item index="7" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NonNullDecl" />
<item index="8" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NonNullType" />
</list>
</value>
</option>

6
Android/.idea/vcs.xml Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
</component>
</project>

View File

@ -13,7 +13,7 @@
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/.gradle" />
</content>
<orderEntry type="jdk" jdkName="JDK" jdkType="JavaSDK" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

View File

@ -1 +0,0 @@
app/build/intermediates/merged_manifests/debug/processDebugManifest/merged/AndroidManifest.xml

View File

@ -4,6 +4,8 @@
<facet type="android-gradle" name="Android-Gradle">
<configuration>
<option name="GRADLE_PROJECT_PATH" value=":app" />
<option name="LAST_SUCCESSFUL_SYNC_AGP_VERSION" value="3.5.2" />
<option name="LAST_KNOWN_AGP_VERSION" value="3.5.2" />
</configuration>
</facet>
<facet type="android" name="Android">
@ -17,31 +19,30 @@
<option name="ALLOW_USER_CONFIGURATION" value="false" />
<option name="MANIFEST_FILE_RELATIVE_PATH" value="/src/main/AndroidManifest.xml" />
<option name="RES_FOLDER_RELATIVE_PATH" value="/src/main/res" />
<option name="RES_FOLDERS_RELATIVE_PATH" value="file://$MODULE_DIR$/src/main/res" />
<option name="RES_FOLDERS_RELATIVE_PATH" value="file://$MODULE_DIR$/src/main/res;file://$MODULE_DIR$/build/generated/res/resValues/debug" />
<option name="TEST_RES_FOLDERS_RELATIVE_PATH" value="" />
<option name="ASSETS_FOLDER_RELATIVE_PATH" value="/src/main/assets" />
</configuration>
</facet>
</component>
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_7">
<output url="file://$MODULE_DIR$/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes" />
<output-test url="file://$MODULE_DIR$/build/intermediates/javac/debugUnitTest/compileDebugUnitTestJavaWithJavac/classes" />
<output url="file://$MODULE_DIR$/build/intermediates/javac/debug/classes" />
<output-test url="file://$MODULE_DIR$/build/intermediates/javac/debugUnitTest/classes" />
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/apt/debug" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/not_namespaced_r_class_sources/debug/processDebugResources/r" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/debug" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/ap_generated_sources/debug/out" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/aidl_source_output_dir/debug/compileDebugAidl/out" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/debug" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/debug" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/debug" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/resValues/debug" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/apt/androidTest/debug" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/not_namespaced_r_class_sources/debugAndroidTest/processDebugAndroidTestResources/r" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/androidTest/debug" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/renderscript_source_output_dir/debug/compileDebugRenderscript/out" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/debug" type="java-resource" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/resValues/debug" type="java-resource" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/ap_generated_sources/debugAndroidTest/out" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/aidl_source_output_dir/debugAndroidTest/compileDebugAndroidTestAidl/out" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/androidTest/debug" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/androidTest/debug" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/androidTest/debug" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/resValues/androidTest/debug" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/apt/test/debug" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/renderscript_source_output_dir/debugAndroidTest/compileDebugAndroidTestRenderscript/out" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/androidTest/debug" type="java-test-resource" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/resValues/androidTest/debug" type="java-test-resource" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/ap_generated_sources/debugUnitTest/out" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/res" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/resources" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/assets" type="java-resource" />
@ -84,47 +85,33 @@
<sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/test/rs" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/test/shaders" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/annotation_processor_list" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/apk_list" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/blame" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/check-libraries" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/check-manifest" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/checkDebugClasspath" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/checkReleaseClasspath" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/compatible_screen_manifest" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/instant_run_merged_manifests" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/javac" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/jniLibs" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/merged_assets" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/merged_manifests" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/prebuild" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/processed_res" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/res" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/rs" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/shader_assets" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/shaders" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/split_list" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/symbols" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/transforms" />
<excludeFolder url="file://$MODULE_DIR$/build/outputs" />
<excludeFolder url="file://$MODULE_DIR$/build/tmp" />
<excludeFolder url="file://$MODULE_DIR$/build" />
</content>
<orderEntry type="jdk" jdkName="Android API 27 Platform" jdkType="Android SDK" />
<orderEntry type="jdk" jdkName="Android API 29 Platform" jdkType="Android SDK" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Gradle: android.arch.lifecycle:common:1.1.0@jar" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:support-fragment-27.1.1" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:support-annotations:27.1.1@jar" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:animated-vector-drawable-27.1.1" level="project" />
<orderEntry type="library" name="Gradle: android.arch.core:runtime-1.1.0" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:support-compat-27.1.1" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:appcompat-v7-27.1.1" level="project" />
<orderEntry type="library" name="Gradle: android.arch.lifecycle:viewmodel-1.1.0" level="project" />
<orderEntry type="library" name="Gradle: android.arch.lifecycle:livedata-core-1.1.0" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:support-vector-drawable-27.1.1" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:support-core-ui-27.1.1" level="project" />
<orderEntry type="library" name="Gradle: android.arch.core:common:1.1.0@jar" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:support-core-utils-27.1.1" level="project" />
<orderEntry type="library" name="Gradle: android.arch.lifecycle:runtime-1.1.0" level="project" />
<orderEntry type="library" name="Gradle: androidx.collection:collection:1.1.0@jar" level="project" />
<orderEntry type="library" name="Gradle: androidx.lifecycle:lifecycle-common:2.1.0@jar" level="project" />
<orderEntry type="library" name="Gradle: androidx.arch.core:core-common:2.1.0@jar" level="project" />
<orderEntry type="library" name="Gradle: androidx.annotation:annotation:1.1.0@jar" level="project" />
<orderEntry type="library" name="Gradle: androidx.appcompat:appcompat:1.1.0@aar" level="project" />
<orderEntry type="library" name="Gradle: androidx.fragment:fragment:1.1.0@aar" level="project" />
<orderEntry type="library" name="Gradle: androidx.appcompat:appcompat-resources:1.1.0@aar" level="project" />
<orderEntry type="library" name="Gradle: androidx.drawerlayout:drawerlayout:1.0.0@aar" level="project" />
<orderEntry type="library" name="Gradle: androidx.viewpager:viewpager:1.0.0@aar" level="project" />
<orderEntry type="library" name="Gradle: androidx.loader:loader:1.0.0@aar" level="project" />
<orderEntry type="library" name="Gradle: androidx.activity:activity:1.0.0@aar" level="project" />
<orderEntry type="library" name="Gradle: androidx.vectordrawable:vectordrawable-animated:1.1.0@aar" level="project" />
<orderEntry type="library" name="Gradle: androidx.vectordrawable:vectordrawable:1.1.0@aar" level="project" />
<orderEntry type="library" name="Gradle: androidx.customview:customview:1.0.0@aar" level="project" />
<orderEntry type="library" name="Gradle: androidx.core:core:1.1.0@aar" level="project" />
<orderEntry type="library" name="Gradle: androidx.cursoradapter:cursoradapter:1.0.0@aar" level="project" />
<orderEntry type="library" name="Gradle: androidx.versionedparcelable:versionedparcelable:1.1.0@aar" level="project" />
<orderEntry type="library" name="Gradle: androidx.lifecycle:lifecycle-viewmodel:2.1.0@aar" level="project" />
<orderEntry type="library" name="Gradle: androidx.lifecycle:lifecycle-runtime:2.1.0@aar" level="project" />
<orderEntry type="library" name="Gradle: androidx.savedstate:savedstate:1.0.0@aar" level="project" />
<orderEntry type="library" name="Gradle: androidx.lifecycle:lifecycle-livedata:2.0.0@aar" level="project" />
<orderEntry type="library" name="Gradle: androidx.lifecycle:lifecycle-livedata-core:2.0.0@aar" level="project" />
<orderEntry type="library" name="Gradle: androidx.interpolator:interpolator:1.0.0@aar" level="project" />
<orderEntry type="library" name="Gradle: androidx.arch.core:core-runtime:2.0.0@aar" level="project" />
</component>
</module>

View File

@ -1,7 +1,7 @@
apply plugin: 'com.android.application'
android {
compileSdkVersion 27
compileSdkVersion 29
signingConfigs {
release {
storeFile file("release2.keystore")
@ -21,6 +21,23 @@ android {
jniDebuggable true
}
}
splits {
// Configures multiple APKs based on ABI
abi {
// Enables building multiple APKs per ABI
enable true
// Resets the list of ABIs that Gradle should create APKs for to none
reset()
// Include just these ...
include "armeabi-v7a", "arm64-v8a", "x86", "x86_64"
// Specifies that we do not want to also generate a universal APK that includes all ABIs
//universalApk false
}
}
lintOptions {
checkReleaseBuilds false
}
@ -28,9 +45,9 @@ android {
defaultConfig {
applicationId "org.deadc0de.apple2ix.basic"
minSdkVersion 14
targetSdkVersion 27
versionCode 23
versionName "2.0.0"
targetSdkVersion 29
versionCode 26
versionName "2.1.0-RC3"
ndk {
moduleName "apple2ix"
}
@ -39,5 +56,5 @@ android {
dependencies {
api fileTree(dir: 'libs', include: ['*.jar'])
api 'com.android.support:appcompat-v7:27.1.1'
api 'androidx.appcompat:appcompat:1.1.0'
}

View File

@ -3,6 +3,7 @@
package="org.deadc0de.apple2ix.basic" >
<uses-feature android:glEsVersion="0x00020000" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<supports-screens
@ -12,6 +13,7 @@
android:smallScreens="false" />
<application
android:requestLegacyExternalStorage="true"
android:allowBackup="true"
android:fullBackupContent="@xml/a2backupscheme"
android:icon="@drawable/ic_launcher"
@ -21,7 +23,7 @@
android:name="org.deadc0de.apple2ix.Apple2Activity"
android:configChanges="mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|screenLayout|fontScale|uiMode|orientation"
android:screenOrientation="sensorLandscape"
android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
android:theme="@style/Theme.AppCompat.NoActionBar.FullScreen"
android:launchMode="singleTask"
android:windowSoftInputMode="stateHidden|adjustNothing" >
<intent-filter>
@ -37,11 +39,18 @@
</activity>
<activity android:name="org.deadc0de.apple2ix.Apple2DiskChooserActivity" />
<activity android:name="org.deadc0de.apple2ix.Apple2DiskChooserActivity"
android:theme="@style/Theme.AppCompat"
/>
<activity android:name="org.deadc0de.apple2ix.Apple2EmailerActivity"
android:theme="@style/Theme.AppCompat"
android:screenOrientation="portrait"
/>
<provider
android:authorities="${applicationId}.provider"
android:name="android.support.v4.content.FileProvider"
android:name="androidx.core.content.FileProvider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data

View File

@ -10,11 +10,11 @@ package android.widget;
import android.content.Context;
import android.graphics.Canvas;
import androidx.appcompat.widget.AppCompatSeekBar;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
public class VerticalSeekBar extends SeekBar {
public class VerticalSeekBar extends AppCompatSeekBar {
public VerticalSeekBar(Context context) {
super(context);

View File

@ -11,10 +11,12 @@
package org.deadc0de.apple2ix;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.graphics.drawable.Drawable;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.widget.AppCompatCheckBox;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@ -219,7 +221,7 @@ public abstract class Apple2AbstractMenu implements Apple2MenuView {
}
protected static CheckBox _addCheckbox(Apple2Activity activity, IMenuEnum setting, View convertView, boolean isChecked) {
CheckBox checkBox = new CheckBox(activity);
AppCompatCheckBox checkBox = new AppCompatCheckBox(activity);
checkBox.setChecked(isChecked);
LinearLayout layout = (LinearLayout) convertView.findViewById(R.id.a2preference_widget_frame);
layout.addView(checkBox);

View File

@ -12,8 +12,6 @@
package org.deadc0de.apple2ix;
import android.Manifest;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.pm.PackageManager;
import android.content.res.AssetManager;
@ -21,6 +19,8 @@ import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.StrictMode;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
@ -28,6 +28,10 @@ import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.Toast;
import org.deadc0de.apple2ix.basic.BuildConfig;
import org.deadc0de.apple2ix.basic.R;
import org.json.JSONObject;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
@ -37,10 +41,8 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.concurrent.atomic.AtomicBoolean;
import org.deadc0de.apple2ix.basic.BuildConfig;
import org.deadc0de.apple2ix.basic.R;
public class Apple2Activity extends Activity implements Apple2DiskChooserActivity.Callback {
public class Apple2Activity extends AppCompatActivity implements Apple2DiskChooserActivity.Callback, Apple2EmailerActivity.Callback
{
private final static String TAG = "Apple2Activity";
@ -96,6 +98,8 @@ public class Apple2Activity extends Activity implements Apple2DiskChooserActivit
private static native void nativeReboot(int resetState);
private static native void nativeLogMessage(String jsonStr);
public final static boolean isNativeBarfed() {
return sNativeBarfed;
}
@ -118,7 +122,7 @@ public class Apple2Activity extends Activity implements Apple2DiskChooserActivit
}
super.onCreate(savedInstanceState);
Log.e(TAG, "onCreate()");
logMessage(LogType.ERROR, TAG, "onCreate()");
// placeholder view on initial launch
if (mView == null) {
@ -127,14 +131,14 @@ public class Apple2Activity extends Activity implements Apple2DiskChooserActivit
Apple2CrashHandler.getInstance().initializeAndSetCustomExceptionHandler(this);
if (sNativeBarfed) {
Log.e(TAG, "NATIVE BARFED...", sNativeBarfedThrowable);
logMessage(LogType.ERROR, TAG, "NATIVE BARFED : " + sNativeBarfedThrowable.getMessage());
return;
}
int sampleRate = DevicePropertyCalculator.getRecommendedSampleRate(this);
int monoBufferSize = DevicePropertyCalculator.getRecommendedBufferSize(this, /*isStereo:*/false);
int stereoBufferSize = DevicePropertyCalculator.getRecommendedBufferSize(this, /*isStereo:*/true);
Log.d(TAG, "Device sampleRate:" + sampleRate + " mono bufferSize:" + monoBufferSize + " stereo bufferSize:" + stereoBufferSize);
logMessage(LogType.DEBUG, TAG, "Device sampleRate:" + sampleRate + " mono bufferSize:" + monoBufferSize + " stereo bufferSize:" + stereoBufferSize);
String dataDir = Apple2Utils.getDataDir(this);
nativeOnCreate(dataDir, sampleRate, monoBufferSize, stereoBufferSize);
@ -153,9 +157,8 @@ public class Apple2Activity extends Activity implements Apple2DiskChooserActivit
// Is there a way to persist the user orientation setting such that we launch in the previously set orientation and avoid getting multiple onCreate() onResume()?! ... Android lifecycle edge cases are so damn kludgishly annoying ...
mSwitchingToPortrait.set(switchingToPortrait);
if (!switchingToPortrait) {
Apple2CrashHandler.getInstance().checkForCrashes(this);
}
Apple2CrashHandler.getInstance().checkForCrashes(this);
boolean extperm = true;
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
@ -185,7 +188,7 @@ public class Apple2Activity extends Activity implements Apple2DiskChooserActivit
Apple2Utils.exposeAPKAssets(Apple2Activity.this);
if (externalStoragePermission) {
Apple2Utils.exposeAPKAssetsToExternal(Apple2Activity.this);
Log.d(TAG, "Finished first time copying #1...");
logMessage(LogType.DEBUG, TAG, "Finished first time copying #1...");
if (!(boolean)Apple2Preferences.getJSONPref(Apple2Preferences.PREF_DOMAIN_INTERFACE, Apple2Preferences.PREF_RELEASE_NOTES, false)) {
Runnable myRunnable = new Runnable() {
@ -207,6 +210,11 @@ public class Apple2Activity extends Activity implements Apple2DiskChooserActivit
mDisksMenu = new Apple2DisksMenu(this);
}
@Override
public void onEmailerFinished() {
Apple2CrashHandler.getInstance().cleanCrashData(this);
}
@Override
public void onDisksChosen(DiskArgs args) {
final String name = args.name;
@ -232,7 +240,7 @@ public class Apple2Activity extends Activity implements Apple2DiskChooserActivit
// perform migration(s) and assets exposure now
Apple2Utils.migrateToExternalStorage(Apple2Activity.this);
Apple2Utils.exposeAPKAssetsToExternal(Apple2Activity.this);
Log.d(TAG, "Finished first time copying #2...");
logMessage(LogType.DEBUG, TAG, "Finished first time copying #2...");
} // else ... we keep nagging on app startup ...
} else {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
@ -253,11 +261,8 @@ public class Apple2Activity extends Activity implements Apple2DiskChooserActivit
break;
}
Log.d(TAG, "onResume()");
logMessage(LogType.DEBUG, TAG, "onResume()");
showSplashScreen(/*dismissable:*/true);
if (!mSwitchingToPortrait.get()) {
Apple2CrashHandler.getInstance().checkForCrashes(this); // NOTE : needs to be called again to clean-up
}
if (mDisksMenu == null) {
break;
@ -320,10 +325,10 @@ public class Apple2Activity extends Activity implements Apple2DiskChooserActivit
if (isEmulationPaused()) {
Apple2Preferences.save(this);
} else {
Log.d(TAG, "Letting native save preferences...");
logMessage(LogType.DEBUG, TAG, "Letting native save preferences...");
}
Log.d(TAG, "onPause()");
logMessage(LogType.INFO, TAG, "onPause() ...");
if (mView != null) {
mView.onPause();
}
@ -415,7 +420,7 @@ public class Apple2Activity extends Activity implements Apple2DiskChooserActivit
} catch (InterruptedIOException e) {
/* EINTR, EAGAIN */
} catch (IOException e) {
Log.e(TAG, "OOPS could not load release_notes.txt!", e);
logMessage(LogType.ERROR, TAG, "OOPS could not load release_notes.txt : " + e.getMessage());
} finally {
if (is != null) {
try {
@ -606,6 +611,7 @@ public class Apple2Activity extends Activity implements Apple2DiskChooserActivit
}
public void quitEmulator() {
logMessage(LogType.INFO, TAG, "Quitting...");
nativeOnQuit();
finish();
new Runnable() {
@ -620,4 +626,30 @@ public class Apple2Activity extends Activity implements Apple2DiskChooserActivit
}
}.run();
}
public enum LogType {
// Constants match
VERBOSE(2),
DEBUG(3),
INFO(4),
WARN(5),
ERROR(6);
private int type;
LogType(int type) {
this.type = type;
}
}
public static void logMessage(LogType type, String tag, String mesg) {
JSONObject map = new JSONObject();
try {
map.put("type", type.type);
map.put("tag", tag);
map.put("mesg", mesg);
nativeLogMessage(map.toString());
} catch (Exception e) {
Log.e(TAG, "OOPS: " + e.getMessage());
}
}
}

View File

@ -11,22 +11,18 @@
package org.deadc0de.apple2ix;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.util.Log;
import androidx.appcompat.app.AlertDialog;
import android.view.View;
import android.widget.ProgressBar;
import org.deadc0de.apple2ix.basic.BuildConfig;
import org.deadc0de.apple2ix.basic.R;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.BufferedWriter;
import java.io.File;
@ -34,10 +30,13 @@ import java.io.FileWriter;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.util.ArrayList;
import java.util.concurrent.atomic.AtomicBoolean;
public class Apple2CrashHandler {
public final static String ALL_CRASH_FILE = "apple2ix_data.zip";
public final static String javaCrashFileName = "jcrash.txt";
public static Apple2CrashHandler getInstance() {
@ -205,222 +204,127 @@ public class Apple2CrashHandler {
}
public void checkForCrashes(final Apple2Activity activity) {
File oldCrashFile = _getCrashLogFile(activity);
if (oldCrashFile.exists()) {
oldCrashFile.delete();
}
if (!areCrashesPresent(activity)) {
return;
}
Apple2Preferences.load(activity);
if (!(boolean) Apple2Preferences.getJSONPref(Apple2SettingsMenu.SETTINGS.CRASH)) {
cleanCrashData(activity);
return;
}
boolean previouslyRanCrashCheck = mAlreadyRanCrashCheck.getAndSet(true);
boolean previouslySentReport = mAlreadySentReport.get();
if (previouslySentReport) {
// here we assume that the crash data was previously sent via email ... if not then we lost it =P
Log.d(TAG, "Cleaning up crash data ...");
int idx = 0;
File[] nativeCrashes = _nativeCrashFiles(activity);
for (File crash : nativeCrashes) {
if (!crash.delete()) {
Log.d(TAG, "Could not unlink crash : " + crash);
}
File processed = new File(_dumpPath2ProcessedPath(crash.getAbsolutePath()));
if (!processed.delete()) {
Log.d(TAG, "Could not unlink processed : " + processed);
}
}
File javaCrashFile = _javaCrashFile(activity);
if (!javaCrashFile.delete()) {
Log.d(TAG, "Could not unlink java crash : " + javaCrashFile);
}
// remove previous log file
File allCrashFile = _getCrashFile(activity);
Apple2Utils.writeFile(new StringBuilder(), allCrashFile);
allCrashFile.delete();
return;
}
if (previouslyRanCrashCheck) {
// don't keep asking on return from backgrounding
return;
}
final AlertDialog crashDialog = new AlertDialog.Builder(activity).setIcon(R.drawable.ic_launcher).setCancelable(true).setTitle(R.string.crasher_send).setMessage(R.string.crasher_send_message).setNegativeButton(R.string.no, null).setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
final AlertDialog crashDialog = new AlertDialog.Builder(activity).setIcon(R.drawable.ic_launcher).setCancelable(true).setTitle(R.string.crasher_send).setMessage(R.string.crasher_send_message).setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
emailCrashesAndLogs(activity);
}
}).create();
activity.registerAndShowDialog(crashDialog);
}
public void emailCrashesAndLogs(final Apple2Activity activity) {
final Apple2SplashScreen splashScreen = activity.getSplashScreen();
if (splashScreen != null) {
splashScreen.setDismissable(false);
}
final ProgressBar bar = (ProgressBar) activity.findViewById(R.id.crash_progressBar);
try {
bar.setVisibility(View.VISIBLE);
} catch (NullPointerException npe) {
/* email logs doesn't show the splash screen */
}
new Thread(new Runnable() {
@Override
public void run() {
android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_BACKGROUND);
final int sampleRate = DevicePropertyCalculator.getRecommendedSampleRate(activity);
final int monoBufferSize = DevicePropertyCalculator.getRecommendedBufferSize(activity, /*isStereo:*/false);
final int stereoBufferSize = DevicePropertyCalculator.getRecommendedBufferSize(activity, /*isStereo:*/true);
StringBuilder summary = new StringBuilder();
// prepend information about this device
summary.append("BRAND: ").append(Build.BRAND).append("\n");
summary.append("MODEL: ").append(Build.MODEL).append("\n");
summary.append("MANUFACTURER: ").append(Build.MANUFACTURER).append("\n");
summary.append("DEVICE: ").append(Build.DEVICE).append("\n");
summary.append("SDK: ").append(Build.VERSION.SDK_INT).append("\n");
summary.append("SAMPLE RATE: ").append(sampleRate).append("\n");
summary.append("MONO BUFSIZE: ").append(monoBufferSize).append("\n");
summary.append("STEREO BUFSIZE: ").append(stereoBufferSize).append("\n");
summary.append("GPU VENDOR: ").append(Apple2Preferences.getJSONPref(SETTINGS.GL_VENDOR)).append("\n");
summary.append("GPU RENDERER: ").append(Apple2Preferences.getJSONPref(SETTINGS.GL_RENDERER)).append("\n");
summary.append("GPU VERSION: ").append(Apple2Preferences.getJSONPref(SETTINGS.GL_VERSION)).append("\n");
final Apple2SplashScreen splashScreen = activity.getSplashScreen();
if (splashScreen != null) {
splashScreen.setDismissable(false);
}
final ProgressBar bar = (ProgressBar) activity.findViewById(R.id.crash_progressBar);
try {
bar.setVisibility(View.VISIBLE);
} catch (NullPointerException npe) {
/* could happen on early lifecycle crashes */
PackageInfo pInfo = activity.getPackageManager().getPackageInfo(activity.getPackageName(), 0);
summary.append("APP VERSION: ").append(pInfo.versionName).append("\n");
} catch (PackageManager.NameNotFoundException e) {
// ...
}
new Thread(new Runnable() {
File[] nativeCrashes = _nativeCrashFiles(activity);
if (nativeCrashes == null) {
nativeCrashes = new File[0];
}
final int len = nativeCrashes.length + 1/* maybe Java crash */ + 1/* exposeSymbols */;
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
if (bar != null) {
bar.setMax(len);
}
}
});
android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_BACKGROUND);
if (nativeCrashes.length > 0) {
Apple2Utils.exposeSymbols(activity);
}
final int sampleRate = DevicePropertyCalculator.getRecommendedSampleRate(activity);
final int monoBufferSize = DevicePropertyCalculator.getRecommendedBufferSize(activity, /*isStereo:*/false);
final int stereoBufferSize = DevicePropertyCalculator.getRecommendedBufferSize(activity, /*isStereo:*/true);
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
if (bar != null) {
bar.setProgress(1);
}
}
});
StringBuilder summary = new StringBuilder();
StringBuilder allCrashData = new StringBuilder();
// iteratively process native crashes
ArrayList<File> allCrashFiles = new ArrayList<File>();
if (nativeCrashes.length > 0) {
for (File crash : nativeCrashes) {
// prepend information about this device
summary.append("BRAND: ").append(Build.BRAND).append("\n");
summary.append("MODEL: ").append(Build.MODEL).append("\n");
summary.append("MANUFACTURER: ").append(Build.MANUFACTURER).append("\n");
summary.append("DEVICE: ").append(Build.DEVICE).append("\n");
summary.append("SDK: ").append(Build.VERSION.SDK_INT).append("\n");
summary.append("SAMPLE RATE: ").append(sampleRate).append("\n");
summary.append("MONO BUFSIZE: ").append(monoBufferSize).append("\n");
summary.append("STEREO BUFSIZE: ").append(stereoBufferSize).append("\n");
summary.append("GPU VENDOR: ").append(Apple2Preferences.getJSONPref(SETTINGS.GL_VENDOR)).append("\n");
summary.append("GPU RENDERER: ").append(Apple2Preferences.getJSONPref(SETTINGS.GL_RENDERER)).append("\n");
summary.append("GPU VERSION: ").append(Apple2Preferences.getJSONPref(SETTINGS.GL_VERSION)).append("\n");
String crashPath = crash.getAbsolutePath();
Apple2Activity.logMessage(Apple2Activity.LogType.DEBUG, TAG, "Processing crash : " + crashPath);
String processedPath = _dumpPath2ProcessedPath(crashPath);
try {
PackageInfo pInfo = activity.getPackageManager().getPackageInfo(activity.getPackageName(), 0);
summary.append("APP VERSION: ").append(pInfo.versionName).append("\n");
} catch (PackageManager.NameNotFoundException e) {
// ...
nativeProcessCrash(crashPath, processedPath); // Run Breakpad minidump_stackwalk
} catch (UnsatisfiedLinkError ule) {
/* could happen on early lifecycle crashes */
}
allCrashData.append(summary);
File[] nativeCrashes = _nativeCrashFiles(activity);
if (nativeCrashes == null) {
nativeCrashes = new File[0];
}
final int len = nativeCrashes.length + 1/* maybe Java crash */ + 1/* exposeSymbols */;
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
if (bar != null) {
bar.setMax(len);
}
}
});
if (len > 0) {
Apple2Utils.exposeSymbols(activity);
}
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
if (bar != null) {
bar.setProgress(1);
}
}
});
boolean summarizedHeader = false;
// iteratively process native crashes
for (File crash : nativeCrashes) {
String crashPath = crash.getAbsolutePath();
Log.d(TAG, "Processing crash : " + crashPath);
String processedPath = _dumpPath2ProcessedPath(crashPath);
try {
nativeProcessCrash(crashPath, processedPath); // Run Breakpad minidump_stackwalk
} catch (UnsatisfiedLinkError ule) {
/* could happen on early lifecycle crashes */
}
StringBuilder crashData = new StringBuilder();
if (!Apple2Utils.readEntireFile(new File(processedPath), crashData)) {
Log.e(TAG, "Error processing crash : " + crashPath);
}
allCrashData.append(">>>>>>> NATIVE CRASH [").append(crashPath).append("]\n");
allCrashData.append(crashData);
summary.append("NATIVE CRASH:\n");
// append succinct information about crashing thread
String[] lines = crashData.toString().split("[\\n\\r][\\n\\r]*");
for (int i = 0, j = 0; i < lines.length; i++) {
// 2 lines of minidump summary
if (i < 2) {
if (!summarizedHeader) {
summary.append(lines[i]);
summary.append("\n");
}
continue;
}
// 1 line of crashing thread and reason
if (i == 2) {
summarizedHeader = true;
summary.append(lines[i]);
summary.append("\n");
continue;
}
// whole lotta modules
if (lines[i].startsWith("Module")) {
continue;
}
// one apparently empty line
if (lines[i].matches("^[ \\t]*$")) {
continue;
}
// append crashing thread backtrace
summary.append(lines[i]);
summary.append("\n");
final int maxSummaryBacktrace = 8;
if (j++ >= maxSummaryBacktrace) {
break;
}
}
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
if (bar != null) {
bar.setProgress(bar.getProgress() + 1);
}
}
});
}
StringBuilder javaCrashData = new StringBuilder();
File javaCrashFile = _javaCrashFile(activity);
if (javaCrashFile.exists()) {
Log.d(TAG, "Reading java crashes file");
if (!Apple2Utils.readEntireFile(javaCrashFile, javaCrashData)) {
Log.e(TAG, "Error processing java crash : " + javaCrashFileName);
}
}
allCrashData.append(">>>>>>> JAVA CRASH DATA\n");
allCrashData.append(javaCrashData);
summary.append("JAVA CRASH:\n");
summary.append(javaCrashData);
allCrashFiles.add(new File(processedPath));
activity.runOnUiThread(new Runnable() {
@Override
@ -430,43 +334,85 @@ public class Apple2CrashHandler {
}
}
});
StringBuilder jsonData = new StringBuilder();
if (Apple2Utils.readEntireFile(new File(homeDir, Apple2Preferences.PREFS_FILE), jsonData)) {
JSONObject obj = null;
try {
obj = new JSONObject(jsonData.toString());
} catch (JSONException e) {
Log.e(TAG, "Error reading preferences : " + e);
}
if (obj != null) {
summary.append("PREFS:\n");
summary.append(obj.toString());
allCrashData.append(">>>>>>> PREFS\n");
allCrashData.append(obj.toString());
}
}
Apple2Utils.unexposeSymbols(activity);
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
try {
bar.setVisibility(View.INVISIBLE);
splashScreen.setDismissable(true);
} catch (NullPointerException npe) {
/* could happen on early lifecycle crashes */
}
}
});
// send report with all the data
_sendEmailToDeveloperWithCrashData(activity, summary, allCrashData);
}
}).start();
summary.append("" + nativeCrashes.length + " Native dumps\n");
}
File javaCrashFile = _javaCrashFile(activity);
if (javaCrashFile.exists()) {
summary.append("Java crash log\n");
allCrashFiles.add(javaCrashFile);
}
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
if (bar != null) {
bar.setProgress(bar.getProgress() + 1);
}
}
});
allCrashFiles.add(new File(homeDir, Apple2Preferences.PREFS_FILE));
if (nativeCrashes.length > 0) {
Apple2Utils.unexposeSymbols(activity);
}
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
try {
bar.setVisibility(View.INVISIBLE);
splashScreen.setDismissable(true);
} catch (NullPointerException npe) {
/* email logs doesn't show the splash screen */
}
}
});
// Add all the log files ...
{
File[] nativeLogs = _nativeLogs(activity);
for (File logFile : nativeLogs) {
allCrashFiles.add(logFile);
}
}
File[] allCrashesAry = new File[allCrashFiles.size()];
File nativeCrashesZip = Apple2Utils.zipFiles(allCrashFiles.toArray(allCrashesAry), _getCrashLogFile(activity));
// send report with all the data
_sendEmailToDeveloperWithCrashData(activity, summary, nativeCrashesZip);
}
}).create();
activity.registerAndShowDialog(crashDialog);
}).start();
}
public void cleanCrashData(Apple2Activity activity) {
Apple2Activity.logMessage(Apple2Activity.LogType.DEBUG, TAG, "Cleaning up crash data ...");
File[] nativeCrashes = _nativeCrashFiles(activity);
for (File crash : nativeCrashes) {
if (!crash.delete()) {
Apple2Activity.logMessage(Apple2Activity.LogType.DEBUG, TAG, "Could not unlink crash : " + crash);
}
File processed = new File(_dumpPath2ProcessedPath(crash.getAbsolutePath()));
if (!processed.delete()) {
Apple2Activity.logMessage(Apple2Activity.LogType.DEBUG, TAG, "Could not unlink processed : " + processed);
}
}
File javaCrashFile = _javaCrashFile(activity);
if (!javaCrashFile.delete()) {
Apple2Activity.logMessage(Apple2Activity.LogType.DEBUG, TAG, "Could not unlink java crash : " + javaCrashFile);
}
/* HACK NOTE -- don't delete the crash log file here ... possibly the email sender program still needs the link!
File allCrashFile = _getCrashLogFile(activity);
Apple2Utils.writeFile(new StringBuilder(), allCrashFile);
allCrashFile.delete();
*/
}
public void performCrash(int crashType) {
@ -511,7 +457,7 @@ public class Apple2CrashHandler {
} catch (InterruptedIOException ie) {
/* EINTR, EAGAIN ... */
} catch (IOException e) {
Log.e(TAG, "Exception attempting to write data : " + e);
Apple2Activity.logMessage(Apple2Activity.LogType.ERROR, TAG, "Exception attempting to write data : " + e);
}
try {
@ -527,6 +473,21 @@ public class Apple2CrashHandler {
return new File(homeDir, javaCrashFileName);
}
private File[] _nativeLogs(Apple2Activity activity) {
FilenameFilter logFilter = new FilenameFilter() {
public boolean accept(File dir, String name) {
File file = new File(dir, name);
if (file.isDirectory()) {
return false;
}
return name.startsWith("apple2ix_log");
}
};
return new File(homeDir).listFiles(logFilter);
}
private File[] _nativeCrashFiles(Apple2Activity activity) {
FilenameFilter dmpFilter = new FilenameFilter() {
public boolean accept(File dir, String name) {
@ -556,59 +517,57 @@ public class Apple2CrashHandler {
return crashPath.substring(0, crashPath.length() - 4) + ".txt";
}
private File _getCrashFile(Apple2Activity activity) {
File allCrashFile;
private File _getCrashLogFile(Apple2Activity activity) {
File file;
String storageState = Environment.getExternalStorageState();
if (storageState.equals(Environment.MEDIA_MOUNTED)) {
allCrashFile = new File(Environment.getExternalStorageDirectory(), "apple2ix_crash.txt");
file = new File(Environment.getExternalStorageDirectory(), ALL_CRASH_FILE);
} else {
allCrashFile = new File(Apple2Utils.getDataDir(activity), "apple2ix_crash.txt");
file = new File(Apple2Utils.getDataDir(activity), ALL_CRASH_FILE);
}
Log.d(TAG, "Writing all crashes to temp file : " + allCrashFile.getAbsolutePath());
return allCrashFile;
return file;
}
private void _sendEmailToDeveloperWithCrashData(Apple2Activity activity, StringBuilder summary, StringBuilder allCrashData) {
mAlreadySentReport.set(true);
private void _sendEmailToDeveloperWithCrashData(Apple2Activity activity, StringBuilder summary, File nativeCrashesZip) {
final boolean alreadyChoosing = Apple2EmailerActivity.sEmailerIsEmailing.getAndSet(true);
if (alreadyChoosing) {
return;
}
// <sigh> ... the disaster that is early Android ... there does not appear to be a reliable way to start an
// email Intent to send both text and an attachment, but we make a valiant (if futile) effort to do so here.
// And the reason to send an attachment is that you trigger an android.os.TransactionTooLargeException with too
// much text data in the EXTRA_TEXT ... </sigh>
Intent emailIntent = new Intent(Intent.ACTION_SENDTO, Uri.fromParts("mailto", "apple2ix_crash@deadcode.org"/*non-zero variant is correct endpoint at the moment*/, null));
emailIntent.putExtra(Intent.EXTRA_SUBJECT, "Crasher");
Intent emailIntent = new Intent(activity, Apple2EmailerActivity.class);
emailIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK/* | Intent.FLAG_ACTIVITY_CLEAR_TOP */);
final int maxCharsEmail = 4096;
int len = summary.length();
len = len < maxCharsEmail ? len : maxCharsEmail;
String summaryData = summary.substring(0, len);
emailIntent.putExtra(Intent.EXTRA_TEXT, "The app crashed, please help!\n\n" + summaryData);
emailIntent.putExtra(Intent.EXTRA_TEXT, "A2IX app logs and crash data\n\n" + summaryData);
File allCrashFile = _getCrashFile(activity);
Apple2Utils.writeFile(allCrashData, allCrashFile);
if (!allCrashFile.setReadable(true, /*ownerOnly:*/false)) {
Log.d(TAG, "Oops, could not set file data readable!");
if (!nativeCrashesZip.setReadable(true, /*ownerOnly:*/false)) {
Apple2Activity.logMessage(Apple2Activity.LogType.DEBUG, TAG, "Oops, could not set crash file data readable!");
}
emailIntent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(allCrashFile));
emailIntent.putExtra(Apple2EmailerActivity.EXTRA_STREAM_PATH, nativeCrashesZip.getAbsolutePath());
Log.d(TAG, "STARTING CHOOSER FOR EMAIL ...");
activity.startActivity(Intent.createChooser(emailIntent, "Send email"));
Log.d(TAG, "AFTER START ACTIVITY ...");
Apple2Activity.logMessage(Apple2Activity.LogType.DEBUG, TAG, "STARTING EMAILER ACTIVITY ...");
Apple2EmailerActivity.sEmailerCallback = activity;
activity.startActivityForResult(emailIntent, Apple2EmailerActivity.SEND_REQUEST_CODE);
}
private final static String TAG = "Apple2CrashHandler";
private final static Apple2CrashHandler sCrashHandler = new Apple2CrashHandler();
private String homeDir;
private Thread.UncaughtExceptionHandler mDefaultExceptionHandler;
private AtomicBoolean mAlreadyRanCrashCheck = new AtomicBoolean(false);
private AtomicBoolean mAlreadySentReport = new AtomicBoolean(false);
private static native void nativePerformCrash(int crashType); // testing
private static native void nativeProcessCrash(String crashFilePath, String crashProcessedPath);
}

View File

@ -11,21 +11,23 @@
package org.deadc0de.apple2ix;
import android.app.Activity;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.provider.DocumentsContract;
import android.support.annotation.Nullable;
import android.util.Log;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import android.provider.OpenableColumns;
import java.util.concurrent.atomic.AtomicBoolean;
public class Apple2DiskChooserActivity extends Activity {
public class Apple2DiskChooserActivity extends AppCompatActivity {
public static final AtomicBoolean sDiskChooserIsChoosing = new AtomicBoolean(false);
public static Callback sDisksCallback;
@ -52,15 +54,44 @@ public class Apple2DiskChooserActivity extends Activity {
resolver.takePersistableUriPermission(uri, (Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION));
pfd = resolver.openFileDescriptor(uri, "rw");
} catch (Throwable t) {
Log.e(TAG, "OOPS, could not get appropriate access to URI ( " + uri + " ) : " + t);
Apple2Activity.logMessage(Apple2Activity.LogType.ERROR, TAG, "OOPS, could not get appropriate access to URI ( " + uri + " ) : " + t);
}
return pfd;
}
@Nullable
public static String getFileNameFromUri(Context ctx, Uri uri) {
String fileName = null;
try {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
throw new RuntimeException("SDK Version not allowed access");
}
if (!DocumentsContract.isDocumentUri(ctx, uri)) {
throw new RuntimeException("Not a Document URI for " + uri);
}
ContentResolver resolver = ctx.getContentResolver();
Cursor returnCursor = resolver.query(uri, null, null, null, null);
int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
returnCursor.moveToFirst();
fileName = returnCursor.getString(nameIndex);
} catch (Throwable t) {
Apple2Activity.logMessage(Apple2Activity.LogType.ERROR, TAG, "OOPS, could not get filename from URI ( " + uri + " ) : " + t);
}
return fileName;
}
@Override
protected void onSaveInstanceState(Bundle outState) {
outState.putBoolean("ran", true);
super.onSaveInstanceState(outState);
}
@Override
@ -85,13 +116,14 @@ public class Apple2DiskChooserActivity extends Activity {
}
boolean ran = b.getBoolean("ran");
/* -- Android onCreate() can be called multiple times, for example, on an orientation change ... this codepath was aborting the disk selection process when an orientation event occurred...
if (ran) {
Log.e(TAG, "OOPS, already ran...");
Apple2Activity.logMessage(Apple2Activity.LogType.ERROR, TAG, "OOPS, already ran...");
finish();
return;
}
*/
////Intent pickIntent = new Intent(Intent.ACTION_GET_CONTENT);
Intent pickIntent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
try {
@ -107,7 +139,7 @@ public class Apple2DiskChooserActivity extends Activity {
startActivityForResult(pickIntent, EDIT_REQUEST_CODE);
}
} catch (Throwable t) {
Log.e(TAG, "OOPS : " + t);
Apple2Activity.logMessage(Apple2Activity.LogType.ERROR, TAG, "OOPS : " + t);
setResult(RESULT_CANCELED);
finish();
}
@ -142,6 +174,7 @@ public class Apple2DiskChooserActivity extends Activity {
if (chosenUri != null) {
chosenPfd = openFileDescriptorFromUri(this, chosenUri);
chosenFileName = getFileNameFromUri(this, chosenUri);
}
}
@ -157,7 +190,7 @@ public class Apple2DiskChooserActivity extends Activity {
@Override
public void finish() {
sDiskChooserIsChoosing.set(false);
String name = chosenUri == null ? "" : chosenUri.toString();
String name = chosenFileName == null ? (chosenUri == null ? "" : chosenUri.toString()) : chosenFileName;
if (sDisksCallback != null) {
sDisksCallback.onDisksChosen(new DiskArgs(name, chosenUri, chosenPfd));
}
@ -168,6 +201,8 @@ public class Apple2DiskChooserActivity extends Activity {
private ParcelFileDescriptor chosenPfd;
private String chosenFileName;
private static final String TAG = "A2DiskChooserActivity";
private static final int EDIT_REQUEST_CODE = 44;

View File

@ -11,13 +11,13 @@
package org.deadc0de.apple2ix;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Build;
import androidx.appcompat.app.AlertDialog;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
@ -34,8 +34,10 @@ import android.widget.RadioButton;
import android.widget.TextView;
import android.widget.Toast;
import org.deadc0de.apple2ix.basic.R;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.File;
import java.io.FilenameFilter;
@ -43,9 +45,6 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import org.deadc0de.apple2ix.basic.R;
import org.json.JSONObject;
public class Apple2DisksMenu implements Apple2MenuView {
private final static String TAG = "Apple2DisksMenu";
@ -370,7 +369,7 @@ public class Apple2DisksMenu implements Apple2MenuView {
try {
diskArgs.pfd.close();
} catch (IOException ioe) {
Log.e(TAG, "Error attempting to close PFD : " + ioe);
Apple2Activity.logMessage(Apple2Activity.LogType.ERROR, TAG, "Error attempting to close PFD : " + ioe);
}
}
diskArgs.pfd = null;
@ -382,7 +381,7 @@ public class Apple2DisksMenu implements Apple2MenuView {
}
} catch (Throwable t) {
Log.d(TAG, "OOPS: " + t);
Apple2Activity.logMessage(Apple2Activity.LogType.DEBUG, TAG, "OOPS: " + t);
}
}
@ -429,12 +428,6 @@ public class Apple2DisksMenu implements Apple2MenuView {
final RadioButton readWrite = (RadioButton) diskConfirmationView.findViewById(R.id.radioButton_readWrite);
readWrite.setChecked(!roChecked);
builder.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
});
builder.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
@ -571,6 +564,10 @@ public class Apple2DisksMenu implements Apple2MenuView {
break;
}
if (diskPath.startsWith(Apple2DisksMenu.EXTERNAL_CHOOSER_SENTINEL)) {
diskPath = diskPath.substring(Apple2DisksMenu.EXTERNAL_CHOOSER_SENTINEL.length());
}
Uri uri = Uri.parse(diskPath);
if (uri == null) {
break;
@ -582,7 +579,7 @@ public class Apple2DisksMenu implements Apple2MenuView {
break;
}
imageName = diskPath.substring(idx + 1);
imageName = Apple2DiskChooserActivity.getFileNameFromUri(mActivity, uri);
} while (false);
LinearLayout layout = (LinearLayout) mDisksView.findViewById((i == 0) ? R.id.a2_newschool_driveA_layout : R.id.a2_newschool_driveB_layout);
@ -742,7 +739,7 @@ public class Apple2DisksMenu implements Apple2MenuView {
@Override
public void onItemClick(AdapterView<?> parent, View view, final int position, long id) {
if (isDirectory[position]) {
Log.d(TAG, "Descending to path : " + filePaths[position]);
Apple2Activity.logMessage(Apple2Activity.LogType.DEBUG, TAG, "Descending to path : " + filePaths[position]);
if (parentIsRootPath && !new File(filePaths[position]).isAbsolute()) {
pushPathStack(parentDisksDir + File.separator + filePaths[position]);
} else {

View File

@ -0,0 +1,143 @@
/*
* Apple // emulator for *nix
*
* This software package is subject to the GNU General Public License
* version 3 or later (your choice) as published by the Free Software
* Foundation.
*
* Copyright 2019 Aaron Culliney
*
*/
package org.deadc0de.apple2ix;
import android.content.Intent;
import android.media.MediaScannerConnection;
import android.net.Uri;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import java.util.concurrent.atomic.AtomicBoolean;
public class Apple2EmailerActivity extends AppCompatActivity {
public static final String EXTRA_STREAM_PATH = Intent.EXTRA_STREAM + "-PATH";
public static final int SEND_REQUEST_CODE = 46;
public static final AtomicBoolean sEmailerIsEmailing = new AtomicBoolean(false);
public static Callback sEmailerCallback;
public interface Callback {
void onEmailerFinished();
}
@Override
protected void onRestoreInstanceState(Bundle inState) {
super.onRestoreInstanceState(inState);
mExtraText = inState.getString(Intent.EXTRA_TEXT);
mExtraStreamPath = inState.getString(EXTRA_STREAM_PATH);
mExtraStream = inState.getParcelable(Intent.EXTRA_STREAM);
}
@Override
protected void onSaveInstanceState(Bundle outState) {
outState.putBoolean("ran", true);
outState.putString(Intent.EXTRA_TEXT, mExtraText);
outState.putString(EXTRA_STREAM_PATH, mExtraStreamPath);
outState.putParcelable(Intent.EXTRA_STREAM, mExtraStream);
super.onSaveInstanceState(outState);
}
@Override
protected void onCreate(Bundle savedState) {
super.onCreate(savedState);
Bundle b;
{
Intent intent = getIntent();
Bundle extras = null;
if (intent != null) {
extras = intent.getExtras();
}
if (savedState != null) {
b = savedState;
} else if (extras != null) {
b = extras;
} else {
b = new Bundle();
}
}
final boolean ran = b.getBoolean("ran");
mExtraText = b.getString(Intent.EXTRA_TEXT);
mExtraStreamPath = b.getString(EXTRA_STREAM_PATH);
MediaScannerConnection.scanFile(this,
/*paths:*/new String[] { mExtraStreamPath },
/*mimeTypes:*/new String[] { "application/zip" },
new MediaScannerConnection.OnScanCompletedListener() {
public void onScanCompleted(String path, Uri uri) {
mExtraStream = uri;
Intent emailIntent = new Intent(Intent.ACTION_SEND);
try {
emailIntent.putExtra(Intent.EXTRA_EMAIL, new String[] { "apple2ix_crash@deadcode.org" });
emailIntent.putExtra(Intent.EXTRA_SUBJECT, "A2IX Report To apple2ix_crash@deadcode.org");
emailIntent.putExtra(Intent.EXTRA_TEXT, mExtraText);
emailIntent.putExtra(Intent.EXTRA_STREAM, mExtraStream);
emailIntent.setType("message/rfc822");
if (!ran) {
Apple2Activity.logMessage(Apple2Activity.LogType.DEBUG, TAG, "STARTING EMAIL ACTIVITY ...");
startActivityForResult(emailIntent, SEND_REQUEST_CODE);
}
} catch (Throwable t) {
Apple2Activity.logMessage(Apple2Activity.LogType.ERROR, TAG, "OOPS : " + t);
setResult(RESULT_CANCELED);
finish();
}
}
});
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode != RESULT_CANCELED) {
// Do something?
}
if (mExtraStream != null) {
int deleted = getContentResolver().delete(mExtraStream, null, null);
}
sEmailerCallback.onEmailerFinished();
setResult(RESULT_OK);
finish();
}
@Override
protected void onDestroy() {
super.onDestroy();
}
@Override
public void finish() {
sEmailerIsEmailing.set(false);
super.finish();
}
private String mExtraText;
private String mExtraStreamPath;
private Uri mExtraStream;
private static final String TAG = "A2EmailerActivity";
}

View File

@ -12,9 +12,11 @@
package org.deadc0de.apple2ix;
import android.content.Context;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.SeekBar;
import android.widget.TextView;
import org.deadc0de.apple2ix.basic.R;
@ -22,18 +24,47 @@ import java.util.ArrayList;
public class Apple2JoystickCalibration implements Apple2MenuView {
private final static String TAG = "Apple2JoystickCalibration";
private final static String TAG = "apple2ix-js-calibrate";
public final static int JOYSTICK_DIVIDER_NUM_CHOICES = Apple2Preferences.DECENT_AMOUNT_OF_CHOICES;
public final static String PREF_SCREEN_DIVISION = "screenDivider";
private static native long nativePollJoystick();
private Apple2Activity mActivity = null;
private View mSettingsView = null;
private ArrayList<Apple2MenuView> mViewStack = null;
private boolean mTouchMenuEnabled = false;
private int mSavedTouchDevice = Apple2SettingsMenu.TouchDeviceVariant.NONE.ordinal();
private Thread joystickPollerThread = null;
public Apple2JoystickCalibration(Apple2Activity activity, ArrayList<Apple2MenuView> viewStack, Apple2SettingsMenu.TouchDeviceVariant variant) {
public static void startCalibration(Apple2Activity activity, Apple2SettingsMenu.TouchDeviceVariant variant) {
ArrayList<Apple2MenuView> viewStack = new ArrayList<Apple2MenuView>();
{
int idx = 0;
while (true) {
Apple2MenuView apple2MenuView = activity.peekApple2View(idx);
if (apple2MenuView == null) {
break;
}
viewStack.add(apple2MenuView);
++idx;
}
}
Apple2JoystickCalibration calibration = new Apple2JoystickCalibration(activity, viewStack, variant);
// show this new view...
calibration.show();
// ...with nothing else underneath 'cept the emulator OpenGL layer
for (Apple2MenuView apple2MenuView : viewStack) {
activity.popApple2View(apple2MenuView);
}
}
private Apple2JoystickCalibration(Apple2Activity activity, ArrayList<Apple2MenuView> viewStack, Apple2SettingsMenu.TouchDeviceVariant variant) {
mActivity = activity;
mViewStack = viewStack;
if (!(variant == Apple2SettingsMenu.TouchDeviceVariant.JOYSTICK || variant == Apple2SettingsMenu.TouchDeviceVariant.JOYSTICK_KEYPAD)) {
@ -43,7 +74,7 @@ public class Apple2JoystickCalibration implements Apple2MenuView {
setup(variant);
}
private void setup(Apple2SettingsMenu.TouchDeviceVariant variant) {
private void setup(final Apple2SettingsMenu.TouchDeviceVariant variant) {
LayoutInflater inflater = (LayoutInflater) mActivity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mSettingsView = inflater.inflate(R.layout.activity_calibrate_joystick, null, false);
@ -72,6 +103,48 @@ public class Apple2JoystickCalibration implements Apple2MenuView {
float val = Apple2Preferences.getFloatJSONPref(Apple2Preferences.PREF_DOMAIN_JOYSTICK, PREF_SCREEN_DIVISION, (JOYSTICK_DIVIDER_NUM_CHOICES >> 1) / (float) JOYSTICK_DIVIDER_NUM_CHOICES);
sb.setProgress((int) (val * JOYSTICK_DIVIDER_NUM_CHOICES));
final TextView axisCoords = (TextView) mSettingsView.findViewById(R.id.axisCoords);
joystickPollerThread = new Thread() {
@Override
public void run() {
Apple2Activity.logMessage(Apple2Activity.LogType.INFO, TAG, "Starting joystick poll thread...");
try {
while (true) {
long cxy = nativePollJoystick();
String t = "";
if (variant == Apple2SettingsMenu.TouchDeviceVariant.JOYSTICK) {
final int x = ((int) (cxy & 0xFF00) >> 8) - 128;
final int y = ((int) (cxy & 0x00FF) >> 0) - 128;
t = "X: " + x + " Y: " + y;
} else {
char ascii = (char) ((cxy & 0xFF000000) >> 24);
int scancode = (char) ((cxy & 0x00FF0000) >> 16);
if (ascii == Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION || scancode == 0) {
// ...
} else {
t = "Key: " + Apple2KeypadChooser.asciiRepresentation(mActivity, ascii);
}
}
final String axisText = t;
mActivity.runOnUiThread(new Runnable() {
@Override
public void run() {
axisCoords.setText(axisText);
}
});
Thread.sleep(100);
}
} catch (Exception e) {
Apple2Activity.logMessage(Apple2Activity.LogType.INFO, TAG, "Stopping joystick poll thread...");
}
}
};
joystickPollerThread.start();
mTouchMenuEnabled = (boolean) Apple2Preferences.getJSONPref(Apple2KeyboardSettingsMenu.SETTINGS.TOUCH_MENU_ENABLED);
Apple2Preferences.setJSONPref(Apple2KeyboardSettingsMenu.SETTINGS.TOUCH_MENU_ENABLED, false);
mSavedTouchDevice = (int) Apple2Preferences.getJSONPref(Apple2SettingsMenu.SETTINGS.CURRENT_INPUT);
@ -96,6 +169,7 @@ public class Apple2JoystickCalibration implements Apple2MenuView {
}
public void dismiss() {
joystickPollerThread.interrupt();
for (Apple2MenuView apple2MenuView : mViewStack) {
if (apple2MenuView != this) {
mActivity.pushApple2View(apple2MenuView);

View File

@ -36,12 +36,18 @@ public class Apple2JoystickSettingsMenu extends Apple2AbstractMenu {
public final static int JOYSTICK_AXIS_SENSITIVITY_INC_NUMCHOICES = (int) ((JOYSTICK_AXIS_SENSITIVITY_MAX - JOYSTICK_AXIS_SENSITIVITY_DEFAULT) / JOYSTICK_AXIS_SENSITIVITY_INC_STEP); // 12
public final static int JOYSTICK_AXIS_SENSITIVITY_NUM_CHOICES = JOYSTICK_AXIS_SENSITIVITY_DEC_NUMCHOICES + JOYSTICK_AXIS_SENSITIVITY_INC_NUMCHOICES; // 15 + 12
public final static int TAPDELAY_NUM_CHOICES = Apple2Preferences.DECENT_AMOUNT_OF_CHOICES;
public final static float TAPDELAY_SCALE = 0.5f;
public final static int TAPDELAY_NUM_CHOICES = (30 + 1); // 0-30 (30Frames == ~0.5sec)
private Apple2SettingsMenu.TouchDeviceVariant mVariant;
public Apple2JoystickSettingsMenu(Apple2Activity activity) {
public Apple2JoystickSettingsMenu(Apple2Activity activity, Apple2SettingsMenu.TouchDeviceVariant variant) {
super(activity);
if (!(variant == Apple2SettingsMenu.TouchDeviceVariant.JOYSTICK || variant == Apple2SettingsMenu.TouchDeviceVariant.JOYSTICK_KEYPAD)) {
throw new RuntimeException("You're doing it wrong");
}
mVariant = variant;
}
@Override
@ -80,6 +86,33 @@ public class Apple2JoystickSettingsMenu extends Apple2AbstractMenu {
}
protected enum SETTINGS implements Apple2AbstractMenu.IMenuEnum {
JOYSTICK_CALIBRATE {
@Override
public final String getTitle(Apple2Activity activity) {
return activity.getResources().getString(R.string.joystick_calibrate);
}
@Override
public final String getSummary(Apple2Activity activity) {
return activity.getResources().getString(R.string.joystick_calibrate_summary);
}
@Override
public String getPrefKey() {
return null;
}
@Override
public Object getPrefDefault() {
return null;
}
@Override
public void handleSelection(Apple2Activity activity, Apple2AbstractMenu settingsMenu, boolean isChecked) {
Apple2JoystickSettingsMenu thisMenu = (Apple2JoystickSettingsMenu)settingsMenu;
Apple2JoystickCalibration.startCalibration(activity, thisMenu.mVariant);
}
},
JOYSTICK_TAP_BUTTON {
@Override
public final String getTitle(Apple2Activity activity) {
@ -227,44 +260,105 @@ public class Apple2JoystickSettingsMenu extends Apple2AbstractMenu {
});
}
},
JOYSTICK_CALIBRATE {
JOYSTICK_SWIPELEFT_BUTTON {
@Override
public final String getTitle(Apple2Activity activity) {
return activity.getResources().getString(R.string.joystick_calibrate);
return activity.getResources().getString(R.string.joystick_button_swipe_left_button);
}
@Override
public final String getSummary(Apple2Activity activity) {
return activity.getResources().getString(R.string.joystick_calibrate_summary);
return activity.getResources().getString(R.string.joystick_button_swipe_left_button_summary);
}
@Override
public void handleSelection(Apple2Activity activity, Apple2AbstractMenu settingsMenu, boolean isChecked) {
ArrayList<Apple2MenuView> viewStack = new ArrayList<Apple2MenuView>();
{
int idx = 0;
while (true) {
Apple2MenuView apple2MenuView = activity.peekApple2View(idx);
if (apple2MenuView == null) {
break;
}
viewStack.add(apple2MenuView);
++idx;
public String getPrefKey() {
return "jsSwipeWestChar";
}
@Override
public Object getPrefDefault() {
return TouchJoystickButtons.NONE.ordinal();
}
@Override
public View getView(final Apple2Activity activity, View convertView) {
convertView = _basicView(activity, this, convertView);
_addPopupIcon(activity, this, convertView);
return convertView;
}
@Override
public void handleSelection(final Apple2Activity activity, final Apple2AbstractMenu settingsMenu, boolean isChecked) {
final IMenuEnum self = this;
_alertDialogHandleSelection(activity, R.string.joystick_button_swipe_left_button, new String[]{
activity.getResources().getString(R.string.joystick_button_button_none),
activity.getResources().getString(R.string.joystick_button_button1),
activity.getResources().getString(R.string.joystick_button_button2),
activity.getResources().getString(R.string.joystick_button_button_both),
}, new IPreferenceLoadSave() {
@Override
public int intValue() {
return (int) Apple2Preferences.getJSONPref(self);
}
}
Apple2JoystickCalibration calibration = new Apple2JoystickCalibration(activity, viewStack, Apple2SettingsMenu.TouchDeviceVariant.JOYSTICK);
// show this new view...
calibration.show();
// ...with nothing else underneath 'cept the emulator OpenGL layer
for (Apple2MenuView apple2MenuView : viewStack) {
activity.popApple2View(apple2MenuView);
}
@Override
public void saveInt(int value) {
Apple2Preferences.setJSONPref(self, TouchJoystickButtons.values()[value].ordinal());
}
});
}
},
JOYSTICK_TAPDELAY {
JOYSTICK_SWIPERIGHT_BUTTON {
@Override
public final String getTitle(Apple2Activity activity) {
return activity.getResources().getString(R.string.joystick_button_swipe_right_button);
}
@Override
public final String getSummary(Apple2Activity activity) {
return activity.getResources().getString(R.string.joystick_button_swipe_right_button_summary);
}
@Override
public String getPrefKey() {
return "jsSwipeEastChar";
}
@Override
public Object getPrefDefault() {
return TouchJoystickButtons.NONE.ordinal();
}
@Override
public View getView(final Apple2Activity activity, View convertView) {
convertView = _basicView(activity, this, convertView);
_addPopupIcon(activity, this, convertView);
return convertView;
}
@Override
public void handleSelection(final Apple2Activity activity, final Apple2AbstractMenu settingsMenu, boolean isChecked) {
final IMenuEnum self = this;
_alertDialogHandleSelection(activity, R.string.joystick_button_swipe_right_button, new String[]{
activity.getResources().getString(R.string.joystick_button_button_none),
activity.getResources().getString(R.string.joystick_button_button1),
activity.getResources().getString(R.string.joystick_button_button2),
activity.getResources().getString(R.string.joystick_button_button_both),
}, new IPreferenceLoadSave() {
@Override
public int intValue() {
return (int) Apple2Preferences.getJSONPref(self);
}
@Override
public void saveInt(int value) {
Apple2Preferences.setJSONPref(self, TouchJoystickButtons.values()[value].ordinal());
}
});
}
},
JOYSTICK_AXIS_SENSITIVITY {
@Override
public final String getTitle(Apple2Activity activity) {
return "";
@ -272,36 +366,55 @@ public class Apple2JoystickSettingsMenu extends Apple2AbstractMenu {
@Override
public final String getSummary(Apple2Activity activity) {
return activity.getResources().getString(R.string.joystick_button_tapdelay_summary);
return activity.getResources().getString(R.string.joystick_axis_sensitivity_summary);
}
@Override
public String getPrefKey() {
return "jsTapDelaySecs";
return "axisSensitivity";
}
@Override
public Object getPrefDefault() {
return ((float) 8 / TAPDELAY_NUM_CHOICES * TAPDELAY_SCALE); // -> 0.2f
return 1.f;
}
@Override
public View getView(final Apple2Activity activity, View convertView) {
final IMenuEnum self = this;
return _sliderView(activity, this, TAPDELAY_NUM_CHOICES, new IPreferenceSlider() {
return _sliderView(activity, this, JOYSTICK_AXIS_SENSITIVITY_NUM_CHOICES, new IPreferenceSlider() {
@Override
public void saveInt(int progress) {
Apple2Preferences.setJSONPref(self, ((float) progress / TAPDELAY_NUM_CHOICES * TAPDELAY_SCALE));
final int pivot = JOYSTICK_AXIS_SENSITIVITY_DEC_NUMCHOICES;
float sensitivity = 1.f;
if (progress < pivot) {
int decAmount = (pivot - progress);
sensitivity -= (JOYSTICK_AXIS_SENSITIVITY_DEC_STEP * decAmount);
} else if (progress > pivot) {
int incAmount = (progress - pivot);
sensitivity += (JOYSTICK_AXIS_SENSITIVITY_INC_STEP * incAmount);
}
Apple2Preferences.setJSONPref(self, sensitivity);
}
@Override
public int intValue() {
return (int) (Apple2Preferences.getFloatJSONPref(self) / TAPDELAY_SCALE * TAPDELAY_NUM_CHOICES);
float sensitivity = Apple2Preferences.getFloatJSONPref(self);
int pivot = JOYSTICK_AXIS_SENSITIVITY_DEC_NUMCHOICES;
if (sensitivity < 1.f) {
pivot = Math.round((sensitivity - JOYSTICK_AXIS_SENSITIVITY_MIN) / JOYSTICK_AXIS_SENSITIVITY_DEC_STEP);
} else if (sensitivity > 1.f) {
sensitivity -= 1.f;
pivot += Math.round(sensitivity / JOYSTICK_AXIS_SENSITIVITY_INC_STEP);
}
return pivot;
}
@Override
public void showValue(int progress, final TextView seekBarValue) {
seekBarValue.setText("" + (((float) progress / TAPDELAY_NUM_CHOICES) * TAPDELAY_SCALE));
saveInt(progress);
int percent = (int) (Apple2Preferences.getFloatJSONPref(self) * 100.f);
seekBarValue.setText("" + percent + "%");
}
});
}
@ -387,7 +500,7 @@ public class Apple2JoystickSettingsMenu extends Apple2AbstractMenu {
if (position < 0 || position >= SETTINGS.size) {
throw new ArrayIndexOutOfBoundsException();
}
return position == SETTINGS.JOYSTICK_AXIS_ON_LEFT.ordinal();
return position <= SETTINGS.JOYSTICK_AXIS_ON_LEFT.ordinal();
}
protected enum SETTINGS implements Apple2AbstractMenu.IMenuEnum {
@ -461,7 +574,7 @@ public class Apple2JoystickSettingsMenu extends Apple2AbstractMenu {
return convertView;
}
},
JOYSTICK_AXIS_SENSITIVITY {
JOYSTICK_TAPDELAY {
@Override
public final String getTitle(Apple2Activity activity) {
return "";
@ -469,55 +582,40 @@ public class Apple2JoystickSettingsMenu extends Apple2AbstractMenu {
@Override
public final String getSummary(Apple2Activity activity) {
return activity.getResources().getString(R.string.joystick_axis_sensitivity_summary);
return activity.getResources().getString(R.string.joystick_button_tapdelay_summary);
}
@Override
public String getPrefKey() {
return "axisSensitivity";
return "jsTapDelayFrames";
}
@Override
public Object getPrefDefault() {
return 1.f;
return 12; // 12 * 16.688millis == ~0.2secs
}
@Override
public View getView(final Apple2Activity activity, View convertView) {
final IMenuEnum self = this;
return _sliderView(activity, this, JOYSTICK_AXIS_SENSITIVITY_NUM_CHOICES, new IPreferenceSlider() {
return _sliderView(activity, this, TAPDELAY_NUM_CHOICES, new IPreferenceSlider() {
@Override
public void saveInt(int progress) {
final int pivot = JOYSTICK_AXIS_SENSITIVITY_DEC_NUMCHOICES;
float sensitivity = 1.f;
if (progress < pivot) {
int decAmount = (pivot - progress);
sensitivity -= (JOYSTICK_AXIS_SENSITIVITY_DEC_STEP * decAmount);
} else if (progress > pivot) {
int incAmount = (progress - pivot);
sensitivity += (JOYSTICK_AXIS_SENSITIVITY_INC_STEP * incAmount);
}
Apple2Preferences.setJSONPref(self, sensitivity);
Apple2Preferences.setJSONPref(self, progress);
}
@Override
public int intValue() {
float sensitivity = Apple2Preferences.getFloatJSONPref(self);
int pivot = JOYSTICK_AXIS_SENSITIVITY_DEC_NUMCHOICES;
if (sensitivity < 1.f) {
pivot = Math.round((sensitivity - JOYSTICK_AXIS_SENSITIVITY_MIN) / JOYSTICK_AXIS_SENSITIVITY_DEC_STEP);
} else if (sensitivity > 1.f) {
sensitivity -= 1.f;
pivot += Math.round(sensitivity / JOYSTICK_AXIS_SENSITIVITY_INC_STEP);
}
return pivot;
return Apple2Preferences.getIntJSONPref(self);
}
@Override
public void showValue(int progress, final TextView seekBarValue) {
saveInt(progress);
int percent = (int) (Apple2Preferences.getFloatJSONPref(self) * 100.f);
seekBarValue.setText("" + percent + "%");
String millis = String.format(java.util.Locale.ROOT, "%.3f", progress * 16.688f);
String framesStr = activity.getResources().getString(R.string.string_frames);
String millisStr = activity.getResources().getString(R.string.string_millis);
String textSummary = "" + progress + " " + framesStr + " (" + millis + " " + millisStr + ")";
seekBarValue.setText(textSummary);
}
});
}
@ -549,9 +647,6 @@ public class Apple2JoystickSettingsMenu extends Apple2AbstractMenu {
return _sliderView(activity, this, JOYSTICK_BUTTON_THRESHOLD_NUM_CHOICES, new IPreferenceSlider() {
@Override
public void saveInt(int progress) {
if (progress == 0) {
progress = 1;
}
progress *= getJoystickButtonSwitchThresholdScale(activity);
Apple2Preferences.setJSONPref(self, progress);
}

View File

@ -29,6 +29,15 @@ public class Apple2KeyboardSettingsMenu extends Apple2AbstractMenu {
private final static String TAG = "KeyboardSettingsMenu";
// These settings must match native side
public final static int kLT = 8;
public final static int kTAB = 9;
public final static int kDN = 10;
public final static int kUP = 11;
public final static int kRET = 13;
public final static int kRT = 21;
public final static int kESC = 27;
public final static int kDEL = 127;
public final static int MOUSETEXT_BEGIN = 0x80;
public final static int MOUSETEXT_CLOSEDAPPLE = MOUSETEXT_BEGIN/*+0x00*/;
public final static int MOUSETEXT_OPENAPPLE = MOUSETEXT_BEGIN + 0x01;
@ -46,7 +55,9 @@ public class Apple2KeyboardSettingsMenu extends Apple2AbstractMenu {
public final static int ICONTEXT_NONACTION = ICONTEXT_KBD_BEGIN + 0x0C;
public final static int SCANCODE_A = 30;
public final static int SCANCODE_C = 46;
public final static int SCANCODE_D = 32;
public final static int SCANCODE_E = 18;
public final static int SCANCODE_F = 33;
public final static int SCANCODE_H = 35;
public final static int SCANCODE_I = 23;
@ -56,17 +67,23 @@ public class Apple2KeyboardSettingsMenu extends Apple2AbstractMenu {
public final static int SCANCODE_M = 50;
public final static int SCANCODE_N = 49;
public final static int SCANCODE_O = 24;
public final static int SCANCODE_P = 25;
public final static int SCANCODE_Q = 16;
public final static int SCANCODE_S = 31;
public final static int SCANCODE_U = 22;
public final static int SCANCODE_W = 17;
public final static int SCANCODE_X = 45;
public final static int SCANCODE_Y = 21;
public final static int SCANCODE_Z = 44;
public final static int SCANCODE_SPACE = 57;
public final static int SCANCODE_SEMICOLON = 39;
public final static int SCANCODE_UP = 103;
public final static int SCANCODE_LEFT = 105;
public final static int SCANCODE_RIGHT = 106;
public final static int SCANCODE_DOWN = 108;
public final static int SCANCODE_COMMA = 51;
public final static int SCANCODE_PERIOD = 52;
public final static int SCANCODE_SLASH = 53;
public Apple2KeyboardSettingsMenu(Apple2Activity activity) {
super(activity);
@ -136,6 +153,102 @@ public class Apple2KeyboardSettingsMenu extends Apple2AbstractMenu {
return convertView;
}
},
KEYBOARD_CHOOSE_ALT {
@Override
public final String getTitle(Apple2Activity activity) {
return activity.getResources().getString(R.string.keyboard_choose_alt);
}
@Override
public final String getSummary(Apple2Activity activity) {
return activity.getResources().getString(R.string.keyboard_choose_alt_summary);
}
@Override
public String getPrefKey() {
return "altPathIndex";
}
@Override
public Object getPrefDefault() {
return 0;
}
@Override
public final View getView(final Apple2Activity activity, View convertView) {
convertView = _basicView(activity, this, convertView);
_addPopupIcon(activity, this, convertView);
return convertView;
}
@Override
public void handleSelection(final Apple2Activity activity, final Apple2AbstractMenu settingsMenu, boolean isChecked) {
File extKeyboardDir = Apple2Utils.getExternalStorageDirectory(activity);
FilenameFilter kbdJsonFilter = new FilenameFilter() {
public boolean accept(File dir, String name) {
File file = new File(dir, name);
if (file.isDirectory()) {
return false;
}
// check file extensions ... sigh ... no String.endsWithIgnoreCase() ?
final String extension = ".kbd.json";
final int nameLen = name.length();
final int extLen = extension.length();
if (nameLen <= extLen) {
return false;
}
String suffix = name.substring(nameLen - extLen, nameLen);
return (suffix.equalsIgnoreCase(extension));
}
};
File[] files = null;
if (extKeyboardDir != null) {
files = extKeyboardDir.listFiles(kbdJsonFilter);
}
if (files == null) {
// read keyboard data from /data/data/...
File keyboardDir = new File(Apple2Utils.getDataDir(activity) + File.separator + "keyboards");
files = keyboardDir.listFiles(kbdJsonFilter);
if (files == null) {
Apple2Activity.logMessage(Apple2Activity.LogType.ERROR, TAG, "OOPS, could not read keyboard data directory");
return;
}
}
Arrays.sort(files);
final File[] allFiles = files;
String[] titles = new String[allFiles.length];
int idx = 0;
for (File file : allFiles) {
titles[idx] = file.getName();
++idx;
}
final String keyboardDirName = extKeyboardDir == null ? "Keyboards" : extKeyboardDir.getPath();
final IMenuEnum self = this;
_alertDialogHandleSelection(activity, keyboardDirName, titles, new IPreferenceLoadSave() {
@Override
public int intValue() {
return (int) Apple2Preferences.getJSONPref(self);
}
@Override
public void saveInt(int value) {
Apple2Preferences.setJSONPref(self, value);
String path = allFiles[value].getPath();
Apple2Preferences.setJSONPref(Apple2Preferences.PREF_DOMAIN_KEYBOARD, "altPath", path);
}
});
}
},
KEYBOARD_VISIBILITY_INACTIVE {
@Override
public final String getTitle(Apple2Activity activity) {
@ -255,6 +368,41 @@ public class Apple2KeyboardSettingsMenu extends Apple2AbstractMenu {
return convertView;
}
},
KEYBOARD_ENABLE_DUO_TOUCH {
@Override
public final String getTitle(Apple2Activity activity) {
return activity.getResources().getString(R.string.keyboard_duotouch_enabled);
}
@Override
public final String getSummary(Apple2Activity activity) {
return activity.getResources().getString(R.string.keyboard_duotouch_enabled_summary);
}
@Override
public String getPrefKey() {
return "duoTouchEnabled";
}
@Override
public Object getPrefDefault() {
return false;
}
@Override
public View getView(final Apple2Activity activity, View convertView) {
convertView = _basicView(activity, this, convertView);
CheckBox cb = _addCheckbox(activity, this, convertView, (boolean) Apple2Preferences.getJSONPref(this));
final IMenuEnum self = this;
cb.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
Apple2Preferences.setJSONPref(self, isChecked);
}
});
return convertView;
}
},
KEYBOARD_ENABLE_LOWERCASE {
@Override
public final String getTitle(Apple2Activity activity) {
@ -290,102 +438,6 @@ public class Apple2KeyboardSettingsMenu extends Apple2AbstractMenu {
return convertView;
}
},
KEYBOARD_CHOOSE_ALT {
@Override
public final String getTitle(Apple2Activity activity) {
return activity.getResources().getString(R.string.keyboard_choose_alt);
}
@Override
public final String getSummary(Apple2Activity activity) {
return activity.getResources().getString(R.string.keyboard_choose_alt_summary);
}
@Override
public String getPrefKey() {
return "altPathIndex";
}
@Override
public Object getPrefDefault() {
return 0;
}
@Override
public final View getView(final Apple2Activity activity, View convertView) {
convertView = _basicView(activity, this, convertView);
_addPopupIcon(activity, this, convertView);
return convertView;
}
@Override
public void handleSelection(final Apple2Activity activity, final Apple2AbstractMenu settingsMenu, boolean isChecked) {
File extKeyboardDir = Apple2Utils.getExternalStorageDirectory(activity);
FilenameFilter kbdJsonFilter = new FilenameFilter() {
public boolean accept(File dir, String name) {
File file = new File(dir, name);
if (file.isDirectory()) {
return false;
}
// check file extensions ... sigh ... no String.endsWithIgnoreCase() ?
final String extension = ".kbd.json";
final int nameLen = name.length();
final int extLen = extension.length();
if (nameLen <= extLen) {
return false;
}
String suffix = name.substring(nameLen - extLen, nameLen);
return (suffix.equalsIgnoreCase(extension));
}
};
File[] files = null;
if (extKeyboardDir != null) {
files = extKeyboardDir.listFiles(kbdJsonFilter);
}
if (files == null) {
// read keyboard data from /data/data/...
File keyboardDir = new File(Apple2Utils.getDataDir(activity) + File.separator + "keyboards");
files = keyboardDir.listFiles(kbdJsonFilter);
if (files == null) {
Log.e(TAG, "OOPS, could not read keyboard data directory");
return;
}
}
Arrays.sort(files);
final File[] allFiles = files;
String[] titles = new String[allFiles.length];
int idx = 0;
for (File file : allFiles) {
titles[idx] = file.getName();
++idx;
}
final String keyboardDirName = extKeyboardDir == null ? "Keyboards" : extKeyboardDir.getPath();
final IMenuEnum self = this;
_alertDialogHandleSelection(activity, keyboardDirName, titles, new IPreferenceLoadSave() {
@Override
public int intValue() {
return (int) Apple2Preferences.getJSONPref(self);
}
@Override
public void saveInt(int value) {
Apple2Preferences.setJSONPref(self, value);
String path = allFiles[value].getPath();
Apple2Preferences.setJSONPref(Apple2Preferences.PREF_DOMAIN_KEYBOARD, "altPath", path);
}
});
}
},
KEYBOARD_GLYPH_SCALE {
@Override
public final String getTitle(Apple2Activity activity) {

View File

@ -15,7 +15,6 @@ import android.content.Context;
import android.os.Handler;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
@ -25,6 +24,7 @@ import java.util.ArrayList;
import org.deadc0de.apple2ix.basic.R;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
public class Apple2KeypadChooser implements Apple2MenuView {
@ -35,7 +35,7 @@ public class Apple2KeypadChooser implements Apple2MenuView {
private ArrayList<Apple2MenuView> mViewStack = null;
private TextView mCurrentChoicePrompt = null;
private STATE_MACHINE mChooserState = STATE_MACHINE.CHOOSE_NORTHWEST;
private STATE_MACHINE mChooserState = STATE_MACHINE.CHOOSE_AXIS_NORTHWEST;
private boolean mTouchMenuEnabled = false;
private int mSavedTouchDevice = Apple2SettingsMenu.TouchDeviceVariant.NONE.ordinal();
@ -50,6 +50,36 @@ public class Apple2KeypadChooser implements Apple2MenuView {
return true;
}
public static boolean isShiftedKey(char ascii) {
switch (ascii) {
case '~':
case '!':
case '@':
case '#':
case '$':
case '%':
case '^':
case '&':
case '*':
case '(':
case ')':
case '_':
case '+':
case '{':
case '}':
case '|':
case ':':
case '"':
case '<':
case '>':
case '?':
return true;
default:
return false;
}
}
public void onKeyTapCalibrationEvent(char ascii, int scancode) {
if (ascii == Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION) {
scancode = -1;
@ -58,34 +88,17 @@ public class Apple2KeypadChooser implements Apple2MenuView {
return;
}
String asciiStr = asciiRepresentation(ascii);
Log.d(TAG, "ascii:'" + asciiStr + "' scancode:" + scancode);
String asciiStr = asciiRepresentation(mActivity, ascii);
Apple2Activity.logMessage(Apple2Activity.LogType.DEBUG, TAG, "ascii:'" + asciiStr + "' scancode:" + scancode);
if (ascii == ' ') {
ascii = Apple2KeyboardSettingsMenu.ICONTEXT_VISUAL_SPACE;
}
mChooserState.setKey(mActivity, ascii, scancode);
Apple2KeypadSettingsMenu.KeyTuple tuple = new Apple2KeypadSettingsMenu.KeyTuple((long) ascii, (long) scancode, isShiftedKey(ascii));
mChooserState.setKey(mActivity, tuple);
Apple2Preferences.setJSONPref(Apple2SettingsMenu.SETTINGS.CURRENT_INPUT, Apple2SettingsMenu.TouchDeviceVariant.JOYSTICK_KEYPAD.ordinal());
Apple2Preferences.sync(mActivity, Apple2Preferences.PREF_DOMAIN_TOUCHSCREEN);
mCurrentChoicePrompt.setText(getNextChoiceString() + asciiStr);
switch (mChooserState) {
case CHOOSE_TAP:
Apple2View.nativeOnTouch(MotionEvent.ACTION_DOWN, 1, 0, new float[]{400.f}, new float[]{400.f});
Apple2View.nativeOnTouch(MotionEvent.ACTION_UP, 1, 0, new float[]{400.f}, new float[]{400.f});
break;
case CHOOSE_SWIPEDOWN:
Apple2View.nativeOnTouch(MotionEvent.ACTION_DOWN, 1, 0, new float[]{400.f}, new float[]{400.f});
Apple2View.nativeOnTouch(MotionEvent.ACTION_MOVE, 1, 0, new float[]{400.f}, new float[]{600.f});
Apple2View.nativeOnTouch(MotionEvent.ACTION_UP, 1, 0, new float[]{400.f}, new float[]{600.f});
break;
case CHOOSE_SWIPEUP:
Apple2View.nativeOnTouch(MotionEvent.ACTION_DOWN, 1, 0, new float[]{400.f}, new float[]{400.f});
Apple2View.nativeOnTouch(MotionEvent.ACTION_MOVE, 1, 0, new float[]{400.f}, new float[]{200.f});
Apple2View.nativeOnTouch(MotionEvent.ACTION_UP, 1, 0, new float[]{400.f}, new float[]{200.f});
break;
default:
break;
}
calibrationContinue();
}
@ -184,31 +197,41 @@ public class Apple2KeypadChooser implements Apple2MenuView {
Apple2Preferences.sync(mActivity, Apple2Preferences.PREF_DOMAIN_TOUCHSCREEN);
}
private String asciiRepresentation(char ascii) {
public static String asciiRepresentation(Apple2Activity activity, char ascii) {
switch (ascii) {
case Apple2KeyboardSettingsMenu.MOUSETEXT_OPENAPPLE:
return mActivity.getResources().getString(R.string.key_open_apple);
return activity.getResources().getString(R.string.key_open_apple);
case Apple2KeyboardSettingsMenu.MOUSETEXT_CLOSEDAPPLE:
return mActivity.getResources().getString(R.string.key_closed_apple);
return activity.getResources().getString(R.string.key_closed_apple);
case Apple2KeyboardSettingsMenu.kUP:
case Apple2KeyboardSettingsMenu.MOUSETEXT_UP:
return mActivity.getResources().getString(R.string.key_up);
return activity.getResources().getString(R.string.key_up);
case Apple2KeyboardSettingsMenu.kLT:
case Apple2KeyboardSettingsMenu.MOUSETEXT_LEFT:
return mActivity.getResources().getString(R.string.key_left);
return activity.getResources().getString(R.string.key_left);
case Apple2KeyboardSettingsMenu.kRT:
case Apple2KeyboardSettingsMenu.MOUSETEXT_RIGHT:
return mActivity.getResources().getString(R.string.key_right);
return activity.getResources().getString(R.string.key_right);
case Apple2KeyboardSettingsMenu.kDN:
case Apple2KeyboardSettingsMenu.MOUSETEXT_DOWN:
return mActivity.getResources().getString(R.string.key_down);
return activity.getResources().getString(R.string.key_down);
case Apple2KeyboardSettingsMenu.ICONTEXT_CTRL:
return mActivity.getResources().getString(R.string.key_ctrl);
return activity.getResources().getString(R.string.key_ctrl);
case Apple2KeyboardSettingsMenu.kESC:
case Apple2KeyboardSettingsMenu.ICONTEXT_ESC:
return mActivity.getResources().getString(R.string.key_esc);
return activity.getResources().getString(R.string.key_esc);
case Apple2KeyboardSettingsMenu.kRET:
case Apple2KeyboardSettingsMenu.ICONTEXT_RETURN:
return mActivity.getResources().getString(R.string.key_ret);
return activity.getResources().getString(R.string.key_ret);
case Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION:
return mActivity.getResources().getString(R.string.key_none);
return activity.getResources().getString(R.string.key_none);
case ' ':
case Apple2KeyboardSettingsMenu.ICONTEXT_VISUAL_SPACE:
return mActivity.getResources().getString(R.string.key_space);
return activity.getResources().getString(R.string.key_space);
case Apple2KeyboardSettingsMenu.kDEL:
return activity.getResources().getString(R.string.key_del);
case Apple2KeyboardSettingsMenu.kTAB:
return activity.getResources().getString(R.string.key_tab);
default:
return "" + ascii;
}
@ -220,98 +243,130 @@ public class Apple2KeypadChooser implements Apple2MenuView {
}
private enum STATE_MACHINE {
CHOOSE_NORTHWEST {
CHOOSE_AXIS_NORTHWEST {
@Override
public String getKeyName(Apple2Activity activity) {
return activity.getResources().getString(R.string.keypad_key_axis_ul);
}
},
CHOOSE_NORTH {
CHOOSE_AXIS_NORTH {
@Override
public String getKeyName(Apple2Activity activity) {
return activity.getResources().getString(R.string.keypad_key_axis_up);
}
},
CHOOSE_NORTHEAST {
CHOOSE_AXIS_NORTHEAST {
@Override
public String getKeyName(Apple2Activity activity) {
return activity.getResources().getString(R.string.keypad_key_axis_ur);
}
},
CHOOSE_WEST {
CHOOSE_AXIS_WEST {
@Override
public String getKeyName(Apple2Activity activity) {
return activity.getResources().getString(R.string.keypad_key_axis_l);
}
},
CHOOSE_CENTER {
CHOOSE_AXIS_CENTER {
@Override
public String getKeyName(Apple2Activity activity) {
return activity.getResources().getString(R.string.keypad_key_axis_c);
}
},
CHOOSE_EAST {
CHOOSE_AXIS_EAST {
@Override
public String getKeyName(Apple2Activity activity) {
return activity.getResources().getString(R.string.keypad_key_axis_r);
}
},
CHOOSE_SOUTHWEST {
CHOOSE_AXIS_SOUTHWEST {
@Override
public String getKeyName(Apple2Activity activity) {
return activity.getResources().getString(R.string.keypad_key_axis_dl);
}
},
CHOOSE_SOUTH {
CHOOSE_AXIS_SOUTH {
@Override
public String getKeyName(Apple2Activity activity) {
return activity.getResources().getString(R.string.keypad_key_axis_dn);
}
},
CHOOSE_SOUTHEAST {
CHOOSE_AXIS_SOUTHEAST {
@Override
public String getKeyName(Apple2Activity activity) {
return activity.getResources().getString(R.string.keypad_key_axis_dr);
}
},
CHOOSE_TAP {
CHOOSE_BUTT_NORTHWEST {
@Override
public String getKeyName(Apple2Activity activity) {
return activity.getResources().getString(R.string.keypad_key_button_tap);
return activity.getResources().getString(R.string.keypad_key_axis_ul);
}
},
CHOOSE_SWIPEUP {
CHOOSE_BUTT_NORTH {
@Override
public String getKeyName(Apple2Activity activity) {
return activity.getResources().getString(R.string.keypad_key_button_swipeup);
return activity.getResources().getString(R.string.keypad_key_axis_up);
}
},
CHOOSE_SWIPEDOWN {
CHOOSE_BUTT_NORTHEAST {
@Override
public String getKeyName(Apple2Activity activity) {
return activity.getResources().getString(R.string.keypad_key_button_swipedown);
return activity.getResources().getString(R.string.keypad_key_axis_ur);
}
},
CHOOSE_BUTT_WEST {
@Override
public String getKeyName(Apple2Activity activity) {
return activity.getResources().getString(R.string.keypad_key_axis_l);
}
},
CHOOSE_BUTT_CENTER {
@Override
public String getKeyName(Apple2Activity activity) {
return activity.getResources().getString(R.string.keypad_key_axis_c);
}
},
CHOOSE_BUTT_EAST {
@Override
public String getKeyName(Apple2Activity activity) {
return activity.getResources().getString(R.string.keypad_key_axis_r);
}
},
CHOOSE_BUTT_SOUTHWEST {
@Override
public String getKeyName(Apple2Activity activity) {
return activity.getResources().getString(R.string.keypad_key_axis_dl);
}
},
CHOOSE_BUTT_SOUTH {
@Override
public String getKeyName(Apple2Activity activity) {
return activity.getResources().getString(R.string.keypad_key_axis_dn);
}
},
CHOOSE_BUTT_SOUTHEAST {
@Override
public String getKeyName(Apple2Activity activity) {
return activity.getResources().getString(R.string.keypad_key_axis_dr);
}
};
public static final int size = STATE_MACHINE.values().length;
private static ArrayList<String> chars = null;
private static ArrayList<String> scans = null;
private static ArrayList<Apple2KeypadSettingsMenu.KeyTuple> axisRosette = new ArrayList<Apple2KeypadSettingsMenu.KeyTuple>();
private static ArrayList<Apple2KeypadSettingsMenu.KeyTuple> buttRosette = new ArrayList<Apple2KeypadSettingsMenu.KeyTuple>();
public void setKey(Apple2Activity activity, int ascii, int scancode) {
public void setKey(Apple2Activity activity, Apple2KeypadSettingsMenu.KeyTuple tuple) {
int ord = ordinal();
if (ord < CHOOSE_TAP.ordinal()) {
chars.set(ord, "" + ascii);
scans.set(ord, "" + scancode);
Apple2KeypadSettingsMenu.KeypadPreset.saveRosettes(chars, scans);
} else if (ord == CHOOSE_TAP.ordinal()) {
Apple2KeypadSettingsMenu.KeypadPreset.saveTouchDownKey(ascii, scancode);
} else if (ord == CHOOSE_SWIPEUP.ordinal()) {
Apple2KeypadSettingsMenu.KeypadPreset.saveSwipeNorthKey(ascii, scancode);
} else if (ord == CHOOSE_SWIPEDOWN.ordinal()) {
Apple2KeypadSettingsMenu.KeypadPreset.saveSwipeSouthKey(ascii, scancode);
int buttbegin = CHOOSE_BUTT_NORTHWEST.ordinal();
if (ord < buttbegin) {
axisRosette.set(ord, tuple);
Apple2KeypadSettingsMenu.KeypadPreset.saveAxisRosette(axisRosette);
} else {
throw new RuntimeException();
ord -= buttbegin;
buttRosette.set(ord, tuple);
Apple2KeypadSettingsMenu.KeypadPreset.saveButtRosette(buttRosette);
}
Apple2Preferences.sync(activity, Apple2Preferences.PREF_DOMAIN_JOYSTICK);
}
@ -319,37 +374,46 @@ public class Apple2KeypadChooser implements Apple2MenuView {
public abstract String getKeyName(Apple2Activity activity);
public void start() {
setupCharsAndScans(axisRosette, Apple2KeypadSettingsMenu.PREF_KPAD_AXIS_ROSETTE);
JSONArray jsonChars = (JSONArray) Apple2Preferences.getJSONPref(Apple2Preferences.PREF_DOMAIN_JOYSTICK, Apple2KeypadSettingsMenu.PREF_KPAD_ROSETTE_CHAR_ARRAY, null);
JSONArray jsonScans = (JSONArray) Apple2Preferences.getJSONPref(Apple2Preferences.PREF_DOMAIN_JOYSTICK, Apple2KeypadSettingsMenu.PREF_KPAD_ROSETTE_SCAN_ARRAY, null);
setupCharsAndScans(buttRosette, Apple2KeypadSettingsMenu.PREF_KPAD_BUTT_ROSETTE);
}
if (jsonChars == null || jsonScans == null) {
Log.v(TAG, "Creating new keypad joystick JSON...");
jsonChars = new JSONArray();
jsonScans = new JSONArray();
for (int i = 0; i < Apple2KeypadSettingsMenu.ROSETTE_SIZE; i++) {
jsonChars.put(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION);
jsonScans.put(-1);
}
}
private void setupCharsAndScans(final ArrayList<Apple2KeypadSettingsMenu.KeyTuple> rosette, final String pref) {
rosette.clear();
int len = jsonChars.length();
if (len != Apple2KeypadSettingsMenu.ROSETTE_SIZE) {
throw new RuntimeException("jsonChars not expected length");
}
if (len != jsonScans.length()) {
throw new RuntimeException("jsonScans not expected length");
}
chars = new ArrayList<String>();
scans = new ArrayList<String>();
try {
JSONArray jsonArray = (JSONArray) Apple2Preferences.getJSONPref(Apple2Preferences.PREF_DOMAIN_JOYSTICK, pref, null);
if (jsonArray == null) {
jsonArray = new JSONArray();
for (int i = 0; i < Apple2KeypadSettingsMenu.ROSETTE_SIZE; i++) {
JSONObject map = new JSONObject();
map.put("ch", (long) Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION);
map.put("scan", -1L);
map.put("isShifted", false);
}
}
int len = jsonArray.length();
if (len != Apple2KeypadSettingsMenu.ROSETTE_SIZE) {
throw new RuntimeException("rosette not expected length");
}
for (int i = 0; i < len; i++) {
Apple2KeypadSettingsMenu.KeypadPreset.addRosetteKey(chars, scans, jsonChars.getInt(i), jsonScans.getInt(i));
JSONObject obj = jsonArray.getJSONObject(i);
long ch = obj.getLong("ch");
long scan = obj.getLong("scan");
boolean isShifted = obj.getBoolean("isShifted");
rosette.add(new Apple2KeypadSettingsMenu.KeyTuple(ch, scan, isShifted));
}
} catch (JSONException e) {
e.printStackTrace();
}
if (rosette.size() != Apple2KeypadSettingsMenu.ROSETTE_SIZE) {
throw new RuntimeException("rosette chars is not correct size");
}
}
public STATE_MACHINE next() {

View File

@ -12,32 +12,35 @@
package org.deadc0de.apple2ix;
import android.view.View;
import android.widget.TextView;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.Toast;
import java.util.ArrayList;
import org.deadc0de.apple2ix.basic.R;
import org.json.JSONArray;
import org.json.JSONObject;
public class Apple2KeypadSettingsMenu extends Apple2AbstractMenu {
private final static String TAG = "Apple2KeypadSettingsMenu";
public final static int KEYREPEAT_NUM_CHOICES = Apple2Preferences.DECENT_AMOUNT_OF_CHOICES;
public final static String PREF_KPAD_ROSETTE_CHAR_ARRAY = "kpAxisRosetteChars";
public final static String PREF_KPAD_ROSETTE_SCAN_ARRAY = "kpAxisRosetteScancodes";
public final static String PREF_KPAD_SWIPE_NORTH_CHAR = "kpSwipeNorthChar";
public final static String PREF_KPAD_SWIPE_NORTH_SCAN = "kpSwipeNorthScancode";
public final static String PREF_KPAD_SWIPE_SOUTH_CHAR = "kpSwipeSouthChar";
public final static String PREF_KPAD_SWIPE_SOUTH_SCAN = "kpSwipeSouthScancode";
public final static String PREF_KPAD_TOUCHDOWN_CHAR = "kpTouchDownChar";
public final static String PREF_KPAD_TOUCHDOWN_SCAN = "kpTouchDownScancode";
public final static String PREF_KPAD_AXIS_ROSETTE = "kpAxisRosette";
public final static String PREF_KPAD_BUTT_ROSETTE = "kpButtRosette";
public final static int ROSETTE_SIZE = 9;
public Apple2KeypadSettingsMenu(Apple2Activity activity) {
private Apple2SettingsMenu.TouchDeviceVariant mVariant;
public Apple2KeypadSettingsMenu(Apple2Activity activity, Apple2SettingsMenu.TouchDeviceVariant variant) {
super(activity);
if (!(variant == Apple2SettingsMenu.TouchDeviceVariant.JOYSTICK || variant == Apple2SettingsMenu.TouchDeviceVariant.JOYSTICK_KEYPAD)) {
throw new RuntimeException("You're doing it wrong");
}
mVariant = variant;
}
@Override
@ -63,6 +66,22 @@ public class Apple2KeypadSettingsMenu extends Apple2AbstractMenu {
return true;
}
public static class KeyTuple {
public long ch;
public long scan;
public boolean isShifted;
public KeyTuple(long ch, long scan) {
this(ch, scan, false);
}
public KeyTuple(long ch, long scan, boolean isShifted) {
this.ch = ch;
this.scan = scan;
this.isShifted = isShifted;
}
}
public enum KeypadPreset {
ARROWS_SPACE {
@Override
@ -72,22 +91,37 @@ public class Apple2KeypadSettingsMenu extends Apple2AbstractMenu {
@Override
public void apply(Apple2Activity activity) {
ArrayList<String> chars = new ArrayList<String>();
ArrayList<String> scans = new ArrayList<String>();
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1);
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.MOUSETEXT_UP, Apple2KeyboardSettingsMenu.SCANCODE_UP);
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1);
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.MOUSETEXT_LEFT, Apple2KeyboardSettingsMenu.SCANCODE_LEFT);
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1);
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.MOUSETEXT_RIGHT, Apple2KeyboardSettingsMenu.SCANCODE_RIGHT);
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1);
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.MOUSETEXT_DOWN, Apple2KeyboardSettingsMenu.SCANCODE_DOWN);
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1);
saveRosettes(chars, scans);
{
ArrayList<KeyTuple> axisRosette = new ArrayList<KeyTuple>();
axisRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1));
axisRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.MOUSETEXT_UP, Apple2KeyboardSettingsMenu.SCANCODE_UP));
axisRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1));
saveTouchDownKey(Apple2KeyboardSettingsMenu.ICONTEXT_VISUAL_SPACE, Apple2KeyboardSettingsMenu.SCANCODE_SPACE);
saveSwipeSouthKey(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1);
saveSwipeNorthKey(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1);
axisRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.MOUSETEXT_LEFT, Apple2KeyboardSettingsMenu.SCANCODE_LEFT));
axisRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1));
axisRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.MOUSETEXT_RIGHT, Apple2KeyboardSettingsMenu.SCANCODE_RIGHT));
axisRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1));
axisRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.MOUSETEXT_DOWN, Apple2KeyboardSettingsMenu.SCANCODE_DOWN));
axisRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1));
saveAxisRosette(axisRosette);
}
{
ArrayList<KeyTuple> buttRosette = new ArrayList<KeyTuple>();
buttRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_SPACE));
buttRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_SPACE));
buttRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_SPACE));
buttRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_SPACE));
buttRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_VISUAL_SPACE, Apple2KeyboardSettingsMenu.SCANCODE_SPACE));
buttRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_SPACE));
buttRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_SPACE));
buttRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_SPACE));
buttRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_SPACE));
saveButtRosette(buttRosette);
}
}
},
AZ_LEFT_RIGHT_SPACE {
@ -98,22 +132,38 @@ public class Apple2KeypadSettingsMenu extends Apple2AbstractMenu {
@Override
public void apply(Apple2Activity activity) {
ArrayList<String> chars = new ArrayList<String>();
ArrayList<String> scans = new ArrayList<String>();
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1);
addRosetteKey(chars, scans, 'A', Apple2KeyboardSettingsMenu.SCANCODE_A);
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1);
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.MOUSETEXT_LEFT, Apple2KeyboardSettingsMenu.SCANCODE_LEFT);
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1);
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.MOUSETEXT_RIGHT, Apple2KeyboardSettingsMenu.SCANCODE_RIGHT);
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1);
addRosetteKey(chars, scans, 'Z', Apple2KeyboardSettingsMenu.SCANCODE_Z);
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1);
saveRosettes(chars, scans);
{
ArrayList<KeyTuple> axisRosette = new ArrayList<KeyTuple>();
saveTouchDownKey(Apple2KeyboardSettingsMenu.ICONTEXT_VISUAL_SPACE, Apple2KeyboardSettingsMenu.SCANCODE_SPACE);
saveSwipeSouthKey(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1);
saveSwipeNorthKey(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1);
axisRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1));
axisRosette.add(new KeyTuple('A', Apple2KeyboardSettingsMenu.SCANCODE_A));
axisRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1));
axisRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.MOUSETEXT_LEFT, Apple2KeyboardSettingsMenu.SCANCODE_LEFT));
axisRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1));
axisRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.MOUSETEXT_RIGHT, Apple2KeyboardSettingsMenu.SCANCODE_RIGHT));
axisRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1));
axisRosette.add(new KeyTuple('Z', Apple2KeyboardSettingsMenu.SCANCODE_Z));
axisRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1));
saveAxisRosette(axisRosette);
}
{
ArrayList<KeyTuple> buttRosette = new ArrayList<KeyTuple>();
buttRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_SPACE));
buttRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_SPACE));
buttRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_SPACE));
buttRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_SPACE));
buttRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_VISUAL_SPACE, Apple2KeyboardSettingsMenu.SCANCODE_SPACE));
buttRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_SPACE));
buttRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_SPACE));
buttRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_SPACE));
buttRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_SPACE));
saveButtRosette(buttRosette);
}
}
},
LEFT_RIGHT_SPACE {
@ -124,22 +174,78 @@ public class Apple2KeypadSettingsMenu extends Apple2AbstractMenu {
@Override
public void apply(Apple2Activity activity) {
ArrayList<String> chars = new ArrayList<String>();
ArrayList<String> scans = new ArrayList<String>();
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1);
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1);
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1);
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.MOUSETEXT_LEFT, Apple2KeyboardSettingsMenu.SCANCODE_LEFT);
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1);
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.MOUSETEXT_RIGHT, Apple2KeyboardSettingsMenu.SCANCODE_RIGHT);
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1);
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1);
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1);
saveRosettes(chars, scans);
{
ArrayList<KeyTuple> axisRosette = new ArrayList<KeyTuple>();
axisRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_LEFT));
axisRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1));
axisRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_RIGHT));
saveTouchDownKey(Apple2KeyboardSettingsMenu.ICONTEXT_VISUAL_SPACE, Apple2KeyboardSettingsMenu.SCANCODE_SPACE);
saveSwipeSouthKey(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1);
saveSwipeNorthKey(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1);
axisRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.MOUSETEXT_LEFT, Apple2KeyboardSettingsMenu.SCANCODE_LEFT));
axisRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1));
axisRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.MOUSETEXT_RIGHT, Apple2KeyboardSettingsMenu.SCANCODE_RIGHT));
axisRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_LEFT));
axisRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1));
axisRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_RIGHT));
saveAxisRosette(axisRosette);
}
{
ArrayList<KeyTuple> buttRosette = new ArrayList<KeyTuple>();
buttRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_SPACE));
buttRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_SPACE));
buttRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_SPACE));
buttRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_SPACE));
buttRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_VISUAL_SPACE, Apple2KeyboardSettingsMenu.SCANCODE_SPACE));
buttRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_SPACE));
buttRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_SPACE));
buttRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_SPACE));
buttRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_SPACE));
saveButtRosette(buttRosette);
}
}
},
QAZ_LEFT_RIGHT_SPACE {
@Override
public String getTitle(Apple2Activity activity) {
return activity.getResources().getString(R.string.keypad_preset_qaz_left_right_space);
}
@Override
public void apply(Apple2Activity activity) {
{
ArrayList<KeyTuple> axisRosette = new ArrayList<KeyTuple>();
axisRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1));
axisRosette.add(new KeyTuple('A', Apple2KeyboardSettingsMenu.SCANCODE_A));
axisRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1));
axisRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.MOUSETEXT_LEFT, Apple2KeyboardSettingsMenu.SCANCODE_LEFT));
axisRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1));
axisRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.MOUSETEXT_RIGHT, Apple2KeyboardSettingsMenu.SCANCODE_RIGHT));
axisRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1));
axisRosette.add(new KeyTuple('Z', Apple2KeyboardSettingsMenu.SCANCODE_Z));
axisRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1));
saveAxisRosette(axisRosette);
}
{
ArrayList<KeyTuple> buttRosette = new ArrayList<KeyTuple>();
buttRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_Q));
buttRosette.add(new KeyTuple('Q', Apple2KeyboardSettingsMenu.SCANCODE_Q));
buttRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_Q));
buttRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_SPACE));
buttRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_VISUAL_SPACE, Apple2KeyboardSettingsMenu.SCANCODE_SPACE));
buttRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_SPACE));
buttRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_SPACE));
buttRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_SPACE));
buttRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_SPACE));
saveButtRosette(buttRosette);
}
}
},
IJKM_SPACE {
@ -150,22 +256,37 @@ public class Apple2KeypadSettingsMenu extends Apple2AbstractMenu {
@Override
public void apply(Apple2Activity activity) {
ArrayList<String> chars = new ArrayList<String>();
ArrayList<String> scans = new ArrayList<String>();
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1);
addRosetteKey(chars, scans, 'I', Apple2KeyboardSettingsMenu.SCANCODE_I);
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1);
addRosetteKey(chars, scans, 'J', Apple2KeyboardSettingsMenu.SCANCODE_J);
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1);
addRosetteKey(chars, scans, 'K', Apple2KeyboardSettingsMenu.SCANCODE_K);
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1);
addRosetteKey(chars, scans, 'M', Apple2KeyboardSettingsMenu.SCANCODE_M);
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1);
saveRosettes(chars, scans);
{
ArrayList<KeyTuple> axisRosette = new ArrayList<KeyTuple>();
axisRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1));
axisRosette.add(new KeyTuple('I', Apple2KeyboardSettingsMenu.SCANCODE_I));
axisRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1));
saveTouchDownKey(Apple2KeyboardSettingsMenu.ICONTEXT_VISUAL_SPACE, Apple2KeyboardSettingsMenu.SCANCODE_SPACE);
saveSwipeSouthKey(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1);
saveSwipeNorthKey(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1);
axisRosette.add(new KeyTuple('J', Apple2KeyboardSettingsMenu.SCANCODE_J));
axisRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1));
axisRosette.add(new KeyTuple('K', Apple2KeyboardSettingsMenu.SCANCODE_K));
axisRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1));
axisRosette.add(new KeyTuple('M', Apple2KeyboardSettingsMenu.SCANCODE_M));
axisRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1));
saveAxisRosette(axisRosette);
}
{
ArrayList<KeyTuple> buttRosette = new ArrayList<KeyTuple>();
buttRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_SPACE));
buttRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_SPACE));
buttRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_SPACE));
buttRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_SPACE));
buttRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_VISUAL_SPACE, Apple2KeyboardSettingsMenu.SCANCODE_SPACE));
buttRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_SPACE));
buttRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_SPACE));
buttRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_SPACE));
buttRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_SPACE));
saveButtRosette(buttRosette);
}
}
},
WADX_SPACE {
@ -176,87 +297,206 @@ public class Apple2KeypadSettingsMenu extends Apple2AbstractMenu {
@Override
public void apply(Apple2Activity activity) {
ArrayList<String> chars = new ArrayList<String>();
ArrayList<String> scans = new ArrayList<String>();
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1);
addRosetteKey(chars, scans, 'W', Apple2KeyboardSettingsMenu.SCANCODE_W);
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1);
addRosetteKey(chars, scans, 'A', Apple2KeyboardSettingsMenu.SCANCODE_A);
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1);
addRosetteKey(chars, scans, 'D', Apple2KeyboardSettingsMenu.SCANCODE_D);
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1);
addRosetteKey(chars, scans, 'X', Apple2KeyboardSettingsMenu.SCANCODE_X);
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1);
saveRosettes(chars, scans);
{
ArrayList<KeyTuple> axisRosette = new ArrayList<KeyTuple>();
axisRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1));
axisRosette.add(new KeyTuple('W', Apple2KeyboardSettingsMenu.SCANCODE_W));
axisRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1));
saveTouchDownKey(Apple2KeyboardSettingsMenu.ICONTEXT_VISUAL_SPACE, Apple2KeyboardSettingsMenu.SCANCODE_SPACE);
saveSwipeSouthKey(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1);
saveSwipeNorthKey(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1);
axisRosette.add(new KeyTuple('A', Apple2KeyboardSettingsMenu.SCANCODE_A));
axisRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1));
axisRosette.add(new KeyTuple('D', Apple2KeyboardSettingsMenu.SCANCODE_D));
axisRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1));
axisRosette.add(new KeyTuple('X', Apple2KeyboardSettingsMenu.SCANCODE_X));
axisRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1));
saveAxisRosette(axisRosette);
}
{
ArrayList<KeyTuple> buttRosette = new ArrayList<KeyTuple>();
buttRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_SPACE));
buttRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_SPACE));
buttRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_SPACE));
buttRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_SPACE));
buttRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_VISUAL_SPACE, Apple2KeyboardSettingsMenu.SCANCODE_SPACE));
buttRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_SPACE));
buttRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_SPACE));
buttRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_SPACE));
buttRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_SPACE));
saveButtRosette(buttRosette);
}
}
},
CRAZY_SEAFOX_KEYS {
LODERUNNER_KEYS {
@Override
public String getTitle(Apple2Activity activity) {
return activity.getResources().getString(R.string.keypad_preset_crazy_seafox);
return activity.getResources().getString(R.string.keypad_preset_loderunner);
}
@Override
public String getToast(Apple2Activity activity) {
return activity.getResources().getString(R.string.keypad_preset_loderunner_toast);
}
@Override
public void apply(Apple2Activity activity) {
// Heh, the entire purpose of the keypad-variant touch joystick is to make this possible ;-)
ArrayList<String> chars = new ArrayList<String>();
ArrayList<String> scans = new ArrayList<String>();
addRosetteKey(chars, scans, 'Y', Apple2KeyboardSettingsMenu.SCANCODE_Y);
addRosetteKey(chars, scans, 'U', Apple2KeyboardSettingsMenu.SCANCODE_U);
addRosetteKey(chars, scans, 'I', Apple2KeyboardSettingsMenu.SCANCODE_I);
addRosetteKey(chars, scans, 'H', Apple2KeyboardSettingsMenu.SCANCODE_H);
addRosetteKey(chars, scans, 'J', Apple2KeyboardSettingsMenu.SCANCODE_J);
addRosetteKey(chars, scans, 'K', Apple2KeyboardSettingsMenu.SCANCODE_K);
addRosetteKey(chars, scans, 'N', Apple2KeyboardSettingsMenu.SCANCODE_N);
addRosetteKey(chars, scans, 'M', Apple2KeyboardSettingsMenu.SCANCODE_M);
addRosetteKey(chars, scans, ',', Apple2KeyboardSettingsMenu.SCANCODE_COMMA);
saveRosettes(chars, scans);
{
ArrayList<KeyTuple> axisRosette = new ArrayList<KeyTuple>();
axisRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1));
axisRosette.add(new KeyTuple('I', Apple2KeyboardSettingsMenu.SCANCODE_I));
axisRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1));
saveTouchDownKey('D', Apple2KeyboardSettingsMenu.SCANCODE_D);
saveSwipeSouthKey('F', Apple2KeyboardSettingsMenu.SCANCODE_F);
saveSwipeNorthKey(Apple2KeyboardSettingsMenu.ICONTEXT_VISUAL_SPACE, Apple2KeyboardSettingsMenu.SCANCODE_SPACE);
axisRosette.add(new KeyTuple('J', Apple2KeyboardSettingsMenu.SCANCODE_J));
axisRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_VISUAL_SPACE, Apple2KeyboardSettingsMenu.SCANCODE_SPACE));
axisRosette.add(new KeyTuple('L', Apple2KeyboardSettingsMenu.SCANCODE_L));
axisRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1));
axisRosette.add(new KeyTuple('K', Apple2KeyboardSettingsMenu.SCANCODE_K));
axisRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1));
saveAxisRosette(axisRosette);
}
{
ArrayList<KeyTuple> buttRosette = new ArrayList<KeyTuple>();
buttRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_U));
buttRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1));
buttRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_O));
buttRosette.add(new KeyTuple('U', Apple2KeyboardSettingsMenu.SCANCODE_U));
buttRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1));
buttRosette.add(new KeyTuple('O', Apple2KeyboardSettingsMenu.SCANCODE_O));
buttRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_U));
buttRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1));
buttRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_O));
saveButtRosette(buttRosette);
}
}
},
ROBOTRON_KEYS {
@Override
public String getTitle(Apple2Activity activity) {
return activity.getResources().getString(R.string.keypad_preset_robotron);
}
@Override
public void apply(Apple2Activity activity) {
{
ArrayList<KeyTuple> axisRosette = new ArrayList<KeyTuple>();
axisRosette.add(new KeyTuple('Q', Apple2KeyboardSettingsMenu.SCANCODE_Q));
axisRosette.add(new KeyTuple('W', Apple2KeyboardSettingsMenu.SCANCODE_W));
axisRosette.add(new KeyTuple('E', Apple2KeyboardSettingsMenu.SCANCODE_E));
axisRosette.add(new KeyTuple('A', Apple2KeyboardSettingsMenu.SCANCODE_A));
axisRosette.add(new KeyTuple('S', Apple2KeyboardSettingsMenu.SCANCODE_S));
axisRosette.add(new KeyTuple('D', Apple2KeyboardSettingsMenu.SCANCODE_D));
axisRosette.add(new KeyTuple('Z', Apple2KeyboardSettingsMenu.SCANCODE_Z));
axisRosette.add(new KeyTuple('X', Apple2KeyboardSettingsMenu.SCANCODE_X));
axisRosette.add(new KeyTuple('C', Apple2KeyboardSettingsMenu.SCANCODE_C));
saveAxisRosette(axisRosette);
}
{
ArrayList<KeyTuple> buttRosette = new ArrayList<KeyTuple>();
buttRosette.add(new KeyTuple('I', Apple2KeyboardSettingsMenu.SCANCODE_I));
buttRosette.add(new KeyTuple('O', Apple2KeyboardSettingsMenu.SCANCODE_O));
buttRosette.add(new KeyTuple('P', Apple2KeyboardSettingsMenu.SCANCODE_P));
buttRosette.add(new KeyTuple('K', Apple2KeyboardSettingsMenu.SCANCODE_K));
buttRosette.add(new KeyTuple('L', Apple2KeyboardSettingsMenu.SCANCODE_L));
buttRosette.add(new KeyTuple(';', Apple2KeyboardSettingsMenu.SCANCODE_SEMICOLON));
buttRosette.add(new KeyTuple(',', Apple2KeyboardSettingsMenu.SCANCODE_COMMA));
buttRosette.add(new KeyTuple('.', Apple2KeyboardSettingsMenu.SCANCODE_PERIOD));
buttRosette.add(new KeyTuple('/', Apple2KeyboardSettingsMenu.SCANCODE_SLASH));
saveButtRosette(buttRosette);
}
}
},
SEAFOX_KEYS {
@Override
public String getTitle(Apple2Activity activity) {
return activity.getResources().getString(R.string.keypad_preset_seafox);
}
@Override
public void apply(Apple2Activity activity) {
{
ArrayList<KeyTuple> axisRosette = new ArrayList<KeyTuple>();
axisRosette.add(new KeyTuple('Y', Apple2KeyboardSettingsMenu.SCANCODE_Y));
axisRosette.add(new KeyTuple('U', Apple2KeyboardSettingsMenu.SCANCODE_U));
axisRosette.add(new KeyTuple('I', Apple2KeyboardSettingsMenu.SCANCODE_I));
axisRosette.add(new KeyTuple('H', Apple2KeyboardSettingsMenu.SCANCODE_H));
axisRosette.add(new KeyTuple('J', Apple2KeyboardSettingsMenu.SCANCODE_J));
axisRosette.add(new KeyTuple('K', Apple2KeyboardSettingsMenu.SCANCODE_K));
axisRosette.add(new KeyTuple('N', Apple2KeyboardSettingsMenu.SCANCODE_N));
axisRosette.add(new KeyTuple('M', Apple2KeyboardSettingsMenu.SCANCODE_M));
axisRosette.add(new KeyTuple(',', Apple2KeyboardSettingsMenu.SCANCODE_COMMA));
saveAxisRosette(axisRosette);
}
{
ArrayList<KeyTuple> buttRosette = new ArrayList<KeyTuple>();
buttRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_D));
buttRosette.add(new KeyTuple('D', Apple2KeyboardSettingsMenu.SCANCODE_D));
buttRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_F));
buttRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1));
buttRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_VISUAL_SPACE, Apple2KeyboardSettingsMenu.SCANCODE_SPACE));
buttRosette.add(new KeyTuple('F', Apple2KeyboardSettingsMenu.SCANCODE_F));
buttRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1));
buttRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1));
buttRosette.add(new KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1));
saveButtRosette(buttRosette);
}
}
};
public static void addRosetteKey(ArrayList<String> chars, ArrayList<String> scans, int aChar, int aScan) {
chars.add("" + aChar);
scans.add("" + aScan);
}
public static void saveRosettes(ArrayList<String> chars, ArrayList<String> scans) {
if (chars.size() != 9) {
throw new RuntimeException("rosette chars is not correct size");
public static void saveAxisRosette(ArrayList<KeyTuple> axisRosette) {
if (axisRosette.size() != 9) {
throw new RuntimeException("axis rosette is not correct size");
}
if (scans.size() != 9) {
throw new RuntimeException("rosette scans is not correct size");
Apple2Preferences.setJSONPref(Apple2Preferences.PREF_DOMAIN_JOYSTICK, PREF_KPAD_AXIS_ROSETTE, toJSONArray(axisRosette));
}
public static void saveButtRosette(ArrayList<KeyTuple> buttRosette) {
if (buttRosette.size() != 9) {
throw new RuntimeException("butt rosette is not correct size");
}
Apple2Preferences.setJSONPref(Apple2Preferences.PREF_DOMAIN_JOYSTICK, PREF_KPAD_ROSETTE_CHAR_ARRAY, new JSONArray(chars));
Apple2Preferences.setJSONPref(Apple2Preferences.PREF_DOMAIN_JOYSTICK, PREF_KPAD_ROSETTE_SCAN_ARRAY, new JSONArray(scans));
Apple2Preferences.setJSONPref(Apple2Preferences.PREF_DOMAIN_JOYSTICK, PREF_KPAD_BUTT_ROSETTE, toJSONArray(buttRosette));
}
public static void saveTouchDownKey(int aChar, int aScan) {
Apple2Preferences.setJSONPref(Apple2Preferences.PREF_DOMAIN_JOYSTICK, PREF_KPAD_TOUCHDOWN_CHAR, aChar);
Apple2Preferences.setJSONPref(Apple2Preferences.PREF_DOMAIN_JOYSTICK, PREF_KPAD_TOUCHDOWN_SCAN, aScan);
}
public static void saveSwipeSouthKey(int aChar, int aScan) {
Apple2Preferences.setJSONPref(Apple2Preferences.PREF_DOMAIN_JOYSTICK, PREF_KPAD_SWIPE_SOUTH_CHAR, aChar);
Apple2Preferences.setJSONPref(Apple2Preferences.PREF_DOMAIN_JOYSTICK, PREF_KPAD_SWIPE_SOUTH_SCAN, aScan);
}
public static void saveSwipeNorthKey(int aChar, int aScan) {
Apple2Preferences.setJSONPref(Apple2Preferences.PREF_DOMAIN_JOYSTICK, PREF_KPAD_SWIPE_NORTH_CHAR, aChar);
Apple2Preferences.setJSONPref(Apple2Preferences.PREF_DOMAIN_JOYSTICK, PREF_KPAD_SWIPE_NORTH_SCAN, aScan);
private static JSONArray toJSONArray(ArrayList<KeyTuple> rosette) {
JSONArray jsonArray = new JSONArray();
try {
for (KeyTuple tuple : rosette) {
JSONObject obj = new JSONObject();
obj.put("ch", tuple.ch);
obj.put("scan", tuple.scan);
obj.put("isShifted", tuple.isShifted);
jsonArray.put(obj);
}
} catch (Exception e) {
throw new RuntimeException(e);
}
return jsonArray;
}
public abstract String getTitle(Apple2Activity activity);
public abstract void apply(Apple2Activity activity);
public String getToast(Apple2Activity activity) {
return null;
}
public static final int size = KeypadPreset.values().length;
public static String[] titles(Apple2Activity activity) {
@ -270,6 +510,33 @@ public class Apple2KeypadSettingsMenu extends Apple2AbstractMenu {
}
enum SETTINGS implements Apple2AbstractMenu.IMenuEnum {
JOYSTICK_CALIBRATE {
@Override
public final String getTitle(Apple2Activity activity) {
return activity.getResources().getString(R.string.joystick_calibrate);
}
@Override
public final String getSummary(Apple2Activity activity) {
return activity.getResources().getString(R.string.joystick_calibrate_summary);
}
@Override
public String getPrefKey() {
return null;
}
@Override
public Object getPrefDefault() {
return null;
}
@Override
public void handleSelection(Apple2Activity activity, Apple2AbstractMenu settingsMenu, boolean isChecked) {
Apple2KeypadSettingsMenu thisMenu = (Apple2KeypadSettingsMenu)settingsMenu;
Apple2JoystickCalibration.startCalibration(activity, thisMenu.mVariant);
}
},
KEYPAD_CHOOSE_KEYS {
@Override
public final String getTitle(Apple2Activity activity) {
@ -319,52 +586,55 @@ public class Apple2KeypadSettingsMenu extends Apple2AbstractMenu {
keypadSettingsMenu.chooseKeys(activity);
} else {
KeypadPreset.values()[value - 1].apply(activity);
String toast = KeypadPreset.values()[value - 1].getToast(activity);
if (toast != null) {
Toast.makeText(activity, toast, Toast.LENGTH_SHORT).show();
}
}
}
});
}
},
KEYPAD_CALIBRATE {
FAST_AUTOREPEAT {
@Override
public final String getTitle(Apple2Activity activity) {
return activity.getResources().getString(R.string.keypad_calibrate);
return activity.getResources().getString(R.string.keypad_autorepeat_fast);
}
@Override
public final String getSummary(Apple2Activity activity) {
return activity.getResources().getString(R.string.keypad_calibrate_summary);
return activity.getResources().getString(R.string.keypad_autorepeat_fast_summary);
}
@Override
public void handleSelection(Apple2Activity activity, Apple2AbstractMenu settingsMenu, boolean isChecked) {
ArrayList<Apple2MenuView> viewStack = new ArrayList<Apple2MenuView>();
{
int idx = 0;
while (true) {
Apple2MenuView apple2MenuView = activity.peekApple2View(idx);
if (apple2MenuView == null) {
break;
}
viewStack.add(apple2MenuView);
++idx;
public String getPrefKey() {
return "kpFastAutoRepeat";
}
@Override
public Object getPrefDefault() {
return true;
}
@Override
public View getView(final Apple2Activity activity, View convertView) {
final IMenuEnum self = this;
convertView = _basicView(activity, this, convertView);
CheckBox cb = _addCheckbox(activity, this, convertView, (boolean) Apple2Preferences.getJSONPref(this));
cb.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
Apple2Preferences.setJSONPref(self, isChecked);
}
}
Apple2JoystickCalibration calibration = new Apple2JoystickCalibration(activity, viewStack, Apple2SettingsMenu.TouchDeviceVariant.JOYSTICK_KEYPAD);
// show this new view...
calibration.show();
// ...with nothing else underneath 'cept the emulator OpenGL layer
for (Apple2MenuView apple2MenuView : viewStack) {
activity.popApple2View(apple2MenuView);
}
});
return convertView;
}
},
KEYPAD_ADVANCED {
JOYSTICK_ADVANCED {
@Override
public final String getTitle(Apple2Activity activity) {
return activity.getResources().getString(R.string.settings_advanced);
return activity.getResources().getString(R.string.settings_advanced_joystick);
}
@Override
@ -374,7 +644,7 @@ public class Apple2KeypadSettingsMenu extends Apple2AbstractMenu {
@Override
public void handleSelection(Apple2Activity activity, Apple2AbstractMenu settingsMenu, boolean isChecked) {
new Apple2KeypadSettingsMenu.KeypadAdvanced(activity).show();
new Apple2JoystickSettingsMenu.JoystickAdvanced(activity).show();
}
};
@ -441,133 +711,4 @@ public class Apple2KeypadSettingsMenu extends Apple2AbstractMenu {
activity.popApple2View(apple2MenuView);
}
}
public static class KeypadAdvanced extends Apple2AbstractMenu {
private final static String TAG = "KeypadAdvanced";
public KeypadAdvanced(Apple2Activity activity) {
super(activity);
}
@Override
public final String[] allTitles() {
return SETTINGS.titles(mActivity);
}
@Override
public final IMenuEnum[] allValues() {
return SETTINGS.values();
}
@Override
public final boolean areAllItemsEnabled() {
return false;
}
@Override
public final boolean isEnabled(int position) {
if (position < 0 || position >= SETTINGS.size) {
throw new ArrayIndexOutOfBoundsException();
}
return position == SETTINGS.JOYSTICK_ADVANCED.ordinal();
}
protected enum SETTINGS implements Apple2AbstractMenu.IMenuEnum {
KEYREPEAT_THRESHOLD {
@Override
public final String getTitle(Apple2Activity activity) {
return "";
}
@Override
public final String getSummary(Apple2Activity activity) {
return activity.getResources().getString(R.string.keypad_repeat_summary);
}
@Override
public String getPrefKey() {
return "keyRepeatThresholdSecs";
}
@Override
public Object getPrefDefault() {
return (float) 4 / KEYREPEAT_NUM_CHOICES;
}
@Override
public View getView(final Apple2Activity activity, View convertView) {
final IMenuEnum self = this;
return _sliderView(activity, this, KEYREPEAT_NUM_CHOICES, new IPreferenceSlider() {
@Override
public void saveInt(int progress) {
Apple2Preferences.setJSONPref(self, (float) progress / KEYREPEAT_NUM_CHOICES);
}
@Override
public int intValue() {
return (int) (Apple2Preferences.getFloatJSONPref(self) * KEYREPEAT_NUM_CHOICES);
}
@Override
public void showValue(int progress, final TextView seekBarValue) {
seekBarValue.setText("" + ((float) progress / KEYREPEAT_NUM_CHOICES));
}
});
}
},
JOYSTICK_ADVANCED {
@Override
public final String getTitle(Apple2Activity activity) {
return activity.getResources().getString(R.string.settings_advanced_joystick);
}
@Override
public final String getSummary(Apple2Activity activity) {
return activity.getResources().getString(R.string.settings_advanced_joystick_summary);
}
@Override
public void handleSelection(Apple2Activity activity, Apple2AbstractMenu settingsMenu, boolean isChecked) {
new Apple2JoystickSettingsMenu.JoystickAdvanced(activity).show();
}
};
public static final int size = SETTINGS.values().length;
@Override
public String getPrefDomain() {
return Apple2Preferences.PREF_DOMAIN_JOYSTICK;
}
@Override
public String getPrefKey() {
return null;
}
@Override
public Object getPrefDefault() {
return null;
}
@Override
public void handleSelection(Apple2Activity activity, Apple2AbstractMenu settingsMenu, boolean isChecked) {
}
@Override
public View getView(Apple2Activity activity, View convertView) {
return _basicView(activity, this, convertView);
}
public static String[] titles(Apple2Activity activity) {
String[] titles = new String[size];
int i = 0;
for (SETTINGS setting : values()) {
titles[i++] = setting.getTitle(activity);
}
return titles;
}
}
}
}

View File

@ -11,13 +11,14 @@
package org.deadc0de.apple2ix;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.graphics.drawable.BitmapDrawable;
import android.net.Uri;
import android.os.Build;
import android.os.ParcelFileDescriptor;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AlertDialog;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
@ -33,7 +34,6 @@ import android.widget.TextView;
import android.widget.Toast;
import org.deadc0de.apple2ix.basic.R;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.File;
@ -179,14 +179,14 @@ public class Apple2MainMenu {
mainMenuView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Log.d(TAG, "position:" + position + " tapped...");
Apple2Activity.logMessage(Apple2Activity.LogType.DEBUG, TAG, "position:" + position + " tapped...");
SETTINGS setting = SETTINGS.values()[position];
setting.handleSelection(Apple2MainMenu.this);
}
});
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.GINGERBREAD_MR1) {
mMainMenuPopup = new PopupWindow(mainPopupContainer, android.app.ActionBar.LayoutParams.WRAP_CONTENT, android.app.ActionBar.LayoutParams.WRAP_CONTENT, true);
mMainMenuPopup = new PopupWindow(mainPopupContainer, ActionBar.LayoutParams.WRAP_CONTENT, ActionBar.LayoutParams.WRAP_CONTENT, true);
} else {
// 2015/03/11 ... there may well be a less hackish way to support Gingerbread, but eh ... diminishing returns
final int TOTAL_MARGINS = 16;
@ -267,7 +267,7 @@ public class Apple2MainMenu {
final RadioButton noAppleSelected = (RadioButton) resetConfirmationView.findViewById(R.id.radioButton_noApple);
noAppleSelected.setChecked(false);
AlertDialog rebootQuitDialog = new AlertDialog.Builder(mActivity).setIcon(R.drawable.ic_launcher).setCancelable(true).setTitle(R.string.quit_reboot).setMessage(R.string.quit_reboot_choice).setView(resetConfirmationView).setPositiveButton(R.string.reset, new DialogInterface.OnClickListener() {
AlertDialog rebootQuitDialog = new AlertDialog.Builder(mActivity).setIcon(R.drawable.ic_launcher).setCancelable(true).setTitle(R.string.quit_reboot).setMessage(R.string.quit_reboot_choice).setView(resetConfirmationView).setNeutralButton(R.string.reset, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
if (!selectionAlreadyHandled.compareAndSet(false, true)) {
@ -283,7 +283,7 @@ public class Apple2MainMenu {
mActivity.rebootEmulation(resetState);
Apple2MainMenu.this.dismiss();
}
}).setNeutralButton(R.string.quit, new DialogInterface.OnClickListener() {
}).setPositiveButton(R.string.quit, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
if (!selectionAlreadyHandled.compareAndSet(false, true)) {
@ -292,7 +292,7 @@ public class Apple2MainMenu {
}
mActivity.quitEmulator();
}
}).setNegativeButton(R.string.cancel, null).create();
}).create();
mActivity.registerAndShowDialog(rebootQuitDialog);
}
@ -337,12 +337,12 @@ public class Apple2MainMenu {
try {
diskArgs.pfd.close(); // at this point diskArgs.pfd !null
} catch (IOException ioe) {
Log.e(TAG, "Error attempting to close PFD : " + ioe);
Apple2Activity.logMessage(Apple2Activity.LogType.ERROR, TAG, "Error attempting to close PFD : " + ioe);
}
diskArgs.pfd = null;
} catch (Exception e) {
Log.e(TAG, "OOPS: " + e);
Apple2Activity.logMessage(Apple2Activity.LogType.ERROR, TAG, "OOPS: " + e);
}
return restored;
@ -386,7 +386,7 @@ public class Apple2MainMenu {
pfds[i] = Apple2DiskChooserActivity.openFileDescriptorFromUri(activity, uri);
if (pfds[i] == null) {
Log.e(TAG, "Did not find URI for drive #" + i + " specified in " + SAVE_FILE + " file : " + diskPath);
Apple2Activity.logMessage(Apple2Activity.LogType.ERROR, TAG, "Did not find URI for drive #" + i + " specified in " + SAVE_FILE + " file : " + diskPath);
} else {
int fd = pfds[i].getFd();
map.put(fdKeys[i], fd);
@ -394,7 +394,7 @@ public class Apple2MainMenu {
} else {
boolean exists = new File(diskPath).exists();
if (!exists) {
Log.e(TAG, "Did not find path for drive #" + i + " specified in " + SAVE_FILE + " file : " + diskPath);
Apple2Activity.logMessage(Apple2Activity.LogType.ERROR, TAG, "Did not find path for drive #" + i + " specified in " + SAVE_FILE + " file : " + diskPath);
}
}
}
@ -407,7 +407,7 @@ public class Apple2MainMenu {
pfds[i].close();
}
} catch (IOException ioe) {
Log.e(TAG, "Error attempting to close PFD #" + i + " : " + ioe);
Apple2Activity.logMessage(Apple2Activity.LogType.ERROR, TAG, "Error attempting to close PFD #" + i + " : " + ioe);
}
}
map = new JSONObject(jsonString);
@ -435,7 +435,7 @@ public class Apple2MainMenu {
final AtomicBoolean selectionAlreadyHandled = new AtomicBoolean(false);
AlertDialog saveRestoreDialog = new AlertDialog.Builder(mActivity).setIcon(R.drawable.ic_launcher).setCancelable(true).setTitle(R.string.saverestore).setMessage(R.string.saverestore_choice).setPositiveButton(R.string.save, new DialogInterface.OnClickListener() {
AlertDialog saveRestoreDialog = new AlertDialog.Builder(mActivity).setIcon(R.drawable.ic_launcher).setCancelable(true).setTitle(R.string.saverestore).setMessage(R.string.saverestore_choice).setNeutralButton(R.string.save, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
if (!selectionAlreadyHandled.compareAndSet(false, true)) {
@ -447,7 +447,7 @@ public class Apple2MainMenu {
mActivity.saveState(jsonString);
Apple2MainMenu.this.dismiss();
}
}).setNeutralButton(R.string.restore, new DialogInterface.OnClickListener() {
}).setPositiveButton(R.string.restore, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
@ -463,7 +463,7 @@ public class Apple2MainMenu {
}
Apple2MainMenu.this.dismiss();
}
}).setNegativeButton(R.string.cancel, null).create();
}).create();
mActivity.registerAndShowDialog(saveRestoreDialog);
}

View File

@ -103,7 +103,7 @@ public class Apple2Preferences {
key = menu.getPrefKey();
val = map.get(key);
} catch (JSONException e) {
Log.d(TAG, "Did not find value for domain:" + menu.getPrefDefault() + " key:" + key);
Apple2Activity.logMessage(Apple2Activity.LogType.DEBUG, TAG, "Did not find value for domain:" + menu.getPrefDefault() + " key:" + key);
}
if (val == null && key != null) {
val = menu.getPrefDefault();
@ -123,7 +123,7 @@ public class Apple2Preferences {
map = _prefDomain(domain);
val = map.get(key);
} catch (JSONException e) {
Log.d(TAG, "Did not find value for domain:" + domain + " key:" + key);
Apple2Activity.logMessage(Apple2Activity.LogType.DEBUG, TAG, "Did not find value for domain:" + domain + " key:" + key);
}
if (val == null) {
val = defaultVal;
@ -143,21 +143,43 @@ public class Apple2Preferences {
try {
return (float) obj;
} catch (ClassCastException e) {
Log.d(TAG, "could not cast object as float");
Apple2Activity.logMessage(Apple2Activity.LogType.DEBUG, TAG, "could not cast object as float");
}
try {
return (float) ((double) obj);
} catch (ClassCastException e) {
Log.d(TAG, "could not cast object as double");
Apple2Activity.logMessage(Apple2Activity.LogType.DEBUG, TAG, "could not cast object as double");
}
try {
return (float) ((int) obj);
} catch (ClassCastException e) {
Log.d(TAG, "could not cast object as int");
Apple2Activity.logMessage(Apple2Activity.LogType.DEBUG, TAG, "could not cast object as int");
}
return (float) ((long) obj);
}
private static int _convertToInt(Object obj) {
if (obj == null) {
return 0;
}
try {
return (int) obj;
} catch (ClassCastException e) {
Apple2Activity.logMessage(Apple2Activity.LogType.DEBUG, TAG, "could not cast object as int");
}
try {
return (int) ((long) obj);
} catch (ClassCastException e) {
Apple2Activity.logMessage(Apple2Activity.LogType.DEBUG, TAG, "could not cast object as long");
}
try {
return (int) ((float) obj);
} catch (ClassCastException e) {
Apple2Activity.logMessage(Apple2Activity.LogType.DEBUG, TAG, "could not cast object as float");
}
return (int) ((double) obj);
}
public static float getFloatJSONPref(Apple2AbstractMenu.IMenuEnum menu) {
return _convertToFloat(getJSONPref(menu));
}
@ -166,6 +188,14 @@ public class Apple2Preferences {
return _convertToFloat(getJSONPref(domain, key, defaultVal));
}
public static int getIntJSONPref(Apple2AbstractMenu.IMenuEnum menu) {
return _convertToInt(getJSONPref(menu));
}
public static int getIntJSONPref(String domain, String key, Object defaultVal) {
return _convertToInt(getJSONPref(domain, key, defaultVal));
}
public static boolean migrate(Apple2Activity activity) {
int versionCode = (int) getJSONPref(PREF_DOMAIN_INTERFACE, PREF_EMULATOR_VERSION, 0);
final boolean firstTime = (versionCode != BuildConfig.VERSION_CODE);
@ -184,284 +214,109 @@ public class Apple2Preferences {
}
Apple2Utils.migrateToExternalStorage(activity);
if (BuildConfig.VERSION_CODE >= 17) {
// FIXME TODO : remove this after most/all app users are on 18+
boolean keypadPreset = false;
Apple2AbstractMenu.IMenuEnum menuEnum = null;
ArrayList<String> keypadJSONChars = new ArrayList<String>();
ArrayList<String> keypadJSONScans = new ArrayList<String>();
for (int i = 0; i < Apple2KeypadSettingsMenu.ROSETTE_SIZE; i++) {
keypadJSONChars.add("" + Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION);
keypadJSONScans.add("-1");
}
int keypadTapChar = Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION;
int keypadTapScan = -1;
int keypadSwipeDownChar = Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION;
int keypadSwipeDownScan = -1;
int keypadSwipeUpChar = Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION;
int keypadSwipeUpScan = -1;
Map<String, ?> oldPrefs = activity.getPreferences(Activity.MODE_PRIVATE).getAll();
for (Map.Entry<String, ?> entry : oldPrefs.entrySet()) {
String key = entry.getKey();
Object val = entry.getValue();
Log.d("OLDPREFS", key + " : " + val.toString());
// remove olde preference ...
activity.getPreferences(Activity.MODE_PRIVATE).edit().remove(key).commit();
try {
switch (key) {
case "HIRES_COLOR": // long
menuEnum = Apple2VideoSettingsMenu.SETTINGS.COLOR_MODE_CONFIGURE;
break;
case "LANDSCAPE_MODE": // bool
menuEnum = Apple2VideoSettingsMenu.SETTINGS.LANDSCAPE_MODE;
break;
case "PORTRAIT_KEYBOARD_POSITION_SCALE": //-> float
menuEnum = Apple2PortraitCalibration.States.CALIBRATE_KEYBOARD_POSITION_SCALE;
val = (int) val / (float) Apple2PortraitCalibration.PORTRAIT_CALIBRATE_NUM_CHOICES;
break;
case "PORTRAIT_FRAMEBUFFER_POSITION_SCALE": // -> float
menuEnum = Apple2PortraitCalibration.States.CALIBRATE_FRAMEBUFFER_POSITION_SCALE;
val = (int) val / (float) Apple2PortraitCalibration.PORTRAIT_CALIBRATE_NUM_CHOICES;
break;
case "PORTRAIT_KEYBOARD_HEIGHT_SCALE": // -> float
menuEnum = Apple2PortraitCalibration.States.CALIBRATE_KEYBOARD_HEIGHT_SCALE;
val = (int) val / (float) Apple2PortraitCalibration.PORTRAIT_CALIBRATE_NUM_CHOICES;
break;
case "SPEAKER_VOLUME": // long
menuEnum = Apple2AudioSettingsMenu.SETTINGS.SPEAKER_VOLUME;
break;
case "MOCKINGBOARD_ENABLED": // bool
menuEnum = Apple2AudioSettingsMenu.SETTINGS.MOCKINGBOARD_ENABLED;
break;
case "MOCKINGBOARD_VOLUME": // long
menuEnum = Apple2AudioSettingsMenu.SETTINGS.MOCKINGBOARD_VOLUME;
break;
case "AUDIO_LATENCY": // -> float
menuEnum = Apple2AudioSettingsMenu.SETTINGS.AUDIO_LATENCY;
val = (int) val / (float) Apple2AudioSettingsMenu.AUDIO_LATENCY_NUM_CHOICES;
break;
case "TOUCH_MENU_ENABLED": // bool
menuEnum = Apple2KeyboardSettingsMenu.SETTINGS.TOUCH_MENU_ENABLED;
break;
case "KEYBOARD_VISIBILITY_ACTIVE": // -> float
menuEnum = Apple2KeyboardSettingsMenu.SETTINGS.KEYBOARD_VISIBILITY_ACTIVE;
val = (int) val / (float) ALPHA_SLIDER_NUM_CHOICES;
break;
case "KEYBOARD_VISIBILITY_INACTIVE": // -> float
menuEnum = Apple2KeyboardSettingsMenu.SETTINGS.KEYBOARD_VISIBILITY_INACTIVE;
val = (int) val / (float) ALPHA_SLIDER_NUM_CHOICES;
break;
case "KEYBOARD_GLYPH_SCALE": // long
menuEnum = Apple2KeyboardSettingsMenu.SETTINGS.KEYBOARD_GLYPH_SCALE;
break;
case "KEYBOARD_LOWERCASE_ENABLED": // bool
menuEnum = Apple2KeyboardSettingsMenu.SETTINGS.KEYBOARD_ENABLE_LOWERCASE;
break;
case "KEYBOARD_CLICK_ENABLED": // bool
menuEnum = Apple2KeyboardSettingsMenu.SETTINGS.KEYBOARD_ENABLE_CLICK;
break;
case "KEYBOARD_ALT": // long
menuEnum = Apple2KeyboardSettingsMenu.SETTINGS.KEYBOARD_CHOOSE_ALT;
break;
case "KEYBOARD_ALT_PATH": // String
setJSONPref(Apple2Preferences.PREF_DOMAIN_KEYBOARD, "altPath", val);
continue;
case "JOYSTICK_SWIPEUP_BUTTON":
menuEnum = Apple2JoystickSettingsMenu.SETTINGS.JOYSTICK_SWIPEUP_BUTTON;
break;
case "JOYSTICK_TAP_BUTTON":
menuEnum = Apple2JoystickSettingsMenu.SETTINGS.JOYSTICK_TAP_BUTTON;
break;
case "JOYSTICK_SWIPEDOWN_BUTTON":
menuEnum = Apple2JoystickSettingsMenu.SETTINGS.JOYSTICK_SWIPEDOWN_BUTTON;
break;
case "JOYSTICK_VISIBILITY": // boolean
menuEnum = Apple2JoystickSettingsMenu.JoystickAdvanced.SETTINGS.JOYSTICK_VISIBILITY;
break;
case "JOYSTICK_BUTTON_THRESHOLD": // -> float
menuEnum = Apple2JoystickSettingsMenu.JoystickAdvanced.SETTINGS.JOYSTICK_BUTTON_THRESHOLD;
val = (int) val * Apple2JoystickSettingsMenu.getJoystickButtonSwitchThresholdScale(activity);
break;
case "JOYSTICK_DIVIDER": // -> float
Apple2Preferences.setJSONPref(Apple2Preferences.PREF_DOMAIN_JOYSTICK, Apple2JoystickCalibration.PREF_SCREEN_DIVISION, (int) val / (float) Apple2JoystickCalibration.JOYSTICK_DIVIDER_NUM_CHOICES);
continue;
case "JOYSTICK_AXIS_ON_LEFT": // bool
menuEnum = Apple2JoystickSettingsMenu.JoystickAdvanced.SETTINGS.JOYSTICK_AXIS_ON_LEFT;
break;
case "JOYSTICK_TAPDELAY": // -> float
menuEnum = Apple2JoystickSettingsMenu.SETTINGS.JOYSTICK_TAPDELAY;
val = ((int) val / (float) Apple2JoystickSettingsMenu.TAPDELAY_NUM_CHOICES * Apple2JoystickSettingsMenu.TAPDELAY_SCALE);
break;
case "JOYSTICK_AZIMUTH_VISIBILITY": // boolean
continue; // removed
case "JOYSTICK_AXIS_SENSITIVIY": // -> float
menuEnum = Apple2JoystickSettingsMenu.JoystickAdvanced.SETTINGS.JOYSTICK_AXIS_SENSITIVITY;
final int pivot = Apple2JoystickSettingsMenu.JOYSTICK_AXIS_SENSITIVITY_DEC_NUMCHOICES;
float sensitivity = 1.f;
if ((int) val < pivot) {
int decAmount = (pivot - (int) val);
sensitivity -= (Apple2JoystickSettingsMenu.JOYSTICK_AXIS_SENSITIVITY_DEC_STEP * decAmount);
} else if ((int) val > pivot) {
int incAmount = ((int) val - pivot);
sensitivity += (Apple2JoystickSettingsMenu.JOYSTICK_AXIS_SENSITIVITY_INC_STEP * incAmount);
}
val = sensitivity;
break;
case "CURRENT_DISK_PATH": // String
menuEnum = Apple2DisksMenu.SETTINGS.CURRENT_DISK_SEARCH_PATH;
try {
val = new JSONArray((String) val);
} catch (JSONException e) {
Log.v(TAG, "JSON error parsing disk path : " + e);
continue;
}
break;
case "CURRENT_DRIVE_A_BUTTON": // bool
menuEnum = Apple2DisksMenu.SETTINGS.CURRENT_DRIVE_A;
break;
case "CURRENT_DISK_A": // String
menuEnum = Apple2DisksMenu.SETTINGS.CURRENT_DISK_PATH_A;
break;
case "CURRENT_DISK_A_RO": // bool
menuEnum = Apple2DisksMenu.SETTINGS.CURRENT_DISK_PATH_A_RO;
break;
case "CURRENT_DISK_B": // String
menuEnum = Apple2DisksMenu.SETTINGS.CURRENT_DISK_PATH_B;
break;
case "CURRENT_DISK_B_RO": // bool
menuEnum = Apple2DisksMenu.SETTINGS.CURRENT_DISK_PATH_B_RO;
break;
case "CURRENT_DISK_RO_BUTTON": // bool
menuEnum = Apple2DisksMenu.SETTINGS.CURRENT_DISK_RO_BUTTON;
break;
case "CURRENT_TOUCH_DEVICE": // long
menuEnum = Apple2SettingsMenu.SETTINGS.CURRENT_INPUT;
break;
case "CRASH_CHECK": // boolean
menuEnum = Apple2SettingsMenu.SETTINGS.CRASH;
break;
case "SHOW_DISK_OPERATIONS": // bool
menuEnum = Apple2SettingsMenu.SETTINGS.SHOW_DISK_OPERATIONS;
break;
case "KEYPAD_KEYS": // long
menuEnum = Apple2KeypadSettingsMenu.SETTINGS.KEYPAD_CHOOSE_KEYS;
if ((int) val != 0) {
Apple2KeypadSettingsMenu.KeypadPreset preset = Apple2KeypadSettingsMenu.KeypadPreset.values()[(int) val - 1];
preset.apply(activity);
keypadPreset = true;
}
break;
case "KEYREPEAT_THRESHOLD": // -> float
menuEnum = Apple2KeypadSettingsMenu.KeypadAdvanced.SETTINGS.KEYREPEAT_THRESHOLD;
val = (int) val / (float) Apple2KeypadSettingsMenu.KEYREPEAT_NUM_CHOICES;
break;
case "KEYPAD_TAP_KEY_SCAN":
keypadTapScan = (int) val;
continue;
case "KEYPAD_TAP_KEY_ASCII":
keypadTapChar = (int) val;
continue;
case "KEYPAD_SWIPEDOWN_KEY_SCAN":
keypadSwipeDownScan = (int) val;
continue;
case "KEYPAD_SWIPEDOWN_KEY_ASCII":
keypadSwipeDownChar = (int) val;
continue;
case "KEYPAD_SWIPEUP_KEY_SCAN":
keypadSwipeUpScan = (int) val;
continue;
case "KEYPAD_SWIPEUP_KEY_ASCII":
keypadSwipeUpChar = (int) val;
continue;
case "KEYPAD_NORTHWEST_KEY_SCAN":
keypadJSONScans.set(0, val.toString());
continue;
case "KEYPAD_NORTHWEST_KEY_ASCII":
keypadJSONChars.set(0, val.toString());
continue;
case "KEYPAD_NORTH_KEY_SCAN":
keypadJSONScans.set(1, val.toString());
continue;
case "KEYPAD_NORTH_KEY_ASCII":
keypadJSONChars.set(1, val.toString());
continue;
case "KEYPAD_NORTHEAST_KEY_SCAN":
keypadJSONScans.set(2, val.toString());
continue;
case "KEYPAD_NORTHEAST_KEY_ASCII":
keypadJSONChars.set(2, val.toString());
continue;
case "KEYPAD_WEST_KEY_SCAN":
keypadJSONScans.set(3, val.toString());
continue;
case "KEYPAD_WEST_KEY_ASCII":
keypadJSONChars.set(3, val.toString());
continue;
case "KEYPAD_CENTER_KEY_SCAN":
keypadJSONScans.set(4, val.toString());
continue;
case "KEYPAD_CENTER_KEY_ASCII":
keypadJSONChars.set(4, val.toString());
continue;
case "KEYPAD_EAST_KEY_SCAN":
keypadJSONScans.set(5, val.toString());
continue;
case "KEYPAD_EAST_KEY_ASCII":
keypadJSONChars.set(5, val.toString());
continue;
case "KEYPAD_SOUTHWEST_KEY_SCAN":
keypadJSONScans.set(6, val.toString());
continue;
case "KEYPAD_SOUTHWEST_KEY_ASCII":
keypadJSONChars.set(6, val.toString());
continue;
case "KEYPAD_SOUTH_KEY_SCAN":
keypadJSONScans.set(7, val.toString());
continue;
case "KEYPAD_SOUTH_KEY_ASCII":
keypadJSONChars.set(7, val.toString());
continue;
case "KEYPAD_SOUTHEAST_KEY_SCAN":
keypadJSONScans.set(8, val.toString());
continue;
case "KEYPAD_SOUTHEAST_KEY_ASCII":
keypadJSONChars.set(8, val.toString());
continue;
default:
continue;
}
setJSONPref(menuEnum, val);
save(activity);
} catch (ClassCastException cce) {
Log.v(TAG, "" + cce);
if (versionCode < 24) {
// migrate tap delay from seconds to frames ...
float secs = getFloatJSONPref(PREF_DOMAIN_JOYSTICK, "jsTapDelaySecs", 9999f);
if (secs != 9999f) {
// UtAIIe 3-13 : "The duration of the television scan is 262 horizontal scans. This is [16.688 milliseconds]"
// recalculate this to a frames value between 0-30 inclusive ...
int framesDelay = Math.round(secs / 0.016688f);
if (framesDelay < 0) {
framesDelay = 0;
} else if (framesDelay > 30) {
framesDelay = 30;
}
setJSONPref(Apple2JoystickSettingsMenu.JoystickAdvanced.SETTINGS.JOYSTICK_TAPDELAY, framesDelay);
}
// handle keypad arrays
if (oldPrefs.size() > 0 && !keypadPreset) {
Apple2KeypadSettingsMenu.KeypadPreset.saveRosettes(keypadJSONChars, keypadJSONScans);
Apple2KeypadSettingsMenu.KeypadPreset.saveTouchDownKey(keypadTapChar, keypadTapScan);
Apple2KeypadSettingsMenu.KeypadPreset.saveSwipeNorthKey(keypadSwipeUpChar, keypadSwipeUpScan);
Apple2KeypadSettingsMenu.KeypadPreset.saveSwipeSouthKey(keypadSwipeDownChar, keypadSwipeDownScan);
// migrate axis rosette arrays to new format ...
try {
ArrayList<Apple2KeypadSettingsMenu.KeyTuple> axisRosette = new ArrayList<Apple2KeypadSettingsMenu.KeyTuple>();
axisRosette.add(new Apple2KeypadSettingsMenu.KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1));
axisRosette.add(new Apple2KeypadSettingsMenu.KeyTuple('I', Apple2KeyboardSettingsMenu.SCANCODE_I));
axisRosette.add(new Apple2KeypadSettingsMenu.KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1));
axisRosette.add(new Apple2KeypadSettingsMenu.KeyTuple('J', Apple2KeyboardSettingsMenu.SCANCODE_J));
axisRosette.add(new Apple2KeypadSettingsMenu.KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1));
axisRosette.add(new Apple2KeypadSettingsMenu.KeyTuple('K', Apple2KeyboardSettingsMenu.SCANCODE_K));
axisRosette.add(new Apple2KeypadSettingsMenu.KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1));
axisRosette.add(new Apple2KeypadSettingsMenu.KeyTuple('M', Apple2KeyboardSettingsMenu.SCANCODE_M));
axisRosette.add(new Apple2KeypadSettingsMenu.KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1));
JSONArray jsonArray;
jsonArray = (JSONArray) getJSONPref(PREF_DOMAIN_JOYSTICK, "kpAxisRosetteChars", null);
if (jsonArray == null || jsonArray.length() != Apple2KeypadSettingsMenu.ROSETTE_SIZE) {
Apple2Activity.logMessage(Apple2Activity.LogType.ERROR, TAG, "Oops, kpAxisRosetteChars is not expected length");
} else {
for (int i = 0; i < Apple2KeypadSettingsMenu.ROSETTE_SIZE; i++) {
Apple2KeypadSettingsMenu.KeyTuple tuple = axisRosette.get(i);
tuple.ch = jsonArray.getLong(i);
}
}
jsonArray = (JSONArray) getJSONPref(PREF_DOMAIN_JOYSTICK, "kpAxisRosetteScancodes", null);
if (jsonArray == null || jsonArray.length() != Apple2KeypadSettingsMenu.ROSETTE_SIZE) {
Apple2Activity.logMessage(Apple2Activity.LogType.ERROR, TAG, "Oops, kpAxisRosetteScancodes is not expected length");
} else {
for (int i = 0; i < Apple2KeypadSettingsMenu.ROSETTE_SIZE; i++) {
Apple2KeypadSettingsMenu.KeyTuple tuple = axisRosette.get(i);
tuple.scan = jsonArray.getLong(i);
}
}
Apple2KeypadSettingsMenu.KeypadPreset.saveAxisRosette(axisRosette);
} catch (Exception e) {
e.printStackTrace();
}
// migrate individual keypad button actions to new button rosette actions ...
{
ArrayList<Apple2KeypadSettingsMenu.KeyTuple> buttRosette = new ArrayList<Apple2KeypadSettingsMenu.KeyTuple>();
int northChar = getIntJSONPref(PREF_DOMAIN_JOYSTICK, "kpSwipeNorthChar", Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION);
int northScan = getIntJSONPref(PREF_DOMAIN_JOYSTICK, "kpSwipeNorthScancode", -1);
int downChar = getIntJSONPref(PREF_DOMAIN_JOYSTICK, "kpTouchDownChar", Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION);
int downScan = getIntJSONPref(PREF_DOMAIN_JOYSTICK, "kpTouchDownScancode", -1);
int southChar = getIntJSONPref(PREF_DOMAIN_JOYSTICK, "kpSwipeSouthChar", Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION);
int southScan = getIntJSONPref(PREF_DOMAIN_JOYSTICK, "kpSwipeSouthScancode", -1);
if (northScan < 0 && downScan < 0 && southScan < 0) {
downChar = Apple2KeyboardSettingsMenu.ICONTEXT_VISUAL_SPACE;
downScan = Apple2KeyboardSettingsMenu.SCANCODE_SPACE;
}
buttRosette.add(new Apple2KeypadSettingsMenu.KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1));
buttRosette.add(new Apple2KeypadSettingsMenu.KeyTuple(northChar, northScan));
buttRosette.add(new Apple2KeypadSettingsMenu.KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1));
buttRosette.add(new Apple2KeypadSettingsMenu.KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1));
buttRosette.add(new Apple2KeypadSettingsMenu.KeyTuple(downChar, downScan));
buttRosette.add(new Apple2KeypadSettingsMenu.KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1));
buttRosette.add(new Apple2KeypadSettingsMenu.KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1));
buttRosette.add(new Apple2KeypadSettingsMenu.KeyTuple(southChar, southScan));
buttRosette.add(new Apple2KeypadSettingsMenu.KeyTuple(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1));
Apple2KeypadSettingsMenu.KeypadPreset.saveButtRosette(buttRosette);
}
JSONObject map = _prefDomain(PREF_DOMAIN_JOYSTICK);
map.remove("jsTapDelaySecs");
map.remove("kpAxisRosetteChars");
map.remove("kpAxisRosetteScancodes");
map.remove("kpButtRosetteChars");
map.remove("kpButtRosetteScancodes");
map.remove("kpSwipeNorthChar");
map.remove("kpSwipeNorthScancode");
map.remove("kpSwipeSouthChar");
map.remove("kpSwipeSouthScancode");
map.remove("kpTouchDownChar");
map.remove("kpTouchDownScancode");
}
save(activity);
@ -478,7 +333,7 @@ public class Apple2Preferences {
StringBuilder jsonString = new StringBuilder();
if (!Apple2Utils.readEntireFile(prefsFile, jsonString)) {
Log.d(TAG, "Oops, could not read JSON file : " + prefsFile);
Apple2Activity.logMessage(Apple2Activity.LogType.DEBUG, TAG, "Oops, could not read JSON file : " + prefsFile);
}
try {
@ -504,7 +359,7 @@ public class Apple2Preferences {
try {
jsonString = sSettings.toString(2);
} catch (JSONException e) {
Log.w(TAG, "Error attempting to pretty-print JSON : " + e);
Apple2Activity.logMessage(Apple2Activity.LogType.WARN, TAG, "Error attempting to pretty-print JSON : " + e);
ex = e;
jsonString = sSettings.toString();
}

View File

@ -11,15 +11,14 @@
package org.deadc0de.apple2ix;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import androidx.appcompat.app.AlertDialog;
import android.util.Log;
import android.view.View;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.TextView;
import org.deadc0de.apple2ix.basic.BuildConfig;
import org.deadc0de.apple2ix.basic.R;
@ -162,7 +161,7 @@ public class Apple2SettingsMenu extends Apple2AbstractMenu {
@Override
public void handleSelection(final Apple2Activity activity, final Apple2AbstractMenu settingsMenu, boolean isChecked) {
new Apple2JoystickSettingsMenu(activity).show();
new Apple2JoystickSettingsMenu(activity, Apple2SettingsMenu.TouchDeviceVariant.JOYSTICK).show();
}
},
KEYPAD_CONFIGURE {
@ -178,7 +177,7 @@ public class Apple2SettingsMenu extends Apple2AbstractMenu {
@Override
public void handleSelection(final Apple2Activity activity, final Apple2AbstractMenu settingsMenu, boolean isChecked) {
new Apple2KeypadSettingsMenu(activity).show();
new Apple2KeypadSettingsMenu(activity, Apple2SettingsMenu.TouchDeviceVariant.JOYSTICK_KEYPAD).show();
}
},
KEYBOARD_CONFIGURE {
@ -350,17 +349,33 @@ public class Apple2SettingsMenu extends Apple2AbstractMenu {
@Override
public void handleSelection(final Apple2Activity activity, final Apple2AbstractMenu settingsMenu, boolean isChecked) {
AlertDialog.Builder builder = new AlertDialog.Builder(activity).setIcon(R.drawable.ic_launcher).setCancelable(true).setTitle(R.string.preferences_reset_really).setMessage(R.string.preferences_reset_warning).setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
AlertDialog.Builder builder = new AlertDialog.Builder(activity).setIcon(R.drawable.ic_launcher).setCancelable(true).setTitle(R.string.preferences_reset_really).setMessage(R.string.preferences_reset_warning).setPositiveButton(R.string.reset, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
Apple2Preferences.reset(activity);
}
}).setNegativeButton(R.string.no, null);
});
AlertDialog dialog = builder.create();
activity.registerAndShowDialog(dialog);
}
},
EMAIL_LOGS {
@Override
public final String getTitle(Apple2Activity activity) {
return activity.getResources().getString(R.string.preferences_email_logs);
}
@Override
public final String getSummary(Apple2Activity activity) {
return activity.getResources().getString(R.string.preferences_email_logs_summary);
}
@Override
public void handleSelection(final Apple2Activity activity, final Apple2AbstractMenu settingsMenu, boolean isChecked) {
Apple2CrashHandler.getInstance().emailCrashesAndLogs(activity);
}
},
CRASH {
@Override
public final String getTitle(Apple2Activity activity) {
@ -426,7 +441,7 @@ public class Apple2SettingsMenu extends Apple2AbstractMenu {
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
Log.d(TAG, "About to NPE : " + str[0].length());
Apple2Activity.logMessage(Apple2Activity.LogType.DEBUG, TAG, "About to NPE : " + str[0].length());
}
});
}

View File

@ -11,9 +11,9 @@
package org.deadc0de.apple2ix;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import androidx.appcompat.app.AlertDialog;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Button;
@ -50,13 +50,13 @@ public class Apple2SplashScreen implements Apple2MenuView {
prefsButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
AlertDialog.Builder builder = new AlertDialog.Builder(mActivity).setIcon(R.drawable.ic_launcher).setCancelable(true).setTitle(R.string.preferences_reset_really).setMessage(R.string.preferences_reset_warning).setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
AlertDialog.Builder builder = new AlertDialog.Builder(mActivity).setIcon(R.drawable.ic_launcher).setCancelable(true).setTitle(R.string.preferences_reset_really).setMessage(R.string.preferences_reset_warning).setPositiveButton(R.string.reset, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
Apple2Preferences.reset(mActivity);
}
}).setNegativeButton(R.string.no, null);
});
AlertDialog dialog = builder.create();
mActivity.registerAndShowDialog(dialog);
}

View File

@ -14,6 +14,7 @@ package org.deadc0de.apple2ix;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.res.AssetManager;
import android.os.AsyncTask;
import android.os.Environment;
import android.util.Log;
import android.view.View;
@ -22,6 +23,8 @@ import android.widget.ProgressBar;
import org.deadc0de.apple2ix.basic.BuildConfig;
import org.deadc0de.apple2ix.basic.R;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
@ -35,6 +38,8 @@ import java.io.InputStream;
import java.io.InterruptedIOException;
import java.io.OutputStream;
import java.util.zip.GZIPOutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
public class Apple2Utils {
@ -61,7 +66,7 @@ public class Apple2Utils {
} catch (InterruptedIOException ie) {
/* EINTR, EAGAIN ... */
} catch (IOException e) {
Log.d(TAG, "Error reading file at path : " + file.toString());
Apple2Activity.logMessage(Apple2Activity.LogType.DEBUG, TAG, "Error reading file at path : " + file.toString());
}
try {
@ -89,7 +94,7 @@ public class Apple2Utils {
} catch (InterruptedIOException ie) {
/* EINTR, EAGAIN ... */
} catch (IOException e) {
Log.e(TAG, "Exception attempting to write data : " + e);
Apple2Activity.logMessage(Apple2Activity.LogType.ERROR, TAG, "Exception attempting to write data : " + e);
}
try {
@ -103,6 +108,93 @@ public class Apple2Utils {
return attempts < maxAttempts;
}
public static File zipFiles(File[] files, File zipFile) {
zipFile.delete();
ZipOutputStream out = null;
do {
try {
zipFile.createNewFile();
} catch (IOException ioe) {
Apple2Activity.logMessage(Apple2Activity.LogType.ERROR, TAG, "Could not create zipfile " + zipFile.getAbsolutePath() + " : " + ioe.getMessage());
break;
}
final int BUF_SIZ = 4096;
BufferedInputStream origin = null;
try {
out = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(zipFile)));
} catch (IOException ioe) {
Apple2Activity.logMessage(Apple2Activity.LogType.ERROR, TAG, "Could not create zip outputStream : " + ioe.getMessage());
break;
}
byte data[] = new byte[BUF_SIZ];
for (File file : files) {
FileInputStream fi = null;
try {
fi = new FileInputStream(file);
} catch (IOException ioe) {
Apple2Activity.logMessage(Apple2Activity.LogType.ERROR, TAG, "Could not create file input stream : " + ioe.getMessage());
continue;
}
origin = new BufferedInputStream(fi, BUF_SIZ);
ZipEntry entry = new ZipEntry(file.getName());
try {
out.putNextEntry(entry);
} catch (IOException ioe) {
Apple2Activity.logMessage(Apple2Activity.LogType.ERROR, TAG, "Could not put next zip entry : " + ioe.getMessage());
continue;
}
final int maxAttempts = 5;
int attempts = 0;
do {
int count;
try {
while ((count = origin.read(data, 0, BUF_SIZ)) != -1) {
out.write(data, 0, count);
}
break;
} catch (InterruptedIOException ie) {
/* EINTR, EAGAIN ... */
} catch (IOException ioe) {
Apple2Activity.logMessage(Apple2Activity.LogType.ERROR, TAG, "Could read/write zip data : " + ioe.getMessage());
break;
}
++attempts;
} while (attempts < maxAttempts);
try {
origin.close();
} catch (IOException ioe) {
// ...
}
}
} while (false);
if (out != null) {
try {
out.close();
} catch (IOException ioe) {
// ...
}
}
if (zipFile.exists()) {
return zipFile;
}
return null;
}
public static void migrateToExternalStorage(Apple2Activity activity) {
do {
@ -182,7 +274,7 @@ public class Apple2Utils {
if (!externalDir.exists()) {
boolean made = externalDir.mkdirs();
if (!made) {
Log.d(TAG, "WARNING: could not make directory : " + sExternalFilesDir);
Apple2Activity.logMessage(Apple2Activity.LogType.DEBUG, TAG, "WARNING: could not make directory : " + sExternalFilesDir);
break;
}
}
@ -218,7 +310,7 @@ public class Apple2Utils {
PackageInfo pi = pm.getPackageInfo(activity.getPackageName(), 0);
sDataDir = pi.applicationInfo.dataDir;
} catch (PackageManager.NameNotFoundException e) {
Log.e(TAG, "" + e);
Apple2Activity.logMessage(Apple2Activity.LogType.ERROR, TAG, "" + e);
if (sDataDir == null) {
sDataDir = "/data/local/tmp";
}
@ -227,7 +319,7 @@ public class Apple2Utils {
return sDataDir;
}
public static void exposeAPKAssetsToExternal(Apple2Activity activity) {
public static void exposeAPKAssetsToExternal(final Apple2Activity activity) {
getExternalStorageDirectory(activity);
if (sExternalFilesDir == null) {
return;
@ -240,29 +332,34 @@ public class Apple2Utils {
try {
bar.setVisibility(View.VISIBLE);
bar.setIndeterminate(true);
AsyncTask.execute(new Runnable() {
@Override
public void run() {
Log.v(TAG, "Overwriting system files in /sdcard/apple2ix/ (external storage) ...");
recursivelyCopyAPKAssets(activity, /*from APK directory:*/"keyboards", /*to location:*/sExternalFilesDir.getAbsolutePath(), false);
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
try {
bar.setVisibility(View.INVISIBLE);
bar.setIndeterminate(false);
} catch (NullPointerException npe) {
Log.v(TAG, "Avoid NPE in exposeAPKAssetsToExternal #2");
}
}
});
}
});
} catch (NullPointerException npe) {
Log.v(TAG, "Avoid NPE in exposeAPKAssetsToExternal #1");
}
}
});
Log.v(TAG, "Overwriting system files in /sdcard/apple2ix/ (external storage) ...");
recursivelyCopyAPKAssets(activity, /*from APK directory:*/"keyboards", /*to location:*/sExternalFilesDir.getAbsolutePath(), false);
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
try {
bar.setVisibility(View.INVISIBLE);
bar.setIndeterminate(false);
} catch (NullPointerException npe) {
Log.v(TAG, "Avoid NPE in exposeAPKAssetsToExternal #2");
}
}
});
}
public static void exposeAPKAssets(Apple2Activity activity) {
public static void exposeAPKAssets(final Apple2Activity activity) {
final ProgressBar bar = (ProgressBar) activity.findViewById(R.id.crash_progressBar);
activity.runOnUiThread(new Runnable() {
@Override
@ -270,39 +367,45 @@ public class Apple2Utils {
try {
bar.setVisibility(View.VISIBLE);
bar.setIndeterminate(true);
AsyncTask.execute(new Runnable() {
@Override
public void run() {
getDataDir(activity);
// FIXME TODO : Heavy-handed migration to 1.1.3 ...
recursivelyDelete(new File(new File(sDataDir, "disks").getAbsolutePath(), "blanks"));
recursivelyDelete(new File(new File(sDataDir, "disks").getAbsolutePath(), "demo"));
recursivelyDelete(new File(new File(sDataDir, "disks").getAbsolutePath(), "eamon"));
recursivelyDelete(new File(new File(sDataDir, "disks").getAbsolutePath(), "logo"));
recursivelyDelete(new File(new File(sDataDir, "disks").getAbsolutePath(), "miscgame"));
Apple2Activity.logMessage(Apple2Activity.LogType.DEBUG, TAG, "First time copying stuff-n-things out of APK for ease-of-NDK access...");
getExternalStorageDirectory(activity);
recursivelyCopyAPKAssets(activity, /*from APK directory:*/"disks", /*to location:*/new File(sDataDir, "disks").getAbsolutePath(), true);
recursivelyCopyAPKAssets(activity, /*from APK directory:*/"keyboards", /*to location:*/new File(sDataDir, "keyboards").getAbsolutePath(), false);
recursivelyCopyAPKAssets(activity, /*from APK directory:*/"shaders", /*to location:*/new File(sDataDir, "shaders").getAbsolutePath(), false);
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
try {
bar.setVisibility(View.INVISIBLE);
bar.setIndeterminate(false);
} catch (NullPointerException npe) {
Log.v(TAG, "Avoid NPE in exposeAPKAssets #1");
}
}
});
}
});
} catch (NullPointerException npe) {
Log.v(TAG, "Avoid NPE in exposeAPKAssets #1");
}
}
});
getDataDir(activity);
// FIXME TODO : Heavy-handed migration to 1.1.3 ...
recursivelyDelete(new File(new File(sDataDir, "disks").getAbsolutePath(), "blanks"));
recursivelyDelete(new File(new File(sDataDir, "disks").getAbsolutePath(), "demo"));
recursivelyDelete(new File(new File(sDataDir, "disks").getAbsolutePath(), "eamon"));
recursivelyDelete(new File(new File(sDataDir, "disks").getAbsolutePath(), "logo"));
recursivelyDelete(new File(new File(sDataDir, "disks").getAbsolutePath(), "miscgame"));
Log.d(TAG, "First time copying stuff-n-things out of APK for ease-of-NDK access...");
getExternalStorageDirectory(activity);
recursivelyCopyAPKAssets(activity, /*from APK directory:*/"disks", /*to location:*/new File(sDataDir, "disks").getAbsolutePath(), true);
recursivelyCopyAPKAssets(activity, /*from APK directory:*/"keyboards", /*to location:*/new File(sDataDir, "keyboards").getAbsolutePath(), false);
recursivelyCopyAPKAssets(activity, /*from APK directory:*/"shaders", /*to location:*/new File(sDataDir, "shaders").getAbsolutePath(), false);
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
try {
bar.setVisibility(View.INVISIBLE);
bar.setIndeterminate(false);
} catch (NullPointerException npe) {
Log.v(TAG, "Avoid NPE in exposeAPKAssets #1");
}
}
});
}
public static void exposeSymbols(Apple2Activity activity) {
@ -316,12 +419,15 @@ public class Apple2Utils {
// TODO FIXME : WARNING : this is super dangerous if there are symlinks !!!
private static void recursivelyDelete(File file) {
if (file.isDirectory()) {
for (File f : file.listFiles()) {
recursivelyDelete(f);
File[] files = file.listFiles();
if (files != null) {
for (File f : files) {
recursivelyDelete(f);
}
}
}
if (!file.delete()) {
Log.d(TAG, "Failed to delete file: " + file);
Apple2Activity.logMessage(Apple2Activity.LogType.DEBUG, TAG, "Failed to delete file: " + file);
}
}
@ -338,7 +444,7 @@ public class Apple2Utils {
} catch (InterruptedIOException e) {
/* EINTR, EAGAIN ... */
} catch (IOException e) {
Log.d(TAG, "OOPS exception attempting to list APK files at : " + srcFileOrDir + " : " + e);
Apple2Activity.logMessage(Apple2Activity.LogType.DEBUG, TAG, "OOPS exception attempting to list APK files at : " + srcFileOrDir + " : " + e);
}
try {
@ -350,7 +456,7 @@ public class Apple2Utils {
} while (attempts < maxAttempts);
if (files == null) {
Log.d(TAG, "OOPS, could not list APK assets at : " + srcFileOrDir);
Apple2Activity.logMessage(Apple2Activity.LogType.DEBUG, TAG, "OOPS, could not list APK assets at : " + srcFileOrDir);
return;
}
@ -359,7 +465,7 @@ public class Apple2Utils {
File dstPath = new File(dstFileOrDir);
if (!dstPath.mkdirs()) {
if (!dstPath.exists()) {
Log.d(TAG, "OOPS, could not mkdirs on " + dstPath);
Apple2Activity.logMessage(Apple2Activity.LogType.DEBUG, TAG, "OOPS, could not mkdirs on " + dstPath);
return;
}
}
@ -387,7 +493,7 @@ public class Apple2Utils {
} catch (InterruptedIOException e) {
/* EINTR, EAGAIN */
} catch (IOException e) {
Log.e(TAG, "Failed to copy asset file: " + srcFileOrDir, e);
Apple2Activity.logMessage(Apple2Activity.LogType.ERROR, TAG, "Failed to copy asset file: " + srcFileOrDir + " : " + e.getMessage());
} finally {
if (is != null) {
try {
@ -462,7 +568,7 @@ public class Apple2Utils {
}
}
} catch (Exception e) {
Log.e(TAG, "OOPS : {e}");
Apple2Activity.logMessage(Apple2Activity.LogType.ERROR, TAG, "OOPS : {e}");
}
}
@ -478,7 +584,7 @@ public class Apple2Utils {
} catch (InterruptedIOException e) {
// EINTR, EAGAIN ...
} catch (IOException e) {
Log.d(TAG, "OOPS exception attempting to copy emulator state file : " + e);
Apple2Activity.logMessage(Apple2Activity.LogType.DEBUG, TAG, "OOPS exception attempting to copy emulator state file : " + e);
}
try {

View File

@ -424,8 +424,8 @@ public class Apple2VideoSettingsMenu extends Apple2AbstractMenu {
return false;
} else {
int orientation = activity.getRequestedOrientation();
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
return orientation != ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT);
return orientation != ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT;
}
}

View File

@ -136,7 +136,7 @@ class Apple2View extends GLSurfaceView implements InputManagerCompat.InputDevice
private static int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
public EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig eglConfig) {
Log.w(TAG, "creating OpenGL ES 2.0 context");
Apple2Activity.logMessage(Apple2Activity.LogType.WARN, TAG, "creating OpenGL ES 2.0 context");
checkEglError("Before eglCreateContext", egl);
int[] attrib_list = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE};
EGLContext context = egl.eglCreateContext(display, eglConfig, EGL10.EGL_NO_CONTEXT, attrib_list);
@ -152,7 +152,7 @@ class Apple2View extends GLSurfaceView implements InputManagerCompat.InputDevice
private static void checkEglError(String prompt, EGL10 egl) {
int error;
while ((error = egl.eglGetError()) != EGL10.EGL_SUCCESS) {
Log.e(TAG, String.format("%s: EGL error: 0x%x", prompt, error));
Apple2Activity.logMessage(Apple2Activity.LogType.ERROR, TAG, String.format("%s: EGL error: 0x%x", prompt, error));
}
}
@ -204,9 +204,9 @@ class Apple2View extends GLSurfaceView implements InputManagerCompat.InputDevice
// Now return the "best" one
EGLConfig best = chooseConfig(egl, display, configs);
if (best == null) {
Log.e(TAG, "OOPS! Did not pick an EGLConfig. What device are you using?! Android will now crash this app...");
Apple2Activity.logMessage(Apple2Activity.LogType.ERROR, TAG, "OOPS! Did not pick an EGLConfig. What device are you using?! Android will now crash this app...");
} else {
Log.w(TAG, "Using EGL CONFIG : ");
Apple2Activity.logMessage(Apple2Activity.LogType.WARN, TAG, "Using EGL CONFIG : ");
printConfig(egl, display, best);
}
return best;
@ -245,9 +245,9 @@ class Apple2View extends GLSurfaceView implements InputManagerCompat.InputDevice
private void printConfigs(EGL10 egl, EGLDisplay display, EGLConfig[] configs) {
int numConfigs = configs.length;
Log.w(TAG, String.format("%d configurations", numConfigs));
Apple2Activity.logMessage(Apple2Activity.LogType.WARN, TAG, String.format("%d configurations", numConfigs));
for (int i = 0; i < numConfigs; i++) {
Log.w(TAG, String.format("Configuration %d:\n", i));
Apple2Activity.logMessage(Apple2Activity.LogType.WARN, TAG, String.format("Configuration %d:\n", i));
printConfig(egl, display, configs[i]);
}
}
@ -328,9 +328,9 @@ class Apple2View extends GLSurfaceView implements InputManagerCompat.InputDevice
int attribute = attributes[i];
String name = names[i];
if (egl.eglGetConfigAttrib(display, config, attribute, value)) {
Log.w(TAG, String.format(" %s: %d\n", name, value[0]));
Apple2Activity.logMessage(Apple2Activity.LogType.WARN, TAG, String.format(" %s: %d\n", name, value[0]));
} else {
// Log.w(TAG, String.format(" %s: failed\n", name));
// Apple2Activity.logMessage(Apple2Activity.LogType.WARN, TAG, String.format(" %s: failed\n", name));
while (egl.eglGetError() != EGL10.EGL_SUCCESS) ;
}
}

View File

@ -17,7 +17,7 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="?android:attr/listPreferredItemHeight"
android:minHeight="?attr/listPreferredItemHeight"
android:gravity="center_vertical"
android:paddingRight="?android:attr/scrollbarSize">

View File

@ -2,7 +2,6 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="@color/black"
android:baselineAligned="false"
android:orientation="vertical">
@ -16,6 +15,8 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginStart="40dp"
android:layout_marginLeft="40dp"
android:text="@string/diskA" />
<RadioButton
@ -37,6 +38,8 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginStart="40dp"
android:layout_marginLeft="40dp"
android:text="@string/disk_read_only" />
<RadioButton

View File

@ -17,7 +17,7 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="?android:attr/listPreferredItemHeight"
android:minHeight="?attr/listPreferredItemHeight"
android:gravity="center_vertical"
android:paddingRight="?android:attr/scrollbarSize">

View File

@ -3,7 +3,7 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="?android:attr/listPreferredItemHeight"
android:minHeight="?attr/listPreferredItemHeight"
android:gravity="center_vertical"
android:paddingRight="?android:attr/scrollbarSize">

View File

@ -2,7 +2,6 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="@color/black"
android:baselineAligned="false"
android:orientation="vertical">
@ -16,6 +15,8 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginStart="40dp"
android:layout_marginLeft="40dp"
android:text="@string/reboot" />
<RadioButton

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginLeft="0dp"
android:layout_marginStart="0dp"
android:layout_marginRight="0dp"
@ -20,4 +20,19 @@
android:layout_alignParentStart="true"
android:layout_marginLeft="0dp"
android:layout_marginStart="0dp" />
<TextView
android:id="@+id/axisCoords"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/seekBar"
android:layout_alignParentStart="true"
android:layout_alignParentLeft="true"
android:layout_marginStart="@dimen/preference_margin_left"
android:layout_marginLeft="@dimen/preference_margin_left"
android:layout_marginTop="@dimen/preference_margin_top"
android:background="@color/white"
android:text="X:255 Y:255"
android:textColor="@color/black" />
</RelativeLayout>

View File

@ -2,6 +2,7 @@
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_marginBottom="5dip"
@ -108,7 +109,7 @@
android:layout_weight="1" />
<ImageView
android:src="@android:drawable/ic_menu_save"
app:srcCompat="@android:drawable/ic_menu_save"
android:layout_gravity="center"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
@ -123,7 +124,7 @@
android:layout_alignStart="@id/disk_selection_newschool_chooser"
android:layout_below="@id/disk_selection_newschool_chooser"
android:gravity="center_vertical"
android:minHeight="?android:attr/listPreferredItemHeight"
android:minHeight="?attr/listPreferredItemHeight"
android:paddingLeft="0dp"
android:paddingStart="0dp"
android:paddingRight="?android:attr/scrollbarSize"
@ -171,7 +172,7 @@
android:layout_alignStart="@id/a2_newschool_driveA_layout"
android:layout_below="@id/a2_newschool_driveA_layout"
android:gravity="center_vertical"
android:minHeight="?android:attr/listPreferredItemHeight"
android:minHeight="?attr/listPreferredItemHeight"
android:paddingLeft="0dp"
android:paddingStart="0dp"
android:paddingRight="?android:attr/scrollbarSize"

View File

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/splashScreen"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@ -16,14 +17,14 @@
android:layout_height="fill_parent"
android:adjustViewBounds="true"
android:scaleType="centerCrop"
android:src="@drawable/apple_iie"
app:srcCompat="@drawable/apple_iie"
android:id="@+id/splashView" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/imageView"
android:src="@drawable/ic_launcher"
app:srcCompat="@drawable/ic_launcher"
android:layout_alignTop="@+id/startButton"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"

View File

@ -56,7 +56,6 @@
<string name="joystick_button_button_none">Keine</string>
<string name="joystick_button_tap_button">Drücke den Feuerknopf</string>
<string name="joystick_button_tap_button_summary">Ausgewählter Feuerknopf</string>
<string name="joystick_button_tapdelay_summary">Joystickknopf-Zeitverzögerung in Sek.</string>
<string name="joystick_button_swipe_up_button">Aufwärts wischen zum feuern</string>
<string name="joystick_button_swipe_up_button_summary">Feuerknopf zum feuern beim aufwärts wischen</string>
<string name="joystick_button_swipe_down_button">Abwärts wischen zum feuern</string>
@ -97,8 +96,6 @@
<string name="keyboard_visibility_inactive">Sichtbarkeit wenn deaktiviert</string>
<string name="keyboard_visibility_inactive_summary">Sichtbarkeit des Keyboard und Touch Menüs wenn deaktiviert</string>
<string name="keypad">Keypad Joystick</string>
<string name="keypad_calibrate">@string/joystick_calibrate</string>
<string name="keypad_calibrate_summary">@string/joystick_calibrate</string>
<string name="keypad_choose">Auswahl der Keypad Tasten…</string>
<string name="keypad_choose_summary">Auswahl der Achsen und Knopf Tasten</string>
<string name="keypad_choose_title">Achse &amp; Knöpfe</string>
@ -117,14 +114,13 @@
<string name="keypad_key_button_tap">antippen</string>
<string name="keypad_key_button_swipeup">Nach oben wischen</string>
<string name="keypad_key_button_swipedown">Nach unten wischen</string>
<string name="keypad_preset_crazy_seafox">Seafox Tasten ;-)</string>
<string name="keypad_preset_seafox">Seafox Tasten…</string>
<string name="keypad_preset_custom">Auswahl einer Anpassung…</string>
<string name="keypad_preset_arrows_space">&#8593;,&#8592;,&#8594;,&#8595;, tippe auf die Leerstaste</string>
<string name="keypad_preset_az_left_right_space">A,Z,&#8592;,&#8594;, tippe auf die Leertaste</string>
<string name="keypad_preset_ijkm_space">I,J,K,M, tippe auf die Leertaste</string>
<string name="keypad_preset_left_right_space">&#8592;,&#8594;, tippe auf die Leertaste</string>
<string name="keypad_preset_wadx_space">W,A,D,X, tippe auf die Leertaste</string>
<string name="keypad_repeat_summary">Tastenwiederholungsschwellwert in Sek.</string>
<string name="keypad_preset_arrows_space">(↑ ← → ↓), tippe auf die Leerstaste</string>
<string name="keypad_preset_az_left_right_space">(A Z ← →), tippe auf die Leerstaste</string>
<string name="keypad_preset_ijkm_space">(I J K M), tippe auf die Leertaste</string>
<string name="keypad_preset_left_right_space">(← →), tippe auf die Leertaste</string>
<string name="keypad_preset_wadx_space">(W A D X), tippe auf die Leertaste</string>
<string name="menu_disks">Lade Disk-Image…</string>
<string name="menu_disks_summary">Einlegen eines Disk ][ Image</string>
<string name="menu_settings">Emulator Einstellungen…</string>
@ -201,5 +197,25 @@
<string name="show_half_scanlines_summary">Renders pixel vertical divisions</string>
<string name="release_notes">Release notes</string>
<string name="release_notes_summary">View notes for this release</string>
<string name="keyboard_duotouch_enabled">Enable dual-touch</string>
<string name="keyboard_duotouch_enabled_summary">Support two-thumb input</string>
<string name="keypad_preset_qaz_left_right_space">(A Z ← →), Leertaste, Q</string>
<string name="keypad_tapdelay_summary">Keypad touch down delay in secs</string>
<string name="keypad_autorepeat_fast_summary">Allows immediate auto-repeat (avoids original 534-801 millis delay)</string>
<string name="keypad_autorepeat_fast">Keypad Immediate Autorepeat</string>
<string name="string_frames">Frames</string>
<string name="string_millis">Millis</string>
<string name="joystick_button_tapdelay_summary">Joystick touch down delay in video frames</string>
<string name="keypad_preset_robotron">Robotron Tasten…</string>
<string name="keypad_preset_loderunner">Lode Runner Tasten…</string>
<string name="keypad_preset_loderunner_toast">Start game with Ctrl-K to activate keyboard</string>
<string name="joystick_button_swipe_left_button">Swipe left fire</string>
<string name="joystick_button_swipe_right_button">Swipe right fire</string>
<string name="joystick_button_swipe_left_button_summary">Button to fire on swipe left</string>
<string name="joystick_button_swipe_right_button_summary">Button to fire on swipe right</string>
<string name="key_del">[DEL]</string>
<string name="key_tab">[TAB]</string>
<string name="preferences_email_logs">Email logs</string>
<string name="preferences_email_logs_summary">Email logs to developer…</string>
</resources>

View File

@ -56,7 +56,6 @@
<string name="joystick_button_button_none">Ninguno</string>
<string name="joystick_button_tap_button">Toque para disparar</string>
<string name="joystick_button_tap_button_summary">Botón para disparar sobre toque abajo</string>
<string name="joystick_button_tapdelay_summary">Toque retardo del joystick en segundos</string>
<string name="joystick_button_swipe_up_button">Pase hacia arriba</string>
<string name="joystick_button_swipe_up_button_summary">Botón para disparar sobre pase el dedo hacia arriba</string>
<string name="joystick_button_swipe_down_button">Pase hacia abajo</string>
@ -95,8 +94,6 @@
<string name="keyboard_visibility_inactive">Visibilidad cuando está inactivo</string>
<string name="keyboard_visibility_inactive_summary">Visibilidad del teclado y menú cuando está inactivo</string>
<string name="keypad">Joystick como teclado numérico</string>
<string name="keypad_calibrate">@string/joystick_calibrate</string>
<string name="keypad_calibrate_summary">@string/joystick_calibrate</string>
<string name="keypad_choose">Teclas del teclado numérico…</string>
<string name="keypad_choose_summary">Elegir las teclas del joystick del teclado numérico</string>
<string name="keypad_choose_title">Ejes y botones</string>
@ -115,14 +112,13 @@
<string name="keypad_key_button_tap">Toque</string>
<string name="keypad_key_button_swipeup">Desliza el dedo hacia arriba</string>
<string name="keypad_key_button_swipedown"> Desliza el dedo hacia abajo</string>
<string name="keypad_preset_crazy_seafox">Seafox keys ;-)</string>
<string name="keypad_preset_seafox">Tecla para Seafox…</string>
<string name="keypad_preset_custom">Elija personalizado…</string>
<string name="keypad_preset_arrows_space">&#8593;,&#8592;,&#8594;,&#8595;, pulse espaciadora</string>
<string name="keypad_preset_az_left_right_space">A,Z,&#8592;,&#8594;, pulse espaciadora</string>
<string name="keypad_preset_ijkm_space">I,J,K,M, pulse espaciadora</string>
<string name="keypad_preset_left_right_space">&#8592;,&#8594;, pulse espaciadora</string>
<string name="keypad_preset_wadx_space">W,A,D,X, pulse espaciadora</string>
<string name="keypad_repeat_summary">Umbral de repetición de teclas en segundos</string>
<string name="keypad_preset_arrows_space">(↑ ← → ↓), pulse barra espaciadora</string>
<string name="keypad_preset_az_left_right_space">(A Z ← →), pulse barra espaciadora</string>
<string name="keypad_preset_ijkm_space">(I J K M), pulse barra espaciadora</string>
<string name="keypad_preset_left_right_space">(← →), pulse barra espaciadora</string>
<string name="keypad_preset_wadx_space">(W A D X), pulse barra espaciadora</string>
<string name="menu_disks">Insertar imagen de disco…</string>
<string name="menu_disks_summary">Insertar imagen de "Disk ]["</string>
<string name="menu_settings">Configuración del emulador…</string>
@ -201,5 +197,25 @@
<string name="show_half_scanlines_summary">Renders pixel vertical divisions</string>
<string name="release_notes">Release notes</string>
<string name="release_notes_summary">View notes for this release</string>
<string name="keyboard_duotouch_enabled">Enable dual-touch</string>
<string name="keyboard_duotouch_enabled_summary">Support two-thumb input</string>
<string name="keypad_preset_qaz_left_right_space">(A Z ← →), barra espaciadora, Q</string>
<string name="keypad_tapdelay_summary">Keypad touch down delay in secs</string>
<string name="keypad_autorepeat_fast_summary">Allows immediate auto-repeat (avoids original 534-801 millis delay)</string>
<string name="keypad_autorepeat_fast">Keypad Immediate Autorepeat</string>
<string name="string_frames">Frames</string>
<string name="string_millis">Millis</string>
<string name="joystick_button_tapdelay_summary">Joystick touch down delay in video frames</string>
<string name="keypad_preset_robotron">Tecla para Robotron…</string>
<string name="keypad_preset_loderunner">Lode Runner keys…</string>
<string name="keypad_preset_loderunner_toast">Start game with Ctrl-K to activate keyboard</string>
<string name="joystick_button_swipe_left_button">Swipe left fire</string>
<string name="joystick_button_swipe_right_button">Swipe right fire</string>
<string name="joystick_button_swipe_left_button_summary">Button to fire on swipe left</string>
<string name="joystick_button_swipe_right_button_summary">Button to fire on swipe right</string>
<string name="key_del">[DEL]</string>
<string name="key_tab">[TAB]</string>
<string name="preferences_email_logs">Email logs</string>
<string name="preferences_email_logs_summary">Email logs to developer…</string>
</resources>

View File

@ -56,7 +56,6 @@
<string name="joystick_button_button_none">Rien</string>
<string name="joystick_button_tap_button">Sélectionner l\'action</string>
<string name="joystick_button_tap_button_summary">Bouton à activer lors d\'une pression vers le bas</string>
<string name="joystick_button_tapdelay_summary">Délai de pression du bouton Joystick en secondes</string>
<string name="joystick_button_swipe_up_button">Lancement du swipe up</string>
<string name="joystick_button_swipe_up_button_summary">Bouton à lancer sur swipe up</string>
<string name="joystick_button_swipe_down_button">Lancement du swipe down</string>
@ -95,8 +94,6 @@
<string name="keyboard_visibility_inactive">Visibilité quand inactif</string>
<string name="keyboard_visibility_inactive_summary">Clavier et menu tactile visible quand inactif</string>
<string name="keypad">Keypad Joystick</string>
<string name="keypad_calibrate">@string/joystick_calibrate</string>
<string name="keypad_calibrate_summary">@string/joystick_calibrate</string>
<string name="keypad_choose">Sélection des touches du keypad…</string>
<string name="keypad_choose_summary">Sélection des touches pour les axes et boutons</string>
<string name="keypad_choose_title">Axis &amp; boutons</string>
@ -115,14 +112,13 @@
<string name="keypad_key_button_tap">Presser</string>
<string name="keypad_key_button_swipeup">Slider vers le haut</string>
<string name="keypad_key_button_swipedown">Slider vers le bas</string>
<string name="keypad_preset_crazy_seafox">Touches Seafox ;-)</string>
<string name="keypad_preset_seafox">Touches Seafox…</string>
<string name="keypad_preset_custom">Choisir customisation…</string>
<string name="keypad_preset_arrows_space">&#8593;,&#8592;,&#8594;,&#8595;, pressez barre d\'espace</string>
<string name="keypad_preset_az_left_right_space">A,Z,&#8592;,&#8594;, pressez barre d\'espace</string>
<string name="keypad_preset_ijkm_space">I,J,K,M, pressez barre d\'espace</string>
<string name="keypad_preset_left_right_space">&#8592;,&#8594;, pressez barre d\'espace</string>
<string name="keypad_preset_wadx_space">W,A,D,X, pressez barre d\'espace</string>
<string name="keypad_repeat_summary">Répétition des touches en secs</string>
<string name="keypad_preset_arrows_space">(↑ ← → ↓), pressez barre d\'espace</string>
<string name="keypad_preset_az_left_right_space">(A Z ← →), pressez barre d\'espace</string>
<string name="keypad_preset_ijkm_space">(I J K M), pressez barre d\'espace</string>
<string name="keypad_preset_left_right_space">(← →), pressez barre d\'espace</string>
<string name="keypad_preset_wadx_space">(W A D X), pressez barre d\'espace</string>
<string name="menu_disks">Chargement de l\'image disque…</string>
<string name="menu_disks_summary">Insérer un fichier image (disque) ][</string>
<string name="menu_settings">Paramètres de l\'émulateur…</string>
@ -201,5 +197,25 @@
<string name="show_half_scanlines_summary">Renders pixel vertical divisions</string>
<string name="release_notes">Release notes</string>
<string name="release_notes_summary">View notes for this release</string>
<string name="keyboard_duotouch_enabled">Enable dual-touch</string>
<string name="keyboard_duotouch_enabled_summary">Support two-thumb input</string>
<string name="keypad_preset_qaz_left_right_space">(A Z ← →) barre d\'espace, Q</string>
<string name="keypad_tapdelay_summary">Keypad touch down delay in secs</string>
<string name="keypad_autorepeat_fast_summary">Allows immediate auto-repeat (avoids original 534-801 millis delay)</string>
<string name="keypad_autorepeat_fast">Keypad Immediate Autorepeat</string>
<string name="string_frames">Frames</string>
<string name="string_millis">Millis</string>
<string name="joystick_button_tapdelay_summary">Joystick touch down delay in video frames</string>
<string name="keypad_preset_robotron">Touches Robotron…</string>
<string name="keypad_preset_loderunner">Touches Lode Runner…</string>
<string name="keypad_preset_loderunner_toast">Start game with Ctrl-K to activate keyboard</string>
<string name="joystick_button_swipe_left_button">Swipe left fire</string>
<string name="joystick_button_swipe_right_button">Swipe right fire</string>
<string name="joystick_button_swipe_left_button_summary">Button to fire on swipe left</string>
<string name="joystick_button_swipe_right_button_summary">Button to fire on swipe right</string>
<string name="key_del">[DEL]</string>
<string name="key_tab">[TAB]</string>
<string name="preferences_email_logs">Email logs</string>
<string name="preferences_email_logs_summary">Email logs to developer…</string>
</resources>

View File

@ -60,7 +60,7 @@
<string name="joystick_button_button_none">None</string>
<string name="joystick_button_tap_button">Tap fire</string>
<string name="joystick_button_tap_button_summary">Button to fire on tap down</string>
<string name="joystick_button_tapdelay_summary">Joystick button tap delay in secs</string>
<string name="joystick_button_tapdelay_summary">Joystick touch down delay in video frames</string>
<string name="joystick_button_swipe_up_button">Swipe up fire</string>
<string name="joystick_button_swipe_up_button_summary">Button to fire on swipe up</string>
<string name="joystick_button_swipe_down_button">Swipe down fire</string>
@ -68,7 +68,7 @@
<string name="joystick_button_threshold_summary">Joystick/keypad button switch threshold in pts (max: &#8531; screen height)</string>
<string name="joystick_calibrate">Calibrate…</string>
<string name="joystick_calibrate_summary">Configure and test current settings</string>
<string name="joystick_configure">Configure joystick…</string>
<string name="joystick_configure">Configure touch joystick…</string>
<string name="joystick_configure_summary">Axis touch, buttons, etc</string>
<string name="joystick_axisleft">Joystick/keypad axis on left</string>
<string name="joystick_axisleft_summary">Joystick/keypad axis on left (buttons on right)</string>
@ -92,7 +92,7 @@
<string name="keyboard_choose_alt_summary">Choose alternative customized layout</string>
<string name="keyboard_click_enabled">Enable key click</string>
<string name="keyboard_click_enabled_summary">Enables key click sound if available</string>
<string name="keyboard_configure">Configure keyboard…</string>
<string name="keyboard_configure">Configure touch keyboard…</string>
<string name="keyboard_configure_summary">Transparency, lowercase, custom keys</string>
<string name="keyboard_glyph_scale">Keyboard glyphs scaled 2x</string>
<string name="keyboard_glyph_scale_summary">(Makes keyboard appear less pixelated on large screens)</string>
@ -103,13 +103,11 @@
<string name="keyboard_visibility_inactive">Visibility when inactive</string>
<string name="keyboard_visibility_inactive_summary">Keyboard and touch menu visibility when inactive</string>
<string name="keypad">Keypad Joystick</string>
<string name="keypad_calibrate">@string/joystick_calibrate</string>
<string name="keypad_calibrate_summary">@string/joystick_calibrate</string>
<string name="keypad_choose">Choose keypad keys…</string>
<string name="keypad_choose_summary">Choose axis and button keys</string>
<string name="keypad_choose_title">Axis &amp; buttons</string>
<string name="keypad_choose_current">Choose XXX Key: </string>
<string name="keypad_configure">Configure keypad joystick…</string>
<string name="keypad_configure">Configure touch keypad joystick…</string>
<string name="keypad_configure_summary">@string/joystick_configure_summary</string>
<string name="keypad_key_axis_c">Center</string>
<string name="keypad_key_axis_dn">Down</string>
@ -123,14 +121,14 @@
<string name="keypad_key_button_tap">Tap</string>
<string name="keypad_key_button_swipeup">Swipe Up</string>
<string name="keypad_key_button_swipedown">Swipe Down</string>
<string name="keypad_preset_crazy_seafox">Seafox keys ;-)</string>
<string name="keypad_preset_seafox">Seafox keys…</string>
<string name="keypad_preset_custom">Choose custom…</string>
<string name="keypad_preset_arrows_space">&#8593;,&#8592;,&#8594;,&#8595;, tap spacebar</string>
<string name="keypad_preset_az_left_right_space">A,Z,&#8592;,&#8594;, tap spacebar</string>
<string name="keypad_preset_ijkm_space">I,J,K,M, tap spacebar</string>
<string name="keypad_preset_left_right_space">&#8592;,&#8594;, tap spacebar</string>
<string name="keypad_preset_wadx_space">W,A,D,X, tap spacebar</string>
<string name="keypad_repeat_summary">Key repeat threshold in secs</string>
<string name="keypad_preset_arrows_space">(↑ ← → ↓), tap spacebar</string>
<string name="keypad_preset_az_left_right_space">(A Z ← →), tap spacebar</string>
<string name="keypad_preset_ijkm_space">(I J K M), tap spacebar</string>
<string name="keypad_preset_left_right_space">(← →), tap spacebar</string>
<string name="keypad_preset_wadx_space">(W A D X), tap spacebar</string>
<string name="keypad_tapdelay_summary">Keypad touch down delay in secs</string>
<string name="menu_disks">Load image or state file…</string>
<string name="menu_disks_summary">Insert Disk ][ image or state file</string>
<string name="menu_settings">Emulator settings…</string>
@ -201,5 +199,23 @@
<string name="show_half_scanlines_summary">Renders pixel vertical divisions</string>
<string name="release_notes">Release notes</string>
<string name="release_notes_summary">View notes for this release</string>
<string name="keyboard_duotouch_enabled">Enable dual-touch</string>
<string name="keyboard_duotouch_enabled_summary">Support two-thumb input</string>
<string name="keypad_preset_qaz_left_right_space">(A Z ← →), tap spacebar, Q up</string>
<string name="keypad_autorepeat_fast">Keypad Immediate Autorepeat</string>
<string name="keypad_autorepeat_fast_summary">Allows immediate auto-repeat (avoids original 534-801 millis delay)</string>
<string name="string_frames">Frames</string>
<string name="string_millis">Millis</string>
<string name="keypad_preset_robotron">Robotron keys…</string>
<string name="keypad_preset_loderunner">Lode Runner keys…</string>
<string name="keypad_preset_loderunner_toast">Start game with Ctrl-K to activate keyboard</string>
<string name="joystick_button_swipe_left_button">Swipe left fire</string>
<string name="joystick_button_swipe_right_button">Swipe right fire</string>
<string name="joystick_button_swipe_right_button_summary">Button to fire on swipe right</string>
<string name="joystick_button_swipe_left_button_summary">Button to fire on swipe left</string>
<string name="key_del">[DEL]</string>
<string name="key_tab">[TAB]</string>
<string name="preferences_email_logs">Email logs</string>
<string name="preferences_email_logs_summary">Email logs to developer…</string>
</resources>

View File

@ -1,8 +1,10 @@
<resources>
<!-- Base application theme. -->
<!-- <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar"> ... this freaks-out CLI builds -->
<!-- Customize your theme here. -->
<!-- </style> -->
<style name="Theme.AppCompat.NoActionBar.FullScreen" parent="@style/Theme.AppCompat.NoActionBar">
<item name="android:windowNoTitle">true</item>
<item name="android:windowActionBar">false</item>
<item name="android:windowFullscreen">true</item>
<item name="android:windowContentOverlay">@null</item>
</style>
</resources>

View File

@ -0,0 +1,24 @@
[
"Alt keyboard optimized for 2400AD",
{
"_comment" : "hex code for special glyphs",
"_AA" : "b5",
"_ESC" : "bc",
"_SP" : "b1",
"_RET" : "8d",
"_LT" : "88",
"_RT" : "95",
"_UP" : "8b",
"_DN" : "8a"
},
[ "", "1", "2", "3", "4", "5", "6", "7", "8", "" ],
[ "_AA", "", "", "", "", "", "", "", "", "Y" ],
[ "S", "", "", "", "", "", "", "", "", "N" ],
[ "R", "", "", "", "", "", "", "", "", "U" ],
[ "W" , "", "", "", "", "", "", "", "", "D" ],
[ "", "_RET", "", "", "", "", "", "", "", "F" ],
[ "_LT", "_SP", "_RT", "", "", "", "", "", "", "L" ],
[ "", "_DN", "", "G", "B", "O", "C", "T", "P","A" ]
]

View File

@ -0,0 +1,24 @@
[
"Alt keyboard optimized for Deathlord",
{
"_comment" : "hex code for special glyphs",
"_AA" : "b5",
"_ESC" : "bc",
"_RET" : "8d",
"_UP" : "8b",
"_LT" : "88",
"_RT" : "95",
"_DN" : "8a",
"_SP" : "b1"
},
[ "", "", "", "1", "2", "3", "4" , "5" , "6", "" ],
[ "Z", "", "", "", "", "&", "'", "(", ")", "$" ],
["_AA", "", "", "", "", "", "P", "", "^", "#"],
[ "N", "", "", "", "", "", "T", "", "U", "Y"],
[ "?", "", "", "", "", "", "_ESC", "", "_SP", "" ],
[ "?", "", "", "", "", "", "", "I", "", "_UP" ],
[ "C", "", "S", "R", "G", "V", "J", "", "K", "_RET"],
[ "A", "", "O", "E", "F", "B", "", "M", "", "_DN" ]
]

View File

@ -0,0 +1,22 @@
[
"Alt keyboard optimized for Windwalker",
{
"_comment" : "hex code for special glyphs",
"_AA" : "b5",
"_ESC" : "bc",
"_SP" : "b1",
"_RET" : "8d",
"_UP" : "8b",
"_DN" : "8a"
},
[ "", "", "", "", "", "", "", "", "", "" ],
[ "", "", "", "", "", "", "", "", "", "" ],
[ "_AA", "", "", "", "", "", "", "", "", "" ],
[ "_ESC", "", "", "", "", "", "", "", "", "_RET" ],
[ "_UP", "", "", "", "", "", "", "", "", "" ],
[ "_DN", "", "", "", "", "", "", "I", "O", "P"],
[ "Y", "", "A", "S", "", "", "", "K", "L", ";"],
[ "N", "", "Z", "X", "", "", "", ",", ".", "/"]
]

View File

@ -1,18 +1,30 @@
Apple2ix (a2ix) 2.0.0-Android Release Notes
Apple2ix (A2IX) 2.1.0-RC2 for Android
TL;DR : new video mode settings!
The major change here is that I now provide full 64bit native builds for arm64-v8a and x86_64 devices. (This is a new G00G requirement for publishing on the Play Store). It frankly isn't something that I would have wanted to foist upon you, dear user! But they are forcing my hand, so ... c'est la vie!
CHANGES:
Anecdotally, I have heard about certain devices that claim to be arm64-v8a, but which DO NOT run 64bit code efficiently. These devices would have been better off continuing to run the armeabi-v7a build of A2IX. But there's nothing I can do about this, since the Play Store serves the specific build it thinks best. So if you are one of the unlucky owners of such a device and A2IX seems horrendously sluggish, (and if you're an adventurous power-user), you could side-load the armeabi-v7a semi-official release from the GitHub project page ;)
- Implemented a more conformant video scanner. This improves emulation fidelity for programs that implement custom video modes (e.g., custom split screen between text & graphics).
MENU INTERFACE:
- New NTSC video display modes including "Color monitor", "Monochrome TV", and "Color TV" modes. Thanks to the AppleWin project and Bill Simms for these modes. Also support "Green screen" monochrome video.
- Migrated menu system to newer-ish Dark Holo theme
- Moved some preferences around a bit for clarity
- New preference to enable/disable half-scanline video effects.
TOUCH KEYBOARD:
- EXPERIMENTAL: New preference to enable/disable fast disk image loading. This may cause audio glitches or other instability. Use at your own risk!
- New preference for dual-thumb touch keyboard handling
- More alternate keyboard presets for old-school RPGs
- Tapping Open/Closed-Apple keys now generate joystick button events
GENERAL INFO:
TOUCH JOYSTICKS:
- More information about how to use Apple2ix for Android : https://deadc0de.org/apple2ix/android
- Keypad Joystick: Improved emulation fidelity for the keyboard auto-repeat circuitry, tied to the emulated video refresh. (In plain English: improved twitch-response ;)
- Keypad Joystick: Full left and right side rosette key configurations (18 total configurable keys)
- Keypad Joystick: More presets for popular games (L0de Runner, R0b0tr0n 2084, ...)
- Keypad+Joystick: Improved calibration mode showing axis/key values
- Keypad+Joystick: Button/key switch threshold now configurable down to zero minimum (improves twitch-reponse)
- Joystick: Can now configure button to fire on left/right swipe (instead of just tap and swipe up/down)
M0AR INFO:
- Apple2ix for Android home page : https://deadc0de.org/apple2ix/android
- Play Store beta-build opt-in URL: https://play.google.com/apps/testing/org.deadc0de.apple2ix.basic

View File

View File

@ -6,7 +6,7 @@ buildscript {
google()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.2.1'
classpath 'com.android.tools.build:gradle:3.5.2'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files

View File

@ -17,4 +17,5 @@
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
android.useDeprecatedNdk=true
android.enableJetifier=true
android.useAndroidX=true

View File

@ -1,6 +1,6 @@
#Mon Nov 12 10:09:47 PST 2018
#Sun Oct 27 16:51:42 PDT 2019
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip

View File

@ -1,4 +1,4 @@
APP_ABI := armeabi armeabi-v7a x86
APP_ABI := armeabi-v7a arm64-v8a x86 x86_64
# Do not change APP_PLATFORM if we care about Gingerbread (2.3.3) devices! We must compile against android-10,
# otherwise we may encounter runtime load-library errors from symbols that should have been inlined against older

View File

@ -243,16 +243,20 @@ void android_keycode_to_emulator(int keyCode, int metaState, bool pressed) {
}
} while (0);
if (key < 0) {
return;
}
//LOG("keyCode:%08x -> key:%02x ('%c') metaState:%08x", keyCode, key, key, metaState);
if (isASCII && _is_ctrl(metaState)) {
key = c_keys_ascii_to_scancode(key);
c_keys_handle_input(key, true, false);
key = keys_ascii2Scancode(key);
keys_handleInput(key, /*is_pressed:*/true, /*is_ascii:*/false);
isASCII = false;
pressed = false;
}
assert(key < 0x80);
c_keys_handle_input(key, pressed, isASCII);
keys_handleInput(key, pressed, isASCII);
}

View File

@ -148,7 +148,7 @@ if test "x$do_build" = "x1" -o "x$do_release" = "x1" ; then
# Symbolicate and move symbols file into location to be deployed on device
SYMFILE=libapple2ix.so.sym
ARCHES_TO_SYMBOLICATE='armeabi armeabi-v7a x86'
ARCHES_TO_SYMBOLICATE='armeabi-v7a arm64-v8a x86 x86_64'
for arch in $ARCHES_TO_SYMBOLICATE ; do
SYMDIR=../assets/symbols/$arch/libapple2ix.so

View File

@ -136,13 +136,17 @@ void Java_org_deadc0de_apple2ix_Apple2CrashHandler_nativeProcessCrash(JNIEnv *en
break;
}
if (android_armArchV7A) {
if (android_arm64Arch) {
ASPRINTF(&symbolsPath, "%s/symbols/arm64-v8a", data_dir);
} else if (android_armArchV7A || android_armArch) {
ASPRINTF(&symbolsPath, "%s/symbols/armeabi-v7a", data_dir);
} else if (android_x86_64) {
ASPRINTF(&symbolsPath, "%s/symbols/x86_64", data_dir);
} else if (android_x86) {
ASPRINTF(&symbolsPath, "%s/symbols/x86", data_dir);
} else /*if (android_armArch)*/ {
ASPRINTF(&symbolsPath, "%s/symbols/armeabi", data_dir);
} /*else { moar archs ... } */
} else {
LOG("unknown symbols architecture!");
}
bool success = crashHandler->processCrash(crashPath, symbolsPath, outputFILE);
if (!success) {

View File

@ -123,8 +123,9 @@ static void discover_cpu_family(void) {
AndroidCpuFamily family = android_getCpuFamily();
uint64_t features = android_getCpuFeatures();
if (family == ANDROID_CPU_FAMILY_X86) {
android_x86 = true;
if (family == ANDROID_CPU_FAMILY_X86 || family == ANDROID_CPU_FAMILY_X86_64) {
android_x86 = (family == ANDROID_CPU_FAMILY_X86);
android_x86_64 = (family == ANDROID_CPU_FAMILY_X86_64);
if (features & ANDROID_CPU_X86_FEATURE_SSSE3) {
LOG("nANDROID_CPU_X86_FEATURE_SSSE3");
android_x86SSSE3Enabled = true;
@ -155,9 +156,7 @@ static void discover_cpu_family(void) {
LOG("ANDROID_CPU_ARM_FEATURE_LDREX_STREX");
}
} else if (family == ANDROID_CPU_FAMILY_ARM64) {
#warning FIXME TODO ...
//android_arm64Arch = true;
android_armArchV7A = true;
android_arm64Arch = true;
}
}
@ -292,7 +291,7 @@ void Java_org_deadc0de_apple2ix_Apple2View_nativeRender(JNIEnv *env, jclass cls)
void Java_org_deadc0de_apple2ix_Apple2Activity_nativeReboot(JNIEnv *env, jclass cls, jint resetState) {
LOG("...");
if (resetState) {
// joystick button settings should be balanced by c_joystick_reset() triggered on CPU thread
// joystick button settings should be balanced by joystick_reset() triggered on CPU thread
if (resetState == 1) {
run_args.joy_button0 = 0xff;
run_args.joy_button1 = 0x0;
@ -388,12 +387,12 @@ jstring Java_org_deadc0de_apple2ix_Apple2DisksMenu_nativeChooseDisk(JNIEnv *env,
if (!json_mapParseLongValue(jsonData, "fd", &fd, 10)) {
TEMP_FAILURE_RETRY(fd = open(path, readOnly ? O_RDONLY : O_RDWR));
if (fd == -1) {
LOG("OOPS could not open disk path : %s", path);
LOG("OOPS could not open disk path : %s (%s)", path, strerror(errno));
}
} else {
fd = dup(fd);
TEMP_FAILURE_RETRY(fd = dup(fd));
if (fd == -1) {
LOG("OOPS could not dup file descriptor!");
LOG("OOPS could not dup file descriptor! (%s)", strerror(errno));
}
}
@ -442,7 +441,7 @@ void Java_org_deadc0de_apple2ix_Apple2DisksMenu_nativeEjectDisk(JNIEnv *env, jcl
disk6_eject(driveA ? 0 : 1);
}
static int _openFdFromJson(OUTPARM int *fdOut, JSON_ref jsonData, const char * const fdKey, const char * const pathKey, int flags, int mode) {
static void _openFdFromJson(OUTPARM int *fdOut, JSON_ref jsonData, const char * const fdKey, const char * const pathKey, int flags, int mode) {
long fd = -1;
char *path = NULL;
@ -463,12 +462,12 @@ static int _openFdFromJson(OUTPARM int *fdOut, JSON_ref jsonData, const char * c
TEMP_FAILURE_RETRY(fd = open(path, flags, mode));
}
if (fd == -1) {
LOG("OOPS could not open state file path %s", path);
LOG("OOPS could not open state file path %s (%s)", path, strerror(errno));
}
} else {
fd = dup(fd);
TEMP_FAILURE_RETRY(fd = dup(fd));
if (fd == -1) {
LOG("OOPS could not dup file descriptor!");
LOG("OOPS could not dup file descriptor! (%s)", strerror(errno));
}
}
} while (0);
@ -615,3 +614,50 @@ void Java_org_deadc0de_apple2ix_Apple2Preferences_nativePrefsSync(JNIEnv *env, j
}
}
jlong Java_org_deadc0de_apple2ix_Apple2JoystickCalibration_nativePollJoystick(JNIEnv *env, jclass cls) {
jlong cxy = 0;
long c = keys_consumeLastKey();
cxy |= (c << 16);
cxy |= ((joy_x & 0xFF) << 8);
cxy |= ((joy_y & 0xFF) << 0);
// last_ascii | last_scancode | joy_x | joy_y
return cxy;
}
void Java_org_deadc0de_apple2ix_Apple2Activity_nativeLogMessage(JNIEnv *env, jclass cls, jstring jJsonString) {
#if TESTING
return NULL;
#endif
const char *jsonString = (*env)->GetStringUTFChars(env, jJsonString, NULL);
JSON_ref jsonData = NULL;
bool ret = json_createFromString(jsonString, &jsonData);
assert(ret > 0);
(*env)->ReleaseStringUTFChars(env, jJsonString, jsonString); jsonString = NULL;
long type = LOG_TYPE_INFO;
json_mapParseLongValue(jsonData, "type", &type, 10);
char *tag = NULL;
json_mapCopyStringValue(jsonData, "tag", &tag);
char *mesg = NULL;
json_mapCopyStringValue(jsonData, "mesg", &mesg);
log_taggedOutputString((log_type_t)type, tag, mesg);
if (tag) {
FREE(tag);
}
if (mesg) {
FREE(mesg);
}
json_destroy(&jsonData);
}

View File

@ -63,8 +63,8 @@ APPLE2_MAIN_SRC = \
$(APPLE2_SRC_PATH)/zlib-helpers.c \
$(APPLE2_SRC_PATH)/../externals/jsmn/jsmn.c
APPLE2_OPTIM_CFLAGS := -Os
APPLE2_BASE_CFLAGS := -DAPPLE2IX=1 -DINTERFACE_TOUCH=1 -DMOBILE_DEVICE=1 -DVIDEO_OPENGL=1 -std=gnu11 -fPIC $(APPLE2_OPTIM_CFLAGS) -I$(APPLE2_SRC_PATH)
APPLE2_OPTIM_CFLAGS := -O2 # match the same optimization level as BUILD_MODE=release for ndk-build
APPLE2_BASE_CFLAGS := -Wall -DAPPLE2IX=1 -DINTERFACE_TOUCH=1 -DMOBILE_DEVICE=1 -DVIDEO_OPENGL=1 -std=gnu11 -fPIC $(APPLE2_OPTIM_CFLAGS) -I$(APPLE2_SRC_PATH)
APPLE2_BASE_LDLIBS := -Wl,-z,text -Wl,-z,noexecstack -llog -landroid -lGLESv2 -lz -lOpenSLES -latomic
LOCAL_WHOLE_STATIC_LIBRARIES += cpufeatures

View File

@ -0,0 +1,516 @@
#
# Copyright (C) 2015 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
import atexit
import base64
import logging
import os
import re
import subprocess
class FindDeviceError(RuntimeError):
pass
class DeviceNotFoundError(FindDeviceError):
def __init__(self, serial):
self.serial = serial
super(DeviceNotFoundError, self).__init__(
'No device with serial {}'.format(serial))
class NoUniqueDeviceError(FindDeviceError):
def __init__(self):
super(NoUniqueDeviceError, self).__init__('No unique device')
class ShellError(RuntimeError):
def __init__(self, cmd, stdout, stderr, exit_code):
super(ShellError, self).__init__(
'`{0}` exited with code {1}'.format(cmd, exit_code))
self.cmd = cmd
self.stdout = stdout
self.stderr = stderr
self.exit_code = exit_code
def get_devices(adb_path='adb'):
with open(os.devnull, 'wb') as devnull:
subprocess.check_call([adb_path, 'start-server'], stdout=devnull,
stderr=devnull)
out = split_lines(subprocess.check_output([adb_path, 'devices']))
# The first line of `adb devices` just says "List of attached devices", so
# skip that.
devices = []
for line in out[1:]:
if not line.strip():
continue
if 'offline' in line:
continue
serial, _ = re.split(r'\s+', line, maxsplit=1)
devices.append(serial)
return devices
def _get_unique_device(product=None, adb_path='adb'):
devices = get_devices(adb_path=adb_path)
if len(devices) != 1:
raise NoUniqueDeviceError()
return AndroidDevice(devices[0], product, adb_path)
def _get_device_by_serial(serial, product=None, adb_path='adb'):
for device in get_devices(adb_path=adb_path):
if device == serial:
return AndroidDevice(serial, product, adb_path)
raise DeviceNotFoundError(serial)
def get_device(serial=None, product=None, adb_path='adb'):
"""Get a uniquely identified AndroidDevice if one is available.
Raises:
DeviceNotFoundError:
The serial specified by `serial` or $ANDROID_SERIAL is not
connected.
NoUniqueDeviceError:
Neither `serial` nor $ANDROID_SERIAL was set, and the number of
devices connected to the system is not 1. Having 0 connected
devices will also result in this error.
Returns:
An AndroidDevice associated with the first non-None identifier in the
following order of preference:
1) The `serial` argument.
2) The environment variable $ANDROID_SERIAL.
3) The single device connnected to the system.
"""
if serial is not None:
return _get_device_by_serial(serial, product, adb_path)
android_serial = os.getenv('ANDROID_SERIAL')
if android_serial is not None:
return _get_device_by_serial(android_serial, product, adb_path)
return _get_unique_device(product, adb_path=adb_path)
def _get_device_by_type(flag, adb_path):
with open(os.devnull, 'wb') as devnull:
subprocess.check_call([adb_path, 'start-server'], stdout=devnull,
stderr=devnull)
try:
serial = subprocess.check_output(
[adb_path, flag, 'get-serialno']).strip()
except subprocess.CalledProcessError:
raise RuntimeError('adb unexpectedly returned nonzero')
if serial == 'unknown':
raise NoUniqueDeviceError()
return _get_device_by_serial(serial, adb_path=adb_path)
def get_usb_device(adb_path='adb'):
"""Get the unique USB-connected AndroidDevice if it is available.
Raises:
NoUniqueDeviceError:
0 or multiple devices are connected via USB.
Returns:
An AndroidDevice associated with the unique USB-connected device.
"""
return _get_device_by_type('-d', adb_path=adb_path)
def get_emulator_device(adb_path='adb'):
"""Get the unique emulator AndroidDevice if it is available.
Raises:
NoUniqueDeviceError:
0 or multiple emulators are running.
Returns:
An AndroidDevice associated with the unique running emulator.
"""
return _get_device_by_type('-e', adb_path=adb_path)
# If necessary, modifies subprocess.check_output() or subprocess.Popen() args
# to run the subprocess via Windows PowerShell to work-around an issue in
# Python 2's subprocess class on Windows where it doesn't support Unicode.
def _get_subprocess_args(args):
# Only do this slow work-around if Unicode is in the cmd line on Windows.
# PowerShell takes 600-700ms to startup on a 2013-2014 machine, which is
# very slow.
if os.name != 'nt' or all(not isinstance(arg, unicode) for arg in args[0]):
return args
def escape_arg(arg):
# Escape for the parsing that the C Runtime does in Windows apps. In
# particular, this will take care of double-quotes.
arg = subprocess.list2cmdline([arg])
# Escape single-quote with another single-quote because we're about
# to...
arg = arg.replace(u"'", u"''")
# ...put the arg in a single-quoted string for PowerShell to parse.
arg = u"'" + arg + u"'"
return arg
# Escape command line args.
argv = map(escape_arg, args[0])
# Cause script errors (such as adb not found) to stop script immediately
# with an error.
ps_code = u'$ErrorActionPreference = "Stop"\r\n'
# Add current directory to the PATH var, to match cmd.exe/CreateProcess()
# behavior.
ps_code += u'$env:Path = ".;" + $env:Path\r\n'
# Precede by &, the PowerShell call operator, and separate args by space.
ps_code += u'& ' + u' '.join(argv)
# Make the PowerShell exit code the exit code of the subprocess.
ps_code += u'\r\nExit $LastExitCode'
# Encode as UTF-16LE (without Byte-Order-Mark) which Windows natively
# understands.
ps_code = ps_code.encode('utf-16le')
# Encode the PowerShell command as base64 and use the special
# -EncodedCommand option that base64 decodes. Base64 is just plain ASCII,
# so it should have no problem passing through Win32 CreateProcessA()
# (which python erroneously calls instead of CreateProcessW()).
return (['powershell.exe', '-NoProfile', '-NonInteractive',
'-EncodedCommand', base64.b64encode(ps_code)],) + args[1:]
# Call this instead of subprocess.check_output() to work-around issue in Python
# 2's subprocess class on Windows where it doesn't support Unicode.
def _subprocess_check_output(*args, **kwargs):
try:
return subprocess.check_output(*_get_subprocess_args(args), **kwargs)
except subprocess.CalledProcessError as e:
# Show real command line instead of the powershell.exe command line.
raise subprocess.CalledProcessError(e.returncode, args[0],
output=e.output)
# Call this instead of subprocess.Popen(). Like _subprocess_check_output().
def _subprocess_Popen(*args, **kwargs):
return subprocess.Popen(*_get_subprocess_args(args), **kwargs)
def split_lines(s):
"""Splits lines in a way that works even on Windows and old devices.
Windows will see \r\n instead of \n, old devices do the same, old devices
on Windows will see \r\r\n.
"""
# rstrip is used here to workaround a difference between splineslines and
# re.split:
# >>> 'foo\n'.splitlines()
# ['foo']
# >>> re.split(r'\n', 'foo\n')
# ['foo', '']
return re.split(r'[\r\n]+', s.rstrip())
def version(adb_path=None):
"""Get the version of adb (in terms of ADB_SERVER_VERSION)."""
adb_path = adb_path if adb_path is not None else ['adb']
version_output = subprocess.check_output(adb_path + ['version'])
pattern = r'^Android Debug Bridge version 1.0.(\d+)$'
result = re.match(pattern, version_output.splitlines()[0])
if not result:
return 0
return int(result.group(1))
class AndroidDevice(object):
# Delimiter string to indicate the start of the exit code.
_RETURN_CODE_DELIMITER = 'x'
# Follow any shell command with this string to get the exit
# status of a program since this isn't propagated by adb.
#
# The delimiter is needed because `printf 1; echo $?` would print
# "10", and we wouldn't be able to distinguish the exit code.
_RETURN_CODE_PROBE = [';', 'echo', '{0}$?'.format(_RETURN_CODE_DELIMITER)]
# Maximum search distance from the output end to find the delimiter.
# adb on Windows returns \r\n even if adbd returns \n. Some old devices
# seem to actually return \r\r\n.
_RETURN_CODE_SEARCH_LENGTH = len(
'{0}255\r\r\n'.format(_RETURN_CODE_DELIMITER))
def __init__(self, serial, product=None, adb_path='adb'):
self.serial = serial
self.product = product
self.adb_cmd = [adb_path]
if self.serial is not None:
self.adb_cmd.extend(['-s', serial])
if self.product is not None:
self.adb_cmd.extend(['-p', product])
self._linesep = None
self._features = None
@property
def linesep(self):
if self._linesep is None:
self._linesep = subprocess.check_output(self.adb_cmd +
['shell', 'echo'])
return self._linesep
@property
def features(self):
if self._features is None:
try:
self._features = split_lines(self._simple_call(['features']))
except subprocess.CalledProcessError:
self._features = []
return self._features
def has_shell_protocol(self):
return version(self.adb_cmd) >= 35 and 'shell_v2' in self.features
def _make_shell_cmd(self, user_cmd):
command = self.adb_cmd + ['shell'] + user_cmd
if not self.has_shell_protocol():
command += self._RETURN_CODE_PROBE
return command
def _parse_shell_output(self, out):
"""Finds the exit code string from shell output.
Args:
out: Shell output string.
Returns:
An (exit_code, output_string) tuple. The output string is
cleaned of any additional stuff we appended to find the
exit code.
Raises:
RuntimeError: Could not find the exit code in |out|.
"""
search_text = out
if len(search_text) > self._RETURN_CODE_SEARCH_LENGTH:
# We don't want to search over massive amounts of data when we know
# the part we want is right at the end.
search_text = search_text[-self._RETURN_CODE_SEARCH_LENGTH:]
partition = search_text.rpartition(self._RETURN_CODE_DELIMITER)
if partition[1] == '':
raise RuntimeError('Could not find exit status in shell output.')
result = int(partition[2])
# partition[0] won't contain the full text if search_text was
# truncated, pull from the original string instead.
out = out[:-len(partition[1]) - len(partition[2])]
return result, out
def _simple_call(self, cmd):
logging.info(' '.join(self.adb_cmd + cmd))
return _subprocess_check_output(
self.adb_cmd + cmd, stderr=subprocess.STDOUT)
def shell(self, cmd):
"""Calls `adb shell`
Args:
cmd: command to execute as a list of strings.
Returns:
A (stdout, stderr) tuple. Stderr may be combined into stdout
if the device doesn't support separate streams.
Raises:
ShellError: the exit code was non-zero.
"""
exit_code, stdout, stderr = self.shell_nocheck(cmd)
if exit_code != 0:
raise ShellError(cmd, stdout, stderr, exit_code)
return stdout, stderr
def shell_nocheck(self, cmd):
"""Calls `adb shell`
Args:
cmd: command to execute as a list of strings.
Returns:
An (exit_code, stdout, stderr) tuple. Stderr may be combined
into stdout if the device doesn't support separate streams.
"""
cmd = self._make_shell_cmd(cmd)
logging.info(' '.join(cmd))
p = _subprocess_Popen(
cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdout, stderr = p.communicate()
if self.has_shell_protocol():
exit_code = p.returncode
else:
exit_code, stdout = self._parse_shell_output(stdout)
return exit_code, stdout, stderr
def shell_popen(self, cmd, kill_atexit=True, preexec_fn=None,
creationflags=0, **kwargs):
"""Calls `adb shell` and returns a handle to the adb process.
This function provides direct access to the subprocess used to run the
command, without special return code handling. Users that need the
return value must retrieve it themselves.
Args:
cmd: Array of command arguments to execute.
kill_atexit: Whether to kill the process upon exiting.
preexec_fn: Argument forwarded to subprocess.Popen.
creationflags: Argument forwarded to subprocess.Popen.
**kwargs: Arguments forwarded to subprocess.Popen.
Returns:
subprocess.Popen handle to the adb shell instance
"""
command = self.adb_cmd + ['shell'] + cmd
# Make sure a ctrl-c in the parent script doesn't kill gdbserver.
if os.name == 'nt':
creationflags |= subprocess.CREATE_NEW_PROCESS_GROUP
else:
if preexec_fn is None:
preexec_fn = os.setpgrp
elif preexec_fn is not os.setpgrp:
fn = preexec_fn
def _wrapper():
fn()
os.setpgrp()
preexec_fn = _wrapper
p = _subprocess_Popen(command, creationflags=creationflags,
preexec_fn=preexec_fn, **kwargs)
if kill_atexit:
atexit.register(p.kill)
return p
def install(self, filename, replace=False):
cmd = ['install']
if replace:
cmd.append('-r')
cmd.append(filename)
return self._simple_call(cmd)
def push(self, local, remote):
return self._simple_call(['push', local, remote])
def pull(self, remote, local):
return self._simple_call(['pull', remote, local])
def sync(self, directory=None):
cmd = ['sync']
if directory is not None:
cmd.append(directory)
return self._simple_call(cmd)
def tcpip(self, port):
return self._simple_call(['tcpip', port])
def usb(self):
return self._simple_call(['usb'])
def reboot(self):
return self._simple_call(['reboot'])
def remount(self):
return self._simple_call(['remount'])
def root(self):
return self._simple_call(['root'])
def unroot(self):
return self._simple_call(['unroot'])
def connect(self, host):
return self._simple_call(['connect', host])
def disconnect(self, host):
return self._simple_call(['disconnect', host])
def forward(self, local, remote):
return self._simple_call(['forward', local, remote])
def forward_list(self):
return self._simple_call(['forward', '--list'])
def forward_no_rebind(self, local, remote):
return self._simple_call(['forward', '--no-rebind', local, remote])
def forward_remove(self, local):
return self._simple_call(['forward', '--remove', local])
def forward_remove_all(self):
return self._simple_call(['forward', '--remove-all'])
def reverse(self, remote, local):
return self._simple_call(['reverse', remote, local])
def reverse_list(self):
return self._simple_call(['reverse', '--list'])
def reverse_no_rebind(self, local, remote):
return self._simple_call(['reverse', '--no-rebind', local, remote])
def reverse_remove_all(self):
return self._simple_call(['reverse', '--remove-all'])
def reverse_remove(self, remote):
return self._simple_call(['reverse', '--remove', remote])
def wait(self):
return self._simple_call(['wait-for-device'])
def get_props(self):
result = {}
output, _ = self.shell(['getprop'])
output = split_lines(output)
pattern = re.compile(r'^\[([^]]+)\]: \[(.*)\]')
for line in output:
match = pattern.match(line)
if match is None:
# apple2ix NOTE : don't freak out here ...
#raise RuntimeError('invalid getprop line: "{}"'.format(line))
continue
key = match.group(1)
value = match.group(2)
if key in result:
raise RuntimeError('duplicate getprop key: "{}"'.format(key))
result[key] = value
return result
def get_prop(self, prop_name):
output = split_lines(self.shell(['getprop', prop_name])[0])
if len(output) != 1:
raise RuntimeError('Too many lines in getprop output:\n' +
'\n'.join(output))
value = output[0]
if not value.strip():
return None
return value
def set_prop(self, prop_name, value):
self.shell(['setprop', prop_name, value])

View File

@ -1,856 +0,0 @@
#!/bin/sh
#
# Copyright (C) 2010 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# This wrapper script is used to launch a native debugging session
# on a given NDK application. The application must be debuggable, i.e.
# its android:debuggable attribute must be set to 'true' in the
# <application> element of its manifest.
#
# See docs/NDK-GDB.TXT for usage description. Essentially, you just
# need to launch ndk-gdb from your application project directory
# after doing ndk-build && ant install && <start-application-on-device>
#
PROGDIR=`dirname $0`
PROGDIR=`cd $PROGDIR && pwd -P`
#set -x
# Check if absolute NDK path contain space
#
case $PROGDIR in
*\ *) echo "ERROR: NDK path cannot contain space"
exit 1
;;
esac
NDK_BUILDTOOLS_PATH=$PROGDIR/build/tools
. $PROGDIR/build/tools/prebuilt-common.sh
. $PROGDIR/build/tools/ndk-common.sh
force_32bit_binaries
# Find if a given shell program is available.
# We need to take care of the fact that the 'which <foo>' command
# may return either an empty string (Linux) or something like
# "no <foo> in ..." (Darwin). Also, we need to redirect stderr
# to /dev/null for Cygwin
#
# $1: program name
# Out: program path, or empty string
# Return: 0 on success, != 0 on error
#
find_program ()
{
local PROG RET
PROG=$(which "$1" 2>/dev/null)
RET=$?
if [ $RET != 0 ]; then
PROG=
fi
echo "$PROG"
return $RET
}
quote_spaces ()
{
echo "$@" | sed -e 's! !\ !g'
}
# If ADB_CMD is not defined, try to find a program named 'adb'
# in our path.
ADB_CMD=${ADB_CMD:-$(find_program adb)}
ADB_FLAGS=${ADB_FLAGS:-}
DEVICE_SERIAL=
JDB_CMD=${JDB_CMD:-$(find_program jdb)}
AWK_CMD=${AWK_CMD:-$(find_program awk)}
DEBUG_PORT=5039
JDB_PORT=65534
UNKNOWN_ABI=$(find_ndk_unknown_archs)
# Delay in seconds between launching the activity and attaching gdbserver on it.
# This is needed because there is no way to know when the activity has really
# started, and sometimes this takes a few seconds.
DELAY=2
PARAMETERS=
OPTION_HELP=no
OPTION_PROJECT=
OPTION_FORCE=no
OPTION_ADB=
OPTION_EXEC=
OPTION_START=no
OPTION_LAUNCH=
OPTION_LAUNCH_LIST=no
OPTION_DELAY=
OPTION_WAIT="-D"
OPTION_PACKAGE_NAME=
check_parameter ()
{
if [ -z "$2" ]; then
echo "ERROR: Missing parameter after option '$1'"
exit 1
fi
}
check_adb_flags ()
{
if [ -n "$ADB_FLAGS" ] ; then
echo "ERROR: Only one of -e, -d or -s <serial> can be used at the same time!"
exit 1
fi
}
get_build_var ()
{
if [ -z "$GNUMAKE" ] ; then
GNUMAKE=make
fi
$GNUMAKE --no-print-dir -f $ANDROID_NDK_ROOT/build/core/build-local.mk -C $PROJECT DUMP_$1 | tail -1
}
get_build_var_for_abi ()
{
if [ -z "$GNUMAKE" ] ; then
GNUMAKE=make
fi
$GNUMAKE --no-print-dir -f $ANDROID_NDK_ROOT/build/core/build-local.mk -C $PROJECT DUMP_$1 APP_ABI=$2 | tail -1
}
# Used to run an awk script on the manifest
run_awk_manifest_script ()
{
$AWK_CMD -f $AWK_SCRIPTS/$1 $PROJECT/$MANIFEST
}
if [ "$HOST_OS" = "cygwin" ] ; then
# Return native path representation from cygwin one
# $1: a cygwin-compatible path (e.g. /cygdrive/c/some/thing)
# Return: path in host windows representation, e.g. C:/some/thing
#
# We use mixed mode (i.e. / as the directory separator) because
# all the tools we use recognize it properly, and it avoids lots
# of escaping nonsense associated with "\"
#
native_path ()
{
cygpath -m $1
}
else # HOST_OS != windows
native_path ()
{
echo "$1"
}
fi # HOST_OS != windows
# We need to ensure the ANDROID_NDK_ROOT is absolute, otherwise calls
# to get_build_var, get_build_var_for_abi and run_awk_manifest_script
# might fail, e.g. when invoked with:
#
# cd $NDKROOT
# ./ndk-gdb --project=/path/to/project
#
path_is_absolute ()
{
local P P2
P=$1 # copy path
P2=${P#/} # remove / prefix, if any
[ "$P" != "$P2" ]
}
if ! path_is_absolute "$ANDROID_NDK_ROOT"; then
ANDROID_NDK_ROOT=$(pwd)/$ANDROID_NDK_ROOT
fi
VERBOSE=no
while [ -n "$1" ]; do
opt="$1"
optarg=`expr "x$opt" : 'x[^=]*=\(.*\)'`
case "$opt" in
--help|-h|-\?)
OPTION_HELP=yes
;;
--verbose)
VERBOSE=yes
;;
-s)
check_parameter $1 $2
check_adb_flags
ADB_FLAGS=" -s"
DEVICE_SERIAL=$2
shift
;;
-s*)
check_adb_flags
optarg=`expr -- "$opt" : '-s\(.*\)'`
ADB_FLAGS=" -s"
DEVICE_SERIAL=$optarg
;;
-p)
check_parameter $1 $2
OPTION_PROJECT="$2"
shift
;;
-p*)
optarg=`expr -- "$opt" : '-p\(.*\)'`
OPTION_PROJECT="$optarg"
;;
--exec=*)
OPTION_EXEC="$optarg"
;;
-x)
check_parameter $1 $2
OPTION_EXEC="$2"
shift
;;
-x*)
optarg=`expr -- "$opt" : '-x\(.*\)'`
OPTION_EXEC="$optarg"
;;
-e)
check_adb_flags
ADB_FLAGS=" -e"
;;
-d)
check_adb_flags
ADB_FLAGS=" -d"
;;
--adb=*) # specify ADB command
OPTION_ADB="$optarg"
;;
--awk=*)
AWK_CMD="$optarg"
;;
--project=*)
OPTION_PROJECT="$optarg"
;;
--port=*)
DEBUG_PORT="$optarg"
;;
--force)
OPTION_FORCE="yes"
;;
--launch-list)
OPTION_LAUNCH_LIST="yes"
;;
--launch=*)
OPTION_LAUNCH="$optarg"
;;
--start)
OPTION_START=yes
;;
--delay=*)
OPTION_DELAY="$optarg"
;;
--nowait)
JDB_PORT=
OPTION_WAIT=
;;
--package=*)
OPTION_PACKAGE_NAME="$optarg"
;;
-*) # unknown options
echo "ERROR: Unknown option '$opt', use --help for list of valid ones."
exit 1
;;
*) # Simply record parameter
if [ -z "$PARAMETERS" ] ; then
PARAMETERS="$opt"
else
PARAMETERS="$PARAMETERS $opt"
fi
;;
esac
shift
done
if [ -z "$JDB_CMD" ] && [ -n "$OPTION_WAIT" ]; then
echo "ERROR: 'jdb' not found; you must either install the JDK, or specify --nowait"
exit 1
fi
if [ -n "$JDB_PORT" ] && [ "$JDB_PORT" = "$DEBUG_PORT" ]; then
echo "ERROR: --port specified cannot be $JDB_PORT without --nowait"
exit 1
fi
if [ "$OPTION_HELP" = "yes" ] ; then
echo "Usage: $PROGNAME [options]"
echo ""
echo "Setup a gdb debugging session for your Android NDK application."
echo "Read $$NDK/docs/NDK-GDB.TXT for complete usage instructions."
echo ""
echo "Valid options:"
echo ""
echo " --help|-h|-? Print this help"
echo " --verbose Enable verbose mode"
echo " --force Kill existing debug session if it exists"
echo " --nowait Don't have application wait for debugger to attach"
echo " (This might cause you to miss some early JNI breakpoints)"
echo " --start Launch application instead of attaching to existing one"
echo " --launch=<name> Same as --start, but specify activity name (see below)"
echo " --launch-list List all launchable activity names from manifest"
echo " --delay=<secs> Delay in seconds between activity start and gdbserver attach."
echo " --project=<path> Specify application project path"
echo " -p <path> Same as --project=<path>"
echo " --package=<name> Specify package name"
echo " --port=<port> Use tcp:localhost:<port> to communicate with gdbserver [$DEBUG_PORT]"
echo " --exec=<file> Execute gdb initialization commands in <file> after connection"
echo " -x <file> Same as --exec=<file>"
echo " --adb=<file> Use specific adb command [$ADB_CMD]"
echo " --awk=<file> Use specific awk command [$AWK_CMD]"
echo " -e Connect to single emulator instance"
echo " -d Connect to single target device"
echo " -s <serial> Connect to specific emulator or device"
echo ""
exit 0
fi
log "Android NDK installation path: $ANDROID_NDK_ROOT"
if [ -n "$OPTION_EXEC" ] ; then
if [ ! -f "$OPTION_EXEC" ]; then
echo "ERROR: Invalid initialization file: $OPTION_EXEC"
exit 1
fi
fi
if [ -n "$OPTION_DELAY" ] ; then
DELAY="$OPTION_DELAY"
fi
# Check ADB tool version
if [ -n "$OPTION_ADB" ] ; then
ADB_CMD=$OPTION_ADB
log "Using specific adb command: $ADB_CMD"
else
if [ -z "$ADB_CMD" ] ; then
echo "ERROR: The 'adb' tool is not in your path."
echo " You can change your PATH variable, or use"
echo " --adb=<executable> to point to a valid one."
exit 1
fi
log "Using default adb command: $ADB_CMD"
fi
ADB_CMD=$(quote_spaces $ADB_CMD)
ADB_VERSION=$("$ADB_CMD" version 2>/dev/null)
if [ $? != 0 ] ; then
echo "ERROR: Could not run ADB with: $ADB_CMD"
exit 1
fi
log "ADB version found: $ADB_VERSION"
if [ "x$DEVICE_SERIAL" = "x" ]; then
log "Using ADB flags: $ADB_FLAGS"
else
log "Using ADB flags: $ADB_FLAGS" \"$DEVICE_SERIAL\"
fi
JDB_CMD=$(quote_spaces $JDB_CMD)
log "Using JDB command: $JDB_CMD"
# Run an ADB command with the right ADB flags
# $1+: adb command parameter
adb_cmd ()
{
if [ "x$DEVICE_SERIAL" = "x" ]; then
"$ADB_CMD" $ADB_FLAGS "$@"
else
# NOTE: We escape $ADB_CMD and $DEVICE_SERIAL in case they contains spaces.
"$ADB_CMD" $ADB_FLAGS "$DEVICE_SERIAL" "$@"
fi
}
# Used internally by adb_var_shell and adb_var_shell2.
# $1: 1 to redirect stderr to $1, 0 otherwise.
# $2: Variable name that will contain the result
# $3+: Command options
_adb_var_shell ()
{
# We need a temporary file to store the output of our command
local CMD_OUT RET OUTPUT VARNAME REDIRECT_STDERR
REDIRECT_STDERR=$1
VARNAME=$2
shift; shift;
CMD_OUT=`mktemp /tmp/ndk-gdb-cmdout-XXXXXX`
# Run the command, while storing the standard output to CMD_OUT
# and appending the exit code as the last line.
if [ "$REDIRECT_STDERR" != 0 ]; then
adb_cmd shell "$@" ";" echo \$? | sed -e 's![[:cntrl:]]!!g' > $CMD_OUT 2>&1
else
adb_cmd shell "$@" ";" echo \$? | sed -e 's![[:cntrl:]]!!g' > $CMD_OUT
fi
# Get last line in log, which contains the exit code from the command
RET=`sed -e '$!d' $CMD_OUT`
# Get output, which corresponds to everything except the last line
OUT=`sed -e '$d' $CMD_OUT`
rm -f $CMD_OUT
eval $VARNAME=\"\$OUT\"
return $RET
}
# Run a command through 'adb shell' and captures its standard output
# into a variable. The function's exit code is the same than the command's.
#
# This is required because there is a bug where "adb shell" always returns
# 0 on the host, even if the command fails on the device.
#
# $1: Variable name (e.g. FOO)
# On exit, $FOO is set to the command's standard output
#
# The return status will be 0 (success) if the command succeeded
# or 1 (failure) otherwise.
adb_var_shell ()
{
_adb_var_shell 0 "$@"
}
# A variant of adb_var_shell that stores both stdout and stderr in the output
# $1: Variable name
adb_var_shell2 ()
{
_adb_var_shell 1 "$@"
}
# Return the PID of a given package or program, or 0 if it doesn't run
# $1: Package name ("com.example.hellojni") or program name ("/lib/gdbserver")
# Out: PID number, or 0 if not running
get_pid_of ()
{
adb_cmd shell ps | $AWK_CMD -f $AWK_SCRIPTS/extract-pid.awk -v PACKAGE="$1"
}
# Check the awk tool
AWK_SCRIPTS=$ANDROID_NDK_ROOT/build/awk
AWK_TEST=`$AWK_CMD -f $AWK_SCRIPTS/check-awk.awk`
if [ $? != 0 ] ; then
echo "ERROR: Could not run '$AWK_CMD' command. Do you have it installed properly?"
exit 1
fi
if [ "$AWK_TEST" != "Pass" ] ; then
echo "ERROR: Your version of 'awk' is obsolete. Please use --awk=<file> to point to Nawk or Gawk!"
exit 1
fi
# Name of the manifest file
MANIFEST=AndroidManifest.xml
# Find the root of the application project.
if [ -n "$OPTION_PROJECT" ] ; then
PROJECT=$OPTION_PROJECT
log "Using specified project path: $PROJECT"
if [ ! -d "$PROJECT" ] ; then
echo "ERROR: Your --project option does not point to a directory!"
exit 1
fi
if [ ! -f "$PROJECT/$MANIFEST" ] ; then
echo "ERROR: Your --project does not point to an Android project path!"
echo " It is missing a $MANIFEST file."
exit 1
fi
else
# Assume we are in the project directory
if [ -f "$MANIFEST" ] ; then
PROJECT=.
else
PROJECT=
CURDIR=`pwd`
while [ "$CURDIR" != "/" ] ; do
if [ -f "$CURDIR/$MANIFEST" ] ; then
PROJECT="$CURDIR"
break
fi
CURDIR=`dirname $CURDIR`
done
if [ -z "$PROJECT" ] ; then
echo "ERROR: Launch this script from an application project directory, or use --project=<path>."
exit 1
fi
fi
log "Using auto-detected project path: $PROJECT"
fi
if [ ! -z "$OPTION_PACKAGE_NAME" ]; then
PACKAGE_NAME="$OPTION_PACKAGE_NAME"
log "Using package name: $PACKAGE_NAME"
else
# Extract the package name from the manifest
PACKAGE_NAME=`run_awk_manifest_script extract-package-name.awk`
if [ $? != 0 -o "$PACKAGE_NAME" = "<none>" ] ; then
echo "ERROR: Could not extract package name from $PROJECT/$MANIFEST."
echo " Please check that the file is well-formed!"
exit 1
fi
log "Found package name: $PACKAGE_NAME"
fi
# If --launch-list is used, list all launchable activities, and be done with it
if [ "$OPTION_LAUNCH_LIST" = "yes" ] ; then
log "Extracting list of launchable activities from manifest:"
run_awk_manifest_script extract-launchable.awk
exit 0
fi
APP_ABIS=`get_build_var APP_ABI`
if [ "$APP_ABIS" != "${APP_ABIS%%all*}" ] ; then
# replace first "all" with all available ABIs
ALL_ABIS=`get_build_var NDK_ALL_ABIS`
APP_ABIS_FRONT="${APP_ABIS%%all*}"
APP_ABIS_BACK="${APP_ABIS#*all}"
APP_ABIS="${APP_ABIS_FRONT}${ALL_ABIS}${APP_ABIS_BACK}"
fi
# replace "armeabi-v7a-hard" with "armeabi-v7a"
APP_ABIS=`echo $APP_ABIS | sed -e 's/armeabi-v7a-hard/armeabi-v7a/g'`
log "ABIs targetted by application: $APP_ABIS"
# Check the ADB command, and that we can connect to the device/emulator
ADB_TEST=`adb_cmd shell ls`
if [ $? != 0 ] ; then
echo "ERROR: Could not connect to device or emulator!"
echo " Please check that an emulator is running or a device is connected"
echo " through USB to this machine. You can use -e, -d and -s <serial>"
echo " in case of multiple ones."
exit 1
fi
# Check that the device is running Froyo (API Level 8) or higher
#
adb_var_shell API_LEVEL getprop ro.build.version.sdk
if [ $? != 0 -o -z "$API_LEVEL" ] ; then
echo "ERROR: Could not find target device's supported API level!"
echo "ndk-gdb will only work if your device is running Android 2.2 or higher."
exit 1
fi
log "Device API Level: $API_LEVEL"
if [ "$API_LEVEL" -lt "8" ] ; then
echo "ERROR: ndk-gdb requires a target device running Android 2.2 (API level 8) or higher."
echo "The target device is running API level $API_LEVEL!"
exit 1
fi
# Get the target device's supported ABI(s)
# And check that they are supported by the application
#
COMPAT_ABI=none
# All modern Android images must support ro.product.cpu.abilist32
# and ro.product.cpu.abilist64. Otherwise fall back to obsolete
# ro.product.cpu.abi and ro.product.cpu.abi2
adb_var_shell CPU_ABILIST64 getprop ro.product.cpu.abilist64
adb_var_shell CPU_ABILIST32 getprop ro.product.cpu.abilist32
CPU_ABIS="$CPU_ABILIST64,$CPU_ABILIST32"
if [ -z "$CPU_ABILIST64" ] && [ -z "$CPU_ABILIST32" ] ; then
adb_var_shell CPU_ABI1 getprop ro.product.cpu.abi
adb_var_shell CPU_ABI2 getprop ro.product.cpu.abi2
CPU_ABIS="$CPU_ABI1,$CPU_ABI2"
fi
# Replace all ',' with space and add trailing space to
# ease whole-word matching of APP_ABI
CPU_ABILIST64=$(echo $CPU_ABILIST64 | tr ',' ' ')
CPU_ABILIST32=$(echo $CPU_ABILIST32 | tr ',' ' ')
CPU_ABIS=$(echo $CPU_ABIS | tr ',' ' ')
log "Device CPU ABIs: $CPU_ABIS"
APP_ABIS=$APP_ABIS" "
adb_var_shell BCFILES run-as $PACKAGE_NAME /system/bin/sh -c "ls lib/*.bc"
####if [ $? = 0 ]; then
#### COMPAT_ABI="$UNKNOWN_ABI"
####else
# Assume that compatible ABI is 32-bit
COMPAT_ABI_BITS=32
# First look compatible ABI in the list of 64-bit ABIs
if [ -n "$CPU_ABILIST64" ] ; then
for CPU_ABI64 in $CPU_ABILIST64; do
if [ "$APP_ABIS" != "${APP_ABIS%$CPU_ABI64 *}" ] ; then
COMPAT_ABI=$CPU_ABI64
COMPAT_ABI_BITS=64
break
fi
done
fi
# If we found nothing - look among 32-bit ABIs
if [ "$COMPAT_ABI" = none ] && [ -n "$CPU_ABILIST32" ] ; then
for CPU_ABI32 in $CPU_ABILIST32; do
if [ "$APP_ABIS" != "${APP_ABIS%$CPU_ABI32 *}" ] ; then
COMPAT_ABI=$CPU_ABI32
break
fi
done
fi
# Lastly, lets check ro.product.cpu.abi and ro.product.cpu.abi2
if [ "$COMPAT_ABI" = none ] && [ -z "$CPU_ABILIST64" ] && [ -z "$CPU_ABILIST32" ]; then
for CPU_ABI in $CPU_ABIS; do
if [ "$APP_ABIS" != "${APP_ABIS%$CPU_ABI *}" ] ; then
COMPAT_ABI=$CPU_ABI
break
fi
done
fi
####fi
if [ "$COMPAT_ABI" = none ] ; then
COMPAT_ABI='armeabi'
fi
log "Compatible device ABI: $COMPAT_ABI"
# Get information from the build system
GDBSETUP_INIT=`get_build_var_for_abi NDK_APP_GDBSETUP $COMPAT_ABI`
log "Using gdb setup init: $GDBSETUP_INIT"
# Find the prefix for gdb-client
if [ "$COMPAT_ABI" != "$UNKNOWN_ABI" ]; then
TOOLCHAIN_PREFIX=`get_build_var_for_abi TOOLCHAIN_PREFIX $COMPAT_ABI`
else
TOOLCHAIN_ABI=$(echo $CPU_ABIS | awk '{print $NF}')
TOOLCHAIN_PREFIX=`get_build_var_for_abi TOOLCHAIN_PREFIX $TOOLCHAIN_ABI`
fi
log "Using toolchain prefix: $TOOLCHAIN_PREFIX"
APP_OUT=`get_build_var_for_abi TARGET_OUT $COMPAT_ABI`
log "Using app out directory: $APP_OUT"
# Check that the application is debuggable, or nothing will work
####DEBUGGABLE=`run_awk_manifest_script extract-debuggable.awk`
####RET=$?
####log "Found debuggable flag: $DEBUGGABLE"
####if [ "$RET" != 0 -o "$DEBUGGABLE" != "true" ] ; then
#### # If gdb.setup exists, then we built with 'ndk-build NDK_DEBUG=1' and it's
#### # ok to not have android:debuggable set to true in the original manifest.
#### # However, if this is not the case, then complain!!
#### if [ -f $PROJECT/libs/$COMPAT_ABI/gdb.setup ] ; then
#### log "Found gdb.setup under libs/$COMPAT_ABI, assuming app was built with NDK_DEBUG=1"
#### else
#### echo "ERROR: Package $PACKAGE_NAME is not debuggable ! You can fix that in two ways:"
#### echo ""
#### echo " - Rebuilt with the NDK_DEBUG=1 option when calling 'ndk-build'."
#### echo ""
#### echo " - Modify your manifest to set android:debuggable attribute to \"true\","
#### echo " then rebuild normally."
#### echo ""
#### echo "After one of these, re-install to the device!"
#### exit 1
#### fi
####else
# DEBUGGABLE is true in the manifest. Let's check that the user didn't change the
# debuggable flag in the manifest without calling ndk-build afterwards.
if [ ! -f $PROJECT/libs/$COMPAT_ABI/gdb.setup ] ; then
echo "ERROR: Could not find gdb.setup under $PROJECT/libs/$COMPAT_ABI"
echo " This usually means you modified your AndroidManifest.xml to set"
echo " the android:debuggable flag to 'true' but did not rebuild the"
echo " native binaries. Please call 'ndk-build' to do so,"
echo " *then* re-install to the device!"
exit 1
fi
####fi
# Find the <dataDir> of the package on the device
adb_var_shell2 DATA_DIR run-as $PACKAGE_NAME /system/bin/sh -c pwd
if [ $? != 0 -o -z "$DATA_DIR" ] ; then
echo "ERROR: Could not extract package's data directory. Are you sure that"
echo " your installed application is debuggable?"
exit 1
fi
log "Found data directory: '$DATA_DIR'"
# Let's check that 'gdbserver' is properly installed on the device too. If 'gdbserver'
# is not there, push 'gdbserver' found in prebuilt.
#
DEVICE_GDBSERVER=$DATA_DIR/lib/gdbserver
adb_var_shell2 GDBSERVER_RESULT run-as $PACKAGE_NAME ls $DEVICE_GDBSERVER
if [ $? != 0 ]; then
# Figure out what's the target-arch and find gdbserver in prebuilt.
TARGET_ARCH=none
for ANDROID_ARCH in $ANDROID_NDK_ROOT/prebuilt/android-*; do
ANDROID_ARCH=${ANDROID_ARCH#$ANDROID_NDK_ROOT/prebuilt/android-}
if [ "$COMPAT_ABI" = "$ANDROID_ARCH" ]; then
TARGET_ARCH=$ANDROID_ARCH
break;
fi
done
if [ $TARGET_ARCH != "none" ]; then
DEVICE_GDBSERVER=/data/local/tmp/gdbserver
adb shell mkdir -p /data/local/tmp
adb push ${ANDROID_NDK_ROOT}/prebuilt/android-${TARGET_ARCH}/gdbserver/gdbserver \
$DEVICE_GDBSERVER
log "Push gdbserver in device"
else
echo "ERROR: Non-debuggable application installed on the target device."
echo " Please re-install the debuggable version!"
exit 1
fi
fi
log "Found device gdbserver: $DEVICE_GDBSERVER"
# Launch the activity if needed
if [ "$OPTION_START" = "yes" ] ; then
# If --launch is used, ignore --start, otherwise extract the first
# launchable activity name from the manifest and use it as if --launch=<name>
# was used instead.
#
if [ -z "$OPTION_LAUNCH" ] ; then
OPTION_LAUNCH=`run_awk_manifest_script extract-launchable.awk | sed 2q`
if [ $? != 0 ] ; then
echo "ERROR: Could not extract name of launchable activity from manifest!"
echo " Try to use --launch=<name> directly instead as a work-around."
exit 1
fi
log "Found first launchable activity: $OPTION_LAUNCH"
if [ -z "$OPTION_LAUNCH" ] ; then
echo "ERROR: It seems that your Application does not have any launchable activity!"
echo " Please fix your manifest file and rebuild/re-install your application."
exit 1
fi
fi
fi
if [ -n "$OPTION_LAUNCH" ] ; then
log "Launching activity: $PACKAGE_NAME/$OPTION_LAUNCH"
adb_var_shell2 DUMMY am start $OPTION_WAIT -n $PACKAGE_NAME/$OPTION_LAUNCH
if [ $? != 0 ] ; then
echo "ERROR: Could not launch specified activity: $OPTION_LAUNCH"
echo " Use --launch-list to dump a list of valid values."
exit 1
fi
# Sleep a bit, it sometimes take one second to start properly
# Note that we use the 'sleep' command on the device here.
run adb_cmd shell sleep $DELAY
fi
# Find the PID of the application being run
PID=$(get_pid_of "$PACKAGE_NAME")
RET=$?
log "Found running PID: $PID"
if [ "$RET" != 0 -o "$PID" = "0" ] ; then
echo "ERROR: Could not extract PID of application on device/emulator."
if [ -n "$OPTION_LAUNCH" ] ; then
echo " Weird, this probably means one of these:"
echo ""
echo " - The installed package does not match your current manifest."
echo " - The application process was terminated."
echo ""
echo " Try using the --verbose option and look at its output for details."
else
echo " Are you sure the application is already started?"
echo " Consider using --start or --launch=<name> if not."
fi
exit 1
fi
# Check that there is no other instance of gdbserver running
GDBSERVER_PID=$(get_pid_of lib/gdbserver)
if [ "$GDBSERVER_PID" != "0" ]; then
if [ "$OPTION_FORCE" = "no" ] ; then
echo "ERROR: Another debug session running, Use --force to kill it."
exit 1
fi
log "Killing existing debugging session"
run adb_cmd shell kill -9 $GDBSERVER_PID
fi
# Launch gdbserver now
DEBUG_SOCKET=debug-socket
adb_var_shell2 DUMMY run-as $PACKAGE_NAME $DEVICE_GDBSERVER +$DEBUG_SOCKET --attach $PID &
if [ $? != 0 ] ; then
echo "ERROR: Could not launch gdbserver on the device?"
exit 1
fi
log "Launched gdbserver succesfully."
# Setup network redirection
log "Setup network redirection"
run adb_cmd forward tcp:$DEBUG_PORT localfilesystem:$DATA_DIR/$DEBUG_SOCKET
if [ $? != 0 ] ; then
echo "ERROR: Could not setup network redirection to gdbserver?"
echo " Maybe using --port=<port> to use a different TCP port might help?"
exit 1
fi
# If we are debugging 64-bit app, then we need to pull linker64,
# app_process64 and libc.so from lib64 directory
LINKER_NAME=linker
LIBDIR_NAME=lib
APP_PROCESS_NAME=app_process32
if [ "$COMPAT_ABI_BITS" = 64 ] ; then
LINKER_NAME=linker64
LIBDIR_NAME=lib64
APP_PROCESS_NAME=app_process64
else
# Old 32-bit devices do not have app_process32. Pull
# app_process in this case
adb_var_shell2 DUMMY test -e /system/bin/$APP_PROCESS_NAME
if [ $? != 0 ] ; then
APP_PROCESS_NAME=app_process
fi
fi
# Get the app_server binary from the device
APP_PROCESS=$APP_OUT/app_process
run adb_cmd pull /system/bin/$APP_PROCESS_NAME `native_path $APP_PROCESS`
log "Pulled $APP_PROCESS_NAME from device/emulator."
run adb_cmd pull /system/bin/$LINKER_NAME `native_path $APP_OUT/$LINKER_NAME`
log "Pulled $LINKER_NAME from device/emulator."
run adb_cmd pull /system/$LIBDIR_NAME/libc.so `native_path $APP_OUT/libc.so`
log "Pulled /system/$LIBDIR_NAME/libc.so from device/emulator."
# Setup JDB connection, for --start or --launch
if [ "$OPTION_START" = "yes" ] || [ -n "$OPTION_LAUNCH" ] ; then
if [ -n "$JDB_PORT" ]; then
log "Setup JDB connection"
run adb_cmd forward tcp:$JDB_PORT jdwp:$PID
sleep 1
$JDB_CMD -connect com.sun.jdi.SocketAttach:hostname=localhost,port=$JDB_PORT &
sleep 1
fi
fi
# If we are debugging UNKNOWN_ABI, download compiled *.so from device.
#
if [ "$COMPAT_ABI" = "$UNKNOWN_ABI" ]; then
for bc in $BCFILES; do
log "Pulled $(basename $bc .bc).so from device/emulator."
adb pull $DATA_DIR/lib/$(basename $bc .bc).so $PROJECT/obj/local/$UNKNOWN_ABI/
done
fi
# Now launch the appropriate gdb client with the right init commands
#
GDBCLIENT=${TOOLCHAIN_PREFIX}gdb
GDBSETUP=$APP_OUT/gdb.setup
cp -f $GDBSETUP_INIT $GDBSETUP
#uncomment the following to debug the remote connection only
#echo "set debug remote 1" >> $GDBSETUP
echo "file `native_path $APP_PROCESS`" >> $GDBSETUP
echo "target remote :$DEBUG_PORT" >> $GDBSETUP
if [ -n "$OPTION_EXEC" ] ; then
cat $OPTION_EXEC >> $GDBSETUP
fi
$GDBCLIENT -x `native_path $GDBSETUP`
set +x

View File

@ -1,8 +1,9 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="9531" systemVersion="15C50" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none">
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none">
<dependencies>
<deployment version="1060" identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="9531"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14490.70"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
@ -375,7 +376,7 @@ CA
</items>
</menu>
<window title="Apple2Mac" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" animationBehavior="default" id="371" userLabel="Window - Apple2Mac" customClass="EmulatorWindow">
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES" unifiedTitleAndToolbar="YES"/>
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
<rect key="contentRect" x="200" y="200" width="568" height="384"/>
<rect key="screenRect" x="0.0" y="0.0" width="1920" height="1178"/>
@ -424,7 +425,7 @@ CA
<outlet property="window" destination="371" id="z3B-S9-PsV"/>
</connections>
</customObject>
<window title="Insert Disks" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" oneShot="NO" releasedWhenClosed="NO" showsToolbarButton="NO" visibleAtLaunch="NO" animationBehavior="default" id="RAk-at-ZT4" userLabel="Window - Insert Disks">
<window title="Insert Disks" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="default" id="RAk-at-ZT4" userLabel="Window - Insert Disks">
<windowStyleMask key="styleMask" titled="YES" closable="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
<rect key="contentRect" x="109" y="132" width="522" height="302"/>
@ -598,19 +599,13 @@ CA
<action selector="startupDiskBChoiceChanged:" target="FHO-g2-V3A" id="tQS-5l-DDf"/>
</connections>
</button>
<box verticalHuggingPriority="750" title="Box" boxType="separator" titlePosition="noTitle" id="7ZU-H6-jQn">
<box verticalHuggingPriority="750" boxType="separator" id="7ZU-H6-jQn">
<rect key="frame" x="12" y="59" width="498" height="5"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<color key="borderColor" white="0.0" alpha="0.41999999999999998" colorSpace="calibratedWhite"/>
<color key="fillColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
<font key="titleFont" metaFont="system"/>
</box>
<box horizontalHuggingPriority="750" title="Box" boxType="separator" titlePosition="noTitle" id="864-Ov-2tE">
<box horizontalHuggingPriority="750" boxType="separator" id="864-Ov-2tE">
<rect key="frame" x="259" y="70" width="5" height="217"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<color key="borderColor" white="0.0" alpha="0.41999999999999998" colorSpace="calibratedWhite"/>
<color key="fillColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
<font key="titleFont" metaFont="system"/>
</box>
<button verticalHuggingPriority="750" id="tLd-IJ-Kjl">
<rect key="frame" x="338" y="13" width="85" height="32"/>
@ -645,7 +640,7 @@ DQ
<outlet property="startupLoadDiskB" destination="Ceo-uO-cRu" id="V0B-0f-YZu"/>
</connections>
</customObject>
<window title="Apple2Mac Preferences" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" oneShot="NO" releasedWhenClosed="NO" showsToolbarButton="NO" visibleAtLaunch="NO" animationBehavior="default" id="Mzv-VG-jce" userLabel="Window - Prefs" customClass="EmulatorWindow">
<window title="Apple2Mac Preferences" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="default" id="Mzv-VG-jce" userLabel="Window - Prefs" customClass="EmulatorWindow">
<windowStyleMask key="styleMask" titled="YES" closable="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
<rect key="contentRect" x="109" y="132" width="580" height="360"/>
@ -721,12 +716,9 @@ DQ
<action selector="peggedChoiceChanged:" target="mUW-Rh-bL1" id="K2H-Vc-15h"/>
</connections>
</button>
<box verticalHuggingPriority="750" title="Box" boxType="separator" titlePosition="noTitle" id="9U0-v9-wTm">
<box verticalHuggingPriority="750" boxType="separator" id="9U0-v9-wTm">
<rect key="frame" x="17" y="183" width="508" height="5"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<color key="borderColor" white="0.0" alpha="0.41999999999999998" colorSpace="calibratedWhite"/>
<color key="fillColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
<font key="titleFont" metaFont="system"/>
</box>
<slider verticalHuggingPriority="750" id="y10-Rm-oDB">
<rect key="frame" x="15" y="115" width="404" height="27"/>
@ -846,12 +838,9 @@ DQ
<rect key="frame" x="10" y="33" width="534" height="298"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<box horizontalHuggingPriority="750" title="Box" boxType="separator" titlePosition="noTitle" id="Fkg-X3-0XG">
<box horizontalHuggingPriority="750" boxType="separator" id="Fkg-X3-0XG">
<rect key="frame" x="265" y="9" width="5" height="286"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<color key="borderColor" white="0.0" alpha="0.41999999999999998" colorSpace="calibratedWhite"/>
<color key="fillColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
<font key="titleFont" metaFont="system"/>
</box>
<customView id="OWJ-x7-P0q" customClass="EmulatorJoystickCalibrationView">
<rect key="frame" x="276" y="39" width="256" height="256"/>

View File

@ -76,6 +76,9 @@
const char *path = [[self.path stringByAppendingPathComponent:[self._disks objectAtIndex:row]] UTF8String];
int fd = -1;
TEMP_FAILURE_RETRY(fd = open(path, ro ? O_RDONLY : O_RDWR));
if (fd == -1) {
LOG("OOPS, open failed for path %s (%s)", path, strerror(errno));
}
const char *errMsg = disk6_insert(fd, drive, path, ro);
(void)errMsg;
if (fd >= 0) {

View File

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14113" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none">
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none">
<dependencies>
<deployment version="1060" identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14113"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14490.70"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
@ -381,7 +381,7 @@ CA
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
<rect key="contentRect" x="200" y="200" width="568" height="384"/>
<rect key="screenRect" x="0.0" y="0.0" width="1920" height="1178"/>
<view key="contentView" id="372" customClass="EmulatorGLView">
<view key="contentView" id="372">
<rect key="frame" x="0.0" y="0.0" width="568" height="384"/>
<autoresizingMask key="autoresizingMask"/>
</view>
@ -441,7 +441,7 @@ CA
<outlet property="window" destination="371" id="z3B-S9-PsV"/>
</connections>
</customObject>
<window title="Insert Disks" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" oneShot="NO" releasedWhenClosed="NO" showsToolbarButton="NO" visibleAtLaunch="NO" animationBehavior="default" id="RAk-at-ZT4" userLabel="Window - Insert Disks">
<window title="Insert Disks" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="default" id="RAk-at-ZT4" userLabel="Window - Insert Disks">
<windowStyleMask key="styleMask" titled="YES" closable="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
<rect key="contentRect" x="109" y="132" width="522" height="302"/>
@ -657,7 +657,7 @@ DQ
<outlet property="startupLoadDiskB" destination="Ceo-uO-cRu" id="V0B-0f-YZu"/>
</connections>
</customObject>
<window title="Apple2Mac Preferences" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" oneShot="NO" releasedWhenClosed="NO" showsToolbarButton="NO" visibleAtLaunch="NO" animationBehavior="default" id="Mzv-VG-jce" userLabel="Window - Prefs" customClass="EmulatorWindow">
<window title="Apple2Mac Preferences" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="default" id="Mzv-VG-jce" userLabel="Window - Prefs" customClass="EmulatorWindow">
<windowStyleMask key="styleMask" titled="YES" closable="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
<rect key="contentRect" x="109" y="132" width="580" height="360"/>
@ -833,7 +833,7 @@ DQ
<action selector="colorChoiceChanged:" target="mUW-Rh-bL1" id="M58-28-Lmr"/>
</connections>
</popUpButton>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" misplaced="YES" id="dak-eg-hHn">
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" id="dak-eg-hHn">
<rect key="frame" x="15" y="276" width="96" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Color :" id="eHo-1T-edz">
@ -842,7 +842,7 @@ DQ
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" misplaced="YES" id="rDh-8p-gf0">
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" id="rDh-8p-gf0">
<rect key="frame" x="15" y="245" width="96" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Monochrome :" id="6zV-cm-vLN">
@ -851,7 +851,7 @@ DQ
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<popUpButton verticalHuggingPriority="750" misplaced="YES" id="9bP-5g-S3a">
<popUpButton verticalHuggingPriority="750" id="9bP-5g-S3a">
<rect key="frame" x="115" y="240" width="158" height="26"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<popUpButtonCell key="cell" type="push" title="Black/white" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="7Nj-L6-N5K" id="UkC-75-g1J">
@ -872,7 +872,7 @@ DQ
<action selector="monochromeColorChoiceChanged:" target="mUW-Rh-bL1" id="FIq-tn-pNv"/>
</connections>
</popUpButton>
<button verticalHuggingPriority="750" misplaced="YES" id="0Av-wg-y41">
<button verticalHuggingPriority="750" id="0Av-wg-y41">
<rect key="frame" x="115" y="219" width="142" height="18"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="check" title="Show half scanlines" bezelStyle="regularSquare" imagePosition="left" inset="2" id="q9I-GX-oEP">
@ -1061,12 +1061,12 @@ DQ
</customObject>
</objects>
<resources>
<image name="CPU" width="64" height="64"/>
<image name="Disks" width="64" height="64"/>
<image name="Fullscreen" width="64" height="64"/>
<image name="CPU" width="32" height="32"/>
<image name="Disks" width="32" height="32"/>
<image name="Fullscreen" width="32" height="32"/>
<image name="NSUser" width="32" height="32"/>
<image name="Prefs" width="64" height="64"/>
<image name="Prefs" width="32" height="32"/>
<image name="Reboot" width="32" height="32"/>
<image name="Stop" width="64" height="64"/>
<image name="Stop" width="32" height="32"/>
</resources>
</document>

View File

@ -1,8 +1,9 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="9531" systemVersion="15C50" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none">
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none">
<dependencies>
<deployment version="1080" identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="9531"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14490.70"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
@ -379,7 +380,7 @@ CA
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
<rect key="contentRect" x="200" y="200" width="568" height="384"/>
<rect key="screenRect" x="0.0" y="0.0" width="1920" height="1178"/>
<view key="contentView" id="372" customClass="EmulatorGLView">
<view key="contentView" id="372">
<rect key="frame" x="0.0" y="0.0" width="568" height="384"/>
<autoresizingMask key="autoresizingMask"/>
</view>
@ -452,7 +453,7 @@ CA
<outlet property="window" destination="371" id="z3B-S9-PsV"/>
</connections>
</customObject>
<window title="Insert Disks" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" oneShot="NO" releasedWhenClosed="NO" showsToolbarButton="NO" visibleAtLaunch="NO" animationBehavior="default" id="RAk-at-ZT4" userLabel="Window - Insert Disks">
<window title="Insert Disks" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="default" id="RAk-at-ZT4" userLabel="Window - Insert Disks">
<windowStyleMask key="styleMask" titled="YES" closable="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
<rect key="contentRect" x="109" y="132" width="522" height="302"/>
@ -626,19 +627,13 @@ CA
<action selector="startupDiskBChoiceChanged:" target="FHO-g2-V3A" id="tQS-5l-DDf"/>
</connections>
</button>
<box verticalHuggingPriority="750" title="Box" boxType="separator" titlePosition="noTitle" id="7ZU-H6-jQn">
<box verticalHuggingPriority="750" boxType="separator" id="7ZU-H6-jQn">
<rect key="frame" x="12" y="59" width="498" height="5"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<color key="borderColor" white="0.0" alpha="0.41999999999999998" colorSpace="calibratedWhite"/>
<color key="fillColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
<font key="titleFont" metaFont="system"/>
</box>
<box horizontalHuggingPriority="750" title="Box" boxType="separator" titlePosition="noTitle" id="864-Ov-2tE">
<box horizontalHuggingPriority="750" boxType="separator" id="864-Ov-2tE">
<rect key="frame" x="259" y="70" width="5" height="217"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<color key="borderColor" white="0.0" alpha="0.41999999999999998" colorSpace="calibratedWhite"/>
<color key="fillColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
<font key="titleFont" metaFont="system"/>
</box>
<button verticalHuggingPriority="750" id="tLd-IJ-Kjl">
<rect key="frame" x="338" y="13" width="85" height="32"/>
@ -673,7 +668,7 @@ DQ
<outlet property="startupLoadDiskB" destination="Ceo-uO-cRu" id="V0B-0f-YZu"/>
</connections>
</customObject>
<window title="Apple2Mac Preferences" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" oneShot="NO" releasedWhenClosed="NO" showsToolbarButton="NO" visibleAtLaunch="NO" animationBehavior="default" id="Mzv-VG-jce" userLabel="Window - Prefs" customClass="EmulatorWindow">
<window title="Apple2Mac Preferences" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="default" id="Mzv-VG-jce" userLabel="Window - Prefs" customClass="EmulatorWindow">
<windowStyleMask key="styleMask" titled="YES" closable="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
<rect key="contentRect" x="109" y="132" width="580" height="360"/>
@ -749,12 +744,9 @@ DQ
<action selector="peggedChoiceChanged:" target="mUW-Rh-bL1" id="K2H-Vc-15h"/>
</connections>
</button>
<box verticalHuggingPriority="750" title="Box" boxType="separator" titlePosition="noTitle" id="9U0-v9-wTm">
<box verticalHuggingPriority="750" boxType="separator" id="9U0-v9-wTm">
<rect key="frame" x="17" y="183" width="508" height="5"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<color key="borderColor" white="0.0" alpha="0.41999999999999998" colorSpace="calibratedWhite"/>
<color key="fillColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
<font key="titleFont" metaFont="system"/>
</box>
<slider verticalHuggingPriority="750" id="y10-Rm-oDB">
<rect key="frame" x="15" y="115" width="404" height="27"/>
@ -874,12 +866,9 @@ DQ
<rect key="frame" x="10" y="33" width="534" height="298"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<box horizontalHuggingPriority="750" title="Box" boxType="separator" titlePosition="noTitle" id="Fkg-X3-0XG">
<box horizontalHuggingPriority="750" boxType="separator" id="Fkg-X3-0XG">
<rect key="frame" x="265" y="9" width="5" height="286"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<color key="borderColor" white="0.0" alpha="0.41999999999999998" colorSpace="calibratedWhite"/>
<color key="fillColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
<font key="titleFont" metaFont="system"/>
</box>
<customView id="OWJ-x7-P0q" customClass="EmulatorJoystickCalibrationView">
<rect key="frame" x="276" y="39" width="256" height="256"/>

View File

@ -72,9 +72,9 @@
}
NSInteger mode = [defaults integerForKey:kApple2ColorConfig];
if (! ((mode >= COLOR_MODE_BW) && (mode < NUM_COLOROPTS)) )
if (! ((mode >= COLOR_MODE_MONO) && (mode < NUM_COLOROPTS)) )
{
mode = COLOR_MODE_BW;
mode = COLOR_MODE_DEFAULT;
}
//[self.videoModePicker d:mode];
//color_mode = (color_mode_t)mode;

View File

@ -88,6 +88,9 @@ static void prefsChangeCallback(const char *domain)
const char *path = [startupDiskA UTF8String];
int fdA = -1;
TEMP_FAILURE_RETRY(fdA = open(path, readOnlyA ? O_RDONLY : O_RDWR));
if (fdA == -1) {
LOG("OOPS, open failed for path %s (%s)", path, strerror(errno));
}
const char *err = disk6_insert(fdA, 0, path, readOnlyA);
if (fdA >= 0) {
TEMP_FAILURE_RETRY(close(fdA));
@ -115,6 +118,9 @@ static void prefsChangeCallback(const char *domain)
const char *path = [startupDiskB UTF8String];
int fdB = -1;
TEMP_FAILURE_RETRY(fdB = open(path, readOnlyB ? O_RDONLY : O_RDWR));
if (fdB == -1) {
LOG("OOPS, open failed for path %s (%s)", path, strerror(errno));
}
const char *err = disk6_insert(fdB, 1, path, readOnlyB);
if (fdB >= 0) {
TEMP_FAILURE_RETRY(close(fdB));
@ -190,8 +196,12 @@ static void prefsChangeCallback(const char *domain)
disk6_eject(drive);
int fd = -1;
TEMP_FAILURE_RETRY(fd = open([path UTF8String], readOnly ? O_RDONLY : O_RDWR));
const char *errMsg = disk6_insert(fd, drive, [path UTF8String], readOnly);
const char *cPath = [path UTF8String];
TEMP_FAILURE_RETRY(fd = open(cPath, readOnly ? O_RDONLY : O_RDWR));
if (fd == -1) {
LOG("OOPS, open failed for path %s (%s)", cPath, strerror(errno));
}
const char *errMsg = disk6_insert(fd, drive, cPath, readOnly);
if (fd >= 0) {
TEMP_FAILURE_RETRY(close(fd));
}

View File

@ -114,7 +114,7 @@
#ifdef KEYPAD_JOYSTICK
if (joy_mode == JOY_KPAD)
{
c_keys_handle_input(-1, 0, 0);
keys_handleInput(/*scancode:*/-1, /*is_pressed:*/false, /*is_ascii:*/false);
}
#endif
}

View File

@ -272,11 +272,11 @@ static void prefsChangeCallback(const char *domain)
{
case ALT_LT:
leftAltEngaged = !leftAltEngaged;
c_keys_handle_input(SCODE_L_ALT, /*pressed:*/leftAltEngaged, /*cooked:*/0);
keys_handleInput(SCODE_L_ALT, /*is_pressed:*/leftAltEngaged, /*is_ascii:*/false);
break;
case ALT_RT:
rightAltEngaged = !rightAltEngaged;
c_keys_handle_input(SCODE_R_ALT, /*pressed:*/rightAltEngaged, /*cooked:*/0);
keys_handleInput(SCODE_R_ALT, /*is_pressed:*/rightAltEngaged, /*is_ascii:*/false);
break;
default:
break;
@ -298,16 +298,16 @@ static void prefsChangeCallback(const char *domain)
switch (scode)
{
case NSUpArrowFunctionKey:
c_keys_handle_input(SCODE_U, pressed, /*cooked:*/0);
keys_handleInput(SCODE_U, pressed, /*is_ascii:*/false);
break;
case NSDownArrowFunctionKey:
c_keys_handle_input(SCODE_D, pressed, /*cooked:*/0);
keys_handleInput(SCODE_D, pressed, /*is_ascii:*/false);
break;
case NSLeftArrowFunctionKey:
c_keys_handle_input(SCODE_L, pressed, /*cooked:*/0);
keys_handleInput(SCODE_L, pressed, /*is_ascii:*/false);
break;
case NSRightArrowFunctionKey:
c_keys_handle_input(SCODE_R, pressed, /*cooked:*/0);
keys_handleInput(SCODE_R, pressed, /*is_ascii:*/false);
break;
default:
break;

View File

@ -252,27 +252,27 @@
}
case SHIFT_LT:
c_keys_handle_input(SCODE_L_SHIFT, ([event modifierFlags] & NSShiftKeyMask), 0);
keys_handleInput(SCODE_L_SHIFT, /*is_pressed:*/([event modifierFlags] & NSShiftKeyMask), /*is_ascii:*/false);
break;
case SHIFT_RT:
c_keys_handle_input(SCODE_R_SHIFT, ([event modifierFlags] & NSShiftKeyMask), 0);
keys_handleInput(SCODE_R_SHIFT, /*is_pressed:*/([event modifierFlags] & NSShiftKeyMask), /*is_ascii:*/false);
break;
case CTRL_LT:
c_keys_handle_input(SCODE_L_CTRL, ([event modifierFlags] & NSControlKeyMask), 0);
keys_handleInput(SCODE_L_CTRL, /*is_pressed:*/([event modifierFlags] & NSControlKeyMask), /*is_ascii:*/false);
break;
case CTRL_RT:
c_keys_handle_input(SCODE_R_CTRL, ([event modifierFlags] & NSControlKeyMask), 0);
keys_handleInput(SCODE_R_CTRL, /*is_pressed:*/([event modifierFlags] & NSControlKeyMask), /*is_ascii:*/false);
break;
case ALT_LT:
c_keys_handle_input(SCODE_L_ALT, ([event modifierFlags] & NSAlternateKeyMask), 0);
keys_handleInput(SCODE_L_ALT, /*is_pressed:*/([event modifierFlags] & NSAlternateKeyMask), /*is_ascii:*/false);
break;
case ALT_RT:
c_keys_handle_input(SCODE_R_ALT, ([event modifierFlags] & NSAlternateKeyMask), 0);
keys_handleInput(SCODE_R_ALT, /*is_pressed:*/([event modifierFlags] & NSAlternateKeyMask), /*is_ascii:*/false);
break;
default:
@ -378,7 +378,7 @@
default:
if ([event modifierFlags] & NSControlKeyMask)
{
scode = c_keys_ascii_to_scancode(scode);
scode = keys_ascii2Scancode(scode);
cooked = 0;
}
else
@ -388,7 +388,7 @@
break;
}
c_keys_handle_input(scode, pressed, cooked);
keys_handleInput(scode, pressed, cooked);
}
- (void)keyUp:(NSEvent *)event

View File

@ -119,9 +119,12 @@
NSString *disks[] = { @"disks", @"external-disks", NULL};
NSString **str = &disks[0];
assert(data_dir == NULL);
while (*str) {
NSString *apple2ixDirString = [documentsDir stringByAppendingPathComponent:apple2ix];
data_dir = strdup([apple2ixDirString UTF8String]);
if (data_dir == NULL) {
data_dir = STRDUP([apple2ixDirString UTF8String]);
}
NSString *documentsPath = [apple2ixDirString stringByAppendingPathComponent:*str];
NSString *resourcesPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:*str];

1253
ChangeLog

File diff suppressed because it is too large Load Diff

View File

@ -15,7 +15,7 @@ noinst_HEADERS = src/common.h src/cpu.h src/disk.h src/glue.h src/vm.h \
\
src/x86/glue-prologue.h \
src/x86/glue-offsets.h src/x86/glue-offsets32.h src/x86/glue-offsets64.h \
src/meta/debug.h src/meta/log.h src/meta/trace.h \
src/meta/debug.h src/meta/log.h src/meta/systrace.h \
\
src/audio/alhelpers.h src/audio/AY8910.h src/audio/mockingboard.h \
src/audio/peripherals.h src/audio/soundcore.h src/audio/speaker.h \

277
NEWS
View File

@ -1,277 +0,0 @@
Changed in 0.8:
Upgraded to modern-ish times...X11 now uses 32bit color!
Audio is now OpenAL instead of raw PC speaker access. We are indebted to
AppleWin for much of the sound code including preliminary suport for
Mockingboard emulation.
Bugfix and simplification for PC Joystick. Joystick emulation is now
bug-for-bug compatible with original Apple ][. Better calibration routines and
better "feel" for emulation of joystick on keypad/arrow-keys.
Better emulation fidelity overall by counting 6502 CPU cycles. This was
necessary for proper output of stream digital audio in OpenAL.
Significant interface and preferences changes and simplifications.
Changed in 0.7.4:
Many bugs have been fixed, including a calling-convention mistake that
could cause unpredictable behavior on reboot. Potential buffer overflows
have also been corrected.
Some key assignments have changed. Joystick buttons are now Left-Alt,
Right-Alt & Insert. Delete, formerly a joystick button, now produces the
DEL ascii code. Reboot is now Break (Ctrl-Pause), and Reset is now
Ctrl-Printscreen. (This is reversed from 0.7.3)
Some significant optimizations were also made in the assembly code. The
large memory-access indirection table is now compressed using virtual
memory tricks, to slightly reduce cache load and swap requirements.
Also many cleanups have been made in the internal code. This is
user-invisible, and far from complete. When done, it will become easier to
add new interface cards, video drivers, and other stuff, as well as port
to other unixes.
Changed in 0.7.3:
More makefile/configuration bugs were fixed.
The `Mystery House' sample disk, which was accidently omitted from my
distributions, has been reinstated. Also, I've added a new sample
disk image - William Night's emulator performance tests.
Changed in 0.7.2:
The configure script will now react to absence of X Windows or SVGAlib by
only building an emulator for the remaining graphics system.
A makefile bug that caused xapple-80col not to depend on
all it's source files was fixed. Also, the alternate character set is now
correctly restored on exit from the <F10> menu.
Some major cleanups were done to the assembly-language files. This is
invisible to the user, but should make further enhancement a little saner.
The banners on the source code have been synchronized.
Changed in 0.7.1:
1. Fixed a makefile bug that caused xapple-80col to be miscompiled.
2. Put sample .apple2 (config file) back in. It was accidentally excluded
from 0.7.
3. The SVGA emulator is again named `apple2', not `sapple2'.
Changed in 0.7: (Sep 98)
Version 0.7 - Michael Deutschmann <michael@talamasca.wkpowerlink.com>
1. Replaced "character.rom" and Aaron's MouseText table in misc.c with
a single file, font.txt, which is converted into a packed c table by a
generator utility.
2. Overhauled Makefile system. We now use GNU autoconf/automake.
Note: This hasn't been tested extensively. Also, there are a few automake
tricks I haven't bothered with - I added them to TODO.
Changed is 0.06: (Aug 98)
-------------------------
Version 0.06 - Aaron Culliney <chernabog@baldmountain.bbn.com,
aculline@bbn.com>
My code changes have nothing to do with my employer, GTE
Internetworking, BBN Technologies. They were written completely on my
own time and on my own machine.
1) Separated SVGAlib specific stuff into svideo.c. Added X Windows
frontend support in xvideo.c. The X frontend works by doing an
X(Shm)PutImage() of the emulator's framebuffer around 30 times a
second. This saves us from changing the internal video routines (they
assume direct access to an 8bit framebuffer), and it's a heck of a lot
faster than trying to do an XPutPixels() on each change. The X
frontend only currently works for 8bit displays. Make sure you run it
using a mode (specified in XF86Config) that has a bit depth of 8 (not
16 or greater).
2) We now build three versions of the emulator: svideo_320x200,
xvideo_320x200, xvideo_640x400. The last one really isn't 640x400 for
optimization reasons (568x384) although it probably should be...
Sorry, but there's no support for switching resolutions on the fly, we
ifdef it in with a new define "_640x400".
3) Provided 80column support for the 640x400 X version. Now you
can PR#3 (running as //e) and get 80 columns. This change required
a resolution of at least 560 horizontal pixels (Apple //e specs).
4) Both SVGA and X versions need to be suid root (for PC speaker port
access and SVGA stuff). But both versions now give up root access
during video initialization. This means that disk access is now done
as *you* (not root) even when g(un)zipping images for loading.
Before images would be g(un)zipped as root. There was something about
this that made me uneasy... Now it's up to you to give the
appropriate user/group permissions to your disk image repository.
I make no claim about the security fitness of this emulator. See the
other READMEs and files for more information/disclaimers.
5) Did a 180 on the disk image selection menu. <RET> now tries
to open disk images as read-only, 'W' for both read-write.
Of course this will only work if you have the correct
file permissions set (see 5 above). I find this more convenient b/c
I play alot of the arcade games where you don't ever save game state.
6) Removed the disk "Information" submenu. I never use it. Do you?
Changes in 0.05: (Feb 98)
-------------------------
Version 0.05 - Aaron Culliney <chernabog@baldmountain.bbn.com,
aculline@bbn.com>
My code changes have nothing to do with my employer, GTE
Internetworking, BBN Technologies. They were written completely on my
own time and on my own machine.
1) Added support for 65c02 instructions. The programs that
I've tested which use them work just fine.
2) Added support for 128k //e EXCEPT 80 column mode, B/W
DHIRES mode, and MouseText character set. The two unimplemented video
modes require major changes to current video routines, thus a next
version... New supported images are copy ii+ 9.0, diagnostics //e,
marble madness, airheart, legend of blacksilver, pirates!...
3) Fixed some old problems with HIRES colors being off around
byte edges in interpolated/color modes. but we still give you the
option to use these "lazy" modes since emulation is faster with them
enabled...
4) You now have several new options in your .apple2 file. I
suggest either copying the distributed one over your existing one, or
merging the changes in.
5) Fixed some potential security bugs where a user could
traverse into sensitive directories by using the disk selection
interface. The current emulator version is not guaranteed to be
foolproof since it has to be installed suid root for normal users to
use it. Sysadmins should take extra precautions as they see fit.
6) Added and deprecated some options in the debugger interface
to support the new 128k //e. see the DEBUGGER file for more info.
DEBUGGER support is not default compiled in.
7) added some extra options to the .config file that you build
into the emulator. One big one: a way to set the max delay count in
the emulator to bring Apple ][ emulation rates down to normal on high
end pentiums!... 100 is the default delay rate (which works just fine
for low end 386-Pentium100's).
Changes in 0.04: (June 97)
-----------------------------
Version 0.04 - Aaron Culliney <chernabog@baldmountain.bbn.com,
aculline@bbn.com>
My code changes have nothing to do with my employer, BBN. They were
written completely on my own time and on my own machine.
1) Added PC Joystick Support. You must have the joystick
kernel loadable module 0.8.0 correctly configured and installed to use
this feature.
2) Changed the way the emulator handles the language card
memory space. We no longer patch rom/ram on lc_c08x functions. I did
this because under certain conditions the previous versions of the
emulator would run a lot slower. Now you may notice the emulator
running a tad slower in general (because of the range checking), but
Ultima 4 and Arctic Fox (formerly unplayable) should now be just fine.
3) Changed the way the .apple2 preferences file is handled. I
Did this mainly to support saving of PC Joystick parameters, and I'd
rather let flex do the dirty work of regexp matching.
4) Changed the disk interface and main interface menu. In the
disk interface, you can now see which disk is in the drive, and with
what permission <rw1> for read/write drive 1. You can eject this disk
or force it to be write-protected. In the main menu screen, you have
more parameters to play around with (associated with the pc joystick
add-on).
5) Disk image files are now opened with user privileges, not
root privileges, even though the program is suid root. gzip'ed disks
are still handled as root, but we no longer call the unsecure system()
to do the dirty work. Instead we fork and directly exec "/bin/gzip".
5) General bug fixes and enhancements.
Changes in 0.03: (Jan-Feb 97)
-----------------------------
Version 0.03 - Aaron Culliney <chernabog@baldmountain.bbn.com>
My code changes have nothing to do with my employer, BBN. They were
written completely on my own time and on my own machine.
1) Fixed language card initialization bug.
2) Improved colors. Seems that Greens and purples we're
switched around. The colors are still slightly off, and color
interpolation seems screwy (TODO).
3) Added apple II debugger interface. This requires flex
version 2.5.2. (You can compile this into the program or leave it
out.) Type F7 to get into the debugger and type a '?' to see a
command summary. Check out the file DEBUGGER for more info.
4) Added support for standard 232960 .nib disks.
5) Added a more intuitive interface to selecting disks. You
can now traverse forward and backward in a directory hierarchy with
the base directory set by your .apple2 config file.
6) changed keymap: shift-p = @ and shift-N = ^, just like my
old II+ keyboard.
Changes in 0.02: (8 Dec 1995)
-----------------------------
* Ctrl-C will not kill the emulator with newer SVGAlib. Please
use SVGAlib > 1.2.9 for best results.
* Rudimentory REPT key handling. It's too fast though.
* The assembler files now compiles under ELF.
* Not every SVGA card can do page-flipping. The emulator now
checks for this and fall back to VGA if it can't.
* Keymap has changed a bit. Backspace and ']' is now <-, '[' is
REPT.
* Disk extension changed to the more common .dsk (and a2d.info to
dsk.info).
Changes in 0.01: (9 Oct 1994)
-----------------------------
* Standard VGA support with some performance degradation.
(When page flipping occurs, 64K memory banks are swapped;
hence the performance degradation.)
* -vga flag switch added, e.g. "apple2 -vga"; forces standard
VGA detection.
* (Trident) TVGA8900 page flipping bug fixed.
* File names may now contain any character codes. (The previous
version had some problems with compressing/uncompressing file
names with extraordinary characters.)
* Diskette selection retains last cursor position.

View File

@ -1,58 +0,0 @@
Known issues with the emulator:
Emulation Fidelity:
- Disk emulation. The emulator is not very realistic. It handles almost
all non-copyprotected disk access okay, but copyprotected or diagnostic
programs may be confused by our drive, which magically spins at precisely
the optimal speed. (see Specific Programs, below).
- Medium Resolution graphics. This is a rarely used //e mode that is to
Low-Res what 80 Column text is to 40 column text. We don't support it as
yet, although it should be relatively simple.
- We don't emulate the //e's vertical blanking interval detection feature.
This is on the TODO list.
Graphics:
- Composite graphics artifacts are not emulated. This is on the TODO list.
- B/W color setting does not apply to lores or double hires. This
generally is not an issue in practice though, as it's really only needed
to avoid color fringing in b/w hires images.
- Double Hires mode is always 140x192 color. Some applications use it as
a 560x192 b/w display however. Note that most applications indicate
which mode they want using the high bit of the dhires data bytes, so it
wouldn't need to be a preferences setting.
- If an 80-column mode is selected in the low-res emulator, nothing will
be written to the display -- the image from the last video mode will
remain on the screen. If a menu is brought up it will `stick'. This may
make people think the emulator crashed, although it will recover if the
application returns to 40-column mode.
Keyboard:
- Presently, the Backspace key is interpreted as Left-Arrow (Code 0x88).
It could be argued that it should be interpreted as Delete (Code 0xff)
instead. Real Apples had no seperate Backspace key, but the //e's Delete
key was in an analogous position to the PC's Backspace). The PC
keyboard's Delete is assigned to 0xff (in //e mode).
Specific Programs:
- Some programs (Computist's Nibbler, Sword of Kadash Master copy for
example) lock up. It appears (in debugger) that they are reading the disk
with the motor off. Perhaps they pulsed the real Apple's drive motor to make
it turn slower?
- ProDOS will refuse to format disks, claiming that the disk is too slow.
- ``Alternate Reality: The City'' seems to get jammed, rapidly changing
the video mode. I'm not sure if this is a real failure, or just a special
effect that takes longer than I'm willing to wait to finish (mode switches
would be much faster on a real Apple.) I can get into the program
with some nontrivial debugger manipulation to `short out' the offending loops.

408
README
View File

@ -1,408 +0,0 @@
**************************************************************************
* Apple II+ Emulator version 0.7 for Linux *
* *
* Original Author: Alexander Jean-Claude Bottema *
* Email : d91a1bo@meryl.csd.uu.se *
* *
* 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 of the License, or *
* (at your option) any later version. *
* *
* This program 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 this program; if not, write to the Free Software *
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
* *
* Modified: Dec 8 1995 by Stephen Lee <sl14@cornell.edu> *
* Modified: Jan 97 by Aaron Culliney <aculline@bbn.com> *
* <chernabog@baldmountain.bbn.com> *
* Modified: Jun 97 by Aaron Culliney <aculline@bbn.com> *
* <chernabog@baldmountain.bbn.com> *
* Modified: Feb 98 by Aaron Culliney <aculline@bbn.com> *
* <chernabog@baldmountain.bbn.com> *
* Modified: Aug 98 by Aaron Culliney <aculline@bbn.com> *
* <chernabog@baldmountain.bbn.com> *
* *
* Modified: Sep 98, Dec 98, Jan 98, Jun 99 *
* by Michael Deutschmann *
* <michael@talamasca.wkpowerlink.com>*
* *
**************************************************************************
NOTE:
This is basically the original README. See the CHANGES file
first for information on this specific version of the emulator. Then
read section 0 (installation) in this file for cookbook compilation /
installation instructions. Other information in this file may be
outdated, so read the manpage for up-to-date runtime instructions.
Contents
========
0. Installation issues (briefly)
1. Why did I make an Apple II+ Emulator when there are so many available?
2. System Files
3. Keyboard & keys
4. Diskette database
5. Future plans
6. Status of the current emulator
7. Can you port this to DOS?
8. Availability
9. Known problems
10. Changes since the last
11. Final words
0. Installation issues (briefly)
================================
Requirements: SVGA lib version 1.2.9 (or later, see 9.) Tested under 1.2.13.
Kernel 2.0.X. Tested under 2.0.36pre12.
libc 4.4.4 (or later). Tested under glibc-2.0.6
joystick 0.8.0 kernel module for PC Joystick support.
flex 2.5.2 (for compiling lex files)
1) Unpack this distribution in a temporary directory.
2) Run "configure" for a normal build, or "configure --enable-debugger"
to add debugger support. (Other GNU autoconf options, such as --prefix,
may of course be used.) You may provide optimizations in the enviroment
variable CFLAGS.
3) Type "make" to make the program. As root, type "make install" to
install the programs in $(BINDIR) and the man page in $(MANDIR), (both
configurable at the configure line). The sysadmin should enable the
setuser bit on "apple2" in order for it to use the VGA. Enabling setuser
on "xapple2" will allow use of the PC speaker, but is not essential.
4) Copy the .apple2 configuration file to your home directory, and
read the manpage that was installed for further information on how to
configure this file. You can configure most of the settings from
within the emulator, (F10 for the menu screen), but you need the
system path correctly set to point to the rom files before you start
the emulator. The emulator won't run without the correct rom files
(see the manpage).
5) After you're done configuring, type "xapple2" to run the
emulator under X or "apple2" to run using svgalib. There are two X11
versions for different resolutions. Once running, F1 selects disk
drive #1, F2 selects disk drive #2, F10 gives you the main menu of
runtime parameters, and F5 shows you the keyboard lay out.
1. Why did I make an Apple II+ Emulator when there are so many available?
=========================================================================
For three reasons. The first is that there are no Apple II emulators
especially written for Linux; hence they do not take advantages that
are commonly provided in Linux systems, e.g. the SVGA library
distribution. However, there is an Apple II emulator for X-Windows
that easily can be compiled on most UN*X architectures, but the major
disadvantage is slow emulation. You cannot obtain a fast emulator by
writing it in C, despite the elegant optimizations provided by GNU-C,
but of course it becomes more portable if you do that.
By contrast, this Apple II emulator is partly written in 386 (AT&T)
assembler; partly in C. Only those issues that were not time critical
were written in C. Especially the CPU emulation was written in
optimized assembler to achieve optimum performance. The emulator
approximately runs twice as fast as an ordinary Apple II+ computer if
it is running on a 486 DX-50.
The second reason is that there is no Apple II emulator which is 100%
usable. Either it is too slow or it is simply too fast (= there is no
option to trim the speed of the emulator). Furthermore, many
implementors happily avoid implementing mixed text/graphics in high
resolution mode; mostly because it complicates the programming. I have
never seen an Apple II+ emulator that is entirely complete. I think
most emulators fail on implementing the undocumented 6502 instructions
(those that are listed as ???), hence some games (or applications) may
not work despite they should.
The third reason is that no emulator support an easy way to switch
diskettes given a database of diskettes. With this emulator you can
easily switch diskettes through an intuitive interface. You can also
add additional information for each diskette (in your database), e.g.
which keys to use for a particular game program. The information is
kept in a plain text file that can be edited with an ordinary text
editor, preferbly GNU-Emacs.
(Aaron removed the above feature)
2. System files
===============
Before you can run the emulator, three vital system files must be
available. These are:
apple_II.rom (12k) This file contains the ROM of your Apple II+.
It is not distributed due to copyright issues.
You have to get this file on your own. If you
have been running another emulator, you can
probably use its ROM files directly. Technically
speaking, this file is a memory dump of the
consecutive addresses from D000 to FFFF. This
file may also be called by other names such as
apple.rom or apple2.rom, but is referenced
internally as apple_II.rom.
appple_IIe.rom Likewise for IIe emulation.
slot6.rom (256 bytes) Memory dump of the consecutive addresses from
C600 to C6FF. This file is not distributed
either due to the same reasons as above. This
file may also be called by other names such as
controller.rom, but is referenced internally
as slot6.rom.
Other important files
---------------------
.apple2 This file is distributed. The file contains default
parameter settings. The most important parameter is
the setting of the system path, i.e. the directory
where the three vital system (ROM) files are
stored. Most of the other parameters can be
changed during run time via the interface
(activated by pressing F10).
Parameters (that can be set in .apple2):
speed = <percentage>% Speed of the emulator
path = <directory> Diskette database directory
color = off Monochrome mode
on Plain color mode
interpolated Interpolated color mode
sound = off Silent mode
pc speaker Sound through PC speaker
joystick = off Joystick disabled
linear Linear joystick mode
pc joystick PC Joystick (see CHANGES)
digital (Atari) digital joystick mode
not yet supported
joystick range 1-256 range of joystick
origin_x = 0-255 Origin of the joystick (X)
origin_y = 0-255 Origin of the joystick (Y)
sensitivity <percentage>% Joystick sensitivity
system_path = <directory> Directory where the system
ROM files are stored.
pc joystick parms You don't want to fool
around with these, you
generate these parms from
F10 screen "Calibrate" menu
item.
dsk.info An example is distributed. This file contains
information for various diskettes kept in the database.
Syntax:
{<Name of diskette>} i.e. the name is written
within curly braces.
<Any information...>
{<Name of another diskette>} ... etc.
3. Keyboard & keys
==================
F1 To switch diskettes in Drive A, Slot 6
F2 To switch diskettes in Drive B, Slot 6
F4, also Pause key Pause
F5 Keyboard layout
F8 Words from the author (removed)
F9 Toggle between maximum speed and configured speed.
F10 General parameter settings
Break (ctrl-Pause) Apple II Reset key
Ctrl Printscreen Reboot Apple II emulator
The numeric keypad is used for joystick emulation.
Left Alt Joystick button 0
Right Alt Joystick button 1
Insert Joystick button 2
F7 Debugger - (if it's compiled into the source).
Edit the Makefile if you don't want the
debugger. The emulator will run slightly
faster without the debugger.
4. Diskette database
====================
The diskettes are provided as plain binary files. These are actually
raw dumps, containing the tracks from 0 to 34. For the standard
143360 byte .dsk format each track is partitioned into sectors
numbered from 0 to 15. Each sector is 256 bytes. Hence, the data is
organized as the following:
File offset (in bytes) Sector Track
---------------------- ------ -----
0 0 0
256 1 0
512 2 0
. . .
. . .
. . .
3840 15 0
4096 0 1
4352 1 1
. . .
. . .
. . .
143104 15 34
To transfer Apple II diskettes into this format requires that you own
an original Apple II. Since the drives provided by the IBM PC's are
not compatible with the original Apple II drives there are no
conversion programs directly available. If you have used other Apple
II emulators it is most likely that the files will work with this
emulator too. It seems to be a common standard to structure the
diskettes in the above described way, e.g. the ApplePC and Apl2em
emulators for DOS uses the same structure.
Emulated diskettes MUST have the .dsk or .nib extension (143360 bytes
or 232960 bytes respectively), otherwise the emulator will not
recognize the file as a valid diskette. However, it is valid to
compress them by using gnu-zip (then the extension becomes .dsk.gz or
.nib.gz). The emulator will automatically decompress/compress them
whenever required (note that it assumes /bin/gzip exists).
Note that you can add information/documentation for the dsk-diskettes
by using an ordinary text editor and edit the file "dsk.info". The
name of the diskette is written in curly braces (without the .dsk
extension) followed by any information.
5. Future plans
===============
I intend to improve the emulator. Actually, for the next major release
(i.e. version 1.00) the emulator will be entirely rewritten. For minor
changes, see file CHANGES.
[Note from Stephen Lee: since this hasn't happened yet, I took to
improving the existing emulator until Alexander release a new one.]
[Note from Aaron Culliney: since this still hasn't happened, I also
took the liberty to fix a few things and add some functionality.]
[v004 note: decided to add in some more features, and fix problems as
they've arisen.]
6. Status of the current emulator
=================================
Works in standard VGA Y
Works in SVGA (then using SVGA facilities) Y
Disk drive emulation (slot 6) (.dsk & .nib) Y
Disk drive emulation (slot 5) N (release 1.00)
High resolution emulation Y
Low resolution emulation Y
Mixed mode (in all resolution modes) Y
Correct color emulation (both Low and Hi-res) Y
Interpolated color emulation Y
Configurable speed Y
Diskette switching Y
Diskette database Y
Interface for parameter settings, etc. Y
Sound emulation (PC speaker) Y
Flashing text Y
Joystick emulation through numeric keypad Y
Virtual console switching Y (only when emu. is paused)
Raw keyboard mode (reading scancodes) Y
Undocumented 6502 instructions Y
Language card (i.e. additonal 16k RAM) Y
Serial card N (maybe release 1.00)
Works on DOS N (AND NEVER WILL)
Apple II Debugger Y
PC Joystick kernel module support Y
7. Can you port this to DOS?
============================
No, for two reasons. The first, and probably the most important, is
that the current emulator use kernel specific issues that are not, and
never will be, available in DOS. Future emulators will be based on the
same principles, so the emulator will never run in DOS. The second
reason is that DOS is a bad operating system. It is a pain in the neck
to write and debug programs in DOS and there is no usable memory
management provided by the kernel (if the DOS "interrupts" can be
called a "kernel" at all).
With this free software I hope that more users will switch to Linux
(which is a great operating system) and I have a dream that one day,
DOS will become a minority.
8. Availability
===============
This distribution is available at ftp.apple.asimov.net, and
tsx-11.mit.edu, and sites mirroring these.
9. Known problems
=================
Problem: SVGAlib version 1.2.8 and below has a bug in
keyboard-handling that makes the '-' key (in the emulator)
unusable.
Solution: Please use SVGAlib version 1.2.9 or above.
Problem: The REPT (repeat) key repeats too fast.
Answer: The current implementation is a kludge by me [Stephen]. I
might fix it later, but again, I might not.
Problem: I can't switch virtual consoles while running the program
under gdb [Aaron].
Answer: Actually you do, it's just that the graphic mode stays valid.
If you have the svgalib utility "textmode", try a "shell
textmode" reset. I haven't played around enough to figure out
how/if you can get back to graphic mode after this.
Problem: Compiling with optimization causes the debugger to choke
[Aaron].
Answer: I'm playing fast and loose with the debugger's assembly hooks.
I think it's something to do with -fomit-frame-pointer. And
since I don't see much speed gain, I'm ignoring the problem
for now.
10. Changes
===========
see file CHANGES
11. Final words
===============
Note that albeit you can switch between digital and linear joystick
emulation, only linear mode is presently supported. I still haven't
figured out how the atari (digital) joystick emulation works, so I'll
save it to the next release (as I said in the previous release :-) )
[PC Joystick mode is just another linear mode. In general linear mode
seems to work fine for most games. If a game seems to be having
trouble with it, change the range from $100 (256) to $80 (128) with
center points at $80 and $40 respectively. Switching around ranges
often seems to do the trick for most games, but I bet I'm not handling
the joystick softswich values correctly. documentation on this is
scant. -Aaron]
I hope you will enjoy this emulator. I do. Many games (that I have
ported) works perfectly, even those that use undocumented 6502
instructions. Suggestions to improvements are welcome. My email
address will be valid at least one more year, i.e. as long as I am a
computer science student at the University of Uppsala in Sweden.
/ Alexander Jean-Claude Bottema (Email: d91a1bo@meryl.csd.uu.se)
Oct. 9 1994 15:44:21

View File

@ -1,154 +0,0 @@
*************************************************************************
* *
* Apple II debugger routines for the Linux-x86 Apple II emulator. *
* by Aaron Culliney - chernabog@baldmountain.bbn.com - (C) 1998 *
* *
* My code changes have nothing to do with my employer, GTE *
* Internetworking, BBN Technologies. They were written completely on my*
* own time and on my own machine. *
* *
*************************************************************************
The debugger console is a mid-size hack onto the main emulator code.
I did it b/c I wanted to fool around with some of my old games while
they were running and to debug the emulator itself.
The code is kinda ugly in some areas, but seems robust enough; (I've
used flex to handle most of the dangerous UI stuff). It runs a bit
slower than when you're in unrestricted emulation mode because it's
doing a lot of switching between C code and asm, copying state, and
checking breakpoints/watchpoints.
KNOWN PROBLEMS:
--------------
When you hit a watchpoint or breakpoint, you have to step over it
before you can use the g{o}, f{inish}, or u{ntil} commands again.
----------------------------------------------------------------------------
Usage:
-----
F7 - enters the debugger. (actually we wait until we've finished with
the current 6502 instruction before we enter the debugger so we're all
synched up if/when we start stepping the machine).
ESC - exits the debugger console.
General Command format:
command {optional part} <mandatory part> (this | that)
----------------------------------------------------------------------------
Disassembling Apple II main memory and language card memory:
d{is} {language card bank} {/bank/}{addrs} {+}{len}
Examples:
"d" - disassemble from current location
"dis +5" - disassemble from current location +5
"dis /01/2000" - (128k (//e) specific)
"dis lc1 d000 5" - disassemble memory +5 at lang card 1 0xd000
Note: {addrs} can be (d000 <-> ffff) or (0 <-> 2fff) for the language
card.
----------------------------------------------------------------------------
Dumping memory:
m{em} {lc1|lc2} {addrs} {+}{len}
a{scii} {lc1|lc2} {addrs} {+}{len}
Examples:
"mem" - dump memory at current location
"m dead" - dump memory at 0xDEAD
"m lc2 2fff 1" - dump memory at lang card 2 0x2FFF +1
"ascii /01/400" - (128k (//e) specific)
Note: {addrs} can be (d000 <-> ffff) or (0 <-> 2fff) for the language
card. Also you need to specify the {addrs} if you're examining lc
memory.
----------------------------------------------------------------------------
Setting memory:
<addrs> {lc1|lc2} : <byteseq>
"4000:deadc0de" - set memory at 0x4000 to 0xDEADC0DE
"50lc2:def" - set memory at lang bank 2 0x50 to 0xDE0F
Note: {addrs} can be (d000 <-> ffff) or (0 <-> 2fff) for the language
card.
----------------------------------------------------------------------------
Displaying machine state (registers, language card, drive, softswitches):
r{egs} - registers
l{ang} - language card settings
dr{ive} - disk drive settings
vm - other virtual machine settings
----------------------------------------------------------------------------
Stepping the machine:
(s{tep} | n{ext}) {len}
f{inish}
u{ntil}
g{o} {addr}
-*step* or *next* 0-255 instructions.
-*finish* current stack-frame (stop at RTS).
-step *until* PC == next instruction (good for finishing loops).
-*go* or jump to {addr} and continue executing until user hits a key.
----------------------------------------------------------------------------
Searching and setting/unsetting memory breakpoints and watchpoints:
sea{rch} {lc1|lc2} <byteseq>
"se deadc0de" - search for 0xDEADC0DE
"search lc2 def" - search lang bank2 (and lang card) for 0xDEF
(b{reak} | w{atch}) {addr}
br{eak} op <byte>
(c{lear} | i{gnore}) {num}
c{lear} op <byte>
sta{tus} - show status of memory watch/breakpoints
op{codes} - show opcodes that we're stopping at
"w c0e9" - watch memory at C0E9
"br" - break at current PC
"br op 20" - break on opcode 20
"clear 1" - clear breakpoint 1
"ig" - ignore all watchpoints
"cl op 20" - clear break on opcode 20
-break or watch addrs. (use in conjunction with g{o})
-clear breakpoints, ignore watchpoints.
-show break and watchpoint status.
Note: breakpoints and watchpoints persist even when you exit the
debugger console. They are only reset if you clear/ignore them or
virtually reboot.
----------------------------------------------------------------------------
Loading and saving state:
bload <file> <addrs>
bsave <file> /<bank>/<addrs> <len>
bload binary 8000 - load file into memory
bsave pic /0/2000 2000 - save memory to file
----------------------------------------------------------------------------
Miscellaneous:
fr{esh} - clear screen of graphics

12
TODO
View File

@ -1,12 +0,0 @@
Obviously this emulator could use extension. We could do with support
for more disk-image formats, additional interface cards, memory-image
save/restore, and other things.
However, most of these things would require hooking into parts of the code
that are likely going to change radically in later versions of the
emulator, due to badly needed cleanups. Thus, I do not recommend trying
to extend this version alone.
If you want to work on the emulator, contact us first. We can give you alpha
versions of the emulator to work on, and coordinate your efforts with
other contributors.

View File

@ -96,7 +96,7 @@ LOCAL_SRC_FILES := \
# Embedded stackwalker sources (to be able to process crashes in-situ on Android device)
ifeq ($(EMBEDDED_STACKWALKER),1)
LOCAL_CPPFLAGS += -DEMBEDDED_STACKWALKER=1 -frtti
LOCAL_CPPFLAGS += -DAPPLE2IX -DEMBEDDED_STACKWALKER=1 -frtti
LOCAL_SRC_FILES += \
src/processor/basic_code_modules.cc \
src/processor/basic_source_line_resolver.cc \

View File

@ -106,7 +106,12 @@
#endif
// A wrapper for the tgkill syscall: send a signal to a specific thread.
#if APPLE2IX
#define tgkill _tgkill
static int _tgkill(pid_t tgid, pid_t tid, int sig) {
#else
static int tgkill(pid_t tgid, pid_t tid, int sig) {
#endif
return syscall(__NR_tgkill, tgid, tid, sig);
return 0;
}

View File

@ -43,6 +43,7 @@
extern "C" {
#endif // __cplusplus
#if !APPLE2IX
struct r_debug {
int r_version;
struct link_map* r_map;
@ -61,6 +62,7 @@ struct link_map {
struct link_map* l_next;
struct link_map* l_prev;
};
#endif // !APPLE2IX
#ifdef __cplusplus
} // extern "C"

View File

@ -46,7 +46,9 @@
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
#if !APPLE2IX
typedef struct user_fxsr_struct user_fpxregs_struct;
#endif // !APPLE2IX
#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus
@ -56,6 +58,7 @@ typedef struct user_fxsr_struct user_fpxregs_struct;
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
#if !APPLE2IX
struct user_regs_struct {
__u64 regs[31];
__u64 sp;
@ -67,6 +70,7 @@ struct user_fpsimd_struct {
__u32 fpsr;
__u32 fpcr;
};
#endif // !APPLE2IX
#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus

View File

@ -15,45 +15,115 @@
#include "cpu.h"
#include "glue-offsets.h"
// ARM register mappings
// r0, r1 are scratch regs, with r0 generally as the "important byte"
#define EffectiveAddr r2 /* 16bit Effective address */
#define PC_Reg r3 /* 16bit 6502 Program Counter */
#define SP_Reg r4 /* 16bit 6502 Stack pointer */
#define F_Reg r5 /* 8bit 6502 flags */
#define Y_Reg r6 /* 8bit 6502 Y register */
#define X_Reg r7 /* 8bit 6502 X register */
#define A_Reg r8 /* 8bit 6502 A register */
// r9 is "ARM platform register" ... used as a scratch register
#define reg_args r10 /* cpu65_run() args register */
#define reg_vmem_r r11 /* cpu65_vmem_r table address */
// r12 is "ARM Intra-Procedure-call scratch register" ... used as a scratch register
// r13 ARM SP
// r14 ARM return addr
// r15 ARM PC
#ifdef __aarch64__
# error 20150205 ARM 64bit untested!!!
# define PTR_SHIFT #4 // 4<<1 = 8
# define ROR_BIT 0x8000000000000000
#else
# define PTR_SHIFT #2 // 2<<1 = 4
# define ROR_BIT 0x80000000
#endif
#if !defined(__APPLE__)
# define NO_UNDERSCORES 1
# define STRBNE strneb
#endif
// ARM register mappings
#define bz beq
#define bnz bne
#define ROR_BIT 0x80000000
#define ROR_SHIFT 30
#ifdef __aarch64__
// NOTE: currently cpu.S contains unified ARM code that sacrifices using some 64bit scratch registers in favor of common codepath.
// We could further optimize the 64 bit port in the future if we separate it out ...
# define DOT_ARM
# define ALIGN .align 2;
# define PTR_SHIFT #3 // 1<<3 = 8
# define BLX blr
# define BX br
// x: 64bit addressing mode
// w: 32bit addressing mode
# define xr0 x0 /* scratch/"important byte" */
# define wr0 w0 /* scratch/"important byte" */
# define xr1 x1 /* scratch */
# define wr1 w1 /* scratch */
# define xr9 x2 /* scratch */
# define wr9 w2 /* scratch */
# define xr12 x5 /* overloaded both scratch */
# define wr12 w5 /* and also ... */
# define arm_flags x5 /* arm_flags (Flag_() macros) */
// NOTE: ARMv8 Procedure Call Standard indicates that x19-x28 are callee saved ... so we can call back into C without needing to
// first save these ...
# define xEffectiveAddr x19 /* 16bit Effective address */
# define EffectiveAddr w19 /* 16bit Effective address */
# define PC_Reg w20 /* 16bit 6502 Program Counter */
# define xSP_Reg x21 /* 16bit 6502 Stack pointer */
# define SP_Reg w21 /* 16bit 6502 Stack pointer */
# define xF_Reg x22 /* 8bit 6502 flags */
# define F_Reg w22 /* 8bit 6502 flags */
# define Y_Reg w23 /* 8bit 6502 Y register */
# define X_Reg w24 /* 8bit 6502 X register */
# define A_Reg w25 /* 8bit 6502 A register */
# define xA_Reg x25 /* 8bit 6502 A register */
# define reg_args x26 /* cpu65_run() args register */
# define reg_vmem_r x27 /* cpu65_vmem_r table address */
// x29 : frame pointer (callee-saved)
// x30 : return address
// xzr/wzr : zero register
// sp : stack pointer
// pc : instruction pointer
#else
# define STRBNE strbne
# define DOT_ARM .arm;
# define ALIGN .balign 4;
# define PTR_SHIFT #2 // 1<<2 = 4
# define BLX blx
# define BX bx
// r0, r1 are scratch regs, with r0 generally as the "important byte"
# define xr0 r0 /* scratch/"important byte" */
# define wr0 r0 /* scratch/"important byte" */
# define xr1 r1 /* scratch */
# define wr1 r1 /* scratch */
# define xr9 r9 /* scratch */
# define wr9 r9 /* scratch */
// NOTE: these need to be preserved in subroutine (C) invocations ... */
# define EffectiveAddr r2 /* 16bit Effective address */
# define xEffectiveAddr r2 /* 16bit Effective address */
# define PC_Reg r3 /* 16bit 6502 Program Counter */
// NOTE: ARMv7 PCS states : "A subroutine must preserve the contents of the registers r4-r8, r10, r11 and SP [...]"
# define xSP_Reg r4 /* 16bit 6502 Stack pointer */
# define SP_Reg r4 /* 16bit 6502 Stack pointer */
# define xF_Reg r5 /* 8bit 6502 flags */
# define F_Reg r5 /* 8bit 6502 flags */
# define Y_Reg r6 /* 8bit 6502 Y register */
# define X_Reg r7 /* 8bit 6502 X register */
# define A_Reg r8 /* 8bit 6502 A register */
# define xA_Reg r8 /* 8bit 6502 A register */
// r9 is "ARM platform register" ... used as a scratch register
# define reg_args r10 /* cpu65_run() args register */
# define reg_vmem_r r11 /* cpu65_vmem_r table address */
// r12 is "ARM Intra-Procedure-call scratch register" ... used as a scratch register
# define xr12 r12 /* overloaded both scratch */
# define wr12 r12 /* and also ... */
# define arm_flags r12 /* arm_flags (Flag_() macros) */
// r13 ARM SP
// r14 ARM LR (return addr)
// r15 ARM PC
#endif
#if NO_UNDERSCORES
# define ENTRY(x) .globl x; .arm; .balign 4; x##:
# define ENTRY(x) .global x; DOT_ARM ALIGN x##:
# define CALL(x) x
#else
# define ENTRY(x) .globl _##x; .arm; .balign 4; _##x##:
# define ENTRY(x) .global _##x; DOT_ARM ALIGN _##x##:
# define CALL(x) _##x
#endif

File diff suppressed because it is too large Load Diff

53
src/arm/glue-offsets64.h Normal file
View File

@ -0,0 +1,53 @@
/* This file is auto-generated for a specific architecture ABI */
#define UNUSED0 0
#define CPU65_TRACE_PROLOGUE 8
#define CPU65_TRACE_ARG 16
#define UNUSED1 24
#define UNUSED2 32
#define CPU65_TRACE_EPILOGUE 40
#define CPU65_TRACE_IRQ 48
#define DEBUG_ILLEGAL_BCD 56
#define CPU65_VMEM_R 64
#define CPU65_VMEM_W 72
#define CPU65_FLAGS_ENCODE 80
#define CPU65_FLAGS_DECODE 88
#define CPU65__OPCODES 96
#define CPU65__OPCYCLES 104
#define BASE_RAMRD 112
#define BASE_RAMWRT 120
#define BASE_TEXTRD 128
#define BASE_TEXTWRT 136
#define BASE_HGRRD 144
#define BASE_HGRWRT 152
#define BASE_STACKZP 160
#define BASE_D000_RD 168
#define BASE_E000_RD 176
#define BASE_D000_WRT 184
#define BASE_E000_WRT 192
#define BASE_C3ROM 200
#define BASE_C4ROM 208
#define BASE_C5ROM 216
#define BASE_CXROM 224
#define SOFTSWITCHES 232
#define GC_CYCLES_TIMER_0 236
#define GC_CYCLES_TIMER_1 240
#define CPU65_CYCLES_TO_EXECUTE 244
#define CPU65_CYCLE_COUNT 248
#define UNUSED3 252
#define INTERRUPT_VECTOR 256
#define RESET_VECTOR 258
#define CPU65_PC 260
#define CPU65_EA 262
#define CPU65_A 264
#define CPU65_F 265
#define CPU65_X 266
#define CPU65_Y 267
#define CPU65_SP 268
#define CPU65_D 269
#define CPU65_RW 270
#define CPU65_OPCODE 271
#define CPU65_OPCYCLES 272
#define CPU65__SIGNAL 273
#define JOY_BUTTON0 274
#define JOY_BUTTON1 275
#define EMUL_REINITIALIZE 276

View File

@ -12,79 +12,123 @@
#include "vm.h"
#include "cpu-regs.h"
#if __aarch64__
# define _GLUE_REG_SAVE \
stp x29, x30, [sp, -16]!;
# define _GLUE_REG_RESTORE \
ldp x29, x30, [sp], 16; \
ret
# define _GLUE_REG_SAVE0 \
stp x0, x30, [sp, -16]!;
# define _GLUE_REG_RESTORE0 \
ldp x0, x30, [sp], 16; \
ret
# define _GLUE_RET \
ret
#else
# define _GLUE_REG_SAVE \
push {EffectiveAddr, PC_Reg, lr};
# define _GLUE_REG_RESTORE \
pop {EffectiveAddr, PC_Reg, pc};
# define _GLUE_REG_SAVE0 \
push {r0, EffectiveAddr, PC_Reg, lr};
# define _GLUE_REG_RESTORE0 \
pop {r0, EffectiveAddr, PC_Reg, pc};
# define _GLUE_RET \
mov pc, lr;
#endif
#define GLUE_EXTERN_C_READ(func)
#define _GLUE_BANK_MAYBE_READ_CX(func,x,pointer) \
ENTRY(func) ldr r0, [reg_args, #SOFTSWITCHES]; \
ldr r1, [reg_args, x ## pointer]; \
tst r0, $SS_CXROM; \
bne 1f; \
push {EffectiveAddr, PC_Reg, lr}; \
blx r1; \
pop {EffectiveAddr, PC_Reg, pc}; \
1: ldrb r0, [r1, EffectiveAddr]; \
mov pc, lr;
ENTRY(func) ldr wr0, [reg_args, #SOFTSWITCHES]; \
ldr xr1, [reg_args, x ## pointer]; \
tst wr0, #SS_CXROM; \
bnz 1f; \
_GLUE_REG_SAVE; \
BLX xr1; \
_GLUE_REG_RESTORE; \
1: ldrb wr0, [xr1, xEffectiveAddr]; \
_GLUE_RET
#define GLUE_BANK_MAYBE_READ_CX(func,pointer) _GLUE_BANK_MAYBE_READ_CX(func,#,pointer)
#define _GLUE_BANK_MAYBE_READ_C3(func,x,pointer) \
ENTRY(func) ldr r0, [reg_args, #SOFTSWITCHES]; \
ldr r1, [reg_args, x ## pointer]; \
tst r0, $SS_CXROM; \
bne 1f; \
tst r0, $SS_C3ROM; \
bne 1f; \
push {EffectiveAddr, PC_Reg, lr}; \
blx r1; \
pop {EffectiveAddr, PC_Reg, pc}; \
1: ldrb r0, [r1, EffectiveAddr]; \
mov pc, lr;
ENTRY(func) ldr wr0, [reg_args, #SOFTSWITCHES]; \
ldr xr1, [reg_args, x ## pointer]; \
tst wr0, #SS_CXROM; \
bnz 1f; \
tst wr0, #SS_C3ROM; \
bnz 1f; \
_GLUE_REG_SAVE; \
BLX xr1; \
_GLUE_REG_RESTORE; \
1: ldrb wr0, [xr1, xEffectiveAddr]; \
_GLUE_RET
#define GLUE_BANK_MAYBE_READ_C3(func,pointer) _GLUE_BANK_MAYBE_READ_C3(func,#,pointer)
#define _GLUE_BANK_READ(func,x,pointer) \
ENTRY(func) ldr r1, [reg_args, x ## pointer]; \
ldrb r0, [r1, EffectiveAddr]; \
mov pc, lr;
ENTRY(func) ldr xr1, [reg_args, x ## pointer]; \
ldrb wr0, [xr1, xEffectiveAddr]; \
_GLUE_RET
#define GLUE_BANK_READ(func,pointer) _GLUE_BANK_READ(func,#,pointer)
#define _GLUE_BANK_WRITE(func,x,pointer) \
ENTRY(func) ldr r1, [reg_args, x ## pointer]; \
strb r0, [r1, EffectiveAddr]; \
mov pc, lr;
ENTRY(func) ldr xr1, [reg_args, x ## pointer]; \
strb wr0, [xr1, xEffectiveAddr]; \
_GLUE_RET
#define GLUE_BANK_WRITE(func,pointer) _GLUE_BANK_WRITE(func,#,pointer)
#define _GLUE_BANK_MAYBEWRITE(func,x,pointer) \
ENTRY(func) ldr r1, [reg_args, x ## pointer]; \
teq r1, #0; \
STRBNE r0, [r1, EffectiveAddr]; \
mov pc, lr;
ENTRY(func) ldr xr1, [reg_args, x ## pointer]; \
eor xr12, xr12, xr12; \
eor xr1, xr1, xr12; \
cmp xr1, #0; \
bz 1f; \
strb wr0, [xr1, xEffectiveAddr]; \
1: _GLUE_RET
#define GLUE_BANK_MAYBEWRITE(func,pointer) _GLUE_BANK_MAYBEWRITE(func,#,pointer)
#define _GLUE_INLINE_READ(func,x,off) \
ENTRY(func) ldrb r0, [reg_args, x ## off]; \
mov pc, lr;
ENTRY(func) ldrb wr0, [reg_args, x ## off]; \
_GLUE_RET
#define GLUE_INLINE_READ(func,off) _GLUE_INLINE_READ(func,#,off)
#define GLUE_NOP(func) \
ENTRY(func) _GLUE_RET
#define GLUE_C_WRITE(func) \
ENTRY(func) push {r0, EffectiveAddr, PC_Reg, lr}; \
and r0, r0, #0xff; \
mov r1, r0; \
mov r0, EffectiveAddr; \
ENTRY(func) _GLUE_REG_SAVE0; \
and wr0, wr0, #0xff; \
mov wr1, wr0; \
mov wr0, EffectiveAddr; \
bl CALL(c_##func); \
pop {r0, EffectiveAddr, PC_Reg, pc};
_GLUE_REG_RESTORE0;
#define GLUE_C_READ(func) \
ENTRY(func) push {EffectiveAddr, PC_Reg, lr}; \
mov r0, EffectiveAddr; \
ENTRY(func) _GLUE_REG_SAVE; \
mov wr0, EffectiveAddr; \
bl CALL(c_##func); \
pop {EffectiveAddr, PC_Reg, pc};
_GLUE_REG_RESTORE;
#define GLUE_C_READ_ALTZP(FUNC) GLUE_C_READ(FUNC)

View File

@ -1253,7 +1253,11 @@ static void MB_Update()
assert(requestedBufSize <= originalRequestedBufSize);
++counter;
} while (bufIdx < originalRequestedBufSize && counter < 2);
assert(bufIdx == originalRequestedBufSize);
if (UNLIKELY(bufIdx != originalRequestedBufSize)) {
// platform audio system getting bogged down?
LOG("WHOA, mockingboard dropping samples %lu != %lu", bufIdx, originalRequestedBufSize);
}
# endif
#endif
@ -1712,11 +1716,8 @@ static bool MB_DSInit()
#if 1 // APPLE2IX
{
int err = 0;
if ((err = pthread_create(&g_hThread, NULL, SSI263Thread, NULL)))
{
LOG("SSI263Thread");
}
int err = TEMP_FAILURE_RETRY(pthread_create(&g_hThread, NULL, SSI263Thread, NULL));
assert(!err);
// assuming time critical ...
# if defined(__APPLE__) || defined(ANDROID)
@ -2255,8 +2256,8 @@ static void RegisterIoHandler(unsigned int uSlot, iorfunction IOReadC0, iowfunct
assert((uintptr_t)IOWriteC0);
for (unsigned int i = 0; i < 16; i++)
{
cpu65_vmem_r[base_addr+i] = IOReadC0;
cpu65_vmem_w[base_addr+i] = IOWriteC0;
cpu65_vmem_r[(base_addr+i)>>8] = IOReadC0;
cpu65_vmem_w[(base_addr+i)>>8] = IOWriteC0;
}
}
@ -2264,8 +2265,8 @@ static void RegisterIoHandler(unsigned int uSlot, iorfunction IOReadC0, iowfunct
base_addr = 0xC000 + (uSlot<<8); // uSlot == 4 => 0xC400 , uSlot == 5 => 0xC500
for (unsigned int i = 0; i < 0x100; i++)
{
//cpu65_vmem_r[base_addr+i] = IOReadCx; -- CANNOT DO THIS HERE -- DEPENDS ON cxrom softswitch
cpu65_vmem_w[base_addr+i] = IOWriteCx;
//cpu65_vmem_r[(base_addr+i)>>8] = IOReadCx; -- CANNOT DO THIS HERE -- DEPENDS ON cxrom softswitch
cpu65_vmem_w[(base_addr+i)>>8] = IOWriteCx;
}
}
#endif
@ -2344,8 +2345,11 @@ void MB_StartOfCpuExecute()
}
// Called by ContinueExecution() at the end of every video frame
void MB_EndOfVideoFrame()
static void MB_EndOfVideoFrame(uint8_t unused)
{
#if 1 // APPLE2IX
(void)unused;
#endif
if(g_SoundcardType == CT_Empty)
return;
#if MB_TRACING
@ -2619,10 +2623,6 @@ static void mb_prefsChanged(const char *domain) {
MB_SetVolumeZeroToTen(goesToTen);
}
static __attribute__((constructor)) void _init_mockingboard(void) {
prefs_registerListener(PREF_DOMAIN_AUDIO, &mb_prefsChanged);
}
static bool _sy6522_saveState(StateHelper_s *helper, SY6522 *sy6522) {
int fd = helper->fd;
@ -3399,3 +3399,13 @@ void mb_traceEnd(void) {
}
}
#endif
static void _init_mockingboard(void) {
prefs_registerListener(PREF_DOMAIN_AUDIO, &mb_prefsChanged);
static video_frame_callback_fn frameCallback = &MB_EndOfVideoFrame;
video_registerFrameCallback(&frameCallback);
}
static __attribute__((constructor)) void __init_mockingboard(void) {
emulator_registerStartupCallback(CTOR_PRIORITY_EARLY, &_init_mockingboard);
}

View File

@ -105,7 +105,6 @@ void MB_InitializeIO(char *pCxRomPeripheral, unsigned int uSlot4, unsigned in
void MB_Mute(void);
void MB_Demute(void);
void MB_StartOfCpuExecute(void);
void MB_EndOfVideoFrame(void);
void MB_UpdateCycles(void);
SS_CARDTYPE MB_GetSoundcardType(void);
void MB_SetSoundcardType(SS_CARDTYPE NewSoundcardType);

View File

@ -276,7 +276,6 @@ PlayQueue_s *playq_createPlayQueue(const unsigned int *nodeIdPtr, unsigned long
#define SELF_TEST 0
#if SELF_TEST
bool do_logging = true;
static void _test_creation(void) {
LOG("begin test");

View File

@ -63,7 +63,7 @@ typedef struct EngineContext_s {
} EngineContext_s;
static AudioBackend_s opensles_audio_backend = { 0 };
static AudioBackend_s opensles_audio_backend = { { 0 } };
// ----------------------------------------------------------------------------
// AudioBuffer_s internal processing routines
@ -71,10 +71,10 @@ static AudioBackend_s opensles_audio_backend = { 0 };
// Check and resets underrun condition (readHead has advanced beyond writeHead)
static inline bool _underrun_check_and_manage(SLVoice *voice, OUTPARM unsigned long *workingBytes) {
SPINLOCK_ACQUIRE(&voice->spinLock);
SPIN_LOCK_FULL(&voice->spinLock);
unsigned long readHead = voice->readHead;
unsigned long readWrapCount = voice->readWrapCount;
SPINLOCK_RELINQUISH(&voice->spinLock);
SPIN_UNLOCK_FULL(&voice->spinLock);
assert(readHead < voice->bufferSize);
assert(voice->writeHead < voice->bufferSize);
@ -160,10 +160,10 @@ static void bqPlayerCallback(SLAndroidSimpleBufferQueueItf bq, void *context) {
++newReadWrapCount0;
}
SPINLOCK_ACQUIRE(&voice0->spinLock);
SPIN_LOCK_FULL(&voice0->spinLock);
voice0->readHead = newReadHead0;
voice0->readWrapCount = newReadWrapCount0;
SPINLOCK_RELINQUISH(&voice0->spinLock);
SPIN_UNLOCK_FULL(&voice0->spinLock);
if (voice1) {
memset(voice1->ringBuffer+voice1->readHead, 0x0, ctx->submitSize); // backfill quiet samples
@ -178,10 +178,10 @@ static void bqPlayerCallback(SLAndroidSimpleBufferQueueItf bq, void *context) {
++newReadWrapCount1;
}
SPINLOCK_ACQUIRE(&voice1->spinLock);
SPIN_LOCK_FULL(&voice1->spinLock);
voice1->readHead = newReadHead1;
voice1->readWrapCount = newReadWrapCount1;
SPINLOCK_RELINQUISH(&voice1->spinLock);
SPIN_UNLOCK_FULL(&voice1->spinLock);
}
} while (0);
@ -230,6 +230,8 @@ static long SLGetPosition(AudioBuffer_s *_this, OUTPARM unsigned long *bytes_que
assert(workingBytes <= voice->bufferSize);
*bytes_queued = workingBytes;
(void)queuedBytes;
} while (0);
return err;
@ -316,10 +318,10 @@ static long SLUnlockStaticBuffer(AudioBuffer_s *_this, unsigned long audio_bytes
static long SLReplay(AudioBuffer_s *_this) {
SLVoice *voice = (SLVoice*)_this->_internal;
SPINLOCK_ACQUIRE(&voice->spinLock);
SPIN_LOCK_FULL(&voice->spinLock);
voice->readHead = 0;
voice->writeHead = voice->replay_index;
SPINLOCK_RELINQUISH(&voice->spinLock);
SPIN_UNLOCK_FULL(&voice->spinLock);
long err = _SLMaybeSubmitAndStart(voice);
#warning FIXME TODO ... how do we handle mockingboard for new OpenSLES buffer queue codepath?
@ -729,6 +731,7 @@ static long opensles_systemPause(AudioContext_s *audio_context) {
EngineContext_s *ctx = (EngineContext_s *)(audio_context->_internal);
SLresult result = (*(ctx->bqPlayerPlay))->SetPlayState(ctx->bqPlayerPlay, SL_PLAYSTATE_PAUSED);
(void)result;
return 0;
}
@ -751,7 +754,7 @@ static long opensles_systemResume(AudioContext_s *audio_context) {
if (state == SL_PLAYSTATE_PAUSED) {
// Balanced resume OK here
SLresult result = (*(ctx->bqPlayerPlay))->SetPlayState(ctx->bqPlayerPlay, SL_PLAYSTATE_PLAYING);
result = (*(ctx->bqPlayerPlay))->SetPlayState(ctx->bqPlayerPlay, SL_PLAYSTATE_PLAYING);
} else if (state == SL_PLAYSTATE_STOPPED) {
// Do not resume for stopped state, let this get forced from CPU/speaker thread otherwise we starve. (The
// stopped state happens if user dynamically changed buffer parameters in menu settings which triggered an

View File

@ -519,8 +519,7 @@ double speaker_cyclesPerSample(void) {
// --------------------------------------------------------------------------------------------------------------------
// VM system entry point
GLUE_C_READ(speaker_toggle)
{
uint8_t speaker_toggle(void) {
ASSERT_ON_CPU_THREAD();
timing_checkpointCycles();

View File

@ -21,6 +21,7 @@ void speaker_destroy(void) CALL_ON_CPU_THREAD;
void speaker_reset(void);
void speaker_flush(void) CALL_ON_CPU_THREAD;
bool speaker_isActive(void);
uint8_t speaker_toggle(void) CALL_ON_CPU_THREAD;
/*
* returns the machine cycles per sample

View File

@ -26,13 +26,13 @@
#define PUBLIC
#define READONLY
#define CALL_ON_UI_THREAD
#define CALL_ON_UI_THREAD // function should only be called on UI thread
#define ASSERT_ON_UI_THREAD() \
assert(video_isRenderThread())
#define ASSERT_NOT_ON_UI_THREAD() \
assert(!video_isRenderThread())
#define CALL_ON_CPU_THREAD
#define CALL_ON_CPU_THREAD // function should only be called on CPU thread
#define ASSERT_ON_CPU_THREAD() \
assert(timing_isCPUThread())
#define ASSERT_NOT_ON_CPU_THREAD() \
@ -117,20 +117,26 @@
#define MAX(a,b) (((a) >= (b)) ? (a) : (b))
#endif
#define SPINLOCK_INIT 0
#define SPINLOCK_ACQUIRED -1
#define SPINLOCK_ACQUIRE(x) \
#define SPINLOCK_INIT 0L
/*
https://gcc.gnu.org/onlinedocs/gcc/_005f_005fsync-Builtins.html
"In most cases, these built-in functions are considered a full barrier. That is, no memory operand is moved across
the operation, either forward or backward. Further, instructions are issued as necessary to prevent the processor
from speculating loads across the operation and from queuing stores after the operation."
*/
#define SPIN_LOCK_FULL(x) \
do { \
long val = __sync_sub_and_fetch((x), 1); \
if (val == SPINLOCK_ACQUIRED) { \
long prev = __sync_fetch_and_or((x), 1L); \
if (prev == SPINLOCK_INIT) { \
break; \
} \
__sync_add_and_fetch((x), 1); \
} while (1);
#define SPINLOCK_RELINQUISH(x) \
__sync_add_and_fetch((x), 1);
usleep(1); \
} while (1)
#define SPIN_UNLOCK_FULL(x) \
__sync_fetch_and_and((x), SPINLOCK_INIT)
// cribbed from AOSP and modified with usleep() and to also ignore EAGAIN (should this be a different errno than EINTR)
#define TEMP_FAILURE_RETRY_FOPEN(exp) ({ \
@ -167,5 +173,26 @@
_rc; })
#endif
#define _STRINGIFY(x) #x
// diagnostic suppression
#if defined(__clang__)
// NOTE: check for Clang first, since it also defines __GNUC__
# define DIAGNOSTIC_SUPPRESS_PUSH(clang_diag, gcc_diag) \
_Pragma("clang diagnostic push") \
_Pragma(_STRINGIFY(clang diagnostic ignored clang_diag))
# define DIAGNOSTIC_SUPPRESS_POP() \
_Pragma("clang diagnostic pop")
#elif defined(__GNUC__)
# define DIAGNOSTIC_SUPPRESS_PUSH(clang_diag, gcc_diag) \
_Pragma("GCC diagnostic push") \
_Pragma(_STRINGIFY(GCC diagnostic ignored gcc_diag))
# define DIAGNOSTIC_SUPPRESS_POP() \
_Pragma("GCC diagnostic pop")
#else
# error "unknown possibly unsupported compiler!"
#endif
#endif // whole file

View File

@ -29,8 +29,8 @@ static pthread_mutex_t irq_mutex = PTHREAD_MUTEX_INITIALIZER;
uint8_t cpu65_flags_encode[256] = { 0 };
uint8_t cpu65_flags_decode[256] = { 0 };
void *cpu65_vmem_r[0x10000] = { 0 };
void *cpu65_vmem_w[0x10000] = { 0 };
void *cpu65_vmem_r[256] = { 0 };
void *cpu65_vmem_w[256] = { 0 };
#if CPU_TRACING
static int8_t opargs[3] = { 0 };
@ -567,8 +567,8 @@ uint8_t cpu65__opcycles[256] = {
// NOTE: currently this is a conversion table between i386 flags <-> 6502 P register
static void init_flags_conversion_tables(void) {
for (unsigned i = 0; i < 256; i++) {
unsigned char val = 0;
for (unsigned int i = 0; i < 256; i++) {
uint8_t val = 0;
if (i & C_Flag) {
val |= C_Flag_6502;
@ -603,7 +603,7 @@ static void init_flags_conversion_tables(void) {
}
cpu65_flags_encode[ i ] = val;
cpu65_flags_decode[ val ] = i;
cpu65_flags_decode[ val ] = (uint8_t)i;
}
}
@ -662,7 +662,7 @@ void cpu65_uninterrupt(int reason) {
}
void cpu65_reboot(void) {
run_args.joy_button0 = 0xff; // OpenApple -- should be balanced by c_joystick_reset() triggers on CPU thread
run_args.joy_button0 = 0xff; // OpenApple -- should be balanced by joystick_reset() triggers on CPU thread
cpu65_interrupt(ResetSig);
}
@ -752,6 +752,8 @@ bool cpu65_loadState(StateHelper_s *helper) {
}
#if CPU_TRACING
extern const struct opcode_struct_s opcodes_65c02[256];
extern const uint8_t opcodes_65c02_numargs[256];
/* -------------------------------------------------------------------------
CPU Tracing routines

View File

@ -47,8 +47,8 @@ extern bool cpu65_loadState(StateHelper_s *helper);
extern void cpu65_direct_write(int ea,int data);
extern void *cpu65_vmem_r[65536];
extern void *cpu65_vmem_w[65536];
extern void *cpu65_vmem_r[256];
extern void *cpu65_vmem_w[256];
extern uint8_t cpu65_flags_encode[256];
extern uint8_t cpu65_flags_decode[256];
@ -87,8 +87,9 @@ void cpu65_trace_checkpoint(void);
# define D_Flag 0x20 /* 6502 Decimal mode */
# define Z_Flag 0x40 /* 6502 Zero */
# define N_Flag 0x80 /* 6502 Negative */
#elif defined(__arm__)
#elif defined(__arm__) || defined(__aarch64__)
// VCZN positions match positions of shifted status register
// ALSO NOTE : changing these WILL AFFECT custom shifting in arm/cpu.S ...
# define V_Flag 0x1
# define C_Flag 0x2
# define Z_Flag 0x4
@ -101,10 +102,7 @@ void cpu65_trace_checkpoint(void);
# define I_Flag 0x20
# define B_Flag 0x40
# define D_Flag 0x80
# define BX_Flags 0x50
# define BI_Flags 0x60
#elif defined(__aarch64__)
# error soon ...
#else
# error unknown machine architecture
#endif

View File

@ -679,8 +679,7 @@ static void _disk_modeSelect(uint16_t ea) {
disk6.ddrw = (ea & 0x1);
}
GLUE_C_READ(disk6_ioRead)
{
uint8_t disk6_ioRead(uint16_t ea) {
uint8_t sw = ea & 0xf;
if (sw <= 0x7) { // C0E0 - C0E7
_disk6_phaseChange(ea);
@ -714,8 +713,7 @@ GLUE_C_READ(disk6_ioRead)
return (ea & 1) ? floating_bus() : disk6.disk_byte;
}
GLUE_C_WRITE(disk6_ioWrite)
{
void disk6_ioWrite(uint16_t ea, uint8_t b) {
uint8_t sw = ea & 0xf;
if (sw <= 0x7) { // C0E0 - C0E7
_disk6_phaseChange(ea);
@ -761,14 +759,6 @@ void disk6_init(void) {
// load Disk II ROM
memcpy(apple_ii_64k[0] + 0xC600, slot6_rom, 0x100);
// disk softswitches
// 0xC0Xi : X = slot 0x6 + 0x8 == 0xE
for (unsigned int i = 0xC0E0; i < 0xC0F0; i++) {
cpu65_vmem_r[i] = disk6_ioRead;
cpu65_vmem_w[i] = disk6_ioWrite;
}
stepper_phases = 0;
disk6.disk[0].phase = disk6.disk[1].phase = 0;
@ -819,32 +809,32 @@ const char *disk6_eject(int drive) {
TEMP_FAILURE_RETRY(ret = msync(disk6.disk[drive].raw_image_data, disk6.disk[drive].whole_len, MS_SYNC));
if (ret) {
LOG("Error syncing file %s", disk6.disk[drive].file_name);
LOG("Error syncing file %s (%s)", disk6.disk[drive].file_name, strerror(errno));
}
}
}
TEMP_FAILURE_RETRY(ret = munmap(disk6.disk[drive].raw_image_data, disk6.disk[drive].whole_len));
if (ret) {
LOG("Error munmap()ping file %s", disk6.disk[drive].file_name);
LOG("Error munmap()ping file %s (%s)", disk6.disk[drive].file_name, strerror(errno));
}
}
if (compressed_size > 0) {
TEMP_FAILURE_RETRY(ret = ftruncate(disk6.disk[drive].fd, compressed_size));
if (ret == -1) {
LOG("OOPS, cannot truncate file descriptor!");
LOG("OOPS, cannot truncate file descriptor! (%s)", strerror(errno));
}
}
TEMP_FAILURE_RETRY(ret = fsync(disk6.disk[drive].fd));
if (ret) {
LOG("Error fsync()ing file %s", disk6.disk[drive].file_name);
LOG("Error fsync()ing file %s (%s)", disk6.disk[drive].file_name, strerror(errno));
}
TEMP_FAILURE_RETRY(ret = close(disk6.disk[drive].fd));
if (ret) {
LOG("Error close()ing file %s", disk6.disk[drive].file_name);
LOG("Error close()ing file %s (%s)", disk6.disk[drive].file_name, strerror(errno));
}
}
@ -913,7 +903,7 @@ const char *disk6_insert(int fd, int drive, const char * const file_name, int re
// disk images inserted read/write are mmap'd/inflated in place ...
TEMP_FAILURE_RETRY(fd = dup(fd));
if (fd == -1) {
LOG("OOPS, could not dup() file descriptor %d", fd);
LOG("OOPS, could not dup() file descriptor %d (%s)", fd, strerror(errno));
err = ERR_CANNOT_DUP;
break;
}
@ -928,7 +918,7 @@ const char *disk6_insert(int fd, int drive, const char * const file_name, int re
TEMP_FAILURE_RETRY( (long)(disk6.disk[drive].raw_image_data = mmap(NULL, disk6.disk[drive].whole_len, (readonly ? PROT_READ : PROT_READ|PROT_WRITE), MAP_SHARED|MAP_FILE, disk6.disk[drive].fd, /*offset:*/0)) );
if (disk6.disk[drive].raw_image_data == MAP_FAILED) {
LOG("OOPS, could not mmap file %s", disk6.disk[drive].file_name);
LOG("OOPS, could not mmap file %s (%s)", disk6.disk[drive].file_name, strerror(errno));
err = ERR_MMAP_FAILED;
break;
}
@ -1007,7 +997,7 @@ void disk6_flush(int drive) {
int ret = -1;
TEMP_FAILURE_RETRY(ret = msync(disk6.disk[drive].raw_image_data, disk6.disk[drive].whole_len, MS_SYNC));
if (ret) {
LOG("Error syncing file %s", disk6.disk[drive].file_name);
LOG("Error syncing file %s (%s)", disk6.disk[drive].file_name, strerror(errno));
}
}

View File

@ -87,10 +87,15 @@ extern const char *disk6_eject(int drive);
// flush all I/O
extern void disk6_flush(int drive);
// save/restore state handling
extern bool disk6_saveState(StateHelper_s *helper);
extern bool disk6_loadState(StateHelper_s *helper);
extern bool disk6_stateExtractDiskPaths(StateHelper_s *helper, JSON_ref json);
// CPU thread I/O
extern uint8_t disk6_ioRead(uint16_t ea) CALL_ON_CPU_THREAD;
extern void disk6_ioWrite(uint16_t ea, uint8_t b) CALL_ON_CPU_THREAD;
#if DISK_TRACING
void disk6_traceToggle(const char *read_file, const char *write_file);
void disk6_traceBegin(const char *read_file, const char *write_file);

View File

@ -698,16 +698,6 @@ static void _display_plotChar(PIXEL_TYPE *fboff, const unsigned int fbPixWidth,
_plot_char80(&fboff, &src, fbPixWidth);
}
#if INTERFACE_CLASSIC
void display_plotChar(const uint8_t col, const uint8_t row, const interface_colorscheme_t cs, const uint8_t c) {
assert(col < 80);
assert(row < 24);
unsigned int off = row * SCANWIDTH * FONT_HEIGHT_PIXELS + col * FONT80_WIDTH_PIXELS + _FB_OFF;
_display_plotChar(fbFull+off, SCANWIDTH, cs, c);
video_setDirty(FB_DIRTY_FLAG);
}
#endif
static void _display_plotLine(PIXEL_TYPE *fb, const unsigned int fbPixWidth, const unsigned int xAdjust, const uint8_t col, const uint8_t row, const interface_colorscheme_t cs, const char *line) {
for (uint8_t x=col; *line; x++, line++) {
char c = *line;
@ -717,6 +707,14 @@ static void _display_plotLine(PIXEL_TYPE *fb, const unsigned int fbPixWidth, con
}
#if INTERFACE_CLASSIC
void display_plotChar(const uint8_t col, const uint8_t row, const interface_colorscheme_t cs, const uint8_t c) {
assert(col < 80);
assert(row < 24);
unsigned int off = row * SCANWIDTH * FONT_HEIGHT_PIXELS + col * FONT80_WIDTH_PIXELS + _FB_OFF;
_display_plotChar(fbFull+off, SCANWIDTH, cs, c);
video_setDirty(FB_DIRTY_FLAG);
}
void display_plotLine(const uint8_t col, const uint8_t row, const interface_colorscheme_t cs, const char *message) {
_display_plotLine(fbFull, /*fbPixWidth:*/SCANWIDTH, /*xAdjust:*/_FB_OFF, col, row, cs, message);
video_setDirty(FB_DIRTY_FLAG);
@ -957,6 +955,11 @@ void display_flashText(void) {
}
PIXEL_TYPE *display_getCurrentFramebuffer(void) {
#if INTERFACE_CLASSIC
if (interface_isShowing()) {
memcpy(/*dst:*/fbDone, /*src:*/fbFull, sizeof(fbDone));
}
#endif
return fbDone;
}

View File

@ -30,6 +30,8 @@
#define GLUE_EXTERN_C_READ(func) extern uint8_t func(uint16_t)
#define GLUE_NOP(func) extern void func(void);
#if VM_TRACING
#define GLUE_C_WRITE(func) \
@ -186,7 +188,7 @@ typedef struct cpu65_run_args_s {
#define OUTPUT_CPU65_RW() printf("#define CPU65_RW %ld\n", offsetof(cpu65_run_args_s, cpu65_rw))
uint8_t cpu65_opcode; // Last opcode
#define OUTPUT_CPU65_OPCODE() printf("#define CPU65_OPCODE %ld\n", offsetof(cpu65_run_args_s, cpu65_opcode))
uint8_t cpu65_opcycles; // Last opcode extra cycles
uint8_t cpu65_opcycles; // Last opcode cycles
#define OUTPUT_CPU65_OPCYCLES() printf("#define CPU65_OPCYCLES %ld\n", offsetof(cpu65_run_args_s, cpu65_opcycles))
uint8_t cpu65__signal;

View File

@ -552,6 +552,9 @@ void c_interface_select_diskette( int drive )
{
int fd = -1;
TEMP_FAILURE_RETRY(fd = open(temp, O_RDWR));
if (fd == -1) {
LOG("OOPS, could not open disk path %s (%s)", temp, strerror(errno));
}
const char *err_str = disk6_insert(fd, drive, temp, /*readonly:*/0);
if (fd > 0) {
TEMP_FAILURE_RETRY(close(fd));
@ -627,6 +630,9 @@ void c_interface_select_diskette( int drive )
int fd = -1;
TEMP_FAILURE_RETRY(fd = open(temp, O_RDWR));
if (fd == -1) {
LOG("OOPS, could not open disk path %s (%s)", temp, strerror(errno));
}
const char *err_str = disk6_insert(fd, drive, temp, /*readonly:*/(toupper(ch) != 'W'));
if (fd > 0) {
TEMP_FAILURE_RETRY(close(fd));
@ -1083,8 +1089,7 @@ void c_interface_parameters()
else if ((ch == kESC) || c_keys_is_interface_key(ch))
{
timing_initialize();
vm_reinitializeAudio();
c_joystick_reset();
joystick_reset();
#if !TESTING
prefs_save();
#endif
@ -1178,7 +1183,7 @@ void c_interface_parameters()
/* calibrate joystick */
if ((ch == 13) && (option == OPT_CALIBRATE))
{
c_joystick_reset();
joystick_reset();
c_calibrate_joystick();
c_interface_print_screen( screen );
}
@ -1241,7 +1246,7 @@ void c_interface_parameters()
ch = toupper(ch);
if (ch == 'Y')
{
c_joystick_reset();
joystick_reset();
cpu65_reboot();
c_interface_exit(ch);
break;
@ -1565,7 +1570,8 @@ void c_interface_begin(int current_key)
pthread_mutex_lock(&classic_interface_lock);
interface_thread_id=1; // interface thread starting ...
interface_key.current_key = current_key;
pthread_create(&interface_thread_id, NULL, (void *)&interface_thread, &interface_key);
int err = TEMP_FAILURE_RETRY(pthread_create(&interface_thread_id, NULL, (void *)&interface_thread, &interface_key));
assert(!err);
pthread_detach(interface_thread_id);
}

Some files were not shown because too many files have changed in this diff Show More