diff options
author | NY00123 <NY00123@vogons.org> | 2018-12-22 21:14:00 +0300 |
---|---|---|
committer | Patryk Obara <dreamer.tan@gmail.com> | 2019-09-15 22:13:21 +0300 |
commit | f1b82ef55a2ec21148af6e82fa0e3693a5661707 (patch) | |
tree | 382b4c628a68fbbae60b61629e7a611eff1dfe7d | |
parent | c9f79add62a16d9b4a43346bf5e13ef5b4c1150b (diff) |
Implement an adaptation to SDL 2.0vogons/sdl2-android-r4178
Alpha-level Android build attached
Imported from: https://www.vogons.org/viewtopic.php?f=41&t=34770&start=140
63 files changed, 13254 insertions, 277 deletions
diff --git a/acinclude.m4 b/acinclude.m4 index 69adc0f03..32b2aa352 100644 --- a/acinclude.m4 +++ b/acinclude.m4 @@ -1,164 +1,147 @@ -dnl AM_PATH_SDL([MINIMUM-VERSION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]]]) -dnl Test for SDL, and define SDL_CFLAGS and SDL_LIBS -dnl -AC_DEFUN([AM_PATH_SDL], -[dnl -dnl Get the cflags and libraries from the sdl-config script -dnl -AC_ARG_WITH(sdl-prefix,[ --with-sdl-prefix=PFX Prefix where SDL is installed (optional)], - sdl_prefix="$withval", sdl_prefix="") -AC_ARG_WITH(sdl-exec-prefix,[ --with-sdl-exec-prefix=PFX Exec prefix where SDL is installed (optional)], - sdl_exec_prefix="$withval", sdl_exec_prefix="") -AC_ARG_ENABLE(sdltest, [ --disable-sdltest Do not try to compile and run a test SDL program], - , enable_sdltest=yes) - +dnl Check if we have SDL (sdl-config, header and library) version >= 1.2.0 +dnl Extra options: --with-sdl-prefix=PATH and --with-sdl={sdl12,sdl2} +dnl Output: +dnl SDL_CFLAGS and SDL_LIBS are set and AC_SUBST-ed +dnl HAVE_SDL_H is AC_DEFINE-d + +AC_DEFUN([EXULT_CHECK_SDL],[ + exult_backupcppflags="$CPPFLAGS" + exult_backupldflags="$LDFLAGS" + exult_backuplibs="$LIBS" + + exult_sdlok=yes + + AC_ARG_WITH(sdl-prefix,[ --with-sdl-prefix=PFX Prefix where SDL is installed (optional)], sdl_prefix="$withval", sdl_prefix="") + AC_ARG_WITH(sdl-exec-prefix,[ --with-sdl-exec-prefix=PFX Exec prefix where SDL is installed (optional)], sdl_exec_prefix="$withval", sdl_exec_prefix="") + AC_ARG_WITH(sdl, [ --with-sdl=sdl12,sdl2 Select a specific version of SDL to use (optional)], sdl_ver="$withval", sdl_ver="") + + dnl First: find sdl-config or sdl2-config + exult_extra_path=$prefix/bin:$prefix/usr/bin + sdl_args="" if test x$sdl_exec_prefix != x ; then sdl_args="$sdl_args --exec-prefix=$sdl_exec_prefix" - if test x${SDL_CONFIG+set} != xset ; then - SDL_CONFIG=$sdl_exec_prefix/bin/sdl-config - fi + exult_extra_path=$sdl_exec_prefix/bin fi if test x$sdl_prefix != x ; then sdl_args="$sdl_args --prefix=$sdl_prefix" - if test x${SDL_CONFIG+set} != xset ; then - SDL_CONFIG=$sdl_prefix/bin/sdl-config - fi + exult_extra_path=$sdl_prefix/bin fi - - AC_PATH_PROG(SDL_CONFIG, sdl-config, no) - min_sdl_version=ifelse([$1], ,0.11.0,$1) - AC_MSG_CHECKING(for SDL - version >= $min_sdl_version) - no_sdl="" + if test x"$sdl_ver" = xsdl12 ; then + exult_sdl_progs=sdl-config + elif test x"$sdl_ver" = xsdl2 ; then + exult_sdl_progs=sdl2-config + else + dnl NB: This line implies we prefer SDL 1.2 to SDL 2.0 + exult_sdl_progs="sdl-config sdl2-config" + fi + AC_PATH_PROGS(SDL_CONFIG, $exult_sdl_progs, no, [$exult_extra_path:$PATH]) if test "$SDL_CONFIG" = "no" ; then - no_sdl=yes + exult_sdlok=no else - SDL_CFLAGS=`$SDL_CONFIG $sdlconf_args --cflags` - SDL_LIBS=`$SDL_CONFIG $sdlconf_args --libs` + SDL_CFLAGS=`$SDL_CONFIG $sdl_args --cflags` + SDL_LIBS=`$SDL_CONFIG $sdl_args --libs` sdl_major_version=`$SDL_CONFIG $sdl_args --version | \ sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\1/'` sdl_minor_version=`$SDL_CONFIG $sdl_args --version | \ sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\2/'` - sdl_micro_version=`$SDL_CONFIG $sdl_config_args --version | \ + sdl_patchlevel=`$SDL_CONFIG $sdl_args --version | \ sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\3/'` - if test "x$enable_sdltest" = "xyes" ; then - ac_save_CFLAGS="$CFLAGS" - ac_save_LIBS="$LIBS" - CFLAGS="$CFLAGS $SDL_CFLAGS" - LIBS="$LIBS $SDL_LIBS" -dnl -dnl Now check if the installed SDL is sufficiently new. (Also sanity -dnl checks the results of sdl-config to some extent -dnl - rm -f conf.sdltest - AC_TRY_RUN([ -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include "SDL.h" - -char* -my_strdup (char *str) -{ - char *new_str; - - if (str) - { - new_str = (char *)malloc ((strlen (str) + 1) * sizeof(char)); - strcpy (new_str, str); - } + if test $sdl_major_version -eq 1 ; then + sdl_ver=sdl12 + else + sdl_ver=sdl2 + fi + fi + + if test x"$sdl_ver" = xsdl2 ; then + REQ_MAJOR=2 + REQ_MINOR=0 + REQ_PATCHLEVEL=0 else - new_str = NULL; - - return new_str; -} - -int main (int argc, char *argv[]) -{ - int major, minor, micro; - char *tmp_version; - - /* This hangs on some systems (?) - system ("touch conf.sdltest"); - */ - { FILE *fp = fopen("conf.sdltest", "a"); if ( fp ) fclose(fp); } - - /* HP/UX 9 (%@#!) writes to sscanf strings */ - tmp_version = my_strdup("$min_sdl_version"); - if (sscanf(tmp_version, "%d.%d.%d", &major, &minor, µ) != 3) { - printf("%s, bad version string\n", "$min_sdl_version"); - exit(1); - } - - if (($sdl_major_version > major) || - (($sdl_major_version == major) && ($sdl_minor_version > minor)) || - (($sdl_major_version == major) && ($sdl_minor_version == minor) && ($sdl_micro_version >= micro))) + REQ_MAJOR=1 + REQ_MINOR=2 + REQ_PATCHLEVEL=0 + fi + REQ_VERSION=$REQ_MAJOR.$REQ_MINOR.$REQ_PATCHLEVEL + + AC_MSG_CHECKING([for SDL - version >= $REQ_VERSION]) + + + dnl Second: include "SDL.h" + + if test x$exult_sdlok = xyes ; then + CPPFLAGS="$CPPFLAGS $SDL_CFLAGS" + AC_COMPILE_IFELSE([AC_LANG_SOURCE([[ + #include "SDL.h" + + int main(int argc, char *argv[]) { return 0; } - else - { - printf("\n*** 'sdl-config --version' returned %d.%d.%d, but the minimum version\n", $sdl_major_version, $sdl_minor_version, $sdl_micro_version); - printf("*** of SDL required is %d.%d.%d. If sdl-config is correct, then it is\n", major, minor, micro); - printf("*** best to upgrade to the required version.\n"); - printf("*** If sdl-config was wrong, set the environment variable SDL_CONFIG\n"); - printf("*** to point to the correct copy of sdl-config, and remove the file\n"); - printf("*** config.cache before re-running configure\n"); - return 1; - } -} + ]],)],sdlh_found=yes,sdlh_found=no) + + if test x$sdlh_found = xno; then + exult_sdlok=no + else + AC_DEFINE(HAVE_SDL_H, 1, [Define to 1 if you have the "SDL.h" header file]) + fi + fi + + dnl Next: version check (cross-compile-friendly idea borrowed from autoconf) + dnl (First check version reported by sdl-config, then confirm + dnl the version in SDL.h matches it) + + if test x$exult_sdlok = xyes ; then + + if test ! \( \( $sdl_major_version -gt $REQ_MAJOR \) -o \( \( $sdl_major_version -eq $REQ_MAJOR \) -a \( \( $sdl_minor_version -gt $REQ_MINOR \) -o \( \( $sdl_minor_version -eq $REQ_MINOR \) -a \( $sdl_patchlevel -gt $REQ_PATCHLEVEL \) \) \) \) \); then + exult_sdlok="no, version < $REQ_VERSION found" + else + AC_COMPILE_IFELSE([AC_LANG_SOURCE([[ + #include "SDL.h" + + int main(int argc, char *argv[]) + { + static int test_array[1-2*!(SDL_MAJOR_VERSION==$sdl_major_version&&SDL_MINOR_VERSION==$sdl_minor_version&&SDL_PATCHLEVEL==$sdl_patchlevel)]; + test_array[0] = 0; + return 0; + } + ]])],,[[exult_sdlok="no, version of SDL.h doesn't match that of sdl-config"]]) + fi + fi + + dnl Next: try linking + + if test "x$exult_sdlok" = xyes; then + LIBS="$LIBS $SDL_LIBS" + + AC_LINK_IFELSE([AC_LANG_SOURCE([[ + #include "SDL.h" -],, no_sdl=yes,[echo $ac_n "cross compiling; assumed OK... $ac_c"]) - CFLAGS="$ac_save_CFLAGS" - LIBS="$ac_save_LIBS" - fi + int main(int argc, char* argv[]) { + SDL_Init(0); + return 0; + } + ]])],sdllinkok=yes,sdllinkok=no) + if test x$sdllinkok = xno; then + exult_sdlok=no + fi fi - if test "x$no_sdl" = x ; then - AC_MSG_RESULT(yes) - ifelse([$2], , :, [$2]) + + AC_MSG_RESULT($exult_sdlok) + + LDFLAGS="$exult_backupldflags" + CPPFLAGS="$exult_backupcppflags" + LIBS="$exult_backuplibs" + + if test "x$exult_sdlok" = xyes; then + AC_SUBST(SDL_CFLAGS) + AC_SUBST(SDL_LIBS) + ifelse([$1], , :, [$1]) else - AC_MSG_RESULT(no) - if test "$SDL_CONFIG" = "no" ; then - echo "*** The sdl-config script installed by SDL could not be found" - echo "*** If SDL was installed in PREFIX, make sure PREFIX/bin is in" - echo "*** your path, or set the SDL_CONFIG environment variable to the" - echo "*** full path to sdl-config." - else - if test -f conf.sdltest ; then - : - else - echo "*** Could not run SDL test program, checking why..." - CFLAGS="$CFLAGS $SDL_CFLAGS" - LIBS="$LIBS $SDL_LIBS" - AC_TRY_LINK([ -#include <stdio.h> -#include "SDL.h" -], [ return 0; ], - [ echo "*** The test program compiled, but did not run. This usually means" - echo "*** that the run-time linker is not finding SDL or finding the wrong" - echo "*** version of SDL. If it is not finding SDL, you'll need to set your" - echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point" - echo "*** to the installed location Also, make sure you have run ldconfig if that" - echo "*** is required on your system" - echo "***" - echo "*** If you have an old version installed, it is best to remove it, although" - echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH"], - [ echo "*** The test program failed to compile or link. See the file config.log for the" - echo "*** exact error that occurred. This usually means SDL was incorrectly installed" - echo "*** or that you have moved SDL since it was installed. In the latter case, you" - echo "*** may want to edit the sdl-config script: $SDL_CONFIG" ]) - CFLAGS="$ac_save_CFLAGS" - LIBS="$ac_save_LIBS" - fi - fi - SDL_CFLAGS="" - SDL_LIBS="" - ifelse([$3], , :, [$3]) + ifelse([$2], , :, [$2]) fi - AC_SUBST(SDL_CFLAGS) - AC_SUBST(SDL_LIBS) - rm -f conf.sdltest -]) +]); dnl Configure Paths for Alsa dnl Some modifications by Richard Boulton <richard-alsa@tartarus.org> @@ -303,6 +286,73 @@ AC_SUBST(ALSA_CFLAGS) AC_SUBST(ALSA_LIBS) ]) +dnl Configure platform for SDL_cdrom compatibility layer. +dnl MUST be called after SDL version check (defined only for SDL 2.0). +dnl Taken off configure.in from SDL 1.2 and then modified +AC_DEFUN([COMPAT_SDL_CDROM_GET_PLATFORM],[ + +if test x"$sdl_ver" = xsdl12 ; then + compat_sdl_cdrom_arch=undefined +elif test x"$sdl_ver" = xsdl2 ; then + case "$host" in + arm-*-elf*) # FIXME: Can we get more specific for iPodLinux? + compat_sdl_cdrom_arch=linux + ;; + *-*-linux*|*-*-uclinux*|*-*-gnu*|*-*-k*bsd*-gnu|*-*-bsdi*|*-*-freebsd*|*-*-dragonfly*|*-*-netbsd*|*-*-openbsd*|*-*-sysv5*|*-*-solaris*|*-*-hpux*|*-*-irix*|*-*-aix*|*-*-osf*) + case "$host" in + *-*-linux*) compat_sdl_cdrom_arch=linux ;; + *-*-uclinux*) compat_sdl_cdrom_arch=linux ;; + *-*-kfreebsd*-gnu) compat_sdl_cdrom_arch=COMPAT_SDL_CDROM_PLATFORM_KFREEBSD_GNU ;; + *-*-knetbsd*-gnu) compat_sdl_cdrom_arch=knetbsd-gnu ;; + *-*-kopenbsd*-gnu) compat_sdl_cdrom_arch=kopenbsd-gnu ;; + *-*-gnu*) compat_sdl_cdrom_arch=gnu ;; # must be last of the gnu variants + *-*-bsdi*) compat_sdl_cdrom_arch=bsdi ;; + *-*-freebsd*) compat_sdl_cdrom_arch=freebsd ;; + *-*-dragonfly*) compat_sdl_cdrom_arch=freebsd ;; + *-*-netbsd*) compat_sdl_cdrom_arch=netbsd ;; + *-*-openbsd*) compat_sdl_cdrom_arch=openbsd ;; + *-*-sysv5*) compat_sdl_cdrom_arch=sysv5 ;; + *-*-solaris*) compat_sdl_cdrom_arch=solaris ;; + *-*-hpux*) compat_sdl_cdrom_arch=hpux ;; + *-*-irix*) compat_sdl_cdrom_arch=irix ;; + *-*-aix*) compat_sdl_cdrom_arch=aix ;; + *-*-osf*) compat_sdl_cdrom_arch=osf ;; + esac + ;; + *-*-qnx*) + compat_sdl_cdrom_arch=qnx + ;; + *-*-cygwin* | *-*-mingw32*) + compat_sdl_cdrom_arch=win32 + ;; + *-wince*) + compat_sdl_cdrom_arch=win32 + ;; + *-*-beos* | *-*-haiku*) + compat_sdl_cdrom_arch=beos + ;; + *-*-darwin* ) + # This could be either full "Mac OS X", or plain "Darwin" which is + # just the OS X kernel sans upper layers like Carbon and Cocoa. + # Next line is broken + compat_sdl_cdrom_arch=macosx + ;; + *-*-mint*) + compat_sdl_cdrom_arch=mint + ;; + *-riscos) + compat_sdl_cdrom_arch=riscos + ;; + *) + compat_sdl_cdrom_arch=undefined + ;; + esac +else + AC_MSG_ERROR([Compatible SDL version not found]) +fi + +]); + AH_TOP([ /* * Copyright (C) 2002-2018 The DOSBox Team diff --git a/android-project/AndroidManifest.xml b/android-project/AndroidManifest.xml new file mode 100644 index 000000000..c4b2f1b49 --- /dev/null +++ b/android-project/AndroidManifest.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.dosbox.emu" + android:versionCode="1" + android:versionName="1.0" + android:installLocation="auto"> + + <application android:label="@string/app_name" + android:icon="@drawable/icon" + android:theme="@android:style/Theme.NoTitleBar.Fullscreen"> + <activity android:name="DOSBoxActivity" + android:configChanges="orientation|screenSize" + android:label="@string/app_name"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + </application> + + <!-- Android 2.3.3 - minimum, 4.2 - target --> + <uses-sdk android:minSdkVersion="10" android:targetSdkVersion="17" /> + + <!-- OpenGL ES 2.0 --> + <uses-feature android:glEsVersion="0x00020000" /> + + <!-- Allow writing to external storage --> + <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> +</manifest> diff --git a/android-project/ant.properties b/android-project/ant.properties new file mode 100644 index 000000000..b0971e891 --- /dev/null +++ b/android-project/ant.properties @@ -0,0 +1,17 @@ +# This file is used to override default values used by the Ant build system. +# +# This file must be checked into Version Control Systems, as it is +# integral to the build system of your project. + +# This file is only used by the Ant script. + +# You can use this to override default values such as +# 'source.dir' for the location of your java source folder and +# 'out.dir' for the location of your output folder. + +# You can also use it define how the release builds are signed by declaring +# the following properties: +# 'key.store' for the location of your keystore and +# 'key.alias' for the name of the key to use. +# The password will be asked during the build when you use the 'release' target. + diff --git a/android-project/build.properties b/android-project/build.properties new file mode 100644 index 000000000..edc7f2305 --- /dev/null +++ b/android-project/build.properties @@ -0,0 +1,17 @@ +# This file is used to override default values used by the Ant build system. +# +# This file must be checked in Version Control Systems, as it is +# integral to the build system of your project. + +# This file is only used by the Ant script. + +# You can use this to override default values such as +# 'source.dir' for the location of your java source folder and +# 'out.dir' for the location of your output folder. + +# You can also use it define how the release builds are signed by declaring +# the following properties: +# 'key.store' for the location of your keystore and +# 'key.alias' for the name of the key to use. +# The password will be asked during the build when you use the 'release' target. + diff --git a/android-project/build.xml b/android-project/build.xml new file mode 100644 index 000000000..00b9bcaa1 --- /dev/null +++ b/android-project/build.xml @@ -0,0 +1,93 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- This should be changed to the name of your project --> +<project name="DOSBox" default="help"> + + <!-- The local.properties file is created and updated by the 'android' tool. + It contains the path to the SDK. It should *NOT* be checked into + Version Control Systems. --> + <property file="local.properties" /> + + <!-- The ant.properties file can be created by you. It is only edited by the + 'android' tool to add properties to it. + This is the place to change some Ant specific build properties. + Here are some properties you may want to change/update: + + source.dir + The name of the source directory. Default is 'src'. + out.dir + The name of the output directory. Default is 'bin'. + + For other overridable properties, look at the beginning of the rules + files in the SDK, at tools/ant/build.xml + + Properties related to the SDK location or the project target should + be updated using the 'android' tool with the 'update' action. + + This file is an integral part of the build system for your + application and should be checked into Version Control Systems. + + --> + <property file="ant.properties" /> + + <!-- if sdk.dir was not set from one of the property file, then + get it from the ANDROID_HOME env var. + This must be done before we load project.properties since + the proguard config can use sdk.dir --> + <property environment="env" /> + <condition property="sdk.dir" value="${env.ANDROID_HOME}"> + <isset property="env.ANDROID_HOME" /> + </condition> + + <!-- The project.properties file is created and updated by the 'android' + tool, as well as ADT. + + This contains project specific properties such as project target, and library + dependencies. Lower level build properties are stored in ant.properties + (or in .classpath for Eclipse projects). + + This file is an integral part of the build system for your + application and should be checked into Version Control Systems. --> + <loadproperties srcFile="project.properties" /> + + <!-- quick check on sdk.dir --> + <fail + message="sdk.dir is missing. Make sure to generate local.properties using 'android update project' or to inject it through the ANDROID_HOME environment variable." + unless="sdk.dir" + /> + + <!-- + Import per project custom build rules if present at the root of the project. + This is the place to put custom intermediary targets such as: + -pre-build + -pre-compile + -post-compile (This is typically used for code obfuscation. + Compiled code location: ${out.classes.absolute.dir} + If this is not done in place, override ${out.dex.input.absolute.dir}) + -post-package + -post-build + -pre-clean + --> + <import file="custom_rules.xml" optional="true" /> + + <!-- Import the actual build file. + + To customize existing targets, there are two options: + - Customize only one target: + - copy/paste the target into this file, *before* the + <import> task. + - customize it to your needs. + - Customize the whole content of build.xml + - copy/paste the content of the rules files (minus the top node) + into this file, replacing the <import> task. + - customize to your needs. + + *********************** + ****** IMPORTANT ****** + *********************** + In all cases you must update the value of version-tag below to read 'custom' instead of an integer, + in order to avoid having your file be overridden by tools such as "android update project" + --> + <!-- version-tag: 1 --> + <import file="${sdk.dir}/tools/ant/build.xml" /> + +</project> diff --git a/android-project/default.properties b/android-project/default.properties new file mode 100644 index 000000000..4d6614c6f --- /dev/null +++ b/android-project/default.properties @@ -0,0 +1,11 @@ +# This file is automatically generated by Android Tools. +# Do not modify this file -- YOUR CHANGES WILL BE ERASED! +# +# This file must be checked in Version Control Systems. +# +# To customize properties used by the Ant build system use, +# "build.properties", and override values to adapt the script to your +# project structure. + +# Project target. +target=android-17 diff --git a/android-project/instructions.txt b/android-project/instructions.txt new file mode 100644 index 000000000..1cee31e58 --- /dev/null +++ b/android-project/instructions.txt @@ -0,0 +1,90 @@ +NOTE: Chances are the (unofficially modified) DOSBox app *will* crash sooner or +later, although one may still build a proper package. +A default for DOSBox settings have been selected to make this (somewhat) +more stable on Android and perform better: output=texture. +Furthermore, cycles=max is the default on Android with this patch, so DOSBox +is (again somewhat) faster out-of-the-box. Do *not* expect great performance, +though. + +Instructions for building DOSBox (unofficial modification) for Android +(originally done on a GNU/Linux desktop, may work on other platforms): + +1. Move/symlink SDL2 into the jni subdirectory. The directory name should be +"SDL" (yeah, without the '2'). The SDL2 library itself can be obtained from +the SDL website: http://www.libsdl.org/download-2.0.php + +2. Also move/symlink SDL2_net into the jni directory. The dir name should be +"SDL_net" (again with no '2'). It can be obtained from the SDL_net webpage: +http://www.libsdl.org/projects/SDL_net/ + +In case of issues you can (probably) disable the usage of SDL_net in config.h, +by disabling the various networking features. + +3. In general, you may want to play with the config.h file. +Note that right now, SDL_sound is not used. + +4. Ensure that the "res" subdirectory has ready icons, as these are *not* +bundled with the patch itself (which is textual). For instance, you should have +a file given by the relative path android-project/res/drawable-ldpi/icon.png. + +5. With the exception of the first three steps (copying the android-project +directory, moving/symlinking SDL and adding source files to Android.mk), +follow all steps from the file docs/README-android.md bundled with SDL 2.0. + +6. Assuming it has actually been built successfully, there are great chances +that the app is going to crash at some point. Maybe the splash screen can be +seen for a little while before the crash. If the app has survived so far, you +should find a dosbox-SVN.conf file on the (internal) SD card that you may like +to edit. On many single-user devices it should be located at the relative path +Android/data/com.dosbox.emu/files/ (relatively to the SD card's root). +Playing with settings like "output", "machine" and "nosound" can help here. In +fact, playing with any DOSBox setting you can think of can help. There are very +great chances that a crash will occur at some later point, though. + +7. Considering this revision of the patch, an on-screen keyboard can be toggled +using the Back button. Furthermore, physical keyboards can also be used +(e.g. via Bluetooth or USB-OTG). For testing only the following "WiFi Keyboard" +app can partially be used like a physical keyboard is connected directly: +https://play.google.com/store/apps/details?id=com.volosyukivan + +Please note that in earlier revisions of this patch, the on-screen keyboard +could be toggled using the Menu button, while the Back button simulated a +client Escape key press by default. However, some newer Android-powered devices +may come with built-in system buttons, but not such a Menu button, implying +that a (relatively) large portion of the screen may be wasted simply for +displaying an on-screen Menu button (and no more). So, the Back button is used +for toggling the on-screen keyboard now. If you are interested in Escape key +emulation, see the last point (and rough sketch) regarding mouse emulation. + +8. Assuming you do get a working command prompt within DOSBox, you may also +type the following command in order to find the location of the dosbox-SVN.conf +file (and save the currently set settings): + +config -writeconf + +9. Be warned that, at least on newer Android setups, the configuration file +(along with the whole directory in which it resides) gets DELETED once you +uninstall DOSBox. In fact, this is the behavior with Android apps in general +(on such setups). + +10. A few very-basic changes have been applied to the mapper UI, but it is +wrong to say it is fully ready. In particular, an accelerometer sensor +(currently identified as an SDL joystick) may interfere with remaps. In such a +case you may wish to manually edit your own customized mapper file or disable +the (host) accelerometer/joystick subsystem. + +11. As for mouse emulation, mouse motion and button presses/releases are +currently emulated by tapping on specific "invisible" portions of the screen. +Yeah, it may not be the best way, but this is what you have for now. +Note that mouse motion is emulated by using the touchscreen the way one uses a +laptop's trackpad, i.e. in a "relative" fashion. For instance, to move a mouse +cursor in some DOS game to the left, tap with a finger on an arbitrary location +on the relevant part of the screen and then drag the finger to the left. + +Here are parts of the touch screen (the whole display) and what each of them +emulates. These are all client mouse events, with the exception of the Escape +key, which is a *host* key event and can be mapped to various things. + +/-----------------------------------------------------------------------\ +| Left | (H)Escape | Motion | Motion | Middle | Right | +\-----------------------------------------------------------------------/ diff --git a/android-project/jni/Android.mk b/android-project/jni/Android.mk new file mode 100644 index 000000000..5053e7d64 --- /dev/null +++ b/android-project/jni/Android.mk @@ -0,0 +1 @@ +include $(call all-subdir-makefiles) diff --git a/android-project/jni/Application.mk b/android-project/jni/Application.mk new file mode 100644 index 000000000..e60d424df --- /dev/null +++ b/android-project/jni/Application.mk @@ -0,0 +1,6 @@ + +# Uncomment this if you're using STL in your project +# See CPLUSPLUS-SUPPORT.html in the NDK documentation for more information +# APP_STL := stlport_static +APP_STL := stlport_shared +APP_ABI := armeabi armeabi-v7a x86 diff --git a/android-project/jni/src/Android.mk b/android-project/jni/src/Android.mk new file mode 100644 index 000000000..a5f9e61e2 --- /dev/null +++ b/android-project/jni/src/Android.mk @@ -0,0 +1,82 @@ +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_MODULE := main + +SDL_PATH := ../SDL +SDL_NET_PATH := ../SDL_net + +LOCAL_C_INCLUDES := $(LOCAL_PATH)/$(SDL_PATH)/include \ + $(LOCAL_PATH)/$(SDL_NET_PATH) \ + ../../../include + +ifeq ($(TARGET_ARCH_ABI),armeabi-v7a) + LOCAL_CPPFLAGS += -DC_TARGETCPU=ARMV7LE +endif +ifeq ($(TARGET_ARCH_ABI),armeabi) + LOCAL_CPPFLAGS += -DC_TARGETCPU=ARMV4LE +endif +ifeq ($(TARGET_ARCH_ABI),x86) + LOCAL_CPPFLAGS += -DC_TARGETCPU=X86 -DC_FPU_X86=1 +endif +ifeq ($(TARGET_ARCH_ABI),mips) + LOCAL_CPPFLAGS += -DC_TARGETCPU=MIPSEL +endif +# Some sizes for a few types (for now architecture independent but possibly not) +LOCAL_CPPFLAGS += -DSIZEOF_INT_P=4 \ + -DSIZEOF_UNSIGNED_CHAR=1 \ + -DSIZEOF_UNSIGNED_INT=4 \ + -DSIZEOF_UNSIGNED_LONG=4 \ + -DSIZEOF_UNSIGNED_LONG_LONG=8 \ + -DSIZEOF_UNSIGNED_SHORT=2 + +LOCAL_SRC_PATH := ../../../src + +LOCAL_CPP_FEATURES += exceptions + +# Note: We don't want any cdrom cpp file differing from cdrom_image.cpp. +# Furthermore, we compile zmbv.cpp only... and even that can't be done +# without zlib. So, let's give up for now. +LOCAL_SRC_FILES := $(SDL_PATH)/src/main/android/SDL_android_main.c \ + $(wildcard $(LOCAL_SRC_PATH)/dosbox.cpp) \ + \ + $(wildcard $(LOCAL_SRC_PATH)/cpu/*.c) \ + $(wildcard $(LOCAL_SRC_PATH)/cpu/*.cpp) \ + $(wildcard $(LOCAL_SRC_PATH)/core_dynrec/*.c) \ + $(wildcard $(LOCAL_SRC_PATH)/core_dynrec/*.cpp) \ + $(wildcard $(LOCAL_SRC_PATH)/core_dyn_x86/*.c) \ + $(wildcard $(LOCAL_SRC_PATH)/core_dyn_x86/*.cpp) \ + $(wildcard $(LOCAL_SRC_PATH)/core_full/*.c) \ + $(wildcard $(LOCAL_SRC_PATH)/core_full/*.cpp) \ + $(wildcard $(LOCAL_SRC_PATH)/core_normal/*.c) \ + $(wildcard $(LOCAL_SRC_PATH)/core_normal/*.cpp) \ + $(wildcard $(LOCAL_SRC_PATH)/debug/*.c) \ + $(wildcard $(LOCAL_SRC_PATH)/debug/*.cpp) \ + \ + $(wildcard $(LOCAL_SRC_PATH)/dos/cdrom.cpp) \ + $(wildcard $(LOCAL_SRC_PATH)/dos/cdrom_image.cpp) \ + $(wildcard $(LOCAL_SRC_PATH)/dos/d*.cpp) \ + \ + $(wildcard $(LOCAL_SRC_PATH)/fpu/*.c) \ + $(wildcard $(LOCAL_SRC_PATH)/fpu/*.cpp) \ + $(wildcard $(LOCAL_SRC_PATH)/gui/*.c) \ + $(wildcard $(LOCAL_SRC_PATH)/gui/*.cpp) \ + $(wildcard $(LOCAL_SRC_PATH)/hardware/*.c) \ + $(wildcard $(LOCAL_SRC_PATH)/hardware/*.cpp) \ + $(wildcard $(LOCAL_SRC_PATH)/hardware/serialport/*.c) \ + $(wildcard $(LOCAL_SRC_PATH)/hardware/serialport/*.cpp) \ + $(wildcard $(LOCAL_SRC_PATH)/ints/*.c) \ + $(wildcard $(LOCAL_SRC_PATH)/ints/*.cpp) \ + $(wildcard $(LOCAL_SRC_PATH)/libs/*.c) \ + $(wildcard $(LOCAL_SRC_PATH)/libs/*.cpp) \ + $(wildcard $(LOCAL_SRC_PATH)/misc/*.c) \ + $(wildcard $(LOCAL_SRC_PATH)/misc/*.cpp) \ + $(wildcard $(LOCAL_SRC_PATH)/shell/*.c) \ + $(wildcard $(LOCAL_SRC_PATH)/shell/*.cpp) + +LOCAL_SHARED_LIBRARIES := SDL2 SDL2_net + +LOCAL_LDLIBS := -lGLESv1_CM -llog + +include $(BUILD_SHARED_LIBRARY) diff --git a/android-project/jni/src/config.h b/android-project/jni/src/config.h new file mode 100644 index 000000000..e2264b83a --- /dev/null +++ b/android-project/jni/src/config.h @@ -0,0 +1,320 @@ +/* config.h. Generated from config.h.in by configure. */ +/* config.h.in. Generated from configure.ac by autoheader. */ + + +/* + * Copyright (C) 2002-2015 The DOSBox Team + * + * 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 Library 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + + +/* Define if building universal (internal helper macro) */ +/* #undef AC_APPLE_UNIVERSAL_BUILD */ + +/* Compiling on BSD */ +/* #undef BSD */ + +/* Determines if the compilers supports always_inline attribute. */ +#define C_ATTRIBUTE_ALWAYS_INLINE 1 + +/* Determines if the compilers supports fastcall attribute. */ +/* #undef C_ATTRIBUTE_FASTCALL */ + +/* Platform for SDL_cdrom compatibility layer (SDL 2.0 only) */ +/* #undef C_COMPAT_SDL_CDROM_PLATFORM */ + +/* Define to 1 to use inlined memory functions in cpu core */ +/* #undef C_CORE_INLINE */ + +/* Define to 1 to enable internal debugger, requires libcurses */ +/* #undef C_DEBUG */ + +/* Define to 1 to use x86 dynamic cpu core */ +/* NOTE: Right now it doesn't compile (possibly due to ASM code), so don't use. +#ifdef __i386__ +#define C_DYNAMIC_X86 1 +#endif +*/ + +/* Define to 1 to use recompiling cpu core. Can not be used together with the + dynamic-x86 core */ +/* NOTE: For now we use this even on x86. */ +#define C_DYNREC 1 + +/* Define to 1 to enable floating point emulation */ +#define C_FPU 1 + +/* Define to 1 to use a x86 assembly fpu core */ +/* NOTE: Defined in Android.mk if desired. */ +/* #undef C_FPU_X86 */ + +/* Determines if the compilers supports attributes for structures. */ +#define C_HAS_ATTRIBUTE 1 + +/* Determines if the compilers supports __builtin_expect for branch + prediction. */ +#define C_HAS_BUILTIN_EXPECT 1 + +/* Define to 1 if you have the mprotect function */ +#define C_HAVE_MPROTECT 1 + +/* Define to 1 to enable heavy debugging, also have to enable C_DEBUG */ +/* #undef C_HEAVY_DEBUG */ + +/* Define to 1 to enable IPX over Internet networking, requires SDL_net */ +#define C_IPX 1 + +/* Define to 1 to enable internal modem support, requires SDL_net */ +#define C_MODEM 1 + +/* Define to 1 to use opengl display output support */ +/* (actually OpenGL ES 1.1 on Android) */ +#define C_OPENGL 1 + +/* Physical CD-ROM mounting support */ +/* #undef C_PHYSICAL_CDROM_MOUNT */ + +/* Define to 1 to enable SDL_sound support */ +/* #undef C_SDL_SOUND */ + +/* Define to 1 if you have setpriority support */ +/* #undef C_SET_PRIORITY 1 */ + +/* Define to 1 to enable screenshots, requires libpng */ +/* #undef C_SSHOT */ + +/* The type of cpu this target has */ +/* NOTE: Defined from Android.mk. */ +#ifndef C_TARGETCPU +#error "Target (host) CPU is unknown! Please check the contents of Android.mk." +#endif + +/* Define to 1 to use a unaligned memory access */ +#define C_UNALIGNED_MEMORY 1 + +/* libm doesn't include powf */ +/* #undef DB_HAVE_NO_POWF */ + +/* struct dirent has d_type */ +#define DIRENT_HAS_D_TYPE 1 + +/* environ can be included */ +/* #undef ENVIRON_INCLUDED */ + +/* environ can be linked */ +#define ENVIRON_LINKED 1 + +/* Define to 1 if you have the <ddraw.h> header file. */ +/* #undef HAVE_DDRAW_H */ + +/* Define to 1 if you have the <inttypes.h> header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the <memory.h> header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if you have the <netinet/in.h> header file. */ +#define HAVE_NETINET_IN_H 1 + +/* Define to 1 if you have the <pwd.h> header file. */ +#define HAVE_PWD_H 1 + +/* Define to 1 if you have the <stdint.h> header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the <stdlib.h> header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the <strings.h> header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the <string.h> header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the <sys/socket.h> header file. */ +#define HAVE_SYS_SOCKET_H 1 + +/* Define to 1 if you have the <sys/stat.h> header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the <sys/types.h> header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the <unistd.h> header file. */ +#define HAVE_UNISTD_H 1 + +/* Compiling on GNU/Linux */ +#define LINUX 1 + +/* Compiling on Mac OS X */ +/* #undef MACOSX */ + +/* Compiling on OS/2 EMX */ +/* #undef OS2 */ + +/* Name of package */ +#define PACKAGE "dosbox" + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "dosbox" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "dosbox SVN" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "dosbox" + +/* Define to the home page for this package. */ +#define PACKAGE_URL "" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "SVN" + +/* The size of `int *', as computed by sizeof. */ +/* NOTE: Defined in Android.mk. */ +/* #undef SIZEOF_INT_P */ + +/* The size of `unsigned char', as computed by sizeof. */ +/* NOTE: Defined in Android.mk. */ +/* #undef SIZEOF_UNSIGNED_CHAR */ + +/* The size of `unsigned int', as computed by sizeof. */ +/* NOTE: Defined in Android.mk. */ +/* #undef SIZEOF_UNSIGNED_INT */ + +/* The size of `unsigned long', as computed by sizeof. */ +/* NOTE: Defined in Android.mk. */ +/* #undef SIZEOF_UNSIGNED_LONG */ + +/* The size of `unsigned long long', as computed by sizeof. */ +/* NOTE: Defined in Android.mk. */ +/* #undef SIZEOF_UNSIGNED_LONG_LONG */ + +/* The size of `unsigned short', as computed by sizeof. */ +/* NOTE: Defined in Android.mk. */ +/* #undef SIZEOF_UNSIGNED_SHORT */ + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Define to 1 if your <sys/time.h> declares `struct tm'. */ +/* #undef TM_IN_SYS_TIME */ + +/* Version number of package */ +#define VERSION "SVN" + +/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most + significant byte first (like Motorola and SPARC, unlike Intel). */ +#if defined AC_APPLE_UNIVERSAL_BUILD +# if defined __BIG_ENDIAN__ +# define WORDS_BIGENDIAN 1 +# endif +#else +# ifndef WORDS_BIGENDIAN +/* # undef WORDS_BIGENDIAN */ +# endif +#endif + +/* Define to empty if `const' does not conform to ANSI C. */ +/* #undef const */ + +/* Define to `__inline__' or `__inline' if that's what the C compiler + calls it, or to nothing if 'inline' is not supported under any name. */ +#ifndef __cplusplus +/* #undef inline */ +#endif + +/* Define to `unsigned int' if <sys/types.h> does not define. */ +/* #undef size_t */ + +/* Define to `int` if you don't have socklen_t */ +/* #undef socklen_t */ + +#if C_ATTRIBUTE_ALWAYS_INLINE +#define INLINE inline __attribute__((always_inline)) +#else +#define INLINE inline +#endif + +#if C_ATTRIBUTE_FASTCALL +#define DB_FASTCALL __attribute__((fastcall)) +#else +#define DB_FASTCALL +#endif + +#if C_HAS_ATTRIBUTE +#define GCC_ATTRIBUTE(x) __attribute__ ((x)) +#else +#define GCC_ATTRIBUTE(x) /* attribute not supported */ +#endif + +#if C_HAS_BUILTIN_EXPECT +#define GCC_UNLIKELY(x) __builtin_expect((x),0) +#define GCC_LIKELY(x) __builtin_expect((x),1) +#else +#define GCC_UNLIKELY(x) (x) +#define GCC_LIKELY(x) (x) +#endif + + +typedef double Real64; + +#if SIZEOF_UNSIGNED_CHAR != 1 +# error "sizeof (unsigned char) != 1" +#else + typedef unsigned char Bit8u; + typedef signed char Bit8s; +#endif + +#if SIZEOF_UNSIGNED_SHORT != 2 +# error "sizeof (unsigned short) != 2" +#else + typedef unsigned short Bit16u; + typedef signed short Bit16s; +#endif + +#if SIZEOF_UNSIGNED_INT == 4 + typedef unsigned int Bit32u; + typedef signed int Bit32s; +#elif SIZEOF_UNSIGNED_LONG == 4 + typedef unsigned long Bit32u; + typedef signed long Bit32s; +#else +# error "can't find sizeof(type) of 4 bytes!" +#endif + +#if SIZEOF_UNSIGNED_LONG == 8 + typedef unsigned long Bit64u; + typedef signed long Bit64s; +#elif SIZEOF_UNSIGNED_LONG_LONG == 8 + typedef unsigned long long Bit64u; + typedef signed long long Bit64s; +#else +# error "can't find data type of 8 bytes" +#endif + +#if SIZEOF_INT_P == 4 + typedef Bit32u Bitu; + typedef Bit32s Bits; +#else + typedef Bit64u Bitu; + typedef Bit64s Bits; +#endif + + diff --git a/android-project/proguard-project.txt b/android-project/proguard-project.txt new file mode 100644 index 000000000..f2fe1559a --- /dev/null +++ b/android-project/proguard-project.txt @@ -0,0 +1,20 @@ +# To enable ProGuard in your project, edit project.properties +# to define the proguard.config property as described in that file. +# +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in ${sdk.dir}/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the ProGuard +# include property in project.properties. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} diff --git a/android-project/project.properties b/android-project/project.properties new file mode 100644 index 000000000..a3ee5ab64 --- /dev/null +++ b/android-project/project.properties @@ -0,0 +1,14 @@ +# This file is automatically generated by Android Tools. +# Do not modify this file -- YOUR CHANGES WILL BE ERASED! +# +# This file must be checked in Version Control Systems. +# +# To customize properties used by the Ant build system edit +# "ant.properties", and override values to adapt the script to your +# project structure. +# +# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): +#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt + +# Project target. +target=android-17 diff --git a/android-project/res/layout/main.xml b/android-project/res/layout/main.xml new file mode 100644 index 000000000..123c4b6ea --- /dev/null +++ b/android-project/res/layout/main.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + > +<TextView + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:text="Hello World, SDLActivity" + /> +</LinearLayout> + diff --git a/android-project/res/values/strings.xml b/android-project/res/values/strings.xml new file mode 100644 index 000000000..2b6a7346e --- /dev/null +++ b/android-project/res/values/strings.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="app_name">DOSBox</string> +</resources> diff --git a/android-project/src/com/dosbox/emu/DOSBoxActivity.java b/android-project/src/com/dosbox/emu/DOSBoxActivity.java new file mode 100644 index 000000000..5be8621b2 --- /dev/null +++ b/android-project/src/com/dosbox/emu/DOSBoxActivity.java @@ -0,0 +1,59 @@ +package com.dosbox.emu; + +import org.libsdl.app.SDLActivity; +import android.content.Context; +import android.content.pm.ActivityInfo; +import android.content.res.Configuration; +import android.os.Bundle; +import android.view.Surface; +import android.view.KeyEvent; +import android.view.inputmethod.InputMethodManager; + +/* + * A sample wrapper class that just calls SDLActivity + */ + +public class DOSBoxActivity extends SDLActivity { + /* Based on volume keys related patch from bug report: + http://bugzilla.libsdl.org/show_bug.cgi?id=1569 */ + + // enable to intercept keys before SDL gets them + @Override + public boolean dispatchKeyEvent(KeyEvent event) { + switch (event.getKeyCode()) { + // Show/Hide on-screen keyboard (but don't toggle text input mode) + case KeyEvent.KEYCODE_BACK: + if (event.getAction() == KeyEvent.ACTION_UP) { + toggleOnScreenKeyboard(); + } + return true; + } + return super.dispatchKeyEvent(event); + } + + // Fix the initial orientation + protected void onCreate(Bundle savedInstanceState) { + /* Use deprecated getOrientation() rather + than getRotation() to support API<8 */ +/* + int rotation = getWindowManager().getDefaultDisplay().getOrientation(); + if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) { + if ((rotation == Surface.ROTATION_0) || (rotation == Surface.ROTATION_90)) + setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); + else + setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT); + } else { // Landscape + if ((rotation == Surface.ROTATION_0) || (rotation == Surface.ROTATION_90)) + setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); + else + setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE); + } +*/ + super.onCreate(savedInstanceState); // Initialize the rest (e.g. SDL) + } + + public void toggleOnScreenKeyboard() { + InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); + imm.toggleSoftInput(0, 0); + } +} diff --git a/android-project/src/org/libsdl/app/SDLActivity.java b/android-project/src/org/libsdl/app/SDLActivity.java new file mode 100644 index 000000000..7be7917f6 --- /dev/null +++ b/android-project/src/org/libsdl/app/SDLActivity.java @@ -0,0 +1,1077 @@ +package org.libsdl.app; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +import android.app.*; +import android.content.*; +import android.view.*; +import android.view.inputmethod.BaseInputConnection; +import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.InputConnection; +import android.view.inputmethod.InputMethodManager; +import android.widget.AbsoluteLayout; +import android.os.*; +import android.util.Log; +import android.graphics.*; +import android.media.*; +import android.hardware.*; + + +/** + SDL Activity +*/ +public class SDLActivity extends Activity { + private static final String TAG = "SDL"; + + // Keep track of the paused state + public static boolean mIsPaused, mIsSurfaceReady, mHasFocus; + public static boolean mExitCalledFromJava; + + // Main components + protected static SDLActivity mSingleton; + protected static SDLSurface mSurface; + protected static View mTextEdit; + protected static ViewGroup mLayout; + protected static SDLJoystickHandler mJoystickHandler; + + // This is what SDL runs in. It invokes SDL_main(), eventually + protected static Thread mSDLThread; + + // Audio + protected static AudioTrack mAudioTrack; + + // Load the .so + static { + System.loadLibrary("stlport_shared"); + System.loadLibrary("SDL2"); + //System.loadLibrary("SDL2_image"); + //System.loadLibrary("SDL2_mixer"); + System.loadLibrary("SDL2_net"); + //System.loadLibrary("SDL2_ttf"); + System.loadLibrary("main"); + } + + + public static void initialize() { + // The static nature of the singleton and Android quirkyness force us to initialize everything here + // Otherwise, when exiting the app and returning to it, these variables *keep* their pre exit values + mSingleton = null; + mSurface = null; + mTextEdit = null; + mLayout = null; + mJoystickHandler = null; + mSDLThread = null; + mAudioTrack = null; + mExitCalledFromJava = false; + mIsPaused = false; + mIsSurfaceReady = false; + mHasFocus = true; + } + + // Setup + @Override + protected void onCreate(Bundle savedInstanceState) { + Log.v("SDL", "onCreate():" + mSingleton); + super.onCreate(savedInstanceState); + + SDLActivity.initialize(); + // So we can call stuff from static callbacks + mSingleton = this; + + // Set up the surface + mSurface = new SDLSurface(getApplication()); + + if(Build.VERSION.SDK_INT >= 12) { + mJoystickHandler = new SDLJoystickHandler_API12(); + } + else { + mJoystickHandler = new SDLJoystickHandler(); + } + + mLayout = new AbsoluteLayout(this); + mLayout.addView(mSurface); + + setContentView(mLayout); + } + + // Events + @Override + protected void onPause() { + Log.v("SDL", "onPause()"); + super.onPause(); + SDLActivity.handlePause(); + } + + @Override + protected void onResume() { + Log.v("SDL", "onResume()"); + super.onResume(); + SDLActivity.handleResume(); + } + + + @Override + public void onWindowFocusChanged(boolean hasFocus) { + super.onWindowFocusChanged(hasFocus); + Log.v("SDL", "onWindowFocusChanged(): " + hasFocus); + + SDLActivity.mHasFocus = hasFocus; + if (hasFocus) { + SDLActivity.handleResume(); + } + } + + @Override + public void onLowMemory() { + Log.v("SDL", "onLowMemory()"); + super.onLowMemory(); + SDLActivity.nativeLowMemory(); + } + + @Override + protected void onDestroy() { + Log.v("SDL", "onDestroy()"); + // Send a quit message to the application + SDLActivity.mExitCalledFromJava = true; + SDLActivity.nativeQuit(); + + // Now wait for the SDL thread to quit + if (SDLActivity.mSDLThread != null) { + try { + SDLActivity.mSDLThread.join(); + } catch(Exception e) { + Log.v("SDL", "Problem stopping thread: " + e); + } + SDLActivity.mSDLThread = null; + + //Log.v("SDL", "Finished waiting for SDL thread"); + } + + super.onDestroy(); + // Reset everything in case the user re opens the app + SDLActivity.initialize(); + } + + @Override + public boolean dispatchKeyEvent(KeyEvent event) { + int keyCode = event.getKeyCode(); + // Ignore certain special keys so they're handled by Android + if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN || + keyCode == KeyEvent.KEYCODE_VOLUME_UP || + keyCode == KeyEvent.KEYCODE_CAMERA || + keyCode == 168 || /* API 11: KeyEvent.KEYCODE_ZOOM_IN */ + keyCode == 169 /* API 11: KeyEvent.KEYCODE_ZOOM_OUT */ + ) { + return false; + } + return super.dispatchKeyEvent(event); + } + + /** Called by onPause or surfaceDestroyed. Even if surfaceDestroyed + * is the first to be called, mIsSurfaceReady should still be set + * to 'true' during the call to onPause (in a usual scenario). + */ + public static void handlePause() { + if (!SDLActivity.mIsPaused && SDLActivity.mIsSurfaceReady) { + SDLActivity.mIsPaused = true; + SDLActivity.nativePause(); + mSurface.enableSensor(Sensor.TYPE_ACCELEROMETER, false); + } + } + + /** Called by onResume or surfaceCreated. An actual resume should be done only when the surface is ready. + * Note: Some Android variants may send multiple surfaceChanged events, so we don't need to resume + * every time we get one of those events, only if it comes after surfaceDestroyed + */ + public static void handleResume() { + if (SDLActivity.mIsPaused && SDLActivity.mIsSurfaceReady && SDLActivity.mHasFocus) { + SDLActivity.mIsPaused = false; + SDLActivity.nativeResume(); + mSurface.enableSensor(Sensor.TYPE_ACCELEROMETER, true); + } + } + + /* The native thread has finished */ + public static void handleNativeExit() { + SDLActivity.mSDLThread = null; + mSingleton.finish(); + } + + + // Messages from the SDLMain thread + static final int COMMAND_CHANGE_TITLE = 1; + static final int COMMAND_UNUSED = 2; + static final int COMMAND_TEXTEDIT_HIDE = 3; + + protected static final int COMMAND_USER = 0x8000; + + /** + * This method is called by SDL if SDL did not handle a message itself. + * This happens if a received message contains an unsupported command. + * Method can be overwritten to handle Messages in a different class. + * @param command the command of the message. + * @param param the parameter of the message. May be null. + * @return if the message was handled in overridden method. + */ + protected boolean onUnhandledMessage(int command, Object param) { + return false; + } + + /** + * A Handler class for Messages from native SDL applications. + * It uses current Activities as target (e.g. for the title). + * static to prevent implicit references to enclosing object. + */ + protected static class SDLCommandHandler extends Handler { + @Override + public void handleMessage(Message msg) { + Context context = getContext(); + if (context == null) { + Log.e(TAG, "error handling message, getContext() returned null"); + return; + } + switch (msg.arg1) { + case COMMAND_CHANGE_TITLE: + if (context instanceof Activity) { + ((Activity) context).setTitle((String)msg.obj); + } else { + Log.e(TAG, "error handling message, getContext() returned no Activity"); + } + break; + case COMMAND_TEXTEDIT_HIDE: + if (mTextEdit != null) { + mTextEdit.setVisibility(View.GONE); + + InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE); + imm.hideSoftInputFromWindow(mTextEdit.getWindowToken(), 0); + } + break; + + default: + if ((context instanceof SDLActivity) && !((SDLActivity) context).onUnhandledMessage(msg.arg1, msg.obj)) { + Log.e(TAG, "error handling message, command is " + msg.arg1); + } + } + } + } + + // Handler for the messages + Handler commandHandler = new SDLCommandHandler(); + + // Send a message from the SDLMain thread + boolean sendCommand(int command, Object data) { + Message msg = commandHandler.obtainMessage(); + msg.arg1 = command; + msg.obj = data; + return commandHandler.sendMessage(msg); + } + + // C functions we call + public static native void nativeInit(); + public static native void nativeLowMemory(); + public static native void nativeQuit(); + public static native void nativePause(); + public static native void nativeResume(); + public static native void onNativeResize(int x, int y, int format); + public static native int onNativePadDown(int device_id, int keycode); + public static native int onNativePadUp(int device_id, int keycode); + public static native void onNativeJoy(int device_id, int axis, + float value); + public static native void onNativeHat(int device_id, int hat_id, + int x, int y); + public static native void onNativeKeyDown(int keycode); + public static native void onNativeKeyUp(int keycode); + public static native void onNativeKeyboardFocusLost(); + public static native void onNativeTouch(int touchDevId, int pointerFingerId, + int action, float x, + float y, float p); + public static native void onNativeAccel(float x, float y, float z); + public static native void onNativeSurfaceChanged(); + public static native void onNativeSurfaceDestroyed(); + public static native void nativeFlipBuffers(); + public static native int nativeAddJoystick(int device_id, String name, + int is_accelerometer, int nbuttons, + int naxes, int nhats, int nballs); + public static native int nativeRemoveJoystick(int device_id); + + public static void flipBuffers() { + SDLActivity.nativeFlipBuffers(); + } + + public static boolean setActivityTitle(String title) { + // Called from SDLMain() thread and can't directly affect the view + return mSingleton.sendCommand(COMMAND_CHANGE_TITLE, title); + } + + public static boolean sendMessage(int command, int param) { + return mSingleton.sendCommand(command, Integer.valueOf(param)); + } + + public static Context getContext() { + return mSingleton; + } + + /** + * @return result of getSystemService(name) but executed on UI thread. + */ + public Object getSystemServiceFromUiThread(final String name) { + final Object lock = new Object(); + final Object[] results = new Object[2]; // array for writable variables + synchronized (lock) { + runOnUiThread(new Runnable() { + @Override + public void run() { + synchronized (lock) { + results[0] = getSystemService(name); + results[1] = Boolean.TRUE; + lock.notify(); + } + } + }); + if (results[1] == null) { + try { + lock.wait(); + } catch (InterruptedException ex) { + ex.printStackTrace(); + } + } + } + return results[0]; + } + + static class ShowTextInputTask implements Runnable { + /* + * This is used to regulate the pan&scan method to have some offset from + * the bottom edge of the input region and the top edge of an input + * method (soft keyboard) + */ + static final int HEIGHT_PADDING = 15; + + public int x, y, w, h; + + public ShowTextInputTask(int x, int y, int w, int h) { + this.x = x; + this.y = y; + this.w = w; + this.h = h; + } + + @Override + public void run() { + AbsoluteLayout.LayoutParams params = new AbsoluteLayout.LayoutParams( + w, h + HEIGHT_PADDING, x, y); + + if (mTextEdit == null) { + mTextEdit = new DummyEdit(getContext()); + + mLayout.addView(mTextEdit, params); + } else { + mTextEdit.setLayoutParams(params); + } + + mTextEdit.setVisibility(View.VISIBLE); + mTextEdit.requestFocus(); + + InputMethodManager imm = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE); + imm.showSoftInput(mTextEdit, 0); + } + } + + public static boolean showTextInput(int x, int y, int w, int h) { + // Transfer the task to the main thread as a Runnable + return mSingleton.commandHandler.post(new ShowTextInputTask(x, y, w, h)); + } + + public static Surface getNativeSurface() { + return SDLActivity.mSurface.getNativeSurface(); + } + + // Audio + public static int audioInit(int sampleRate, boolean is16Bit, boolean isStereo, int desiredFrames) { + int channelConfig = isStereo ? AudioFormat.CHANNEL_CONFIGURATION_STEREO : AudioFormat.CHANNEL_CONFIGURATION_MONO; + int audioFormat = is16Bit ? AudioFormat.ENCODING_PCM_16BIT : AudioFormat.ENCODING_PCM_8BIT; + int frameSize = (isStereo ? 2 : 1) * (is16Bit ? 2 : 1); + + Log.v("SDL", "SDL audio: wanted " + (isStereo ? "stereo" : "mono") + " " + (is16Bit ? "16-bit" : "8-bit") + " " + (sampleRate / 1000f) + "kHz, " + desiredFrames + " frames buffer"); + + // Let the user pick a larger buffer if they really want -- but ye + // gods they probably shouldn't, the minimums are horrifyingly high + // latency already + desiredFrames = Math.max(desiredFrames, (AudioTrack.getMinBufferSize(sampleRate, channelConfig, audioFormat) + frameSize - 1) / frameSize); + + if (mAudioTrack == null) { + mAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, sampleRate, + channelConfig, audioFormat, desiredFrames * frameSize, AudioTrack.MODE_STREAM); + + // Instantiating AudioTrack can "succeed" without an exception and the track may still be invalid + // Ref: https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/media/java/android/media/AudioTrack.java + // Ref: http://developer.android.com/reference/android/media/AudioTrack.html#getState() + + if (mAudioTrack.getState() != AudioTrack.STATE_INITIALIZED) { + Log.e("SDL", "Failed during initialization of Audio Track"); + mAudioTrack = null; + return -1; + } + + mAudioTrack.play(); + } + + Log.v("SDL", "SDL audio: got " + ((mAudioTrack.getChannelCount() >= 2) ? "stereo" : "mono") + " " + ((mAudioTrack.getAudioFormat() == AudioFormat.ENCODING_PCM_16BIT) ? "16-bit" : "8-bit") + " " + (mAudioTrack.getSampleRate() / 1000f) + "kHz, " + desiredFrames + " frames buffer"); + + return 0; + } + + public static void audioWriteShortBuffer(short[] buffer) { + for (int i = 0; i < buffer.length; ) { + int result = mAudioTrack.write(buffer, i, buffer.length - i); + if (result > 0) { + i += result; + } else if (result == 0) { + try { + Thread.sleep(1); + } catch(InterruptedException e) { + // Nom nom + } + } else { + Log.w("SDL", "SDL audio: error return from write(short)"); + return; + } + } + } + + public static void audioWriteByteBuffer(byte[] buffer) { + for (int i = 0; i < buffer.length; ) { + int result = mAudioTrack.write(buffer, i, buffer.length - i); + if (result > 0) { + i += result; + } else if (result == 0) { + try { + Thread.sleep(1); + } catch(InterruptedException e) { + // Nom nom + } + } else { + Log.w("SDL", "SDL audio: error return from write(byte)"); + return; + } + } + } + + public static void audioQuit() { + if (mAudioTrack != null) { + mAudioTrack.stop(); + mAudioTrack = null; + } + } + + // Input + + /** + * @return an array which may be empty but is never null. + */ + public static int[] inputGetInputDeviceIds(int sources) { + int[] ids = InputDevice.getDeviceIds(); + int[] filtered = new int[ids.length]; + int used = 0; + for (int i = 0; i < ids.length; ++i) { + InputDevice device = InputDevice.getDevice(ids[i]); + if ((device != null) && ((device.getSources() & sources) != 0)) { + filtered[used++] = device.getId(); + } + } + return Arrays.copyOf(filtered, used); + } + + // Joystick glue code, just a series of stubs that redirect to the SDLJoystickHandler instance + public static boolean handleJoystickMotionEvent(MotionEvent event) { + return mJoystickHandler.handleMotionEvent(event); + } + + public static void pollInputDevices() { + if (SDLActivity.mSDLThread != null) { + mJoystickHandler.pollInputDevices(); + } + } + +} + +/** + Simple nativeInit() runnable +*/ +class SDLMain implements Runnable { + @Override + public void run() { + // Runs SDL_main() + SDLActivity.nativeInit(); + + //Log.v("SDL", "SDL thread terminated"); + } +} + + +/** + SDLSurface. This is what we draw on, so we need to know when it's created + in order to do anything useful. + + Because of this, that's where we set up the SDL thread +*/ +class SDLSurface extends SurfaceView implements SurfaceHolder.Callback, + View.OnKeyListener, View.OnTouchListener, SensorEventListener { + + // Sensors + protected static SensorManager mSensorManager; + protected static Display mDisplay; + + // Keep track of the surface size to normalize touch events + protected static float mWidth, mHeight; + + // Startup + public SDLSurface(Context context) { + super(context); + getHolder().addCallback(this); + + setFocusable(true); + setFocusableInTouchMode(true); + requestFocus(); + setOnKeyListener(this); + setOnTouchListener(this); + + mDisplay = ((WindowManager)context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay(); + mSensorManager = (SensorManager)context.getSystemService(Context.SENSOR_SERVICE); + + if(Build.VERSION.SDK_INT >= 12) { + setOnGenericMotionListener(new SDLGenericMotionListener_API12()); + } + + // Some arbitrary defaults to avoid a potential division by zero + mWidth = 1.0f; + mHeight = 1.0f; + } + + public Surface getNativeSurface() { + return getHolder().getSurface(); + } + + // Called when we have a valid drawing surface + @Override + public void surfaceCreated(SurfaceHolder holder) { + Log.v("SDL", "surfaceCreated()"); + holder.setType(SurfaceHolder.SURFACE_TYPE_GPU); + } + + // Called when we lose the surface + @Override + public void surfaceDestroyed(SurfaceHolder holder) { + Log.v("SDL", "surfaceDestroyed()"); + // Call this *before* setting mIsSurfaceReady to 'false' + SDLActivity.handlePause(); + SDLActivity.mIsSurfaceReady = false; + SDLActivity.onNativeSurfaceDestroyed(); + } + + // Called when the surface is resized + @Override + public void surfaceChanged(SurfaceHolder holder, + int format, int width, int height) { + Log.v("SDL", "surfaceChanged()"); + + int sdlFormat = 0x15151002; // SDL_PIXELFORMAT_RGB565 by default + switch (format) { + case PixelFormat.A_8: + Log.v("SDL", "pixel format A_8"); + break; + case PixelFormat.LA_88: + Log.v("SDL", "pixel format LA_88"); + break; + case PixelFormat.L_8: + Log.v("SDL", "pixel format L_8"); + break; + case PixelFormat.RGBA_4444: + Log.v("SDL", "pixel format RGBA_4444"); + sdlFormat = 0x15421002; // SDL_PIXELFORMAT_RGBA4444 + break; + case PixelFormat.RGBA_5551: + Log.v("SDL", "pixel format RGBA_5551"); + sdlFormat = 0x15441002; // SDL_PIXELFORMAT_RGBA5551 + break; + case PixelFormat.RGBA_8888: + Log.v("SDL", "pixel format RGBA_8888"); + sdlFormat = 0x16462004; // SDL_PIXELFORMAT_RGBA8888 + break; + case PixelFormat.RGBX_8888: + Log.v("SDL", "pixel format RGBX_8888"); + sdlFormat = 0x16261804; // SDL_PIXELFORMAT_RGBX8888 + break; + case PixelFormat.RGB_332: + Log.v("SDL", "pixel format RGB_332"); + sdlFormat = 0x14110801; // SDL_PIXELFORMAT_RGB332 + break; + case PixelFormat.RGB_565: + Log.v("SDL", "pixel format RGB_565"); + sdlFormat = 0x15151002; // SDL_PIXELFORMAT_RGB565 + break; + case PixelFormat.RGB_888: + Log.v("SDL", "pixel format RGB_888"); + // Not sure this is right, maybe SDL_PIXELFORMAT_RGB24 instead? + sdlFormat = 0x16161804; // SDL_PIXELFORMAT_RGB888 + break; + default: + Log.v("SDL", "pixel format unknown " + format); + break; + } + + mWidth = width; + mHeight = height; + SDLActivity.onNativeResize(width, height, sdlFormat); + Log.v("SDL", "Window size:" + width + "x"+height); + + // Set mIsSurfaceReady to 'true' *before* making a call to handleResume + SDLActivity.mIsSurfaceReady = true; + SDLActivity.onNativeSurfaceChanged(); + + + if (SDLActivity.mSDLThread == null) { + // This is the entry point to the C app. + // Start up the C app thread and enable sensor input for the first time + + SDLActivity.mSDLThread = new Thread(new SDLMain(), "SDLThread"); + enableSensor(Sensor.TYPE_ACCELEROMETER, true); + SDLActivity.mSDLThread.start(); + + // Set up a listener thread to catch when the native thread ends + new Thread(new Runnable(){ + @Override + public void run(){ + try { + SDLActivity.mSDLThread.join(); + } + catch(Exception e){} + finally{ + // Native thread has finished + if (! SDLActivity.mExitCalledFromJava) { + SDLActivity.handleNativeExit(); + } + } + } + }).start(); + } + } + + // unused + @Override + public void onDraw(Canvas canvas) {} + + + // Key events + @Override + public boolean onKey(View v, int keyCode, KeyEvent event) { + // Dispatch the different events depending on where they come from + // Some SOURCE_DPAD or SOURCE_GAMEPAD are also SOURCE_KEYBOARD + // So, we try to process them as DPAD or GAMEPAD events first, if that fails we try them as KEYBOARD + + if ( (event.getSource() & 0x00000401) != 0 || /* API 12: SOURCE_GAMEPAD */ + (event.getSource() & InputDevice.SOURCE_DPAD) != 0 ) { + if (event.getAction() == KeyEvent.ACTION_DOWN) { + if (SDLActivity.onNativePadDown(event.getDeviceId(), keyCode) == 0) { + return true; + } + } else if (event.getAction() == KeyEvent.ACTION_UP) { + if (SDLActivity.onNativePadUp(event.getDeviceId(), keyCode) == 0) { + return true; + } + } + } + + // HACK: Get input from all keys of a soft-keyboard, + // even if InputDevice.SOURCE_KEYBOARD is not the source. +// if( (event.getSource() & InputDevice.SOURCE_KEYBOARD) != 0) { + if (event.getAction() == KeyEvent.ACTION_DOWN) { + //Log.v("SDL", "key down: " + keyCode); + SDLActivity.onNativeKeyDown(keyCode); + return true; + } + else if (event.getAction() == KeyEvent.ACTION_UP) { + //Log.v("SDL", "key up: " + keyCode); + SDLActivity.onNativeKeyUp(keyCode); + return true; + } +// } + + return false; + } + + // Touch events + @Override + public boolean onTouch(View v, MotionEvent event) { + /* Ref: http://developer.android.com/training/gestures/multi.html */ + final int touchDevId = event.getDeviceId(); + final int pointerCount = event.getPointerCount(); + int action = event.getActionMasked(); + int pointerFingerId; + int i = -1; + float x,y,p; + + switch(action) { + case MotionEvent.ACTION_MOVE: + for (i = 0; i < pointerCount; i++) { + pointerFingerId = event.getPointerId(i); + x = event.getX(i) / mWidth; + y = event.getY(i) / mHeight; + p = event.getPressure(i); + SDLActivity.onNativeTouch(touchDevId, pointerFingerId, action, x, y, p); + } + break; + + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_DOWN: + // Primary pointer up/down, the index is always zero + i = 0; + case MotionEvent.ACTION_POINTER_UP: + case MotionEvent.ACTION_POINTER_DOWN: + // Non primary pointer up/down + if (i == -1) { + i = event.getActionIndex(); + } + + pointerFingerId = event.getPointerId(i); + x = event.getX(i) / mWidth; + y = event.getY(i) / mHeight; + p = event.getPressure(i); + SDLActivity.onNativeTouch(touchDevId, pointerFingerId, action, x, y, p); + break; + + default: + break; + } + + return true; + } + + // Sensor events + public void enableSensor(int sensortype, boolean enabled) { + // TODO: This uses getDefaultSensor - what if we have >1 accels? + if (enabled) { + mSensorManager.registerListener(this, + mSensorManager.getDefaultSensor(sensortype), + SensorManager.SENSOR_DELAY_GAME, null); + } else { + mSensorManager.unregisterListener(this, + mSensorManager.getDefaultSensor(sensortype)); + } + } + + @Override + public void onAccuracyChanged(Sensor sensor, int accuracy) { + // TODO + } + + @Override + public void onSensorChanged(SensorEvent event) { + if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) { + float x, y; + switch (mDisplay.getRotation()) { + case Surface.ROTATION_90: + x = -event.values[1]; + y = event.values[0]; + break; + case Surface.ROTATION_270: + x = event.values[1]; + y = -event.values[0]; + break; + case Surface.ROTATION_180: + x = -event.values[1]; + y = -event.values[0]; + break; + default: + x = event.values[0]; + y = event.values[1]; + break; + } + SDLActivity.onNativeAccel(-x / SensorManager.GRAVITY_EARTH, + y / SensorManager.GRAVITY_EARTH, + event.values[2] / SensorManager.GRAVITY_EARTH - 1); + } + } +} + +/* This is a fake invisible editor view that receives the input and defines the + * pan&scan region + */ +class DummyEdit extends View implements View.OnKeyListener { + InputConnection ic; + + public DummyEdit(Context context) { + super(context); + setFocusableInTouchMode(true); + setFocusable(true); + setOnKeyListener(this); + } + + @Override + public boolean onCheckIsTextEditor() { + return true; + } + + @Override + public boolean onKey(View v, int keyCode, KeyEvent event) { + + // This handles the hardware keyboard input + if (event.isPrintingKey()) { + if (event.getAction() == KeyEvent.ACTION_DOWN) { + ic.commitText(String.valueOf((char) event.getUnicodeChar()), 1); + } + return true; + } + + if (event.getAction() == KeyEvent.ACTION_DOWN) { + SDLActivity.onNativeKeyDown(keyCode); + return true; + } else if (event.getAction() == KeyEvent.ACTION_UP) { + SDLActivity.onNativeKeyUp(keyCode); + return true; + } + + return false; + } + + // + @Override + public boolean onKeyPreIme (int keyCode, KeyEvent event) { + // As seen on StackOverflow: http://stackoverflow.com/questions/7634346/keyboard-hide-event + // FIXME: Discussion at http://bugzilla.libsdl.org/show_bug.cgi?id=1639 + // FIXME: This is not a 100% effective solution to the problem of detecting if the keyboard is showing or not + // FIXME: A more effective solution would be to change our Layout from AbsoluteLayout to Relative or Linear + // FIXME: And determine the keyboard presence doing this: http://stackoverflow.com/questions/2150078/how-to-check-visibility-of-software-keyboard-in-android + // FIXME: An even more effective way would be if Android provided this out of the box, but where would the fun be in that :) + if (event.getAction()==KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_BACK) { + if (SDLActivity.mTextEdit != null && SDLActivity.mTextEdit.getVisibility() == View.VISIBLE) { + SDLActivity.onNativeKeyboardFocusLost(); + } + } + return super.onKeyPreIme(keyCode, event); + } + + @Override + public InputConnection onCreateInputConnection(EditorInfo outAttrs) { + ic = new SDLInputConnection(this, true); + + outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_EXTRACT_UI + | 33554432 /* API 11: EditorInfo.IME_FLAG_NO_FULLSCREEN */; + + return ic; + } +} + +class SDLInputConnection extends BaseInputConnection { + + public SDLInputConnection(View targetView, boolean fullEditor) { + super(targetView, fullEditor); + + } + + @Override + public boolean sendKeyEvent(KeyEvent event) { + + /* + * This handles the keycodes from soft keyboard (and IME-translated + * input from hardkeyboard) + */ + int keyCode = event.getKeyCode(); + if (event.getAction() == KeyEvent.ACTION_DOWN) { + if (event.isPrintingKey()) { + commitText(String.valueOf((char) event.getUnicodeChar()), 1); + } + SDLActivity.onNativeKeyDown(keyCode); + return true; + } else if (event.getAction() == KeyEvent.ACTION_UP) { + + SDLActivity.onNativeKeyUp(keyCode); + return true; + } + return super.sendKeyEvent(event); + } + + @Override + public boolean commitText(CharSequence text, int newCursorPosition) { + + nativeCommitText(text.toString(), newCursorPosition); + + return super.commitText(text, newCursorPosition); + } + + @Override + public boolean setComposingText(CharSequence text, int newCursorPosition) { + + nativeSetComposingText(text.toString(), newCursorPosition); + + return super.setComposingText(text, newCursorPosition); + } + + public native void nativeCommitText(String text, int newCursorPosition); + + public native void nativeSetComposingText(String text, int newCursorPosition); + + @Override + public boolean deleteSurroundingText(int beforeLength, int afterLength) { + // Workaround to capture backspace key. Ref: http://stackoverflow.com/questions/14560344/android-backspace-in-webview-baseinputconnection + if (beforeLength == 1 && afterLength == 0) { + // backspace + return super.sendKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL)) + && super.sendKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DEL)); + } + + return super.deleteSurroundingText(beforeLength, afterLength); + } +} + +/* A null joystick handler for API level < 12 devices (the accelerometer is handled separately) */ +class SDLJoystickHandler { + + public boolean handleMotionEvent(MotionEvent event) { + return false; + } + + public void pollInputDevices() { + } +} + +/* Actual joystick functionality available for API >= 12 devices */ +class SDLJoystickHandler_API12 extends SDLJoystickHandler { + + class SDLJoystick { + public int device_id; + public String name; + public ArrayList<InputDevice.MotionRange> axes; + public ArrayList<InputDevice.MotionRange> hats; + } + class RangeComparator implements Comparator<InputDevice.MotionRange> + { + @Override + public int compare(InputDevice.MotionRange arg0, InputDevice.MotionRange arg1) { + return arg0.getAxis() - arg1.getAxis(); + } + } + + private ArrayList<SDLJoystick> mJoysticks; + + public SDLJoystickHandler_API12() { + + mJoysticks = new ArrayList<SDLJoystick>(); + } + + @Override + public void pollInputDevices() { + int[] deviceIds = InputDevice.getDeviceIds(); + // It helps processing the device ids in reverse order + // For example, in the case of the XBox 360 wireless dongle, + // so the first controller seen by SDL matches what the receiver + // considers to be the first controller + + for(int i=deviceIds.length-1; i>-1; i--) { + SDLJoystick joystick = getJoystick(deviceIds[i]); + if (joystick == null) { + joystick = new SDLJoystick(); + InputDevice joystickDevice = InputDevice.getDevice(deviceIds[i]); + if( (joystickDevice.getSources() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) { + joystick.device_id = deviceIds[i]; + joystick.name = joystickDevice.getName(); + joystick.axes = new ArrayList<InputDevice.MotionRange>(); + joystick.hats = new ArrayList<InputDevice.MotionRange>(); + + List<InputDevice.MotionRange> ranges = joystickDevice.getMotionRanges(); + Collections.sort(ranges, new RangeComparator()); + for (InputDevice.MotionRange range : ranges ) { + if ((range.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0 ) { + if (range.getAxis() == MotionEvent.AXIS_HAT_X || + range.getAxis() == MotionEvent.AXIS_HAT_Y) { + joystick.hats.add(range); + } + else { + joystick.axes.add(range); + } + } + } + + mJoysticks.add(joystick); + SDLActivity.nativeAddJoystick(joystick.device_id, joystick.name, 0, -1, + joystick.axes.size(), joystick.hats.size()/2, 0); + } + } + } + + /* Check removed devices */ + ArrayList<Integer> removedDevices = new ArrayList<Integer>(); + for(int i=0; i < mJoysticks.size(); i++) { + int device_id = mJoysticks.get(i).device_id; + int j; + for (j=0; j < deviceIds.length; j++) { + if (device_id == deviceIds[j]) break; + } + if (j == deviceIds.length) { + removedDevices.add(device_id); + } + } + + for(int i=0; i < removedDevices.size(); i++) { + int device_id = removedDevices.get(i); + SDLActivity.nativeRemoveJoystick(device_id); + for (int j=0; j < mJoysticks.size(); j++) { + if (mJoysticks.get(j).device_id == device_id) { + mJoysticks.remove(j); + break; + } + } + } + } + + protected SDLJoystick getJoystick(int device_id) { + for(int i=0; i < mJoysticks.size(); i++) { + if (mJoysticks.get(i).device_id == device_id) { + return mJoysticks.get(i); + } + } + return null; + } + + @Override + public boolean handleMotionEvent(MotionEvent event) { + if ( (event.getSource() & InputDevice.SOURCE_JOYSTICK) != 0) { + int actionPointerIndex = event.getActionIndex(); + int action = event.getActionMasked(); + switch(action) { + case MotionEvent.ACTION_MOVE: + SDLJoystick joystick = getJoystick(event.getDeviceId()); + if ( joystick != null ) { + for (int i = 0; i < joystick.axes.size(); i++) { + InputDevice.MotionRange range = joystick.axes.get(i); + /* Normalize the value to -1...1 */ + float value = ( event.getAxisValue( range.getAxis(), actionPointerIndex) - range.getMin() ) / range.getRange() * 2.0f - 1.0f; + SDLActivity.onNativeJoy(joystick.device_id, i, value ); + } + for (int i = 0; i < joystick.hats.size(); i+=2) { + int hatX = Math.round(event.getAxisValue( joystick.hats.get(i).getAxis(), actionPointerIndex ) ); + int hatY = Math.round(event.getAxisValue( joystick.hats.get(i+1).getAxis(), actionPointerIndex ) ); + SDLActivity.onNativeHat(joystick.device_id, i/2, hatX, hatY ); + } + } + break; + default: + break; + } + } + return true; + } +} + +class SDLGenericMotionListener_API12 implements View.OnGenericMotionListener { + // Generic Motion (mouse hover, joystick...) events go here + // We only have joysticks yet + @Override + public boolean onGenericMotion(View v, MotionEvent event) { + return SDLActivity.handleJoystickMotionEvent(event); + } +} diff --git a/android-project/src/org/libsdl/app/note.txt b/android-project/src/org/libsdl/app/note.txt new file mode 100644 index 000000000..83fcedb46 --- /dev/null +++ b/android-project/src/org/libsdl/app/note.txt @@ -0,0 +1,4 @@ +Modifications to SDLActivity.java: +1. stlport_shared and SDL2_net are loaded. +2. Non-gamepad/dpad keycodes that don't have any source are still assumed to +come from a keyboard (a workaround for full input from soft keyboard). diff --git a/configure.ac b/configure.ac index fb8d28fbe..691fbf467 100644 --- a/configure.ac +++ b/configure.ac @@ -27,27 +27,94 @@ if test x$host = xi386-pc-os2-emx ; then fi dnl Check for SDL -SDL_VERSION=1.2.0 -AM_PATH_SDL($SDL_VERSION, - :, - AC_MSG_ERROR([*** SDL version $SDL_VERSION not found!]) -) +EXULT_CHECK_SDL(:,AC_MSG_ERROR([[*** SDL not found!]])) LIBS="$LIBS $SDL_LIBS" CPPFLAGS="$CPPFLAGS $SDL_CFLAGS" -dnl Check if SDL is 1.2.x (1.3 not supported) -AC_MSG_CHECKING([SDL version only being 1.2.X]) +dnl Check if SDL is 1.2.x, 2.0.x +AC_MSG_CHECKING([for SDL version being 1.2.x or 2.0.x]) AC_COMPILE_IFELSE([AC_LANG_SOURCE([ #include "SDL.h" void blah(){ -#if SDL_MINOR_VERSION != 2 -#error "Only SDL 1.2 supported" +#if !((SDL_MAJOR_VERSION == 1) && (SDL_MINOR_VERSION == 2)) && !((SDL_MAJOR_VERSION == 2) && (SDL_MINOR_VERSION == 0)) +#error "Only SDL 1.2 and 2.0 supported" #endif ; } ])],AC_MSG_RESULT([yes]),[ AC_MSG_RESULT([no]) - AC_MSG_ERROR([Only libSDL 1.2.X supported])]) + AC_MSG_ERROR([Only libSDL 1.2.x or 2.0.x supported])]) + +dnl Check for SDL_cdrom compatibility layer platform (used with SDL 2.0 only) +AC_MSG_CHECKING(for SDL_cdrom compatibility layer platform) +COMPAT_SDL_CDROM_GET_PLATFORM(:) +AH_TEMPLATE(C_COMPAT_SDL_CDROM_PLATFORM,[Platform for SDL_cdrom compatibility layer (SDL 2.0 only)]) + +physical_cdrom_mount=1 + +case $compat_sdl_cdrom_arch in + linux|solaris) + AC_DEFINE(C_COMPAT_SDL_CDROM_PLATFORM,[COMPAT_SDL_CDROM_PLATFORM_LINUX]) + AC_MSG_RESULT([linux]) + ;; + *freebsd*) + AC_DEFINE(C_COMPAT_SDL_CDROM_PLATFORM,[COMPAT_SDL_CDROM_PLATFORM_FREEBSD]) + AC_MSG_RESULT([freebsd]) + ;; + *openbsd*|*netbsd*) + AC_DEFINE(C_COMPAT_SDL_CDROM_PLATFORM,[COMPAT_SDL_CDROM_PLATFORM_OPENBSD]) + AC_MSG_RESULT([openbsd]) + ;; + bsdi) + AC_DEFINE(C_COMPAT_SDL_CDROM_PLATFORM,[COMPAT_SDL_CDROM_PLATFORM_BSDI]) + AC_MSG_RESULT([bsdi]) + ;; + aix) + AC_DEFINE(C_COMPAT_SDL_CDROM_PLATFORM,[COMPAT_SDL_CDROM_PLATFORM_AIX]) + AC_MSG_RESULT([aix]) + ;; + osf) + AC_DEFINE(C_COMPAT_SDL_CDROM_PLATFORM,[COMPAT_SDL_CDROM_PLATFORM_OSF]) + AC_MSG_RESULT([osf]) + ;; + qnx) + AC_DEFINE(C_COMPAT_SDL_CDROM_PLATFORM,[COMPAT_SDL_CDROM_PLATFORM_QNX]) + AC_MSG_RESULT([qnx]) + ;; + win32) + AC_DEFINE(C_COMPAT_SDL_CDROM_PLATFORM,[COMPAT_SDL_CDROM_PLATFORM_WIN32]) + AC_MSG_RESULT([win32]) + ;; + beos) + AC_DEFINE(C_COMPAT_SDL_CDROM_PLATFORM,[COMPAT_SDL_CDROM_PLATFORM_BEOS]) + AC_MSG_RESULT([beos]) + ;; + macosx) + AC_DEFINE(C_COMPAT_SDL_CDROM_PLATFORM,[COMPAT_SDL_CDROM_PLATFORM_MACOSX]) + AC_MSG_RESULT([macosx]) + ;; + mint) + AC_DEFINE(C_COMPAT_SDL_CDROM_PLATFORM,[COMPAT_SDL_CDROM_PLATFORM_MINT]) + AC_MSG_RESULT([mint]) + ;; + *) + physical_cdrom_mount=0 + AC_MSG_RESULT([disabled]) +esac + +if test x"$sdl_ver" = xsdl12 ; then + physical_cdrom_mount=1 +fi + +AC_MSG_CHECKING(for physical cdrom mounting support) +AH_TEMPLATE(C_PHYSICAL_CDROM_MOUNT,[Physical CD-ROM mounting support]) +if test x"$physical_cdrom_mount" = x1 ; then + AC_MSG_RESULT(yes) + AC_DEFINE(C_PHYSICAL_CDROM_MOUNT,[1]) +else + AC_MSG_RESULT(no) +fi + dnl Checks for header files. @@ -437,23 +504,29 @@ AH_TEMPLATE(C_MODEM,[Define to 1 to enable internal modem support, requires SDL_ AH_TEMPLATE(C_IPX,[Define to 1 to enable IPX over Internet networking, requires SDL_net]) AC_CHECK_HEADER(SDL_net.h,have_sdl_net_h=yes,) +if test x"$sdl_ver" = xsdl2 ; then + LIBSDL_HEADER="SDL2" +else + LIBSDL_HEADER="SDL" +fi + if test x$host = xi386-pc-os2-emx ; then - AC_MSG_CHECKING(for SDLNet_Init in SDL_net); + AC_MSG_CHECKING(for SDLNet_Init in ${LIBSDL_HEADER}_net); LIBS_BACKUP=$LIBS; - LIBS="$LIBS -lSDL_Net"; + LIBS="$LIBS -l${LIBSDL_HEADER}_Net"; AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <SDL_Net.h>]],[[ SDLNet_Init (); ]])], [AC_MSG_RESULT(yes); have_sdl_net_lib=yes], AC_MSG_RESULT(no)) LIBS=$LIBS_BACKUP else -AC_CHECK_LIB(SDL_net, SDLNet_Init, have_sdl_net_lib=yes, , ) +AC_CHECK_LIB(${LIBSDL_HEADER}_net, SDLNet_Init, have_sdl_net_lib=yes, , ) fi if test x$have_sdl_net_lib = xyes -a x$have_sdl_net_h = xyes ; then - LIBS="$LIBS -lSDL_net" + LIBS="$LIBS -l${LIBSDL_HEADER}_net" AC_DEFINE(C_MODEM,1) AC_DEFINE(C_IPX,1) else - AC_MSG_WARN([Can't find SDL_net, internal modem and ipx disabled]) + AC_MSG_WARN([Can't find ${LIBSDL_HEADER}_net, internal modem and ipx disabled]) fi AH_TEMPLATE(C_X11_XKB,[define to 1 if you have XKBlib.h and X11 lib]) @@ -625,6 +698,9 @@ src/misc/Makefile src/shell/Makefile src/platform/Makefile src/platform/visualc/Makefile +src/sdl_cdrom/Makefile +src/sdl_cdrom/macos/Makefile +src/sdl_cdrom/macosx/Makefile visualc_net/Makefile include/Makefile docs/Makefile diff --git a/src/Makefile.am b/src/Makefile.am index a4029e835..d33c06668 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,6 +1,6 @@ AM_CPPFLAGS = -I$(top_srcdir)/include -SUBDIRS = cpu debug dos fpu gui hardware libs ints misc shell platform +SUBDIRS = cpu debug dos fpu gui hardware libs ints misc shell platform sdl_cdrom bin_PROGRAMS = dosbox @@ -14,7 +14,8 @@ endif dosbox_SOURCES = dosbox.cpp $(ico_stuff) dosbox_LDADD = cpu/libcpu.a debug/libdebug.a dos/libdos.a fpu/libfpu.a hardware/libhardware.a gui/libgui.a \ ints/libints.a misc/libmisc.a shell/libshell.a hardware/mame/libmame.a \ - hardware/serialport/libserial.a libs/gui_tk/libgui_tk.a + hardware/serialport/libserial.a libs/gui_tk/libgui_tk.a \ + sdl_cdrom/libcsdlcdrom.a sdl_cdrom/macos/libcsdlcdrommacos.a sdl_cdrom/macosx/libcsdlcdrommacosx.a EXTRA_DIST = winres.rc dosbox.ico diff --git a/src/dos/cdrom.cpp b/src/dos/cdrom.cpp index 9608e87d1..c42a8e77f 100644 --- a/src/dos/cdrom.cpp +++ b/src/dos/cdrom.cpp @@ -21,6 +21,7 @@ // SDL CDROM // ****************************************************** + #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> @@ -30,6 +31,8 @@ #include "support.h" #include "cdrom.h" +#if C_PHYSICAL_CDROM_MOUNT + CDROM_Interface_SDL::CDROM_Interface_SDL(void) { driveID = 0; oldLeadOut = 0; @@ -143,6 +146,8 @@ bool CDROM_Interface_SDL::LoadUnloadMedia(bool unload) { return success; } +#endif /* C_PHYSICAL_CDROM_MOUNT */ + int CDROM_GetMountType(char* path, int forceCD) { // 0 - physical CDROM // 1 - Iso file @@ -157,6 +162,7 @@ int CDROM_GetMountType(char* path, int forceCD) { upcase(buffer); #endif +#if C_PHYSICAL_CDROM_MOUNT int num = SDL_CDNumDrives(); // If cd drive is forced then check if its in range and return 0 if ((forceCD>=0) && (forceCD<num)) { @@ -169,6 +175,7 @@ int CDROM_GetMountType(char* path, int forceCD) { cdName = SDL_CDName(i); if (strcmp(buffer,cdName)==0) return 0; }; +#endif // Detect ISO struct stat file_stat; @@ -214,5 +221,3 @@ bool CDROM_Interface_Fake :: GetMediaTrayStatus(bool& mediaPresent, bool& mediaC trayOpen = false; return true; } - - diff --git a/src/dos/cdrom.h b/src/dos/cdrom.h index 803c7aa2f..e797ef6eb 100644 --- a/src/dos/cdrom.h +++ b/src/dos/cdrom.h @@ -32,6 +32,11 @@ #include "mem.h" #include "mixer.h" #include "SDL.h" +#if SDL_VERSION_ATLEAST(2,0,0) +/* Do include this, even if C_PHYSICAL_CDROM_MOUNT is *not* defined, +due to MSF_TO_FRAMES and FRAMES_TO_MSF (and indirectly also CD_FPS). */ +#include "../sdl_cdrom/compat_SDL_cdrom.h" +#endif #include "SDL_thread.h" #if defined(C_SDL_SOUND) @@ -41,7 +46,9 @@ #define RAW_SECTOR_SIZE 2352 #define COOKED_SECTOR_SIZE 2048 +#if C_PHYSICAL_CDROM_MOUNT enum { CDROM_USE_SDL, CDROM_USE_ASPI, CDROM_USE_IOCTL_DIO, CDROM_USE_IOCTL_DX, CDROM_USE_IOCTL_MCI }; +#endif typedef struct SMSF { unsigned char min; @@ -84,6 +91,7 @@ public: virtual void InitNewMedia (void) {}; }; +#if C_PHYSICAL_CDROM_MOUNT class CDROM_Interface_SDL : public CDROM_Interface { public: @@ -112,6 +120,7 @@ private: int driveID; Uint32 oldLeadOut; }; +#endif /* C_PHYSICAL_CDROM_MOUNT */ class CDROM_Interface_Fake : public CDROM_Interface { @@ -237,6 +246,8 @@ typedef std::vector<Track>::iterator track_it; Bit8u subUnit; }; +#if C_PHYSICAL_CDROM_MOUNT + #if defined (WIN32) /* Win 32 */ #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers @@ -394,4 +405,6 @@ private: #endif /* LINUX */ +#endif /* C_PHYSICAL_CDROM_MOUNT */ + #endif /* __CDROM_INTERFACE__ */ diff --git a/src/dos/cdrom_aspi_win32.cpp b/src/dos/cdrom_aspi_win32.cpp index cc8de4e35..99b52a1ec 100644 --- a/src/dos/cdrom_aspi_win32.cpp +++ b/src/dos/cdrom_aspi_win32.cpp @@ -17,11 +17,14 @@ */ +#include "dosbox.h" + +#if C_PHYSICAL_CDROM_MOUNT + #if defined (WIN32) #include <ctype.h> -#include "dosbox.h" #include "cdrom.h" #include "support.h" @@ -766,3 +769,4 @@ bool CDROM_Interface_Aspi::ReadSectors(PhysPt buffer, bool raw, unsigned long se }; #endif +#endif /* C_PHYSICAL_CDROM_MOUNT */ diff --git a/src/dos/cdrom_ioctl_linux.cpp b/src/dos/cdrom_ioctl_linux.cpp index fb73986e8..079b440e4 100644 --- a/src/dos/cdrom_ioctl_linux.cpp +++ b/src/dos/cdrom_ioctl_linux.cpp @@ -17,8 +17,11 @@ */ -#include <string.h> #include "cdrom.h" + +#if C_PHYSICAL_CDROM_MOUNT + +#include <string.h> #include "support.h" #if defined (LINUX) @@ -95,3 +98,4 @@ bool CDROM_Interface_Ioctl::SetDevice(char* path, int forceCD) } #endif +#endif /* C_PHYSICAL_CDROM_MOUNT */ diff --git a/src/dos/cdrom_ioctl_os2.cpp b/src/dos/cdrom_ioctl_os2.cpp index 6e32af55b..30eb1a50a 100644 --- a/src/dos/cdrom_ioctl_os2.cpp +++ b/src/dos/cdrom_ioctl_os2.cpp @@ -17,8 +17,11 @@ */ -#include <string.h> #include "dosbox.h" + +#if C_PHYSICAL_CDROM_MOUNT + +#include <string.h> #include "cdrom.h" #if defined (OS2) @@ -150,3 +153,4 @@ bool CDROM_Interface_Ioctl::SetDevice(char* path, int forceCD) { } #endif +#endif /* C_PHYSICAL_CDROM_MOUNT */ diff --git a/src/dos/cdrom_ioctl_win32.cpp b/src/dos/cdrom_ioctl_win32.cpp index 465e6bd62..bd277bcc0 100644 --- a/src/dos/cdrom_ioctl_win32.cpp +++ b/src/dos/cdrom_ioctl_win32.cpp @@ -17,6 +17,10 @@ */ +#include "cdrom.h" + +#if C_PHYSICAL_CDROM_MOUNT + #if defined (WIN32) // ***************************************************************** @@ -27,16 +31,14 @@ #include <io.h> #if (defined (_MSC_VER)) || (defined __MINGW64_VERSION_MAJOR) -#include <ntddcdrm.h> // Ioctl stuff #include <winioctl.h> // Ioctl stuff +#include <ntddcdrm.h> // Ioctl stuff #else #include "ddk/ntddcdrm.h" // Ioctl stuff #endif #include <mmsystem.h> -#include "cdrom.h" - // for a more sophisticated implementation of the mci cdda functionality // see the SDL sources, which the mci_ functions are based on @@ -621,3 +623,4 @@ void CDROM_Interface_Ioctl::Close(void) { } #endif +#endif /* C_PHYSICAL_CDROM_MOUNT */ diff --git a/src/dos/dos_mscdex.cpp b/src/dos/dos_mscdex.cpp index a6d1b9c32..004afdf22 100644 --- a/src/dos/dos_mscdex.cpp +++ b/src/dos/dos_mscdex.cpp @@ -48,7 +48,9 @@ #define REQUEST_STATUS_ERROR 0x8000 // Use cdrom Interface +#if C_PHYSICAL_CDROM_MOUNT int useCdromInterface = CDROM_USE_SDL; +#endif int forceCD = -1; static Bitu MSCDEX_Strategy_Handler(void); @@ -253,6 +255,7 @@ int CMscdex::AddDrive(Bit16u _drive, char* physicalPath, Bit8u& subUnit) int result = 0; // Get Mounttype and init needed cdrom interface switch (CDROM_GetMountType(physicalPath,forceCD)) { +#if C_PHYSICAL_CDROM_MOUNT case 0x00: { LOG(LOG_MISC,LOG_NORMAL)("MSCDEX: Mounting physical cdrom: %s" ,physicalPath); #if defined (WIN32) @@ -295,6 +298,7 @@ int CMscdex::AddDrive(Bit16u _drive, char* physicalPath, Bit8u& subUnit) LOG(LOG_MISC,LOG_NORMAL)("MSCDEX: SDL Interface."); #endif } break; +#endif /* C_PHYSICAL_CDROM_MOUNT */ case 0x01: // iso cdrom interface LOG(LOG_MISC,LOG_NORMAL)("MSCDEX: Mounting iso file as cdrom: %s", physicalPath); cdrom[numDrives] = new CDROM_Interface_Image((Bit8u)numDrives); @@ -1327,10 +1331,12 @@ bool MSCDEX_HasMediaChanged(Bit8u subUnit) return true; } +#if C_PHYSICAL_CDROM_MOUNT void MSCDEX_SetCDInterface(int intNr, int numCD) { useCdromInterface = intNr; forceCD = numCD; } +#endif void MSCDEX_ShutDown(Section* /*sec*/) { delete mscdex; diff --git a/src/dos/dos_programs.cpp b/src/dos/dos_programs.cpp index cdea5d7b1..517e7886b 100644 --- a/src/dos/dos_programs.cpp +++ b/src/dos/dos_programs.cpp @@ -56,7 +56,12 @@ Bitu DEBUG_EnableDebugger(void); #endif +#if C_PHYSICAL_CDROM_MOUNT +#define PROGRAM_INTRO_CDROM_SUFFIX "PROGRAM_INTRO_CDROM_PHYS" void MSCDEX_SetCDInterface(int intNr, int forceCD); +#else +#define PROGRAM_INTRO_CDROM_SUFFIX "PROGRAM_INTRO_CDROM_NOPHYS" +#endif static Bitu ZDRIVE_NUM = 25; class MOUNT : public Program { @@ -184,11 +189,15 @@ public: } /* Show list of cdroms */ if (cmd->FindExist("-cd",false)) { +#if C_PHYSICAL_CDROM_MOUNT int num = SDL_CDNumDrives(); WriteOut(MSG_Get("PROGRAM_MOUNT_CDROMS_FOUND"),num); for (int i=0; i<num; i++) { WriteOut("%2d. %s\n",i,SDL_CDName(i)); }; +#else + WriteOut(MSG_Get("PROGRAM_MOUNT_PHYS_CDROMS_NOT_SUPPORTED")); +#endif return; } @@ -326,9 +335,10 @@ public: if (temp_line[temp_line.size()-1]!=CROSS_FILESPLIT) temp_line+=CROSS_FILESPLIT; Bit8u bit8size=(Bit8u) sizes[1]; if (type=="cdrom") { + int error = 0; +#if C_PHYSICAL_CDROM_MOUNT int num = -1; cmd->FindInt("-usecd",num,true); - int error = 0; if (cmd->FindExist("-aspi",false)) { MSCDEX_SetCDInterface(CDROM_USE_ASPI, num); } else if (cmd->FindExist("-ioctl_dio",false)) { @@ -357,6 +367,20 @@ public: MSCDEX_SetCDInterface(CDROM_USE_IOCTL_DIO, num); #endif } +#else /* C_PHYSICAL_CDROM_MOUNT */ + if (cmd->FindExist("-usecd",false) + || cmd->FindExist("-aspi",false) + || cmd->FindExist("-ioctl_dio",false) + || cmd->FindExist("-ioctl_dx",false) +#if defined (WIN32) + || cmd->FindExist("-ioctl_mci",false) +#endif + || cmd->FindExist("-noioctl",false) + ) { + WriteOut(MSG_Get("PROGRAM_MOUNT_PHYS_CDROMS_NOT_SUPPORTED")); + /* Just ignore, mount anyway */ + } +#endif /* C_PHYSICAL_CDROM_MOUNT */ newdrive = new cdromDrive(drive,temp_line.c_str(),sizes[0],bit8size,sizes[2],0,mediaid,error); // Check Mscdex, if it worked out... switch (error) { @@ -1069,7 +1093,8 @@ public: /* Only run if called from the first shell (Xcom TFTD runs any intro file in the path) */ if(DOS_PSP(dos.psp()).GetParent() != DOS_PSP(DOS_PSP(dos.psp()).GetParent()).GetParent()) return; if(cmd->FindExist("cdrom",false)) { - WriteOut(MSG_Get("PROGRAM_INTRO_CDROM")); + WriteOut(MSG_Get("PROGRAM_INTRO_CDROM_PREFIX")); + WriteOut(MSG_Get(PROGRAM_INTRO_CDROM_SUFFIX)); return; } if(cmd->FindExist("mount",false)) { @@ -1087,7 +1112,8 @@ public: DOS_ReadFile (STDIN,&c,&n); DisplayMount(); DOS_ReadFile (STDIN,&c,&n); - WriteOut(MSG_Get("PROGRAM_INTRO_CDROM")); + WriteOut(MSG_Get("PROGRAM_INTRO_CDROM_PREFIX")); + WriteOut(MSG_Get(PROGRAM_INTRO_CDROM_SUFFIX)); DOS_ReadFile (STDIN,&c,&n); WriteOut(MSG_Get("PROGRAM_INTRO_SPECIAL")); } @@ -1359,7 +1385,9 @@ public: WriteOut(MSG_Get("PROGRAM_IMGMOUNT_ALREADY_MOUNTED")); return; } +#if C_PHYSICAL_CDROM_MOUNT MSCDEX_SetCDInterface(CDROM_USE_SDL, -1); +#endif // create new drives for all images std::vector<DOS_Drive*> isoDisks; std::vector<std::string>::size_type i; @@ -1514,7 +1542,8 @@ static void KEYB_ProgramStart(Program * * make) { void DOS_SetupPrograms(void) { /*Add Messages */ - MSG_Add("PROGRAM_MOUNT_CDROMS_FOUND","CDROMs found: %d\n"); + MSG_Add("PROGRAM_MOUNT_CDROMS_FOUND","CDROMs found: %d\n"); // C_PHYSICAL_CDROM_MOUNT + MSG_Add("PROGRAM_MOUNT_PHYS_CDROMS_NOT_SUPPORTED","Physical CDROMs aren't fully supported. IMGMOUNT may be more useful.\n"); // !C_PHYSICAL_CDROM_MOUNT MSG_Add("PROGRAM_MOUNT_STATUS_FORMAT","%-5s %-58s %-12s\n"); MSG_Add("PROGRAM_MOUNT_STATUS_2","Drive %c is mounted as %s\n"); MSG_Add("PROGRAM_MOUNT_STATUS_1","The currently mounted drives are:\n"); @@ -1603,7 +1632,7 @@ void DOS_SetupPrograms(void) { "enter a directory (recognised by the \033[33;1m[]\033[0m in a directory listing).\n" "You can run programs/files which end with \033[31m.exe .bat\033[0m and \033[31m.com\033[0m.\n" ); - MSG_Add("PROGRAM_INTRO_CDROM", + MSG_Add("PROGRAM_INTRO_CDROM_PREFIX", "\033[2J\033[32;1mHow to mount a Real/Virtual CD-ROM Drive in DOSBox:\033[0m\n" "DOSBox provides CD-ROM emulation on several levels.\n" "\n" @@ -1614,6 +1643,8 @@ void DOS_SetupPrograms(void) { "If it doesn't work you might have to tell DOSBox the label of the CD-ROM:\n" "\033[34;1mmount d C:\\example -t cdrom -label CDLABEL\033[0m\n" "\n" + ); + MSG_Add("PROGRAM_INTRO_CDROM_PHYS", // C_PHYSICAL_CDROM_MOUNT "The \033[33mnext\033[0m level adds some low-level support.\n" "Therefore only works on CD-ROM drives:\n" "\033[34;1mmount d \033[0;31mD:\\\033[34;1m -t cdrom -usecd \033[33m0\033[0m\n" @@ -1628,6 +1659,14 @@ void DOS_SetupPrograms(void) { "Replace the \033[33;1m0\033[0m in \033[34;1m-usecd \033[33m0\033[0m with the number reported for your CD-ROM if you type:\n" "\033[34;1mmount -cd\033[0m\n" ); + MSG_Add("PROGRAM_INTRO_CDROM_NOPHYS", // !C_PHYSICAL_CDROM_MOUNT + "The \033[33mhigher\033[0m level adds CD-ROM image mounting support.\n" + "Therefore only works on supported CD-ROM images:\n" + "\033[34;1mimgmount d \033[0;31mD:\\example.img\033[34;1m -t cdrom\033[0m\n" + "\n" + "Replace \033[0;31mD:\\\033[0m with the location of your CD-ROM.\n" + "Replace \033[0;31mD:\\example.img\033[0m with the location of your CD-ROM image.\n" + ); MSG_Add("PROGRAM_INTRO_SPECIAL", "\033[2J\033[32;1mSpecial keys:\033[0m\n" "These are the default keybindings.\n" diff --git a/src/dos/drive_local.cpp b/src/dos/drive_local.cpp index 46ebb7df5..b8956eb40 100644 --- a/src/dos/drive_local.cpp +++ b/src/dos/drive_local.cpp @@ -273,7 +273,12 @@ again: find_size=(Bit32u) stat_block.st_size; struct tm *time; +#ifdef __ANDROID__ // temp_stat.st_mtime is of type unsigned long, not time_t + time_t rawtime=stat_block.st_mtime; + if((time=localtime(&rawtime))!=0){ +#else if((time=localtime(&stat_block.st_mtime))!=0){ +#endif find_date=DOS_PackDate((Bit16u)(time->tm_year+1900),(Bit16u)(time->tm_mon+1),(Bit16u)time->tm_mday); find_time=DOS_PackTime((Bit16u)time->tm_hour,(Bit16u)time->tm_min,(Bit16u)time->tm_sec); } else { @@ -391,7 +396,12 @@ bool localDrive::FileStat(const char* name, FileStat_Block * const stat_block) { if(stat(newname,&temp_stat)!=0) return false; /* Convert the stat to a FileStat */ struct tm *time; +#ifdef __ANDROID__ // temp_stat.st_mtime is of type unsigned long, not time_t + time_t rawtime=temp_stat.st_mtime; + if((time=localtime(&rawtime))!=0) { +#else if((time=localtime(&temp_stat.st_mtime))!=0) { +#endif stat_block->time=DOS_PackTime((Bit16u)time->tm_hour,(Bit16u)time->tm_min,(Bit16u)time->tm_sec); stat_block->date=DOS_PackDate((Bit16u)(time->tm_year+1900),(Bit16u)(time->tm_mon+1),(Bit16u)time->tm_mday); } else { @@ -530,7 +540,12 @@ bool localFile::UpdateDateTimeFromHost(void) { struct stat temp_stat; fstat(fileno(fhandle),&temp_stat); struct tm * ltime; +#ifdef __ANDROID__ // temp_stat.st_mtime is of type unsigned long, not time_t + time_t rawtime=temp_stat.st_mtime; + if((ltime=localtime(&rawtime))!=0) { +#else if((ltime=localtime(&temp_stat.st_mtime))!=0) { +#endif time=DOS_PackTime((Bit16u)ltime->tm_hour,(Bit16u)ltime->tm_min,(Bit16u)ltime->tm_sec); date=DOS_PackDate((Bit16u)(ltime->tm_year+1900),(Bit16u)(ltime->tm_mon+1),(Bit16u)ltime->tm_mday); } else { diff --git a/src/gui/sdl_mapper.cpp b/src/gui/sdl_mapper.cpp index 2e6f7def9..0f51cb99a 100644 --- a/src/gui/sdl_mapper.cpp +++ b/src/gui/sdl_mapper.cpp @@ -44,7 +44,8 @@ enum { CLR_WHITE=2, CLR_RED=3, CLR_BLUE=4, - CLR_GREEN=5 + CLR_GREEN=5, + CLR_LAST }; enum BB_Types { @@ -67,7 +68,8 @@ enum BC_Types { #define MAXSTICKS 8 #define MAXACTIVE 16 -#define MAXBUTTON 32 +// Use 36 for Android (KEYCODE_BUTTON_1..16 are mapped to SDL buttons 20..35) +#define MAXBUTTON 36 #define MAXBUTTON_CAP 16 class CEvent; @@ -299,6 +301,8 @@ protected: }; +#if !SDL_VERSION_ATLEAST(2,0,0) + #define MAX_SDLKEYS 323 static bool usescancodes; @@ -469,23 +473,41 @@ Bitu GetKeyCode(SDL_keysym keysym) { } } +#endif // SDL 1.2 + class CKeyBind; class CKeyBindGroup; class CKeyBind : public CBind { public: +#if SDL_VERSION_ATLEAST(2,0,0) + CKeyBind(CBindList * _list,SDL_Scancode _key) : CBind(_list) { +#else CKeyBind(CBindList * _list,SDLKey _key) : CBind(_list) { +#endif key = _key; } void BindName(char * buf) { +#if SDL_VERSION_ATLEAST(2,0,0) + sprintf(buf,"Key %s",SDL_GetScancodeName(key)); +#else sprintf(buf,"Key %s",SDL_GetKeyName(MapSDLCode((Bitu)key))); +#endif } void ConfigName(char * buf) { +#if SDL_VERSION_ATLEAST(2,0,0) + sprintf(buf,"key %d",key); +#else sprintf(buf,"key %d",MapSDLCode((Bitu)key)); +#endif } public: +#if SDL_VERSION_ATLEAST(2,0,0) + SDL_Scancode key; +#else SDLKey key; +#endif }; class CKeyBindGroup : public CBindGroup { @@ -501,28 +523,44 @@ public: if (strncasecmp(buf,configname,strlen(configname))) return 0; StripWord(buf);char * num=StripWord(buf); Bitu code=ConvDecWord(num); +#if SDL_VERSION_ATLEAST(2,0,0) + CBind * bind=CreateKeyBind((SDL_Scancode)code); +#else if (usescancodes) { if (code<MAX_SDLKEYS) code=scancode_map[code]; else code=0; } CBind * bind=CreateKeyBind((SDLKey)code); +#endif return bind; } CBind * CreateEventBind(SDL_Event * event) { if (event->type!=SDL_KEYDOWN) return 0; +#if SDL_VERSION_ATLEAST(2,0,0) + return CreateKeyBind(event->key.keysym.scancode); +#else return CreateKeyBind((SDLKey)GetKeyCode(event->key.keysym)); +#endif }; bool CheckEvent(SDL_Event * event) { if (event->type!=SDL_KEYDOWN && event->type!=SDL_KEYUP) return false; +#if SDL_VERSION_ATLEAST(2,0,0) + Bitu key = event->key.keysym.scancode; +#else // SDL 1.2 Bitu key=GetKeyCode(event->key.keysym); // LOG_MSG("key type %i is %x [%x %x]",event->type,key,event->key.keysym.sym,event->key.keysym.scancode); assert(Bitu(event->key.keysym.sym)<keys); +#endif // SDL 2.0/1.2 if (event->type==SDL_KEYDOWN) ActivateBindList(&lists[key],0x7fff,true); else DeactivateBindList(&lists[key],true); return 0; } +#if SDL_VERSION_ATLEAST(2,0,0) + CBind * CreateKeyBind(SDL_Scancode _key) { +#else CBind * CreateKeyBind(SDLKey _key) { if (!usescancodes) assert((Bitu)_key<keys); +#endif return new CKeyBind(&lists[(Bitu)_key],_key); } private: @@ -675,7 +713,11 @@ public: if (axes_cap>axes) axes_cap=axes; hats_cap=emulated_hats; if (hats_cap>hats) hats_cap=hats; +#if SDL_VERSION_ATLEAST(2,0,0) + LOG_MSG("Using joystick %s with %d axes, %d buttons and %d hat(s)",SDL_JoystickNameForIndex(stick),axes,buttons,hats); +#else LOG_MSG("Using joystick %s with %d axes, %d buttons and %d hat(s)",SDL_JoystickName(stick),axes,buttons,hats); +#endif } ~CStickBindGroup() { SDL_JoystickClose(sdl_joystick); @@ -712,7 +754,7 @@ public: if (abs(event->jaxis.value)<25000) return 0; return CreateAxisBind(event->jaxis.axis,event->jaxis.value>0); } else if (event->type==SDL_JOYBUTTONDOWN) { - if (event->button.which!=stick) return 0; + if (event->jbutton.which!=stick) return 0; #if defined (REDUCE_JOYSTICK_POLLING) return CreateButtonBind(event->jbutton.button%button_wrap); #else @@ -881,7 +923,11 @@ private: return configname; } const char * BindStart(void) { +#if SDL_VERSION_ATLEAST(2,0,0) + if (sdl_joystick!=NULL) return SDL_JoystickNameForIndex(stick); +#else if (sdl_joystick!=NULL) return SDL_JoystickName(stick); +#endif else return "[missing joystick]"; } @@ -1240,6 +1286,11 @@ protected: }; static struct CMapper { +#if SDL_VERSION_ATLEAST(2,0,0) + SDL_Window * window; + SDL_Rect draw_rect; + SDL_Surface * draw_surface_nonpaletted; // Needed for SDL_BlitScaled +#endif SDL_Surface * surface; SDL_Surface * draw_surface; bool exit; @@ -1277,7 +1328,7 @@ void CBindGroup::DeactivateBindList(CBindList * list,bool ev_trigger) { } static void DrawText(Bitu x,Bitu y,const char * text,Bit8u color) { - Bit8u * draw=((Bit8u *)mapper.surface->pixels)+(y*mapper.surface->pitch)+x; + Bit8u * draw=((Bit8u *)mapper.draw_surface->pixels)+(y*mapper.draw_surface->w)+x; while (*text) { Bit8u * font=&int10_font_14[(*text)*14]; Bitu i,j;Bit8u * draw_line=draw; @@ -1288,7 +1339,7 @@ static void DrawText(Bitu x,Bitu y,const char * text,Bit8u color) { else *(draw_line+j)=CLR_BLACK; map<<=1; } - draw_line+=mapper.surface->pitch; + draw_line+=mapper.draw_surface->w; } text++;draw+=8; } @@ -1305,14 +1356,14 @@ public: } virtual void Draw(void) { if (!enabled) return; - Bit8u * point=((Bit8u *)mapper.surface->pixels)+(y*mapper.surface->pitch)+x; + Bit8u * point=((Bit8u *)mapper.draw_surface->pixels)+(y*mapper.draw_surface->w)+x; for (Bitu lines=0;lines<dy;lines++) { if (lines==0 || lines==(dy-1)) { for (Bitu cols=0;cols<dx;cols++) *(point+cols)=color; } else { *point=color;*(point+dx-1)=color; } - point+=mapper.surface->pitch; + point+=mapper.draw_surface->w; } } virtual bool OnTop(Bitu _x,Bitu _y) { @@ -1343,13 +1394,21 @@ protected: const char * text; }; +class CClickableTextButton : public CTextButton { +public: + CClickableTextButton(Bitu _x,Bitu _y,Bitu _dx,Bitu _dy,const char * _text) : CTextButton(_x,_y,_dx,_dy,_text) {} + void BindColor(void) { + this->SetColor(CLR_WHITE); + } +}; + class CEventButton; static CEventButton * last_clicked = NULL; -class CEventButton : public CTextButton { +class CEventButton : public CClickableTextButton { public: CEventButton(Bitu _x,Bitu _y,Bitu _dx,Bitu _dy,const char * _text,CEvent * _event) - : CTextButton(_x,_y,_dx,_dy,_text) { + : CClickableTextButton(_x,_y,_dx,_dy,_text) { event=_event; } void BindColor(void) { @@ -1391,10 +1450,10 @@ void CCaptionButton::Change(const char * format,...) { static void change_action_text(const char* text,Bit8u col); static void MAPPER_SaveBinds(void); -class CBindButton : public CTextButton { +class CBindButton : public CClickableTextButton { public: CBindButton(Bitu _x,Bitu _y,Bitu _dx,Bitu _dy,const char * _text,BB_Types _type) - : CTextButton(_x,_y,_dx,_dy,_text) { + : CClickableTextButton(_x,_y,_dx,_dy,_text) { type=_type; } void Click(void) { @@ -1433,10 +1492,10 @@ protected: BB_Types type; }; -class CCheckButton : public CTextButton { +class CCheckButton : public CClickableTextButton { public: CCheckButton(Bitu _x,Bitu _y,Bitu _dx,Bitu _dy,const char * _text,BC_Types _type) - : CTextButton(_x,_y,_dx,_dy,_text) { + : CClickableTextButton(_x,_y,_dx,_dy,_text) { type=_type; } void Draw(void) { @@ -1457,13 +1516,13 @@ public: break; } if (checked) { - Bit8u * point=((Bit8u *)mapper.surface->pixels)+((y+2)*mapper.surface->pitch)+x+dx-dy+2; + Bit8u * point=((Bit8u *)mapper.draw_surface->pixels)+((y+2)*mapper.draw_surface->w)+x+dx-dy+2; for (Bitu lines=0;lines<(dy-4);lines++) { memset(point,color,dy-4); - point+=mapper.surface->pitch; + point+=mapper.draw_surface->w; } } - CTextButton::Draw(); + CClickableTextButton::Draw(); } void Click(void) { switch (type) { @@ -1589,25 +1648,53 @@ public: case MK_f1:case MK_f2:case MK_f3:case MK_f4: case MK_f5:case MK_f6:case MK_f7:case MK_f8: case MK_f9:case MK_f10:case MK_f11:case MK_f12: +#if SDL_VERSION_ATLEAST(2,0,0) + key=SDL_SCANCODE_F1+(defkey-MK_f1); +#else key=SDLK_F1+(defkey-MK_f1); +#endif break; case MK_return: +#if SDL_VERSION_ATLEAST(2,0,0) + key=SDL_SCANCODE_RETURN; +#else key=SDLK_RETURN; +#endif break; case MK_kpminus: +#if SDL_VERSION_ATLEAST(2,0,0) + key=SDL_SCANCODE_KP_MINUS; +#else key=SDLK_KP_MINUS; +#endif break; case MK_scrolllock: +#if SDL_VERSION_ATLEAST(2,0,0) + key=SDL_SCANCODE_SCROLLLOCK; +#else key=SDLK_SCROLLOCK; +#endif break; case MK_pause: +#if SDL_VERSION_ATLEAST(2,0,0) + key=SDL_SCANCODE_PAUSE; +#else key=SDLK_PAUSE; +#endif break; case MK_printscreen: +#if SDL_VERSION_ATLEAST(2,0,0) + key=SDL_SCANCODE_PRINTSCREEN; +#else key=SDLK_PRINT; +#endif break; case MK_home: +#if SDL_VERSION_ATLEAST(2,0,0) + key=SDL_SCANCODE_HOME; +#else key=SDLK_HOME; +#endif break; } sprintf(buf,"%s \"key %d%s%s%s\"", @@ -1689,14 +1776,27 @@ static void SetActiveEvent(CEvent * event) { } } +#if SDL_VERSION_ATLEAST(2,0,0) +extern SDL_Window * GFX_SetSDLSurfaceWindow(Bit16u width, Bit16u height); +extern SDL_Rect GFX_GetSDLSurfaceSubwindowDims(Bit16u width, Bit16u height); +extern void GFX_UpdateDisplayDimensions(int width, int height); +#endif + static void DrawButtons(void) { - SDL_FillRect(mapper.surface,0,0); - SDL_LockSurface(mapper.surface); + SDL_FillRect(mapper.draw_surface,0,CLR_BLACK); for (CButton_it but_it = buttons.begin();but_it!=buttons.end();but_it++) { (*but_it)->Draw(); } - SDL_UnlockSurface(mapper.surface); +#if SDL_VERSION_ATLEAST(2,0,0) + // We can't just use SDL_BlitScaled (say for Android) in one step + SDL_BlitSurface(mapper.draw_surface, NULL, mapper.draw_surface_nonpaletted, NULL); + SDL_BlitScaled(mapper.draw_surface_nonpaletted, NULL, mapper.surface, &mapper.draw_rect); + //SDL_BlitSurface(mapper.draw_surface, NULL, mapper.surface, NULL); + SDL_UpdateWindowSurface(mapper.window); +#else + SDL_BlitSurface(mapper.draw_surface, NULL, mapper.surface, NULL); SDL_Flip(mapper.surface); +#endif } static CKeyEvent * AddKeyButtonEvent(Bitu x,Bitu y,Bitu dx,Bitu dy,char const * const title,char const * const entry,KBD_KEYS key) { @@ -2003,7 +2103,7 @@ static void CreateLayout(void) { bind_but.bind_title->Change("Bind Title"); } -static SDL_Color map_pal[6]={ +static SDL_Color map_pal[CLR_LAST]={ {0x00,0x00,0x00,0x00}, //0=black {0x7f,0x7f,0x7f,0x00}, //1=grey {0xff,0xff,0xff,0x00}, //2=white @@ -2042,6 +2142,49 @@ static struct { const char * eventend; Bitu key; } DefaultKeys[]={ + +#if SDL_VERSION_ATLEAST(2,0,0) + + {"f1",SDL_SCANCODE_F1}, {"f2",SDL_SCANCODE_F2}, {"f3",SDL_SCANCODE_F3}, {"f4",SDL_SCANCODE_F4}, + {"f5",SDL_SCANCODE_F5}, {"f6",SDL_SCANCODE_F6}, {"f7",SDL_SCANCODE_F7}, {"f8",SDL_SCANCODE_F8}, + {"f9",SDL_SCANCODE_F9}, {"f10",SDL_SCANCODE_F10}, {"f11",SDL_SCANCODE_F11}, {"f12",SDL_SCANCODE_F12}, + + {"1",SDL_SCANCODE_1}, {"2",SDL_SCANCODE_2}, {"3",SDL_SCANCODE_3}, {"4",SDL_SCANCODE_4}, + {"5",SDL_SCANCODE_5}, {"6",SDL_SCANCODE_6}, {"7",SDL_SCANCODE_7}, {"8",SDL_SCANCODE_8}, + {"9",SDL_SCANCODE_9}, {"0",SDL_SCANCODE_0}, + + {"a",SDL_SCANCODE_A}, {"b",SDL_SCANCODE_B}, {"c",SDL_SCANCODE_C}, {"d",SDL_SCANCODE_D}, + {"e",SDL_SCANCODE_E}, {"f",SDL_SCANCODE_F}, {"g",SDL_SCANCODE_G}, {"h",SDL_SCANCODE_H}, + {"i",SDL_SCANCODE_I}, {"j",SDL_SCANCODE_J}, {"k",SDL_SCANCODE_K}, {"l",SDL_SCANCODE_L}, + {"m",SDL_SCANCODE_M}, {"n",SDL_SCANCODE_N}, {"o",SDL_SCANCODE_O}, {"p",SDL_SCANCODE_P}, + {"q",SDL_SCANCODE_Q}, {"r",SDL_SCANCODE_R}, {"s",SDL_SCANCODE_S}, {"t",SDL_SCANCODE_T}, + {"u",SDL_SCANCODE_U}, {"v",SDL_SCANCODE_V}, {"w",SDL_SCANCODE_W}, {"x",SDL_SCANCODE_X}, + {"y",SDL_SCANCODE_Y}, {"z",SDL_SCANCODE_Z}, {"space",SDL_SCANCODE_SPACE}, + {"esc",SDL_SCANCODE_ESCAPE}, {"equals",SDL_SCANCODE_EQUALS}, {"grave",SDL_SCANCODE_GRAVE}, + {"tab",SDL_SCANCODE_TAB}, {"enter",SDL_SCANCODE_RETURN}, {"bspace",SDL_SCANCODE_BACKSPACE}, + {"lbracket",SDL_SCANCODE_LEFTBRACKET}, {"rbracket",SDL_SCANCODE_RIGHTBRACKET}, + {"minus",SDL_SCANCODE_MINUS}, {"capslock",SDL_SCANCODE_CAPSLOCK}, {"semicolon",SDL_SCANCODE_SEMICOLON}, + {"quote", SDL_SCANCODE_APOSTROPHE}, {"backslash",SDL_SCANCODE_BACKSLASH}, {"lshift",SDL_SCANCODE_LSHIFT}, + {"rshift",SDL_SCANCODE_RSHIFT}, {"lalt",SDL_SCANCODE_LALT}, {"ralt",SDL_SCANCODE_RALT}, + {"lctrl",SDL_SCANCODE_LCTRL}, {"rctrl",SDL_SCANCODE_RCTRL}, {"comma",SDL_SCANCODE_COMMA}, + {"period",SDL_SCANCODE_PERIOD}, {"slash",SDL_SCANCODE_SLASH}, {"printscreen",SDL_SCANCODE_PRINTSCREEN}, + {"scrolllock",SDL_SCANCODE_SCROLLLOCK}, {"pause",SDL_SCANCODE_PAUSE}, {"pagedown",SDL_SCANCODE_PAGEDOWN}, + {"pageup",SDL_SCANCODE_PAGEUP}, {"insert",SDL_SCANCODE_INSERT}, {"home",SDL_SCANCODE_HOME}, + {"delete",SDL_SCANCODE_DELETE}, {"end",SDL_SCANCODE_END}, {"up",SDL_SCANCODE_UP}, + {"left",SDL_SCANCODE_LEFT}, {"down",SDL_SCANCODE_DOWN}, {"right",SDL_SCANCODE_RIGHT}, + {"kp_0",SDL_SCANCODE_KP_0}, {"kp_1",SDL_SCANCODE_KP_1}, {"kp_2",SDL_SCANCODE_KP_2}, {"kp_3",SDL_SCANCODE_KP_3}, + {"kp_4",SDL_SCANCODE_KP_4}, {"kp_5",SDL_SCANCODE_KP_5}, {"kp_6",SDL_SCANCODE_KP_6}, {"kp_7",SDL_SCANCODE_KP_7}, + {"kp_8",SDL_SCANCODE_KP_8}, {"kp_9",SDL_SCANCODE_KP_9}, {"numlock",SDL_SCANCODE_NUMLOCKCLEAR}, + {"kp_divide",SDL_SCANCODE_KP_DIVIDE}, {"kp_multiply",SDL_SCANCODE_KP_MULTIPLY}, + {"kp_minus",SDL_SCANCODE_KP_MINUS}, {"kp_plus",SDL_SCANCODE_KP_PLUS}, + {"kp_period",SDL_SCANCODE_KP_PERIOD}, {"kp_enter",SDL_SCANCODE_KP_ENTER}, + + /* Is that the extra backslash key ("less than" key) */ + /* on some keyboards with the 102-keys layout?? */ + {"lessthan",SDL_SCANCODE_NONUSBACKSLASH}, + +#else // !SDL_VERSION_ATLEAST(2,0,0) + {"f1",SDLK_F1}, {"f2",SDLK_F2}, {"f3",SDLK_F3}, {"f4",SDLK_F4}, {"f5",SDLK_F5}, {"f6",SDLK_F6}, {"f7",SDLK_F7}, {"f8",SDLK_F8}, {"f9",SDLK_F9}, {"f10",SDLK_F10}, {"f11",SDLK_F11}, {"f12",SDLK_F12}, @@ -2083,6 +2226,8 @@ static struct { {"lessthan",SDLK_LESS}, #endif +#endif // !SDL_VERSION_ATLEAST(2,0,0) + {0,0} }; @@ -2094,10 +2239,17 @@ static void CreateDefaultBinds(void) { CreateStringBind(buffer); i++; } +#if SDL_VERSION_ATLEAST(2,0,0) + sprintf(buffer,"mod_1 \"key %d\"",SDL_SCANCODE_RCTRL);CreateStringBind(buffer); + sprintf(buffer,"mod_1 \"key %d\"",SDL_SCANCODE_LCTRL);CreateStringBind(buffer); + sprintf(buffer,"mod_2 \"key %d\"",SDL_SCANCODE_RALT);CreateStringBind(buffer); + sprintf(buffer,"mod_2 \"key %d\"",SDL_SCANCODE_LALT);CreateStringBind(buffer); +#else sprintf(buffer,"mod_1 \"key %d\"",SDLK_RCTRL);CreateStringBind(buffer); sprintf(buffer,"mod_1 \"key %d\"",SDLK_LCTRL);CreateStringBind(buffer); sprintf(buffer,"mod_2 \"key %d\"",SDLK_RALT);CreateStringBind(buffer); sprintf(buffer,"mod_2 \"key %d\"",SDLK_LALT);CreateStringBind(buffer); +#endif for (CHandlerEventVector_it hit=handlergroup.begin();hit!=handlergroup.end();hit++) { (*hit)->MakeDefaultBind(buffer); CreateStringBind(buffer); @@ -2190,17 +2342,92 @@ void MAPPER_CheckEvent(SDL_Event * event) { void BIND_MappingEvents(void) { SDL_Event event; + static bool isButtonPressed = false; + static CButton *lastHoveredButton = NULL; while (SDL_PollEvent(&event)) { switch (event.type) { + case SDL_MOUSEBUTTONDOWN: + isButtonPressed = true; + /* Further check where are we pointing at right now */ + case SDL_MOUSEMOTION: + if (!isButtonPressed) + break; +#if SDL_VERSION_ATLEAST(2,0,0) + /* Normalize position in case a scaled sub-window is used (say on Android) */ + event.button.x=(event.button.x-mapper.draw_rect.x)*mapper.draw_surface->w/mapper.draw_rect.w; + if ((event.button.x<0) || (event.button.x>=mapper.draw_surface->w)) + break; + event.button.y=(event.button.y-mapper.draw_rect.y)*mapper.draw_surface->h/mapper.draw_rect.h; + if ((event.button.y<0) || (event.button.y>=mapper.draw_surface->h)) + break; +#endif + /* Maybe we have been pointing at a specific button for a little while */ + if (lastHoveredButton) { + /* Check if there's any change */ + if (lastHoveredButton->OnTop(event.button.x,event.button.y)) + break; + /* Not pointing at given button anymore */ + if (lastHoveredButton == last_clicked) + lastHoveredButton->Click(); + else + lastHoveredButton->BindColor(); + mapper.redraw=true; + lastHoveredButton=NULL; + } + /* Check which button are we currently pointing at */ + for (CButton_it but_it = buttons.begin();but_it!=buttons.end();but_it++) { + if (dynamic_cast<CClickableTextButton *>(*but_it) && (*but_it)->OnTop(event.button.x,event.button.y)) { + (*but_it)->SetColor(CLR_RED); + mapper.redraw=true; + lastHoveredButton=*but_it; + break; + } + } + break; case SDL_MOUSEBUTTONUP: + isButtonPressed = false; + if (lastHoveredButton) { + /* For most buttons the actual new color is going to be green; But not for a few others. */ + lastHoveredButton->BindColor(); + mapper.redraw=true; + lastHoveredButton = NULL; + } +#if SDL_VERSION_ATLEAST(2,0,0) + /* Normalize position in case a scaled sub-window is used (say on Android) */ + event.button.x=(event.button.x-mapper.draw_rect.x)*mapper.draw_surface->w/mapper.draw_rect.w; + if ((event.button.x<0) || (event.button.x>=mapper.draw_surface->w)) + break; + event.button.y=(event.button.y-mapper.draw_rect.y)*mapper.draw_surface->h/mapper.draw_rect.h; + if ((event.button.y<0) || (event.button.y>=mapper.draw_surface->h)) + break; +#endif /* Check the press */ for (CButton_it but_it = buttons.begin();but_it!=buttons.end();but_it++) { - if ((*but_it)->OnTop(event.button.x,event.button.y)) { + if (dynamic_cast<CClickableTextButton *>(*but_it) && (*but_it)->OnTop(event.button.x,event.button.y)) { (*but_it)->Click(); + break; } - } + } break; +#if SDL_VERSION_ATLEAST(2,0,0) + case SDL_WINDOWEVENT: + /* The resize event MAY arrive e.g. when the mapper is + * toggled, at least on X11. Furthermore, the restore + * event should be handled on Android. + */ + if ((event.window.event == SDL_WINDOWEVENT_RESIZED) + || (event.window.event == SDL_WINDOWEVENT_RESTORED)) { + mapper.surface = SDL_GetWindowSurface(mapper.window); + if (mapper.surface == NULL) E_Exit("Couldn't refresh mapper window surface after resize or restoration: %s",SDL_GetError()); + GFX_UpdateDisplayDimensions(event.window.data1, event.window.data2); + mapper.draw_rect=GFX_GetSDLSurfaceSubwindowDims(640,480); + DrawButtons(); + } + break; +#endif case SDL_QUIT: + isButtonPressed = false; + lastHoveredButton = NULL; mapper.exit=true; break; default: @@ -2275,7 +2502,11 @@ static void InitializeJoysticks(void) { static void CreateBindGroups(void) { bindgroups.clear(); +#if SDL_VERSION_ATLEAST(2,0,0) + new CKeyBindGroup(SDL_NUM_SCANCODES); +#else new CKeyBindGroup(SDLK_LAST); +#endif if (joytype != JOY_NONE) { #if defined (REDUCE_JOYSTICK_POLLING) // direct access to the SDL joystick, thus removed from the event handling @@ -2358,11 +2589,29 @@ void MAPPER_RunInternal() { /* Be sure that there is no update in progress */ GFX_EndUpdate( 0 ); - mapper.surface=SDL_SetVideoMode_Wrap(640,480,8,0); +#if SDL_VERSION_ATLEAST(2,0,0) + mapper.window=GFX_SetSDLSurfaceWindow(640,480); + if (mapper.window == NULL) E_Exit("Could not initialize video mode for mapper: %s",SDL_GetError()); + mapper.surface=SDL_GetWindowSurface(mapper.window); + if (mapper.surface == NULL) E_Exit("Could not retrieve window surface for mapper: %s",SDL_GetError()); +#else + mapper.surface=SDL_SetVideoMode_Wrap(640,480,0,0); if (mapper.surface == NULL) E_Exit("Could not initialize video mode for mapper: %s",SDL_GetError()); +#endif /* Set some palette entries */ - SDL_SetPalette(mapper.surface, SDL_LOGPAL|SDL_PHYSPAL, map_pal, 0, 6); + mapper.draw_surface=SDL_CreateRGBSurface(0,640,480,8,0,0,0,0); +#if SDL_VERSION_ATLEAST(2,0,0) + // Needed for SDL_BlitScaled + mapper.draw_surface_nonpaletted=SDL_CreateRGBSurface(0,640,480,32,0x0000ff00,0x00ff0000,0xff000000,0); + mapper.draw_rect=GFX_GetSDLSurfaceSubwindowDims(640,480); + // Sorry, but SDL_SetSurfacePalette requires a full palette. + SDL_Palette *sdl2_map_pal_ptr = SDL_AllocPalette(256); + SDL_SetPaletteColors(sdl2_map_pal_ptr, map_pal, 0, CLR_LAST); + SDL_SetSurfacePalette(mapper.draw_surface, sdl2_map_pal_ptr); +#else // !SDL_VERSION_ATLEAST(2,0,0) + SDL_SetPalette(mapper.draw_surface, SDL_LOGPAL|SDL_PHYSPAL, map_pal, 0, CLR_LAST); +#endif if (last_clicked) { last_clicked->BindColor(); last_clicked=NULL; @@ -2378,10 +2627,23 @@ void MAPPER_RunInternal() { if (mapper.redraw) { mapper.redraw=false; DrawButtons(); + } else { +#if SDL_VERSION_ATLEAST(2,0,0) + SDL_UpdateWindowSurface(mapper.window); +#else + SDL_Flip(mapper.surface); +#endif } BIND_MappingEvents(); SDL_Delay(1); } + /* ONE SHOULD NOT FORGET TO DO THIS! + Unless a memory leak is desired... */ + SDL_FreeSurface(mapper.draw_surface); +#if SDL_VERSION_ATLEAST(2,0,0) + SDL_FreeSurface(mapper.draw_surface_nonpaletted); + SDL_FreePalette(sdl2_map_pal_ptr); +#endif #if defined (REDUCE_JOYSTICK_POLLING) SDL_JoystickEventState(SDL_DISABLE); #endif @@ -2430,6 +2692,8 @@ void MAPPER_StartUp(Section * sec) { mapper.sticks.num_groups=0; memset(&virtual_joysticks,0,sizeof(virtual_joysticks)); +#if !SDL_VERSION_ATLEAST(2,0,0) + usescancodes = false; if (section->Get_bool("usescancodes")) { @@ -2554,6 +2818,8 @@ void MAPPER_StartUp(Section * sec) { } } +#endif // !SDL_VERSION_ATLEAST(2,0,0) + Prop_path* pp = section->Get_path("mapperfile"); mapper.filename = pp->realpath; MAPPER_AddHandler(&MAPPER_Run,MK_f1,MMOD1,"mapper","Mapper"); diff --git a/src/gui/sdlmain.cpp b/src/gui/sdlmain.cpp index 9dde5dcb4..53d73d8de 100644 --- a/src/gui/sdlmain.cpp +++ b/src/gui/sdlmain.cpp @@ -31,9 +31,15 @@ #include <signal.h> #include <process.h> #endif +#ifdef __ANDROID__ +#include <android/log.h> +#endif #include "cross.h" #include "SDL.h" +#if SDL_VERSION_ATLEAST(2,0,0) && C_PHYSICAL_CDROM_MOUNT +#include "../sdl_cdrom/compat_SDL_cdrom.h" +#endif #include "dosbox.h" #include "video.h" @@ -50,11 +56,19 @@ #include "cross.h" #include "control.h" +#if SDL_VERSION_ATLEAST(2,0,0) +#define MAPPERFILE "mapper-sdl2-" VERSION ".map" +#else #define MAPPERFILE "mapper-" VERSION ".map" +#endif //#define DISABLE_JOYSTICK #if C_OPENGL +#ifdef __ANDROID__ +#include "SDL_opengles.h" +#else #include "SDL_opengl.h" +#endif #ifndef APIENTRY #define APIENTRY @@ -81,12 +95,14 @@ typedef GLvoid* (APIENTRYP PFNGLMAPBUFFERARBPROC) (GLenum target, GLenum access) typedef GLboolean (APIENTRYP PFNGLUNMAPBUFFERARBPROC) (GLenum target); #endif +#ifndef __ANDROID__ PFNGLGENBUFFERSARBPROC glGenBuffersARB = NULL; PFNGLBINDBUFFERARBPROC glBindBufferARB = NULL; PFNGLDELETEBUFFERSARBPROC glDeleteBuffersARB = NULL; PFNGLBUFFERDATAARBPROC glBufferDataARB = NULL; PFNGLMAPBUFFERARBPROC glMapBufferARB = NULL; PFNGLUNMAPBUFFERARBPROC glUnmapBufferARB = NULL; +#endif #endif //C_OPENGL @@ -129,8 +145,12 @@ struct private_hwdata { enum SCREEN_TYPES { SCREEN_SURFACE, +#if SDL_VERSION_ATLEAST(2,0,0) + SCREEN_TEXTURE, +#else SCREEN_SURFACE_DDRAW, SCREEN_OVERLAY, +#endif SCREEN_OPENGL }; @@ -148,10 +168,13 @@ struct SDL_Block { bool inited; bool active; //If this isn't set don't draw bool updating; +#if SDL_VERSION_ATLEAST(2,0,0) + bool update_display_contents; + bool resizing_window; +#endif struct { Bit32u width; Bit32u height; - Bit32u bpp; Bitu flags; double scalex,scaley; GFX_CallBack_t callback; @@ -161,45 +184,72 @@ struct SDL_Block { struct { Bit16u width, height; bool fixed; +#if SDL_VERSION_ATLEAST(2,0,0) + bool display_res; +#endif } full; struct { Bit16u width, height; } window; Bit8u bpp; +#if SDL_VERSION_ATLEAST(2,0,0) + Bit32u sdl2pixelFormat; +#endif bool fullscreen; bool lazy_fullscreen; bool lazy_fullscreen_req; - bool doublebuf; + bool vsync; SCREEN_TYPES type; SCREEN_TYPES want_type; } desktop; #if C_OPENGL struct { +#if SDL_VERSION_ATLEAST(2,0,0) + SDL_GLContext context; +#endif Bitu pitch; void * framebuf; GLuint buffer; GLuint texture; +#ifdef __ANDROID__ // OpenGL ES + GLfloat vertCoords[8]; + GLfloat texCoords[8]; +#else // OpenGL (not ES) GLuint displaylist; +#endif GLint max_texsize; bool bilinear; bool packed_pixel; bool paletted_texture; bool pixel_buffer_object; } opengl; -#endif +#endif // C_OPENGL +#if !SDL_VERSION_ATLEAST(2,0,0) struct { SDL_Surface * surface; #if C_DDRAW RECT rect; #endif } blit; +#endif // Not SDL v2.0 struct { PRIORITY_LEVELS focus; PRIORITY_LEVELS nofocus; } priority; SDL_Rect clip; SDL_Surface * surface; +#if SDL_VERSION_ATLEAST(2,0,0) + SDL_Window * window; + SDL_Renderer * renderer; + const char * rendererDriver; + int displayNumber; + struct { + SDL_Texture * texture; + SDL_PixelFormat * pixelFormat; + } texture; +#else SDL_Overlay * overlay; +#endif SDL_cond *cond; struct { bool autolock; @@ -207,6 +257,13 @@ struct SDL_Block { bool requestlock; bool locked; Bitu sensitivity; +#ifdef __ANDROID__ + SDL_FingerID leftMouseFingerID, rightMouseFingerID, + middleMouseFingerID, mouseMotionFingerID, + escKeyFingerID; + bool isLeftMouseFingerUsed, isRightMouseFingerUsed, + isMiddleMouseFingerUsed, isEscKeyFingerUsed; +#endif } mouse; SDL_Rect updateRects[1024]; Bitu num_joysticks; @@ -216,12 +273,14 @@ struct SDL_Block { Bit32u focus_ticks; #endif // state of alt-keys for certain special handlings - Bit8u laltstate; - Bit8u raltstate; + SDL_EventType laltstate; + SDL_EventType raltstate; }; static SDL_Block sdl; +#if !SDL_VERSION_ATLEAST(2,0,0) + #define SETMODE_SAVES 1 //Don't set Video Mode if nothing changes. #define SETMODE_SAVES_CLEAR 1 //Clear the screen, when the Video Mode is reused SDL_Surface* SDL_SetVideoMode_Wrap(int width,int height,int bpp,Bit32u flags){ @@ -279,6 +338,42 @@ SDL_Surface* SDL_SetVideoMode_Wrap(int width,int height,int bpp,Bit32u flags){ return s; } +#endif // !SDL_VERSION_ATLEAST(2,0,0) + +#if SDL_VERSION_ATLEAST(2,0,0) +static int SDL_Init_Wrapper(void) +{ + // Don't init timers, GetTicks seems to work fine and they can use a fair amount of power (Macs again) + // Please report problems with audio and other things. + int result = ( SDL_Init( SDL_INIT_AUDIO|SDL_INIT_VIDEO /* | SDL_INIT_TIMER */ + |SDL_INIT_NOPARACHUTE + )); +#if C_PHYSICAL_CDROM_MOUNT + return (result < 0) ? result : Compat_SDL_CDROMInit(); +#else + return result; +#endif +} +#else +static int SDL_Init_Wrapper(void) +{ + // Don't init timers, GetTicks seems to work fine and they can use a fair amount of power (Macs again) + // Please report problems with audio and other things. + return (SDL_Init( SDL_INIT_AUDIO|SDL_INIT_VIDEO /* | SDL_INIT_TIMER */ + |SDL_INIT_CDROM + |SDL_INIT_NOPARACHUTE + )); +} +#endif + +static void SDL_Quit_Wrapper(void) +{ +#if SDL_VERSION_ATLEAST(2,0,0) && C_PHYSICAL_CDROM_MOUNT + Compat_SDL_CDROMQuit(); +#endif + SDL_Quit(); +} + extern const char* RunningProgram; extern bool CPU_CycleAutoAdjust; //Globals for keyboard initialisation @@ -298,7 +393,11 @@ void GFX_SetTitle(Bit32s cycles,Bits frameskip,bool paused){ } if(paused) strcat(title," PAUSED"); +#if SDL_VERSION_ATLEAST(2,0,0) + SDL_SetWindowTitle(sdl.window,title); +#else SDL_WM_SetCaption(title,VERSION); +#endif } static unsigned char logo[32*32*4]= { @@ -314,8 +413,13 @@ static void GFX_SetIcon() { #else SDL_Surface* logos= SDL_CreateRGBSurfaceFrom((void*)logo,32,32,32,128,0x000000ff,0x0000ff00,0x00ff0000,0); #endif + +#if SDL_VERSION_ATLEAST(2,0,0) + SDL_SetWindowIcon(sdl.window, logos); +#else SDL_WM_SetIcon(logos,NULL); -#endif +#endif // SDL_VERSION_ATLEAST(2,0,0) +#endif // !defined(MACOSX) } @@ -336,12 +440,21 @@ static void PauseDOSBox(bool pressed) { while (SDL_PollEvent(&event)) { // flush event queue. } - + /* NOTE: This is one of the few places where we use SDL key codes + with SDL 2.0, rather than scan codes. Is that the correct behavior? */ while (paused) { SDL_WaitEvent(&event); // since we're not polling, cpu usage drops to 0. switch (event.type) { case SDL_QUIT: KillSwitch(true); break; +#if SDL_VERSION_ATLEAST(2,0,0) + case SDL_WINDOWEVENT: + if (event.window.event == SDL_WINDOWEVENT_RESTORED) { + // We may need to re-create a texture and more + GFX_ResetScreen(); + } + break; +#endif case SDL_KEYDOWN: // Must use Pause/Break Key to resume. case SDL_KEYUP: if(event.key.keysym.sym == SDLK_PAUSE) { @@ -351,7 +464,15 @@ static void PauseDOSBox(bool pressed) { break; } #if defined (MACOSX) - if (event.key.keysym.sym == SDLK_q && (event.key.keysym.mod == KMOD_RMETA || event.key.keysym.mod == KMOD_LMETA) ) { + if (event.key.keysym.sym == SDLK_q && +#if SDL_VERSION_ATLEAST(2,0,0) + (event.key.keysym.mod == KMOD_RGUI || + event.key.keysym.mod == KMOD_LGUI) +#else + (event.key.keysym.mod == KMOD_RMETA || + event.key.keysym.mod == KMOD_LMETA) +#endif + ) { /* On macs, all aps exit when pressing cmd-q */ KillSwitch(true); break; @@ -361,19 +482,26 @@ static void PauseDOSBox(bool pressed) { } } +#if !SDL_VERSION_ATLEAST(2,0,0) #if defined (WIN32) bool GFX_SDLUsingWinDIB(void) { return sdl.using_windib; } #endif +#endif /* Reset the screen with current values in the sdl structure */ Bitu GFX_GetBestMode(Bitu flags) { - Bitu testbpp,gotbpp; +#if !SDL_VERSION_ATLEAST(2,0,0) + Bitu testbpp,gotbpp; +#endif switch (sdl.desktop.want_type) { case SCREEN_SURFACE: check_surface: flags &= ~GFX_LOVE_8; //Disable love for 8bpp modes +#if !SDL_VERSION_ATLEAST(2,0,0) + /* TODO: Maybe remove support for output=surface with SDL 2.0? */ + /* Check if we can satisfy the depth it loves */ if (flags & GFX_LOVE_8) testbpp=8; else if (flags & GFX_LOVE_15) testbpp=15; @@ -386,7 +514,11 @@ check_gotbpp: if (sdl.desktop.fullscreen) gotbpp=SDL_VideoModeOK(640,480,testbpp,SDL_FULLSCREEN|SDL_HWSURFACE|SDL_HWPALETTE); else gotbpp=sdl.desktop.bpp; /* If we can't get our favorite mode check for another working one */ - switch (gotbpp) { + switch (gotbpp) +#else // SDL_VERSION_ATLEAST(2,0,0) + switch (sdl.desktop.bpp) +#endif + { case 8: if (flags & GFX_CAN_8) flags&=~(GFX_CAN_15|GFX_CAN_16|GFX_CAN_32); break; @@ -403,6 +535,7 @@ check_gotbpp: } flags |= GFX_CAN_RANDOM; break; +#if !SDL_VERSION_ATLEAST(2,0,0) #if C_DDRAW case SCREEN_SURFACE_DDRAW: if (!(flags&(GFX_CAN_15|GFX_CAN_16|GFX_CAN_32))) goto check_surface; @@ -420,8 +553,14 @@ check_gotbpp: flags|=GFX_SCALING; flags&=~(GFX_CAN_8|GFX_CAN_15|GFX_CAN_16); break; +#endif // !SDL_VERSION_ATLEAST(2,0,0) +#if C_OPENGL || SDL_VERSION_ATLEAST(2,0,0) #if C_OPENGL case SCREEN_OPENGL: +#endif +#if SDL_VERSION_ATLEAST(2,0,0) + case SCREEN_TEXTURE: +#endif //We only accept 32bit output from the scalers here if (!(flags&GFX_CAN_32)) goto check_surface; flags|=GFX_SCALING; @@ -461,19 +600,160 @@ static int int_log2 (int val) { return log; } +#if SDL_VERSION_ATLEAST(2,0,0) + +static SDL_Window * GFX_SetSDLWindowMode(Bit16u width, Bit16u height, bool fullscreen, SCREEN_TYPES screenType) { + static SCREEN_TYPES lastType = SCREEN_SURFACE; + if (sdl.renderer) { + SDL_DestroyRenderer(sdl.renderer); + sdl.renderer=0; + } + if (sdl.texture.pixelFormat) { + SDL_FreeFormat(sdl.texture.pixelFormat); + sdl.texture.pixelFormat = 0; + } + if (sdl.texture.texture) { + SDL_DestroyTexture(sdl.texture.texture); + sdl.texture.texture=0; + } +#if C_OPENGL + if (sdl.opengl.context) { + SDL_GL_DeleteContext(sdl.opengl.context); + sdl.opengl.context=0; + } +#endif + int currWidth, currHeight; + if (sdl.window && sdl.resizing_window) { + SDL_GetWindowSize(sdl.window, &currWidth, &currHeight); + sdl.update_display_contents = ((width <= currWidth) && (height <= currHeight)); + + return sdl.window; + } + /* If we change screen type, recreate the window. Furthermore, if + * it is our very first time then we simply create a new window. + */ + if (!sdl.window || (lastType != screenType)) { + lastType = screenType; + if (sdl.window) { + SDL_DestroyWindow(sdl.window); + } + /* Don't create a fullscreen immediately. Reasons: + * 1. This theoretically allows us to set window resolution and + * then let SDL2 remember it for later (even if not actually done). + * 2. It's a bit less glitchy to set a custom display mode for a + * full screen, albeit it's still not perfect (at least on X11). + */ + sdl.window = SDL_CreateWindow("", + SDL_WINDOWPOS_UNDEFINED_DISPLAY(sdl.displayNumber), + SDL_WINDOWPOS_UNDEFINED_DISPLAY(sdl.displayNumber), + width, height, + ((screenType == SCREEN_OPENGL) ? SDL_WINDOW_OPENGL : 0) | SDL_WINDOW_SHOWN + ); + if (!sdl.window) { + return sdl.window; + } + GFX_SetTitle(-1,-1,false); //refresh title. + if (!fullscreen) { + goto finish; + } + } + /* Fullscreen mode switching has its limits, and is also problematic on + * some window managers. For now, the following may work up to some + * level. On X11, SDL_VIDEO_X11_LEGACY_FULLSCREEN=1 can also help, + * although it has its own issues. + * Suggestion: Use the desktop res if possible, with output=surface + * if one is not interested in scaling. + * On Android, desktop res is the only way. + */ + if (fullscreen) { + SDL_DisplayMode displayMode; + SDL_GetWindowDisplayMode(sdl.window, &displayMode); + displayMode.w = width; + displayMode.h = height; + SDL_SetWindowDisplayMode(sdl.window, &displayMode); + + SDL_SetWindowFullscreen(sdl.window, sdl.desktop.full.display_res ? SDL_WINDOW_FULLSCREEN_DESKTOP : SDL_WINDOW_FULLSCREEN); + } else { + SDL_SetWindowFullscreen(sdl.window, 0); + + SDL_SetWindowSize(sdl.window, width, height); + } + // Maybe some requested fullscreen resolution is unsupported? +finish: + SDL_GetWindowSize(sdl.window, &currWidth, &currHeight); + sdl.update_display_contents = ((width <= currWidth) && (height <= currHeight)); + return sdl.window; +} + +// Used for the mapper UI and more: Creates a fullscreen window with desktop res +// on Android, and a non-fullscreen window with the input dimensions otherwise. +SDL_Window * GFX_SetSDLSurfaceWindow(Bit16u width, Bit16u height) { +#ifdef __ANDROID__ + return GFX_SetSDLWindowMode(sdl.desktop.full.width, sdl.desktop.full.height, true, SCREEN_SURFACE); +#else + return GFX_SetSDLWindowMode(width, height, false, SCREEN_SURFACE); +#endif +} + +// Returns the rectangle in the current window to be used for scaling a +// sub-window with the given dimensions, like the mapper UI. +SDL_Rect GFX_GetSDLSurfaceSubwindowDims(Bit16u width, Bit16u height) { + SDL_Rect rect; +#ifdef __ANDROID__ + // Wider than width:height + if (height*sdl.desktop.full.width > sdl.desktop.full.height*width) { + rect.w=sdl.desktop.full.height*width/height; + rect.h=sdl.desktop.full.height; + rect.x=(sdl.desktop.full.width-rect.w)/2; + rect.y=0; + } else { // NOT wider than width:height + rect.w=sdl.desktop.full.width; + rect.h=sdl.desktop.full.width*height/width; + rect.x=0; + rect.y=(sdl.desktop.full.height-rect.h)/2; + } +#else + rect.x=rect.y=0; + rect.w=width; + rect.h=height; +#endif + return rect; +} -static SDL_Surface * GFX_SetupSurfaceScaled(Bit32u sdl_flags, Bit32u bpp) { +// Currently used for an initial test here +static SDL_Window * GFX_SetSDLOpenGLWindow(Bit16u width, Bit16u height) { +#ifdef __ANDROID__ + return GFX_SetSDLWindowMode(sdl.desktop.full.width, sdl.desktop.full.height, true, SCREEN_OPENGL); +#else + return GFX_SetSDLWindowMode(width, height, false, SCREEN_OPENGL); +#endif +} + +#endif // SDL_VERSION_ATLEAST(2,0,0) + +// Different functions, similar function bodies (SDL 1.2 vs 2.0) + +#if SDL_VERSION_ATLEAST(2,0,0) +static SDL_Window * GFX_SetupWindowScaled(SCREEN_TYPES screenType) +#else +static SDL_Surface * GFX_SetupSurfaceScaled(Bit32u sdl_flags, Bit32u bpp) +#endif +{ Bit16u fixedWidth; Bit16u fixedHeight; if (sdl.desktop.fullscreen) { fixedWidth = sdl.desktop.full.fixed ? sdl.desktop.full.width : 0; fixedHeight = sdl.desktop.full.fixed ? sdl.desktop.full.height : 0; +#if !SDL_VERSION_ATLEAST(2,0,0) sdl_flags |= SDL_FULLSCREEN|SDL_HWSURFACE; +#endif } else { fixedWidth = sdl.desktop.window.width; fixedHeight = sdl.desktop.window.height; +#if !SDL_VERSION_ATLEAST(2,0,0) sdl_flags |= SDL_HWSURFACE; +#endif } if (fixedWidth && fixedHeight) { double ratio_w=(double)fixedWidth/(sdl.draw.width*sdl.draw.scalex); @@ -489,27 +769,59 @@ static SDL_Surface * GFX_SetupSurfaceScaled(Bit32u sdl_flags, Bit32u bpp) { sdl.clip.w=(Bit16u)(sdl.draw.width*sdl.draw.scalex*ratio_h + 0.4); sdl.clip.h=(Bit16u)fixedHeight; } - if (sdl.desktop.fullscreen) + if (sdl.desktop.fullscreen) { +#if SDL_VERSION_ATLEAST(2,0,0) + sdl.window = GFX_SetSDLWindowMode(fixedWidth, fixedHeight, sdl.desktop.fullscreen, screenType); +#else sdl.surface = SDL_SetVideoMode_Wrap(fixedWidth,fixedHeight,bpp,sdl_flags); - else +#endif + } else { +#if SDL_VERSION_ATLEAST(2,0,0) + sdl.window = GFX_SetSDLWindowMode(sdl.clip.w, sdl.clip.h, sdl.desktop.fullscreen, screenType); +#else sdl.surface = SDL_SetVideoMode_Wrap(sdl.clip.w,sdl.clip.h,bpp,sdl_flags); +#endif + } +#if SDL_VERSION_ATLEAST(2,0,0) + if (sdl.window && SDL_GetWindowFlags(sdl.window) & SDL_WINDOW_FULLSCREEN) { + int windowWidth; + SDL_GetWindowSize(sdl.window, &windowWidth, NULL); + sdl.clip.x=(Sint16)((windowWidth-sdl.clip.w)/2); +#else if (sdl.surface && sdl.surface->flags & SDL_FULLSCREEN) { sdl.clip.x=(Sint16)((sdl.surface->w-sdl.clip.w)/2); - sdl.clip.y=(Sint16)((sdl.surface->h-sdl.clip.h)/2); +#endif +#ifdef __ANDROID__ + /* Portrait orientation and on-screen keyboards + are commonly found on that platform */ + sdl.clip.y=0; +#else + sdl.clip.y=(Sint16)((fixedHeight-sdl.clip.h)/2); +#endif } else { sdl.clip.x = 0; sdl.clip.y = 0; } +#if SDL_VERSION_ATLEAST(2,0,0) + return sdl.window; +#else return sdl.surface; +#endif } else { sdl.clip.x=0;sdl.clip.y=0; sdl.clip.w=(Bit16u)(sdl.draw.width*sdl.draw.scalex); sdl.clip.h=(Bit16u)(sdl.draw.height*sdl.draw.scaley); +#if SDL_VERSION_ATLEAST(2,0,0) + sdl.window = GFX_SetSDLWindowMode(sdl.clip.w, sdl.clip.h, sdl.desktop.fullscreen, screenType); + return sdl.window; +#else sdl.surface=SDL_SetVideoMode_Wrap(sdl.clip.w,sdl.clip.h,bpp,sdl_flags); return sdl.surface; +#endif } } +#if !SDL_VERSION_ATLEAST(2,0,0) // NOTE: Do we need this? Never used and can't be used as-is with SDL 2.0 void GFX_TearDown(void) { if (sdl.updating) GFX_EndUpdate( 0 ); @@ -519,6 +831,7 @@ void GFX_TearDown(void) { sdl.blit.surface=0; } } +#endif Bitu GFX_SetSize(Bitu width,Bitu height,Bitu flags,double scalex,double scaley,GFX_CallBack_t callback) { if (sdl.updating) @@ -530,41 +843,77 @@ Bitu GFX_SetSize(Bitu width,Bitu height,Bitu flags,double scalex,double scaley,G sdl.draw.scalex=scalex; sdl.draw.scaley=scaley; - int bpp=0; Bitu retFlags = 0; +#if !SDL_VERSION_ATLEAST(2,0,0) + int bpp=0; if (sdl.blit.surface) { SDL_FreeSurface(sdl.blit.surface); sdl.blit.surface=0; } +#endif switch (sdl.desktop.want_type) { case SCREEN_SURFACE: dosurface: +#if !SDL_VERSION_ATLEAST(2,0,0) if (flags & GFX_CAN_8) bpp=8; if (flags & GFX_CAN_15) bpp=15; if (flags & GFX_CAN_16) bpp=16; if (flags & GFX_CAN_32) bpp=32; +#endif sdl.desktop.type=SCREEN_SURFACE; sdl.clip.w=width; sdl.clip.h=height; if (sdl.desktop.fullscreen) { if (sdl.desktop.full.fixed) { sdl.clip.x=(Sint16)((sdl.desktop.full.width-width)/2); +#ifdef __ANDROID__ + /* Portrait orientation and on-screen keyboards + are commonly found on that platform */ + sdl.clip.y=0; +#else sdl.clip.y=(Sint16)((sdl.desktop.full.height-height)/2); +#endif +#if SDL_VERSION_ATLEAST(2,0,0) + sdl.window = GFX_SetSDLWindowMode(sdl.desktop.full.width, + sdl.desktop.full.height, + sdl.desktop.fullscreen, sdl.desktop.type); + if (sdl.window == NULL) + E_Exit("Could not set fullscreen video mode %ix%i-%i: %s",sdl.desktop.full.width,sdl.desktop.full.height,sdl.desktop.bpp,SDL_GetError()); + /* This may be required after an ALT-TAB leading to a window + minimize, which further effectively shrinks its size */ + if ((sdl.clip.x < 0) || (sdl.clip.y < 0)) { + sdl.update_display_contents = false; + } +#else sdl.surface=SDL_SetVideoMode_Wrap(sdl.desktop.full.width,sdl.desktop.full.height,bpp, SDL_FULLSCREEN | ((flags & GFX_CAN_RANDOM) ? SDL_SWSURFACE : SDL_HWSURFACE) | - (sdl.desktop.doublebuf ? SDL_DOUBLEBUF|SDL_ASYNCBLIT : 0) | SDL_HWPALETTE); + (sdl.desktop.vsync ? SDL_DOUBLEBUF|SDL_ASYNCBLIT : 0) | SDL_HWPALETTE); if (sdl.surface == NULL) E_Exit("Could not set fullscreen video mode %ix%i-%i: %s",sdl.desktop.full.width,sdl.desktop.full.height,bpp,SDL_GetError()); +#endif } else { sdl.clip.x=0;sdl.clip.y=0; +#if SDL_VERSION_ATLEAST(2,0,0) + sdl.window = GFX_SetSDLWindowMode(width, height, + sdl.desktop.fullscreen, sdl.desktop.type); + if (sdl.window == NULL) + E_Exit("Could not set fullscreen video mode %ix%i-%i: %s",(int)width,(int)height,sdl.desktop.bpp,SDL_GetError()); +#else sdl.surface=SDL_SetVideoMode_Wrap(width,height,bpp, SDL_FULLSCREEN | ((flags & GFX_CAN_RANDOM) ? SDL_SWSURFACE : SDL_HWSURFACE) | - (sdl.desktop.doublebuf ? SDL_DOUBLEBUF|SDL_ASYNCBLIT : 0)|SDL_HWPALETTE); + (sdl.desktop.vsync ? SDL_DOUBLEBUF|SDL_ASYNCBLIT : 0)|SDL_HWPALETTE); if (sdl.surface == NULL) E_Exit("Could not set fullscreen video mode %ix%i-%i: %s",(int)width,(int)height,bpp,SDL_GetError()); +#endif } } else { sdl.clip.x=0;sdl.clip.y=0; +#if SDL_VERSION_ATLEAST(2,0,0) + sdl.window = GFX_SetSDLWindowMode(width, height, + sdl.desktop.fullscreen, sdl.desktop.type); + if (sdl.window == NULL) + E_Exit("Could not set windowed video mode %ix%i-%i: %s",(int)width,(int)height,sdl.desktop.bpp,SDL_GetError()); +#else sdl.surface=SDL_SetVideoMode_Wrap(width,height,bpp,(flags & GFX_CAN_RANDOM) ? SDL_SWSURFACE : SDL_HWSURFACE); #ifdef WIN32 if (sdl.surface == NULL) { @@ -586,42 +935,116 @@ dosurface: #endif if (sdl.surface == NULL) E_Exit("Could not set windowed video mode %ix%i-%i: %s",(int)width,(int)height,bpp,SDL_GetError()); +#endif // !SDL_VERSION_ATLEAST(2,0,0) } - if (sdl.surface) { - switch (sdl.surface->format->BitsPerPixel) { +#if SDL_VERSION_ATLEAST(2,0,0) + sdl.surface = SDL_GetWindowSurface(sdl.window); + if (sdl.surface == NULL) + E_Exit("Could not retrieve window surface: %s",SDL_GetError()); +#endif + switch (sdl.surface->format->BitsPerPixel) { case 8: retFlags = GFX_CAN_8; - break; + break; case 15: retFlags = GFX_CAN_15; break; case 16: retFlags = GFX_CAN_16; - break; + break; case 32: retFlags = GFX_CAN_32; - break; - } - if (retFlags && (sdl.surface->flags & SDL_HWSURFACE)) - retFlags |= GFX_HARDWARE; - if (retFlags && (sdl.surface->flags & SDL_DOUBLEBUF)) { - sdl.blit.surface=SDL_CreateRGBSurface(SDL_HWSURFACE, - sdl.draw.width, sdl.draw.height, - sdl.surface->format->BitsPerPixel, - sdl.surface->format->Rmask, - sdl.surface->format->Gmask, - sdl.surface->format->Bmask, - 0); - /* If this one fails be ready for some flickering... */ - } + break; + } +#if SDL_VERSION_ATLEAST(2,0,0) + /* Fix a glitch with aspect=true occuring when + changing between modes with different dimensions */ + SDL_FillRect(sdl.surface, NULL, SDL_MapRGB(sdl.surface->format, 0, 0, 0)); + SDL_UpdateWindowSurface(sdl.window); +#else + if (retFlags && (sdl.surface->flags & SDL_HWSURFACE)) + retFlags |= GFX_HARDWARE; + if (retFlags && (sdl.surface->flags & SDL_DOUBLEBUF)) { + sdl.blit.surface=SDL_CreateRGBSurface(SDL_HWSURFACE, + sdl.draw.width, sdl.draw.height, + sdl.surface->format->BitsPerPixel, + sdl.surface->format->Rmask, + sdl.surface->format->Gmask, + sdl.surface->format->Bmask, + 0); + /* If this one fails be ready for some flickering... */ + } +#endif + break; +#if SDL_VERSION_ATLEAST(2,0,0) + case SCREEN_TEXTURE: + { + if (!GFX_SetupWindowScaled(sdl.desktop.want_type)) { + LOG_MSG("SDL:Can't set video mode, falling back to surface"); + goto dosurface; + } + if (strcmp(sdl.rendererDriver, "auto")) + SDL_SetHint(SDL_HINT_RENDER_DRIVER, sdl.rendererDriver); + sdl.renderer = SDL_CreateRenderer(sdl.window, -1, + SDL_RENDERER_ACCELERATED | + (sdl.desktop.vsync ? SDL_RENDERER_PRESENTVSYNC : 0)); + if (!sdl.renderer) { + LOG_MSG("%s\n", SDL_GetError()); + LOG_MSG("SDL:Can't create renderer, falling back to surface"); + goto dosurface; + } + /* SDL_PIXELFORMAT_ARGB8888 is possible with most + rendering drivers, "opengles" being a notable exception */ + sdl.texture.texture = SDL_CreateTexture(sdl.renderer, SDL_PIXELFORMAT_ARGB8888, + SDL_TEXTUREACCESS_STREAMING, width, height); + /* SDL_PIXELFORMAT_ABGR8888 (not RGB) is the + only supported format for the "opengles" driver */ + if (!sdl.texture.texture) { + if (flags & GFX_RGBONLY) goto dosurface; + sdl.texture.texture = SDL_CreateTexture(sdl.renderer, SDL_PIXELFORMAT_ABGR8888, + SDL_TEXTUREACCESS_STREAMING, width, height); + } + if (!sdl.texture.texture) { + SDL_DestroyRenderer(sdl.renderer); + sdl.renderer = NULL; + LOG_MSG("SDL:Can't create texture, falling back to surface"); + goto dosurface; + } + SDL_SetRenderDrawColor(sdl.renderer, 0, 0, 0, SDL_ALPHA_OPAQUE); + sdl.desktop.type=SCREEN_TEXTURE; + Uint32 pixelFormat; + SDL_QueryTexture(sdl.texture.texture, &pixelFormat, NULL, NULL, NULL); + sdl.texture.pixelFormat = SDL_AllocFormat(pixelFormat); + switch (SDL_BITSPERPIXEL(pixelFormat)) { + case 8: + retFlags = GFX_CAN_8; + break; + case 15: + retFlags = GFX_CAN_15; + break; + case 16: + retFlags = GFX_CAN_16; + break; + case 24: /* SDL_BYTESPERPIXEL is probably 4, though. */ + case 32: + retFlags = GFX_CAN_32; + break; } + retFlags |= GFX_SCALING; + SDL_RendererInfo rendererInfo; + SDL_GetRendererInfo(sdl.renderer, &rendererInfo); + LOG_MSG("Using driver \"%s\" for renderer", rendererInfo.name); + if (rendererInfo.flags & SDL_RENDERER_ACCELERATED) + retFlags |= GFX_HARDWARE; break; + } +#else // !SDL_VERSION_ATLEAST(2,0,0) #if C_DDRAW case SCREEN_SURFACE_DDRAW: if (flags & GFX_CAN_15) bpp=15; if (flags & GFX_CAN_16) bpp=16; if (flags & GFX_CAN_32) bpp=32; - if (!GFX_SetupSurfaceScaled((sdl.desktop.doublebuf && sdl.desktop.fullscreen) ? SDL_DOUBLEBUF : 0,bpp)) goto dosurface; + if (!GFX_SetupSurfaceScaled((sdl.desktop.vsync && sdl.desktop.fullscreen) ? SDL_DOUBLEBUF : 0,bpp)) goto dosurface; sdl.blit.rect.top=sdl.clip.y; sdl.blit.rect.left=sdl.clip.x; sdl.blit.rect.right=sdl.clip.x+sdl.clip.w; @@ -669,13 +1092,17 @@ dosurface: sdl.desktop.type=SCREEN_OVERLAY; retFlags = GFX_CAN_32 | GFX_SCALING | GFX_HARDWARE; break; +#endif // !SDL_VERSION_ATLEAST(2,0,0) #if C_OPENGL case SCREEN_OPENGL: { +#ifndef __ANDROID__ if (sdl.opengl.pixel_buffer_object) { glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT, 0); if (sdl.opengl.buffer) glDeleteBuffersARB(1, &sdl.opengl.buffer); - } else if (sdl.opengl.framebuf) { + } else +#endif + if (sdl.opengl.framebuf) { free(sdl.opengl.framebuf); } sdl.opengl.framebuf=0; @@ -686,30 +1113,67 @@ dosurface: goto dosurface; } SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 ); +#ifdef __ANDROID__ + /* WARNING: OpenGL ES v2.0 is NOT backwards compatible + * with v1.1! For simplicity we force v1.1 for now, + * although v2.0 is probably the better way. + */ + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 1); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1); +#endif +#if SDL_VERSION_ATLEAST(2,0,0) + GFX_SetupWindowScaled(sdl.desktop.want_type); + /* We may simply use SDL_BYTESPERPIXEL + here rather than SDL_BITSPERPIXEL */ + if (!sdl.window || SDL_BYTESPERPIXEL(SDL_GetWindowPixelFormat(sdl.window))<2) { + LOG_MSG("SDL:OPENGL:Can't open drawing window, are you running in 16bpp(or higher) mode?"); + goto dosurface; + } + sdl.opengl.context = SDL_GL_CreateContext(sdl.window); + if (sdl.opengl.context == NULL) { + LOG_MSG("SDL:OPENGL:Can't create OpenGL context, falling back to surface"); + goto dosurface; + } + /* Sync to VBlank if desired */ + SDL_GL_SetSwapInterval(sdl.desktop.vsync ? 1 : 0); +#else // !SDL_VERSION_ATLEAST(2,0,0) #if SDL_VERSION_ATLEAST(1, 2, 11) - SDL_GL_SetAttribute( SDL_GL_SWAP_CONTROL, 0 ); + SDL_GL_SetAttribute( SDL_GL_SWAP_CONTROL, sdl.desktop.vsync ? 1 : 0 ); #endif GFX_SetupSurfaceScaled(SDL_OPENGL,0); if (!sdl.surface || sdl.surface->format->BitsPerPixel<15) { LOG_MSG("SDL:OPENGL: Can't open drawing surface, are you running in 16bpp (or higher) mode?"); goto dosurface; } +#endif // !SDL_VERSION_ATLEAST(2,0,0) /* Create the texture and display list */ +#ifndef __ANDROID__ if (sdl.opengl.pixel_buffer_object) { glGenBuffersARB(1, &sdl.opengl.buffer); glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT, sdl.opengl.buffer); glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_EXT, width*height*4, NULL, GL_STREAM_DRAW_ARB); glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT, 0); - } else { + } else +#endif + { sdl.opengl.framebuf=malloc(width*height*4); //32 bit color } sdl.opengl.pitch=width*4; - if(sdl.clip.x ==0 && sdl.clip.y ==0 && sdl.desktop.fullscreen && !sdl.desktop.full.fixed && (sdl.clip.w != sdl.surface->w || sdl.clip.h != sdl.surface->h)) { -// LOG_MSG("attempting to fix the centering to %d %d %d %d",(sdl.surface->w-sdl.clip.w)/2,(sdl.surface->h-sdl.clip.h)/2,sdl.clip.w,sdl.clip.h); - glViewport((sdl.surface->w-sdl.clip.w)/2,(sdl.surface->h-sdl.clip.h)/2,sdl.clip.w,sdl.clip.h); +#if SDL_VERSION_ATLEAST(2,0,0) + int windowWidth, windowHeight; + SDL_GetWindowSize(sdl.window, &windowWidth, &windowHeight); +#else + int windowWidth = sdl.surface->w, windowHeight = sdl.surface->h; +#endif + if(sdl.clip.x ==0 && sdl.clip.y ==0 && sdl.desktop.fullscreen && !sdl.desktop.full.fixed && (sdl.clip.w != windowWidth || sdl.clip.h != windowHeight)) { +// LOG_MSG("attempting to fix the centering to %d %d %d %d",(windowWidth-sdl.clip.w)/2,(windowHeight-sdl.clip.h)/2,sdl.clip.w,sdl.clip.h); + glViewport((windowWidth-sdl.clip.w)/2,(windowHeight-sdl.clip.h)/2,sdl.clip.w,sdl.clip.h); } else { - glViewport(sdl.clip.x,sdl.clip.y,sdl.clip.w,sdl.clip.h); + /* We don't just pass sdl.clip.y as-is, so we cover the case of non-vertical + * centering on Android (in order to leave room for the on-screen keyboard) + */ + glViewport(sdl.clip.x,windowHeight-(sdl.clip.y+sdl.clip.h),sdl.clip.w,sdl.clip.h); } glMatrixMode (GL_PROJECTION); @@ -717,8 +1181,15 @@ dosurface: glGenTextures(1,&sdl.opengl.texture); glBindTexture(GL_TEXTURE_2D,sdl.opengl.texture); // No borders +#ifdef __ANDROID__ + /* Plain OpenGL ES (v1.1) has no mention + of GL_CLAMP, so use GL_CLAMP_TO_EDGE */ + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); +#else glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); +#endif if (!sdl.opengl.bilinear || ( (sdl.clip.h % height) == 0 && (sdl.clip.w % width) == 0) ) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); @@ -727,12 +1198,13 @@ dosurface: glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); } +#ifdef __ANDROID__ // OpenGL ES + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texsize, texsize, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); +#else glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, texsize, texsize, 0, GL_BGRA_EXT, GL_UNSIGNED_BYTE, 0); +#endif glClearColor (0.0, 0.0, 0.0, 1.0); - glClear(GL_COLOR_BUFFER_BIT); - SDL_GL_SwapBuffers(); - glClear(GL_COLOR_BUFFER_BIT); glShadeModel (GL_FLAT); glDisable (GL_DEPTH_TEST); glDisable (GL_LIGHTING); @@ -744,9 +1216,18 @@ dosurface: GLfloat tex_width=((GLfloat)(width)/(GLfloat)texsize); GLfloat tex_height=((GLfloat)(height)/(GLfloat)texsize); +#ifdef __ANDROID__ + /* Display lists are not available with OpenGL ES (1.0, 2.0). + Furthermore, we can't use glBegin, glTexCoord2f and glEnd. */ + sdl.opengl.texCoords[0] = 0; sdl.opengl.texCoords[1] = tex_height; // lower left + sdl.opengl.texCoords[2] = tex_width; sdl.opengl.texCoords[3] = tex_height; // lower right + sdl.opengl.texCoords[4] = tex_width; sdl.opengl.texCoords[5] = 0; // upper right + sdl.opengl.texCoords[6] = 0; sdl.opengl.texCoords[7] = 0; // upper left +#else if (glIsList(sdl.opengl.displaylist)) glDeleteLists(sdl.opengl.displaylist, 1); sdl.opengl.displaylist = glGenLists(1); glNewList(sdl.opengl.displaylist, GL_COMPILE); + glClear(GL_COLOR_BUFFER_BIT); glBindTexture(GL_TEXTURE_2D, sdl.opengl.texture); glBegin(GL_QUADS); // lower left @@ -759,10 +1240,13 @@ dosurface: glTexCoord2f(0,0); glVertex2f(-1.0f, 1.0f); glEnd(); glEndList(); +#endif sdl.desktop.type=SCREEN_OPENGL; retFlags = GFX_CAN_32 | GFX_SCALING; +#ifndef __ANDROID__ if (sdl.opengl.pixel_buffer_object) retFlags |= GFX_HARDWARE; +#endif break; }//OPENGL #endif //C_OPENGL @@ -776,18 +1260,28 @@ dosurface: return retFlags; } + void GFX_CaptureMouse(void) { sdl.mouse.locked=!sdl.mouse.locked; if (sdl.mouse.locked) { +#if SDL_VERSION_ATLEAST(2,0,0) + SDL_SetRelativeMouseMode(SDL_TRUE); +#else SDL_WM_GrabInput(SDL_GRAB_ON); +#endif SDL_ShowCursor(SDL_DISABLE); } else { +#if SDL_VERSION_ATLEAST(2,0,0) + SDL_SetRelativeMouseMode(SDL_FALSE); +#else SDL_WM_GrabInput(SDL_GRAB_OFF); +#endif if (sdl.mouse.autoenable || !sdl.mouse.autolock) SDL_ShowCursor(SDL_ENABLE); } mouselocked=sdl.mouse.locked; } +#if !SDL_VERSION_ATLEAST(2,0,0) // NOTE: Do we need this? void GFX_UpdateSDLCaptureState(void) { if (sdl.mouse.locked) { SDL_WM_GrabInput(SDL_GRAB_ON); @@ -799,6 +1293,7 @@ void GFX_UpdateSDLCaptureState(void) { CPU_Reset_AutoAdjust(); GFX_SetTitle(-1,-1,false); } +#endif // SDL 1.2 bool mouselocked; //Global variable for mapper static void CaptureMouse(bool pressed) { @@ -871,25 +1366,36 @@ bool GFX_LazyFullscreenRequested(void) { return false; } +#if !SDL_VERSION_ATLEAST(2,0,0) // NOTE: Do we need this? void GFX_RestoreMode(void) { GFX_SetSize(sdl.draw.width,sdl.draw.height,sdl.draw.flags,sdl.draw.scalex,sdl.draw.scaley,sdl.draw.callback); GFX_UpdateSDLCaptureState(); } +#endif bool GFX_StartUpdate(Bit8u * & pixels,Bitu & pitch) { +#if SDL_VERSION_ATLEAST(2,0,0) + if (!sdl.update_display_contents) + return false; +#endif if (!sdl.active || sdl.updating) return false; switch (sdl.desktop.type) { case SCREEN_SURFACE: +#if !SDL_VERSION_ATLEAST(2,0,0) if (sdl.blit.surface) { if (SDL_MUSTLOCK(sdl.blit.surface) && SDL_LockSurface(sdl.blit.surface)) return false; pixels=(Bit8u *)sdl.blit.surface->pixels; pitch=sdl.blit.surface->pitch; - } else { + } else +#endif + { +#if !SDL_VERSION_ATLEAST(2,0,0) if (SDL_MUSTLOCK(sdl.surface) && SDL_LockSurface(sdl.surface)) return false; +#endif pixels=(Bit8u *)sdl.surface->pixels; pixels+=sdl.clip.y*sdl.surface->pitch; pixels+=sdl.clip.x*sdl.surface->format->BytesPerPixel; @@ -897,6 +1403,19 @@ bool GFX_StartUpdate(Bit8u * & pixels,Bitu & pitch) { } sdl.updating=true; return true; +#if SDL_VERSION_ATLEAST(2,0,0) + case SCREEN_TEXTURE: + { + void * texPixels; + int texPitch; + if (SDL_LockTexture(sdl.texture.texture, NULL, &texPixels, &texPitch) < 0) + return false; + pixels = (Bit8u *)texPixels; + pitch = texPitch; + sdl.updating=true; + return true; + } +#else // !SDL_VERSION_ATLEAST(2,0,0) #if C_DDRAW case SCREEN_SURFACE_DDRAW: if (SDL_LockSurface(sdl.blit.surface)) { @@ -907,20 +1426,25 @@ bool GFX_StartUpdate(Bit8u * & pixels,Bitu & pitch) { pitch=sdl.blit.surface->pitch; sdl.updating=true; return true; -#endif +#endif // DirectDraw case SCREEN_OVERLAY: if (SDL_LockYUVOverlay(sdl.overlay)) return false; pixels=(Bit8u *)*(sdl.overlay->pixels); pitch=*(sdl.overlay->pitches); sdl.updating=true; return true; +#endif // !SDL_VERSION_ATLEAST(2,0,0) #if C_OPENGL case SCREEN_OPENGL: +#ifndef __ANDROID__ if(sdl.opengl.pixel_buffer_object) { glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT, sdl.opengl.buffer); pixels=(Bit8u *)glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT, GL_WRITE_ONLY); } else +#endif + { pixels=(Bit8u *)sdl.opengl.framebuf; + } pitch=sdl.opengl.pitch; sdl.updating=true; return true; @@ -936,11 +1460,16 @@ void GFX_EndUpdate( const Bit16u *changedLines ) { #if C_DDRAW int ret; #endif +#if SDL_VERSION_ATLEAST(2,0,0) + if (!sdl.update_display_contents) + return; +#endif if (!sdl.updating) return; sdl.updating=false; switch (sdl.desktop.type) { case SCREEN_SURFACE: +#if !SDL_VERSION_ATLEAST(2,0,0) if (SDL_MUSTLOCK(sdl.surface)) { if (sdl.blit.surface) { SDL_UnlockSurface(sdl.blit.surface); @@ -950,7 +1479,9 @@ void GFX_EndUpdate( const Bit16u *changedLines ) { SDL_UnlockSurface(sdl.surface); } SDL_Flip(sdl.surface); - } else if (changedLines) { + } else +#endif + if (changedLines) { Bitu y = 0, index = 0, rectCount = 0; while (y < sdl.draw.height) { if (!(index & 1)) { @@ -971,9 +1502,21 @@ void GFX_EndUpdate( const Bit16u *changedLines ) { index++; } if (rectCount) +#if SDL_VERSION_ATLEAST(2,0,0) + SDL_UpdateWindowSurfaceRects( sdl.window, sdl.updateRects, rectCount ); +#else SDL_UpdateRects( sdl.surface, rectCount, sdl.updateRects ); +#endif } break; +#if SDL_VERSION_ATLEAST(2,0,0) + case SCREEN_TEXTURE: + SDL_UnlockTexture(sdl.texture.texture); + SDL_RenderClear(sdl.renderer); + SDL_RenderCopy(sdl.renderer, sdl.texture.texture, NULL, &sdl.clip); + SDL_RenderPresent(sdl.renderer); + break; +#else // !SDL_VERSION_ATLEAST(2,0,0) #if C_DDRAW case SCREEN_SURFACE_DDRAW: SDL_UnlockSurface(sdl.blit.surface); @@ -993,13 +1536,15 @@ void GFX_EndUpdate( const Bit16u *changedLines ) { } SDL_Flip(sdl.surface); break; -#endif +#endif // DirectDraw case SCREEN_OVERLAY: SDL_UnlockYUVOverlay(sdl.overlay); SDL_DisplayYUVOverlay(sdl.overlay,&sdl.clip); break; +#endif // !SDL_VERSION_ATLEAST(2,0,0) #if C_OPENGL case SCREEN_OPENGL: +#ifndef __ANDROID__ if (sdl.opengl.pixel_buffer_object) { glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT); glBindTexture(GL_TEXTURE_2D, sdl.opengl.texture); @@ -1008,8 +1553,14 @@ void GFX_EndUpdate( const Bit16u *changedLines ) { GL_UNSIGNED_INT_8_8_8_8_REV, 0); glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT, 0); glCallList(sdl.opengl.displaylist); - SDL_GL_SwapBuffers(); - } else if (changedLines) { +#if SDL_VERSION_ATLEAST(2,0,0) + SDL_GL_SwapWindow(sdl.window); +#else + SDL_GL_SwapBuffers(); +#endif + } else +#endif // ifndef __ANDROID__ + if (changedLines) { Bitu y = 0, index = 0; glBindTexture(GL_TEXTURE_2D, sdl.opengl.texture); while (y < sdl.draw.height) { @@ -1018,15 +1569,43 @@ void GFX_EndUpdate( const Bit16u *changedLines ) { } else { Bit8u *pixels = (Bit8u *)sdl.opengl.framebuf + y * sdl.opengl.pitch; Bitu height = changedLines[index]; +#ifdef __ANDROID__ + /* Try GL_UNSIGNED_BYTE... */ + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, y, + sdl.draw.width, height, GL_RGBA, + GL_UNSIGNED_BYTE, pixels ); +#else glTexSubImage2D(GL_TEXTURE_2D, 0, 0, y, sdl.draw.width, height, GL_BGRA_EXT, GL_UNSIGNED_INT_8_8_8_8_REV, pixels ); +#endif y += height; } index++; } +#ifdef __ANDROID__ + /* We can't use display lists with OpenGL ES + and we should use Vertex Buffer Arrays */ + glClear(GL_COLOR_BUFFER_BIT); + glBindTexture(GL_TEXTURE_2D, sdl.opengl.texture); + + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + + glVertexPointer(2, GL_FLOAT, 0, sdl.opengl.vertCoords); + glTexCoordPointer(2, GL_FLOAT, 0, sdl.opengl.texCoords); + glDrawArrays(GL_TRIANGLE_FAN,0,4); + + glDisableClientState(GL_VERTEX_ARRAY); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); +#else // !__ANDROID__ glCallList(sdl.opengl.displaylist); +#endif // !__ANDROID__ +#if SDL_VERSION_ATLEAST(2,0,0) + SDL_GL_SwapWindow(sdl.window); +#else SDL_GL_SwapBuffers(); +#endif } break; #endif @@ -1037,6 +1616,7 @@ void GFX_EndUpdate( const Bit16u *changedLines ) { void GFX_SetPalette(Bitu start,Bitu count,GFX_PalEntry * entries) { +#if !SDL_VERSION_ATLEAST(2,0,0) // NOTE: Better remove this if SDL 1.2 isn't supported? /* I should probably not change the GFX_PalEntry :) */ if (sdl.surface->flags & SDL_HWPALETTE) { if (!SDL_SetPalette(sdl.surface,SDL_PHYSPAL,(SDL_Color *)entries,start,count)) { @@ -1047,13 +1627,20 @@ void GFX_SetPalette(Bitu start,Bitu count,GFX_PalEntry * entries) { E_Exit("SDL:Can't set palette"); } } +#endif } Bitu GFX_GetRGB(Bit8u red,Bit8u green,Bit8u blue) { switch (sdl.desktop.type) { case SCREEN_SURFACE: +#if !SDL_VERSION_ATLEAST(2,0,0) case SCREEN_SURFACE_DDRAW: +#endif return SDL_MapRGB(sdl.surface->format,red,green,blue); +#if SDL_VERSION_ATLEAST(2,0,0) + case SCREEN_TEXTURE: + return SDL_MapRGB(sdl.texture.pixelFormat,red,green,blue); +#else case SCREEN_OVERLAY: { Bit8u y = ( 9797*(red) + 19237*(green) + 3734*(blue) ) >> 15; @@ -1065,10 +1652,15 @@ Bitu GFX_GetRGB(Bit8u red,Bit8u green,Bit8u blue) { return (u << 0) | (y << 8) | (v << 16) | (y << 24); #endif } +#endif // !SDL_VERSION_ATLEAST(2,0,0) case SCREEN_OPENGL: -// return ((red << 0) | (green << 8) | (blue << 16)) | (255 << 24); - //USE BGRA +#ifdef __ANDROID__ + //Use RGBA on Android with OpenGL ES v1.1 + return ((red << 0) | (green << 8) | (blue << 16)) | (255 << 24); +#else + //USE BGRA otherwise return ((blue << 0) | (green << 8) | (red << 16)) | (255 << 24); +#endif } return 0; } @@ -1083,6 +1675,36 @@ void GFX_Start() { sdl.active=true; } +/* NOTE: The following appears to do its job on Android only *before* + * a screen rotation occurs. After that, the old dimensions are retrieved. + * For the updated dimensions we should listen to a window resize event. + */ +#if SDL_VERSION_ATLEAST(2,0,0) +void GFX_ObtainDisplayDimensions() { + SDL_Rect displayDimensions; + SDL_GetDisplayBounds(sdl.displayNumber, &displayDimensions); + sdl.desktop.full.width = displayDimensions.w; + sdl.desktop.full.height = displayDimensions.h; + +} + +/* Manually update display dimensions in case of a window resize, + * IF there is the need for that ("yes" on Android, "no" otherwise). + * Used for the mapper UI on Android. + * Reason is the usage of GFX_GetSDLSurfaceSubwindowDims, as well as a + * mere notification of the fact that the window's dimensions are modified. + */ +void GFX_UpdateDisplayDimensions(int width, int height) { + if (sdl.desktop.full.display_res && sdl.desktop.fullscreen) { + /* Note: We should not use GFX_ObtainDisplayDimensions + (SDL_GetDisplayBounds) on Android after a screen rotation: + The older values from application startup are returned. */ + sdl.desktop.full.width = width; + sdl.desktop.full.height = height; + } +} +#endif + static void GUI_ShutDown(Section * /*sec*/) { GFX_Stop(); if (sdl.draw.callback) (sdl.draw.callback)( GFX_CallBackStop ); @@ -1173,13 +1795,21 @@ static void GUI_StartUp(Section * sec) { Section_prop * section=static_cast<Section_prop *>(sec); sdl.active=false; sdl.updating=false; +#if SDL_VERSION_ATLEAST(2,0,0) + sdl.resizing_window=false; + sdl.update_display_contents=true; +#endif GFX_SetIcon(); sdl.desktop.lazy_fullscreen=false; sdl.desktop.lazy_fullscreen_req=false; +#ifdef __ANDROID__ + sdl.desktop.fullscreen=true; +#else sdl.desktop.fullscreen=section->Get_bool("fullscreen"); +#endif sdl.wait_on_error=section->Get_bool("waitonerror"); Prop_multival* p=section->Get_multival("priority"); @@ -1208,10 +1838,18 @@ static void GUI_StartUp(Section * sec) { sdl.mouse.locked=false; mouselocked=false; //Global for mapper sdl.mouse.requestlock=false; +#ifdef __ANDROID__ + sdl.mouse.isLeftMouseFingerUsed=sdl.mouse.isRightMouseFingerUsed=false; + sdl.mouse.isMiddleMouseFingerUsed=sdl.mouse.isEscKeyFingerUsed=false; + /* We force fullscreen desktop resolution */ + sdl.desktop.full.fixed=true; +#else sdl.desktop.full.fixed=false; const char* fullresolution=section->Get_string("fullresolution"); +#endif sdl.desktop.full.width = 0; sdl.desktop.full.height = 0; +#ifndef __ANDROID__ if(fullresolution && *fullresolution) { char res[100]; safe_strncpy( res, fullresolution, sizeof( res )); @@ -1228,9 +1866,10 @@ static void GUI_StartUp(Section * sec) { } } } - +#endif sdl.desktop.window.width = 0; sdl.desktop.window.height = 0; +#ifndef __ANDROID__ const char* windowresolution=section->Get_string("windowresolution"); if(windowresolution && *windowresolution) { char res[100]; @@ -1245,7 +1884,24 @@ static void GUI_StartUp(Section * sec) { } } } - sdl.desktop.doublebuf=section->Get_bool("fulldouble"); +#endif + + sdl.desktop.vsync=section->Get_bool("vsync"); + +#if SDL_VERSION_ATLEAST(2,0,0) + + sdl.displayNumber=section->Get_int("display"); + if ((sdl.displayNumber < 0) || (sdl.displayNumber >= SDL_GetNumVideoDisplays())) { + sdl.displayNumber = 0; + LOG_MSG("SDL:Display number out of bounds, switching back to 0"); + } + sdl.desktop.full.display_res = sdl.desktop.full.fixed && (!sdl.desktop.full.width || !sdl.desktop.full.height); + if (sdl.desktop.full.display_res) { + GFX_ObtainDisplayDimensions(); + } + +#else // !SDL_VERSION_ATLEAST(2,0,0) + #if SDL_VERSION_ATLEAST(1, 2, 10) #ifdef WIN32 const SDL_VideoInfo* vidinfo = SDL_GetVideoInfo(); @@ -1286,6 +1942,8 @@ static void GUI_StartUp(Section * sec) { sdl.desktop.full.height=768; #endif } +#endif // !SDL_VERSION_ATLEAST(2,0,0) + sdl.mouse.autoenable=section->Get_bool("autolock"); if (!sdl.mouse.autoenable) SDL_ShowCursor(SDL_DISABLE); sdl.mouse.autolock=false; @@ -1297,12 +1955,22 @@ static void GUI_StartUp(Section * sec) { if (output == "surface") { sdl.desktop.want_type=SCREEN_SURFACE; +#if SDL_VERSION_ATLEAST(2,0,0) + } else if (output == "texture") { + sdl.desktop.want_type=SCREEN_TEXTURE; + SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear"); + } else if (output == "texturenb") { + sdl.desktop.want_type=SCREEN_TEXTURE; + // Currently the default, but... oh well + SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "nearest"); +#else // !SDL_VERSION_ATLEAST(2,0,0) #if C_DDRAW } else if (output == "ddraw") { sdl.desktop.want_type=SCREEN_SURFACE_DDRAW; #endif } else if (output == "overlay") { sdl.desktop.want_type=SCREEN_OVERLAY; +#endif // !SDL_VERSION_ATLEAST(2,0,0) #if C_OPENGL } else if (output == "opengl") { sdl.desktop.want_type=SCREEN_OPENGL; @@ -1316,19 +1984,56 @@ static void GUI_StartUp(Section * sec) { sdl.desktop.want_type=SCREEN_SURFACE;//SHOULDN'T BE POSSIBLE anymore } +#if SDL_VERSION_ATLEAST(2,0,0) + sdl.texture.texture=0; + sdl.texture.pixelFormat=0; + sdl.window=0; + sdl.renderer=0; + sdl.rendererDriver = section->Get_string("renderer"); +#else sdl.overlay=0; +#endif + +#if (defined C_OPENGL) && (defined __ANDROID__) // OpenGL ES + static const GLfloat vertCoords[] = { + -1, -1, // lower left + 1, -1, // lower right + 1, 1, // upper right + -1, 1 // upper left + }; + memcpy(sdl.opengl.vertCoords, vertCoords, sizeof(vertCoords)); +#endif + #if C_OPENGL if(sdl.desktop.want_type==SCREEN_OPENGL){ /* OPENGL is requested */ +#if SDL_VERSION_ATLEAST(2,0,0) + if (!GFX_SetSDLOpenGLWindow(640,400)) { + LOG_MSG("Could not create OpenGL window, switching back to surface"); + sdl.desktop.want_type=SCREEN_SURFACE; + } else { + sdl.opengl.context = SDL_GL_CreateContext(sdl.window); + if (sdl.opengl.context == 0) { + LOG_MSG("Could not create OpenGL context, switching back to surface"); + sdl.desktop.want_type=SCREEN_SURFACE; + } + } + if (sdl.desktop.want_type==SCREEN_OPENGL) { +#else // Same story but for SDL 1.2 sdl.surface=SDL_SetVideoMode_Wrap(640,400,0,SDL_OPENGL); if (sdl.surface == NULL) { LOG_MSG("Could not initialize OpenGL, switching back to surface"); sdl.desktop.want_type=SCREEN_SURFACE; } else { +#endif // End of SDL specific video mode check. Let's assume it has passed... sdl.opengl.buffer=0; sdl.opengl.framebuf=0; sdl.opengl.texture=0; +#ifndef __ANDROID__ sdl.opengl.displaylist=0; +#endif + glGetIntegerv (GL_MAX_TEXTURE_SIZE, &sdl.opengl.max_texsize); +#ifndef __ANDROID__ glGenBuffersARB = (PFNGLGENBUFFERSARBPROC)SDL_GL_GetProcAddress("glGenBuffersARB"); glBindBufferARB = (PFNGLBINDBUFFERARBPROC)SDL_GL_GetProcAddress("glBindBufferARB"); glDeleteBuffersARB = (PFNGLDELETEBUFFERSARBPROC)SDL_GL_GetProcAddress("glDeleteBuffersARB"); @@ -1345,19 +2050,38 @@ static void GUI_StartUp(Section * sec) { } else { sdl.opengl.packed_pixel=sdl.opengl.paletted_texture=false; } +#endif // ifndef __ANDROID__ } } /* OPENGL is requested end */ #endif //OPENGL /* Initialize screen for first time */ +#if SDL_VERSION_ATLEAST(2,0,0) + if (!GFX_SetSDLSurfaceWindow(640,400)) + E_Exit("Could not initialize video: %s",SDL_GetError()); + sdl.surface = SDL_GetWindowSurface(sdl.window); + if (sdl.surface == NULL) E_Exit("Could not retrieve window surface: %s",SDL_GetError()); + SDL_Rect splash_rect=GFX_GetSDLSurfaceSubwindowDims(640,400); + sdl.desktop.sdl2pixelFormat = sdl.surface->format->format; + LOG_MSG("SDL:Current window pixel format: %s", SDL_GetPixelFormatName(sdl.desktop.sdl2pixelFormat)); + /* Do NOT use SDL_BITSPERPIXEL here - It returns 24 for + SDL_PIXELFORMAT_RGB888, while SDL_BYTESPERPIXEL returns 4. + To compare, with SDL 1.2 the detected desktop color depth is 32 bpp. */ + sdl.desktop.bpp=8*SDL_BYTESPERPIXEL(sdl.desktop.sdl2pixelFormat); +#else sdl.surface=SDL_SetVideoMode_Wrap(640,400,0,0); if (sdl.surface == NULL) E_Exit("Could not initialize video: %s",SDL_GetError()); sdl.desktop.bpp=sdl.surface->format->BitsPerPixel; +#endif if (sdl.desktop.bpp==24) { LOG_MSG("SDL: You are running in 24 bpp mode, this will slow down things!"); } GFX_Stop(); +#if SDL_VERSION_ATLEAST(2,0,0) + SDL_SetWindowTitle(sdl.window,"DOSBox"); +#else SDL_WM_SetCaption("DOSBox",VERSION); +#endif /* The endian part is intentionally disabled as somehow it produces correct results without according to rhoenie*/ //#if SDL_BYTEORDER == SDL_BIG_ENDIAN @@ -1373,6 +2097,9 @@ static void GUI_StartUp(Section * sec) { /* Please leave the Splash screen stuff in working order in DOSBox. We spend a lot of time making DOSBox. */ SDL_Surface* splash_surf = SDL_CreateRGBSurface(SDL_SWSURFACE, 640, 400, 32, rmask, gmask, bmask, 0); if (splash_surf) { +#if SDL_VERSION_ATLEAST(2,0,0) + SDL_SetSurfaceBlendMode(splash_surf, SDL_BLENDMODE_BLEND); +#endif SDL_FillRect(splash_surf, NULL, SDL_MapRGB(splash_surf->format, 0, 0, 0)); Bit8u* tmpbufp = new Bit8u[640*400*3]; @@ -1408,23 +2135,51 @@ static void GUI_StartUp(Section * sec) { if (ct<1) { SDL_FillRect(sdl.surface, NULL, SDL_MapRGB(sdl.surface->format, 0, 0, 0)); +#if SDL_VERSION_ATLEAST(2,0,0) + SDL_SetSurfaceAlphaMod(splash_surf, 255); + SDL_BlitScaled(splash_surf, NULL, sdl.surface, &splash_rect); +#else SDL_SetAlpha(splash_surf, SDL_SRCALPHA,255); SDL_BlitSurface(splash_surf, NULL, sdl.surface, NULL); +#endif +#if SDL_VERSION_ATLEAST(2,0,0) + SDL_UpdateWindowSurface(sdl.window); +#else SDL_Flip(sdl.surface); +#endif } else if (ct>=max_splash_loop-splash_fade) { if (use_fadeout) { SDL_FillRect(sdl.surface, NULL, SDL_MapRGB(sdl.surface->format, 0, 0, 0)); +#if SDL_VERSION_ATLEAST(2,0,0) + SDL_SetSurfaceAlphaMod(splash_surf, (Bit8u)((max_splash_loop-1-ct)*255/(splash_fade-1))); + SDL_BlitScaled(splash_surf, NULL, sdl.surface, &splash_rect); +#else SDL_SetAlpha(splash_surf, SDL_SRCALPHA, (Bit8u)((max_splash_loop-1-ct)*255/(splash_fade-1))); SDL_BlitSurface(splash_surf, NULL, sdl.surface, NULL); +#endif +#if SDL_VERSION_ATLEAST(2,0,0) + SDL_UpdateWindowSurface(sdl.window); +#else SDL_Flip(sdl.surface); +#endif } + } else { // Fix a possible glitch +#if SDL_VERSION_ATLEAST(2,0,0) + SDL_UpdateWindowSurface(sdl.window); +#else + SDL_Flip(sdl.surface); +#endif } } if (use_fadeout) { SDL_FillRect(sdl.surface, NULL, SDL_MapRGB(sdl.surface->format, 0, 0, 0)); +#if SDL_VERSION_ATLEAST(2,0,0) + SDL_UpdateWindowSurface(sdl.window); +#else SDL_Flip(sdl.surface); +#endif } SDL_FreeSurface(splash_surf); delete [] tmpbufp; @@ -1442,7 +2197,11 @@ static void GUI_StartUp(Section * sec) { MAPPER_AddHandler(&PauseDOSBox, MK_pause, MMOD2, "pause", "Pause DBox"); #endif /* Get Keyboard state of numlock and capslock */ +#if SDL_VERSION_ATLEAST(2,0,0) + SDL_Keymod keystate = SDL_GetModState(); +#else SDLMod keystate = SDL_GetModState(); +#endif if(keystate&KMOD_NUM) startup_state_numlock = true; if(keystate&KMOD_CAPS) startup_state_capslock = true; } @@ -1456,6 +2215,83 @@ void Mouse_AutoLock(bool enable) { } } +#if defined(__ANDROID__) +/* The way mouse emulation is done here is based on the +following (horizontal) partitioning of the touchscreen: + +/-----------------------------------------------------------------------\ +| Left | (H)Escape | Motion | Motion | Middle | Right | +\-----------------------------------------------------------------------/ + +Note that the simulated Escape key is the host one +(not the emulator's), so it can be re-mapped. +Furthermore, that should be considered a hack for now. */ +static void HandleTouchFinger(SDL_TouchFingerEvent * tfinger) { + void MAPPER_CheckEvent(SDL_Event * event); + switch (tfinger->type) { + case SDL_FINGERDOWN: + if (tfinger->x >= 0.83f) { // Right button + sdl.mouse.rightMouseFingerID = tfinger->fingerId; + if (!sdl.mouse.isRightMouseFingerUsed) { + Mouse_ButtonPressed(1); + sdl.mouse.isRightMouseFingerUsed = true; + } + } else if (tfinger->x >= 0.67f) { // Middle button + sdl.mouse.middleMouseFingerID = tfinger->fingerId; + if (!sdl.mouse.isMiddleMouseFingerUsed) { + Mouse_ButtonPressed(2); + sdl.mouse.isMiddleMouseFingerUsed = true; + } + } else if (tfinger->x >= 0.33f) { // Motion + sdl.mouse.mouseMotionFingerID = tfinger->fingerId; + } else if (tfinger->x >= 0.17f) { // (Host) Escape key + sdl.mouse.escKeyFingerID = tfinger->fingerId; + if (!sdl.mouse.isEscKeyFingerUsed) { + SDL_Event event; + event.type = SDL_KEYDOWN; + event.key.keysym.scancode = SDL_SCANCODE_ESCAPE; + MAPPER_CheckEvent(&event); + sdl.mouse.isEscKeyFingerUsed = true; + } + } else { // Left button + sdl.mouse.leftMouseFingerID = tfinger->fingerId; + if (!sdl.mouse.isLeftMouseFingerUsed) { + Mouse_ButtonPressed(0); + sdl.mouse.isLeftMouseFingerUsed = true; + } + } + break; + case SDL_FINGERUP: + if ((sdl.mouse.leftMouseFingerID == tfinger->fingerId) && sdl.mouse.isLeftMouseFingerUsed) { + Mouse_ButtonReleased(0); + sdl.mouse.isLeftMouseFingerUsed = false; + } else if ((sdl.mouse.rightMouseFingerID == tfinger->fingerId) && sdl.mouse.isRightMouseFingerUsed) { + Mouse_ButtonReleased(1); + sdl.mouse.isRightMouseFingerUsed = false; + } else if ((sdl.mouse.middleMouseFingerID == tfinger->fingerId) && sdl.mouse.isMiddleMouseFingerUsed) { + Mouse_ButtonReleased(2); + sdl.mouse.isMiddleMouseFingerUsed = false; + } else if ((sdl.mouse.escKeyFingerID == tfinger->fingerId) && sdl.mouse.isEscKeyFingerUsed) { + SDL_Event event; + event.type = SDL_KEYUP; + event.key.keysym.scancode = SDL_SCANCODE_ESCAPE; + MAPPER_CheckEvent(&event); + sdl.mouse.isEscKeyFingerUsed = false; + } + break; + case SDL_FINGERMOTION: + /* We basically IGNORE the absolute coordinates, since + we emulate relative mouse movement all the time. */ + if (sdl.mouse.mouseMotionFingerID == tfinger->fingerId) { + Mouse_CursorMoved((float)tfinger->dx*sdl.clip.w*sdl.mouse.sensitivity/100.0f, + (float)tfinger->dy*sdl.clip.h*sdl.mouse.sensitivity/100.0f, + 0, 0, true); + } + break; + } +} + +#else // Not on Android static void HandleMouseMotion(SDL_MouseMotionEvent * motion) { if (sdl.mouse.locked || !sdl.mouse.autoenable) Mouse_CursorMoved((float)motion->xrel*sdl.mouse.sensitivity/100.0f, @@ -1504,6 +2340,7 @@ static void HandleMouseButton(SDL_MouseButtonEvent * button) { break; } } +#endif // End of touch/mouse differentiation void GFX_LosingFocus(void) { sdl.laltstate=SDL_KEYUP; @@ -1522,12 +2359,40 @@ bool GFX_IsFullscreen(void) { #define DB_POLLSKIP 1 #endif -#if defined(LINUX) +#if (defined(LINUX) && !SDL_VERSION_ATLEAST(2,0,0)) #define SDL_XORG_FIX 1 #else #define SDL_XORG_FIX 0 #endif +#if SDL_VERSION_ATLEAST(2,0,0) +void GFX_HandleVideoResize(int width, int height) { + /* Maybe a screen rotation has just occurred, so we simply resize. + There may be a different cause for a forced resized, though. */ + if (sdl.desktop.full.display_res && sdl.desktop.fullscreen) { + /* Note: We should not use GFX_ObtainDisplayDimensions + (SDL_GetDisplayBounds) on Android after a screen rotation: + The older values from application startup are returned. */ + sdl.desktop.full.width = width; + sdl.desktop.full.height = height; + } + /* Even if the new window's dimensions are actually the desired ones + * we may still need to re-obtain a new window surface or do + * a different thing. So we basically reset the screen, but without + * touching the window itself (or else we may end in an infinite loop). + * + * Furthermore, if the new dimensions are *not* the desired ones, we + * don't fight it. Rather than attempting to resize it back, we simply + * keep the window as-is and disable screen updates. This is done + * in SDL_SetSDLWindowSurface by setting sdl.update_display_contents + * to false. + */ + sdl.resizing_window = true; + GFX_ResetScreen(); + sdl.resizing_window = false; +} +#endif + void GFX_Events() { //Don't poll too often. This can be heavy on the OS, especially Macs. //In idle mode 3000-4000 polls are done per second without this check. @@ -1565,6 +2430,98 @@ void GFX_Events() { #endif switch (event.type) { +#if SDL_VERSION_ATLEAST(2,0,0) + case SDL_WINDOWEVENT: + switch (event.window.event) { + case SDL_WINDOWEVENT_RESTORED: + /* We may need to re-create a texture + * and more on Android. Another case: + * Update surface while using X11. + */ + GFX_ResetScreen(); + continue; + case SDL_WINDOWEVENT_RESIZED: + GFX_HandleVideoResize(event.window.data1, event.window.data2); + continue; + case SDL_WINDOWEVENT_EXPOSED: + if (sdl.draw.callback) sdl.draw.callback( GFX_CallBackRedraw ); + continue; + case SDL_WINDOWEVENT_FOCUS_GAINED: + if (sdl.desktop.fullscreen && !sdl.mouse.locked) + GFX_CaptureMouse(); + SetPriority(sdl.priority.focus); + CPU_Disable_SkipAutoAdjust(); + break; + case SDL_WINDOWEVENT_FOCUS_LOST: + if (sdl.mouse.locked) { +#ifdef WIN32 + if (sdl.desktop.fullscreen) { + VGA_KillDrawing(); + GFX_ForceFullscreenExit(); + } +#endif + GFX_CaptureMouse(); + } + SetPriority(sdl.priority.nofocus); + GFX_LosingFocus(); + CPU_Enable_SkipAutoAdjust(); + break; + default: ; + } + + /* Non-focus priority is set to pause; check to see if we've lost window or input focus + * i.e. has the window been minimised or made inactive? + */ + if (sdl.priority.nofocus == PRIORITY_LEVEL_PAUSE) { + if ((event.window.event == SDL_WINDOWEVENT_FOCUS_LOST) || (event.window.event == SDL_WINDOWEVENT_MINIMIZED)) { + /* Window has lost focus, pause the emulator. + * This is similar to what PauseDOSBox() does, but the exit criteria is different. + * Instead of waiting for the user to hit Alt-Break, we wait for the window to + * regain window or input focus. + */ + bool paused = true; + SDL_Event ev; + + GFX_SetTitle(-1,-1,true); + KEYBOARD_ClrBuffer(); +// SDL_Delay(500); +// while (SDL_PollEvent(&ev)) { + // flush event queue. +// } + + while (paused) { + // WaitEvent waits for an event rather than polling, so CPU usage drops to zero + SDL_WaitEvent(&ev); + + switch (ev.type) { + case SDL_QUIT: throw(0); break; // a bit redundant at linux at least as the active events gets before the quit event. + case SDL_WINDOWEVENT: // wait until we get window focus back + if ((ev.window.event == SDL_WINDOWEVENT_FOCUS_LOST) || (ev.window.event == SDL_WINDOWEVENT_MINIMIZED) || (ev.window.event == SDL_WINDOWEVENT_FOCUS_GAINED) || (ev.window.event == SDL_WINDOWEVENT_RESTORED) || (ev.window.event == SDL_WINDOWEVENT_EXPOSED)) { + // We've got focus back, so unpause and break out of the loop + if ((ev.window.event == SDL_WINDOWEVENT_FOCUS_GAINED) || (ev.window.event == SDL_WINDOWEVENT_RESTORED) || (ev.window.event == SDL_WINDOWEVENT_EXPOSED)) { + paused = false; + GFX_SetTitle(-1,-1,false); + } + + /* Now poke a "release ALT" command into the keyboard buffer + * we have to do this, otherwise ALT will 'stick' and cause + * problems with the app running in the DOSBox. + */ + KEYBOARD_AddKey(KBD_leftalt, false); + KEYBOARD_AddKey(KBD_rightalt, false); + if (ev.window.event == SDL_WINDOWEVENT_RESTORED) { + // We may need to re-create a texture and more + GFX_ResetScreen(); + } + } + break; + } + } + } + } + break; +#endif // SDL_VERSION_ATLEAST(2,0,0) +#if !SDL_VERSION_ATLEAST(2,0,0) case SDL_ACTIVEEVENT: if (event.active.state & SDL_APPINPUTFOCUS) { if (event.active.gain) { @@ -1638,6 +2595,14 @@ void GFX_Events() { } } break; +#endif // !SDL_VERSION_ATLEAST(2,0,0) +#if defined(__ANDROID__) + case SDL_FINGERDOWN: + case SDL_FINGERUP: + case SDL_FINGERMOTION: + HandleTouchFinger(&event.tfinger); + break; +#else case SDL_MOUSEMOTION: HandleMouseMotion(&event.motion); break; @@ -1645,21 +2610,26 @@ void GFX_Events() { case SDL_MOUSEBUTTONUP: HandleMouseButton(&event.button); break; +#endif +#if !SDL_VERSION_ATLEAST(2,0,0) case SDL_VIDEORESIZE: -// HandleVideoResize(&event.resize); + //HandleVideoResize(event.resize.w, event.resize.h); break; +#endif case SDL_QUIT: throw(0); break; +#if !SDL_VERSION_ATLEAST(2,0,0) case SDL_VIDEOEXPOSE: if (sdl.draw.callback) sdl.draw.callback( GFX_CallBackRedraw ); break; +#endif #ifdef WIN32 case SDL_KEYDOWN: case SDL_KEYUP: // ignore event alt+tab - if (event.key.keysym.sym==SDLK_LALT) sdl.laltstate = event.key.type; - if (event.key.keysym.sym==SDLK_RALT) sdl.raltstate = event.key.type; + if (event.key.keysym.sym==SDLK_LALT) sdl.laltstate = (SDL_EventType)event.key.type; + if (event.key.keysym.sym==SDLK_RALT) sdl.raltstate = (SDL_EventType)event.key.type; if (((event.key.keysym.sym==SDLK_TAB)) && ((sdl.laltstate==SDL_KEYDOWN) || (sdl.raltstate==SDL_KEYDOWN))) break; // This can happen as well. @@ -1671,7 +2641,15 @@ void GFX_Events() { case SDL_KEYDOWN: case SDL_KEYUP: /* On macs CMD-Q is the default key to close an application */ - if (event.key.keysym.sym == SDLK_q && (event.key.keysym.mod == KMOD_RMETA || event.key.keysym.mod == KMOD_LMETA) ) { + if (event.key.keysym.sym == SDLK_q && +#if SDL_VERSION_ATLEAST(2,0,0) + (event.key.keysym.mod == KMOD_RGUI || + event.key.keysym.mod == KMOD_LGUI) +#else + (event.key.keysym.mod == KMOD_RMETA || + event.key.keysym.mod == KMOD_LMETA) +#endif + ) { KillSwitch(true); break; } @@ -1710,7 +2688,11 @@ void GFX_ShowMsg(char const* format,...) { vsprintf(buf,format,msg); strcat(buf,"\n"); va_end(msg); +#ifdef __ANDROID__ + __android_log_print(ANDROID_LOG_DEBUG, "DOSBox", buf); +#else if(!no_stdout) printf("%s",buf); //Else buf is parsed again. +#endif } @@ -1722,13 +2704,17 @@ void Config_Add_SDL() { Prop_int* Pint; Prop_multival* Pmulti; +#ifndef __ANDROID__ Pbool = sdl_sec->Add_bool("fullscreen",Property::Changeable::Always,false); Pbool->Set_help("Start dosbox directly in fullscreen. (Press ALT-Enter to go back)"); +#endif - Pbool = sdl_sec->Add_bool("fulldouble",Property::Changeable::Always,false); - Pbool->Set_help("Use double buffering in fullscreen. It can reduce screen flickering, but it can also result in a slow DOSBox."); + Pbool = sdl_sec->Add_bool("vsync",Property::Changeable::Always,false); + Pbool->Set_help("Sync to Vblank IF supported by the output device and renderer.\n" + "It can reduce screen flickering, but it can also result in a slow DOSBox."); - Pstring = sdl_sec->Add_string("fullresolution",Property::Changeable::Always,"original"); +#ifndef __ANDROID__ + Pstring = sdl_sec->Add_string("fullresolution",Property::Changeable::Always,"0x0"); Pstring->Set_help("What resolution to use for fullscreen: original, desktop or a fixed size (e.g. 1024x768).\n" "Using your monitor's native resolution with aspect=true might give the best results.\n" "If you end up with small window on a large screen, try an output different from surface." @@ -1738,20 +2724,53 @@ void Config_Add_SDL() { Pstring = sdl_sec->Add_string("windowresolution",Property::Changeable::Always,"original"); Pstring->Set_help("Scale the window to this size IF the output device supports hardware scaling.\n" "(output=surface does not!)"); +#endif const char* outputs[] = { - "surface", "overlay", -#if C_OPENGL - "opengl", "openglnb", -#endif + "surface", +#if SDL_VERSION_ATLEAST(2, 0, 0) + "texture", + "texturenb", +#else // !SDL_VERSION_ATLEAST(2, 0, 0) + "overlay", #if C_DDRAW "ddraw", #endif +#endif // !SDL_VERSION_ATLEAST(2, 0, 0) +#if C_OPENGL + "opengl", "openglnb", +#endif 0 }; +#if SDL_VERSION_ATLEAST(2, 0, 0) + Pstring = sdl_sec->Add_string("output",Property::Changeable::Always,"texture"); +#else Pstring = sdl_sec->Add_string("output",Property::Changeable::Always,"surface"); +#endif Pstring->Set_help("What video system to use for output."); Pstring->Set_values(outputs); +#if SDL_VERSION_ATLEAST(2,0,0) + const char* renderers[] = { + "auto", +#ifdef WIN32 + "direct3d", +#endif +#ifdef __ANDROID__ + "opengles2", + "opengles", +#else // On any platform other than Android + "opengl", +#endif +#ifdef MACOSX + "metal", +#endif + "software", + 0 }; + Pstring = sdl_sec->Add_string("renderer",Property::Changeable::Always,"auto"); + Pstring->Set_help("Choose a renderer driver if output=texture or output=texturenb. Use output=auto for an automatic choice."); + Pstring->Set_values(renderers); +#endif // SDL_VERSION_ATLEAST(2,0,0) + Pbool = sdl_sec->Add_bool("autolock",Property::Changeable::Always,true); Pbool->Set_help("Mouse will automatically lock, if you click on the screen. (Press CTRL-F10 to unlock)"); @@ -1778,8 +2797,10 @@ void Config_Add_SDL() { Pstring = sdl_sec->Add_path("mapperfile",Property::Changeable::Always,MAPPERFILE); Pstring->Set_help("File used to load/save the key/event mappings from. Resetmapper only works with the default value."); +#if !SDL_VERSION_ATLEAST(2,0,0) Pbool = sdl_sec->Add_bool("usescancodes",Property::Changeable::Always,true); Pbool->Set_help("Avoid usage of symkeys, might not work on all operating systems."); +#endif } static void show_warning(char const * const message) { @@ -1791,8 +2812,15 @@ static void show_warning(char const * const message) { #endif printf("%s",message); if(textonly) return; +#if SDL_VERSION_ATLEAST(2, 0, 0) + if (!sdl.window) + if (!GFX_SetSDLSurfaceWindow(640,400)) return; + sdl.surface = SDL_GetWindowSurface(sdl.window); +#else if(!sdl.surface) sdl.surface = SDL_SetVideoMode_Wrap(640,400,0,0); +#endif if(!sdl.surface) return; + #if SDL_BYTEORDER == SDL_BIG_ENDIAN Bit32u rmask = 0xff000000; Bit32u gmask = 0x00ff0000; @@ -1820,7 +2848,11 @@ static void show_warning(char const * const message) { } SDL_BlitSurface(splash_surf, NULL, sdl.surface, NULL); +#if SDL_VERSION_ATLEAST(2, 0, 0) + SDL_UpdateWindowSurface(sdl.window); +#else SDL_Flip(sdl.surface); +#endif SDL_Delay(12000); } @@ -1850,6 +2882,8 @@ static void launcheditor() { extern void DEBUG_ShutDown(Section * /*sec*/); #endif +void MIXER_CloseAudioDevice(void); + void restart_program(std::vector<std::string> & parameters) { char** newargs = new char* [parameters.size()+1]; // parameter 0 is the executable path @@ -1857,9 +2891,9 @@ void restart_program(std::vector<std::string> & parameters) { // last one is NULL for(Bitu i = 0; i < parameters.size(); i++) newargs[i]=(char*)parameters[i].c_str(); newargs[parameters.size()] = NULL; - SDL_CloseAudio(); + MIXER_CloseAudioDevice(); SDL_Delay(50); - SDL_Quit(); + SDL_Quit_Wrapper(); #if C_DEBUG // shutdown curses DEBUG_ShutDown(NULL); @@ -2043,14 +3077,16 @@ int main(int argc, char* argv[]) { */ putenv(const_cast<char*>("SDL_DISABLE_LOCK_KEYS=1")); #endif - // Don't init timers, GetTicks seems to work fine and they can use a fair amount of power (Macs again) - // Please report problems with audio and other things. - if ( SDL_Init( SDL_INIT_AUDIO|SDL_INIT_VIDEO | /*SDL_INIT_TIMER |*/ SDL_INIT_CDROM - |SDL_INIT_NOPARACHUTE - ) < 0 ) E_Exit("Can't init SDL %s",SDL_GetError()); + if ( SDL_Init_Wrapper() < 0 ) + E_Exit("Can't init SDL %s",SDL_GetError()); sdl.inited = true; #ifndef DISABLE_JOYSTICK +#ifdef __ANDROID__ + // Disable accelerometer-as-joystick emulation + // (available for backwards compatibility) + SDL_SetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, "0"); +#endif //Initialise Joystick separately. This way we can warn when it fails instead //of exiting the application if( SDL_InitSubSystem(SDL_INIT_JOYSTICK) < 0 ) LOG_MSG("Failed to init joystick support"); @@ -2059,6 +3095,7 @@ int main(int argc, char* argv[]) { sdl.laltstate = SDL_KEYUP; sdl.raltstate = SDL_KEYUP; +#if !SDL_VERSION_ATLEAST(2, 0, 0) // Not relevant for SDL 2.0 at the moment #if defined (WIN32) #if SDL_VERSION_ATLEAST(1, 2, 10) sdl.using_windib=true; @@ -2087,7 +3124,8 @@ int main(int argc, char* argv[]) { if (SDL_VideoDriverName(sdl_drv_name,128)!=NULL) { if (strcmp(sdl_drv_name,"windib")==0) LOG_MSG("SDL_Init: Starting up with SDL windib video driver.\n Try to update your video card and directx drivers!"); } -#endif +#endif // WIN32 +#endif // !SDL_VERSION_ATLEAST(2, 0, 0) sdl.num_joysticks=SDL_NumJoysticks(); /* Parse configuration files */ @@ -2200,10 +3238,17 @@ int main(int argc, char* argv[]) { sticky_keys(true); //Might not be needed if the shutdown function switches to windowed mode, but it doesn't hurt #endif //Force visible mouse to end user. Somehow this sometimes doesn't happen +#if SDL_VERSION_ATLEAST(2,0,0) + SDL_SetRelativeMouseMode(SDL_FALSE); +#else SDL_WM_GrabInput(SDL_GRAB_OFF); +#endif SDL_ShowCursor(SDL_ENABLE); - SDL_Quit();//Let's hope sdl will quit as well when it catches an exception + SDL_Quit_Wrapper();//Let's hope sdl will quit as well when it catches an exception +#ifdef __ANDROID__ + exit(0); // Actually quits application... and hopefully(?) removes static values +#endif return 0; } diff --git a/src/hardware/mixer.cpp b/src/hardware/mixer.cpp index 1fbe4443f..ff40e1c94 100644 --- a/src/hardware/mixer.cpp +++ b/src/hardware/mixer.cpp @@ -86,6 +86,10 @@ static struct { bool nosound; Bit32u freq; Bit32u blocksize; +#if SDL_VERSION_ATLEAST(2,0,0) + //Note: As stated earlier, all sdl code shall rather be in sdlmain + SDL_AudioDeviceID sdldevice; +#endif } mixer; Bit8u MixTemp[MIXER_BUFSIZE]; @@ -127,6 +131,22 @@ void MIXER_DelChannel(MixerChannel* delchan) { } } +static void MIXER_LockAudioDevice(void) { +#if SDL_VERSION_ATLEAST(2,0,0) + SDL_LockAudioDevice(mixer.sdldevice); +#else + SDL_LockAudio(); +#endif +} + +static void MIXER_UnlockAudioDevice(void) { +#if SDL_VERSION_ATLEAST(2,0,0) + SDL_UnlockAudioDevice(mixer.sdldevice); +#else + SDL_UnlockAudio(); +#endif +} + void MixerChannel::UpdateVolume(void) { volmul[0]=(Bits)((1 << MIXER_VOLSHIFT)*scale*volmain[0]*mixer.mastervol[0]); volmul[1]=(Bits)((1 << MIXER_VOLSHIFT)*scale*volmain[1]*mixer.mastervol[1]); @@ -148,9 +168,9 @@ void MixerChannel::Enable(bool _yesno) { enabled=_yesno; if (enabled) { freq_counter = 0; - SDL_LockAudio(); + MIXER_LockAudioDevice(); if (done<mixer.done) done=mixer.done; - SDL_UnlockAudio(); + MIXER_UnlockAudioDevice(); } } @@ -382,14 +402,14 @@ void MixerChannel::AddSamples_s32_nonnative(Bitu len,const Bit32s * data) { } void MixerChannel::FillUp(void) { - SDL_LockAudio(); + MIXER_LockAudioDevice(); if (!enabled || done<mixer.done) { - SDL_UnlockAudio(); + MIXER_UnlockAudioDevice(); return; } float index=PIC_TickIndex(); Mix((Bitu)(index*mixer.needed)); - SDL_UnlockAudio(); + MIXER_UnlockAudioDevice(); } extern bool ticksLocked; @@ -439,12 +459,12 @@ static void MIXER_MixData(Bitu needed) { } static void MIXER_Mix(void) { - SDL_LockAudio(); + MIXER_LockAudioDevice(); MIXER_MixData(mixer.needed); mixer.tick_counter += mixer.tick_add; mixer.needed+=(mixer.tick_counter >> TICK_SHIFT); mixer.tick_counter &= TICK_MASK; - SDL_UnlockAudio(); + MIXER_UnlockAudioDevice(); } static void MIXER_Mix_NoSound(void) { @@ -468,6 +488,9 @@ static void MIXER_Mix_NoSound(void) { } static void SDLCALL MIXER_CallBack(void * userdata, Uint8 *stream, int len) { +#if SDL_VERSION_ATLEAST(2,0,0) + memset(stream, 0, len); +#endif Bitu need=(Bitu)len/MIXER_SSIZE; Bit16s * output=(Bit16s *)stream; Bitu reduce; @@ -696,7 +719,11 @@ void MIXER_Init(Section* sec) { LOG_MSG("MIXER: No Sound Mode Selected."); mixer.tick_add=calc_tickadd(mixer.freq); TIMER_AddTickHandler(MIXER_Mix_NoSound); +#if SDL_VERSION_ATLEAST(2,0,0) + } else if ((mixer.sdldevice = SDL_OpenAudioDevice(NULL, 0, &spec, &obtained, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE)) ==0 ) { +#else } else if (SDL_OpenAudio(&spec, &obtained) <0 ) { +#endif mixer.nosound = true; LOG_MSG("MIXER: Can't open audio: %s , running in nosound mode.",SDL_GetError()); mixer.tick_add=calc_tickadd(mixer.freq); @@ -708,7 +735,11 @@ void MIXER_Init(Section* sec) { mixer.blocksize=obtained.samples; mixer.tick_add=calc_tickadd(mixer.freq); TIMER_AddTickHandler(MIXER_Mix); +#if SDL_VERSION_ATLEAST(2,0,0) + SDL_PauseAudioDevice(mixer.sdldevice, 0); +#else SDL_PauseAudio(0); +#endif } mixer.min_needed=section->Get_int("prebuffer"); if (mixer.min_needed>100) mixer.min_needed=100; @@ -717,3 +748,16 @@ void MIXER_Init(Section* sec) { mixer.needed=mixer.min_needed+1; PROGRAMS_MakeFile("MIXER.COM",MIXER_ProgramStart); } + +void MIXER_CloseAudioDevice(void) { + if (!mixer.nosound) { +#if SDL_VERSION_ATLEAST(2,0,0) + if (mixer.sdldevice != 0) { + SDL_CloseAudioDevice(mixer.sdldevice); + mixer.sdldevice = 0; + } +#else + SDL_CloseAudio(); +#endif + } +} diff --git a/src/misc/cross.cpp b/src/misc/cross.cpp index 021d8f35c..4e3fe89a6 100644 --- a/src/misc/cross.cpp +++ b/src/misc/cross.cpp @@ -30,6 +30,10 @@ #include <shlobj.h> #endif +#if defined(__ANDROID__) +#include "SDL_system.h" // For SDL_AndroidGetExternalStoragePath +#endif + #if defined HAVE_SYS_TYPES_H && defined HAVE_PWD_H #include <sys/types.h> #include <pwd.h> @@ -61,6 +65,10 @@ void Cross::GetPlatformConfigDir(std::string& in) { #elif defined(MACOSX) in = "~/Library/Preferences"; ResolveHomedir(in); +#elif defined(__ANDROID__) + in = SDL_AndroidGetExternalStoragePath(); + //Assume external storage (possibly internal) is available + ResolveHomedir(in); #else in = "~/.dosbox"; ResolveHomedir(in); @@ -88,6 +96,11 @@ void Cross::CreatePlatformConfigDir(std::string& in) { in = "~/Library/Preferences/"; ResolveHomedir(in); //Don't create it. Assume it exists +#elif defined(__ANDROID__) + in = SDL_AndroidGetExternalStoragePath(); + //Assume external storage (possibly internal) is available + ResolveHomedir(in); + mkdir(in.c_str(),0770); #else in = "~/.dosbox"; ResolveHomedir(in); diff --git a/src/sdl_cdrom/Makefile.am b/src/sdl_cdrom/Makefile.am new file mode 100644 index 000000000..2cd2eb3ff --- /dev/null +++ b/src/sdl_cdrom/Makefile.am @@ -0,0 +1,10 @@ +SUBDIRS = macos macosx +AM_CPPFLAGS = -I$(top_srcdir)/include + +noinst_LIBRARIES = libcsdlcdrom.a +EXTRA_DIST = compat_SDL_cdrom.h SDL_syscdrom.h +libcsdlcdrom_a_SOURCES = compat_SDL_cdrom.c SDL_syscdrom_aix.c SDL_syscdrom_beos.cc \ + SDL_syscdrom_bsdi.c SDL_syscdrom_dc.c SDL_syscdrom_dummy.c \ + SDL_syscdrom_freebsd.c SDL_syscdrom_linux.c SDL_syscdrom_mint.c \ + SDL_syscdrom_openbsd.c SDL_syscdrom_os2.c SDL_syscdrom_osf.c \ + SDL_syscdrom_qnx.c SDL_syscdrom_win32.c diff --git a/src/sdl_cdrom/SDL_syscdrom.h b/src/sdl_cdrom/SDL_syscdrom.h new file mode 100644 index 000000000..0feeee5b4 --- /dev/null +++ b/src/sdl_cdrom/SDL_syscdrom.h @@ -0,0 +1,76 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2012 Sam Lantinga + + This library is SDL_free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "SDL_config.h" + +/* This is the system specific header for the SDL CD-ROM API */ + +/* Structure of CD audio control functions */ +extern struct CDcaps { + /* Get the name of the specified drive */ + const char *(*Name)(int drive); + + /* Open the specified drive, returning a drive id, or -1 on error */ + int (*Open)(int drive); + + /* Get table-of-contents (number of tracks + track info) for disk. + The TOC information should be stored in the cdrom structure. + This function should return 0 on success, or -1 on error. + */ + int (*GetTOC)(SDL_CD *cdrom); + + /* Return the current status and play position, in frames, of the + drive. 'position' may be NULL, and if so, should be ignored. + */ + CDstatus (*Status)(SDL_CD *cdrom, int *position); + + /* Play from frame 'start' to 'start+len' */ + int (*Play)(SDL_CD *cdrom, int start, int len); + + /* Pause play */ + int (*Pause)(SDL_CD *cdrom); + + /* Resume play */ + int (*Resume)(SDL_CD *cdrom); + + /* Stop play */ + int (*Stop)(SDL_CD *cdrom); + + /* Eject the current disk */ + int (*Eject)(SDL_CD *cdrom); + + /* Close the specified drive */ + void (*Close)(SDL_CD *cdrom); +} SDL_CDcaps; + +/* The number of available CD-ROM drives on the system */ +extern int SDL_numcds; + +/* Function to scan the system for CD-ROM drives and fill SDL_CDcaps. + * This function should set SDL_numcds to the number of available CD + * drives. Drive 0 should be the system default CD-ROM. + * It should return 0, or -1 on an unrecoverable fatal error. +*/ +extern int SDL_SYS_CDInit(void); + +/* Function to perform any system-specific CD-ROM related cleanup */ +extern void SDL_SYS_CDQuit(void); + diff --git a/src/sdl_cdrom/SDL_syscdrom_aix.c b/src/sdl_cdrom/SDL_syscdrom_aix.c new file mode 100644 index 000000000..70d2bbef1 --- /dev/null +++ b/src/sdl_cdrom/SDL_syscdrom_aix.c @@ -0,0 +1,665 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2012 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Carsten Griwodz + griff@kom.tu-darmstadt.de + + based on linux/SDL_syscdrom.c by Sam Lantinga +*/ +#include "config.h" +#define COMPAT_SDL_CDROM_PLATFORM_AIX 1 + +#if (C_COMPAT_SDL_CDROM_PLATFORM == COMPAT_SDL_CDROM_PLATFORM_AIX) + +#include "SDL_config.h" + +/*#ifdef SDL_CDROM_AIX*/ + +/* Functions for system-level CD-ROM audio control */ + +/*#define DEBUG_CDROM 1*/ + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <unistd.h> + +#include <sys/ioctl.h> +#include <sys/devinfo.h> +#include <sys/mntctl.h> +#include <sys/statfs.h> +#include <sys/vmount.h> +#include <fstab.h> +#include <sys/scdisk.h> + +#include "compat_SDL_cdrom.h" +#include "SDL_syscdrom.h" + +/* The maximum number of CD-ROM drives we'll detect */ +#define MAX_DRIVES 16 + +/* A list of available CD-ROM drives */ +static char *SDL_cdlist[MAX_DRIVES]; +static dev_t SDL_cdmode[MAX_DRIVES]; + +/* The system-dependent CD control functions */ +static const char *SDL_SYS_CDName(int drive); +static int SDL_SYS_CDOpen(int drive); +static int SDL_SYS_CDGetTOC(SDL_CD *cdrom); +static CDstatus SDL_SYS_CDStatus(SDL_CD *cdrom, int *position); +static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length); +static int SDL_SYS_CDPause(SDL_CD *cdrom); +static int SDL_SYS_CDResume(SDL_CD *cdrom); +static int SDL_SYS_CDStop(SDL_CD *cdrom); +static int SDL_SYS_CDEject(SDL_CD *cdrom); +static void SDL_SYS_CDClose(SDL_CD *cdrom); +static int SDL_SYS_CDioctl(int id, int command, void *arg); + +/* Check a drive to see if it is a CD-ROM */ +static int CheckDrive(char *drive, struct stat *stbuf) +{ + int is_cd; + int cdfd; + int ret; + struct devinfo info; + + /* If it doesn't exist, return -1 */ + if ( stat(drive, stbuf) < 0 ) { + return -1; + } + + /* If it does exist, verify that it's an available CD-ROM */ + is_cd = 0; + if ( S_ISCHR(stbuf->st_mode) || S_ISBLK(stbuf->st_mode) ) { + cdfd = open(drive, (O_RDONLY|O_EXCL|O_NONBLOCK), 0); + if ( cdfd >= 0 ) { + ret = SDL_SYS_CDioctl( cdfd, IOCINFO, &info ); + if ( ret < 0 ) { + /* Some kind of error */ + is_cd = 0; + } else { + if ( info.devtype == DD_CDROM ) { + is_cd = 1; + } else { + is_cd = 0; + } + } + close(cdfd); + } +#ifdef DEBUG_CDROM + else + { + fprintf(stderr, "Could not open drive %s (%s)\n", drive, strerror(errno)); + } +#endif + } + return is_cd; +} + +/* Add a CD-ROM drive to our list of valid drives */ +static void AddDrive(char *drive, struct stat *stbuf) +{ + int i; + + if ( SDL_numcds < MAX_DRIVES ) { + /* Check to make sure it's not already in our list. + This can happen when we see a drive via symbolic link. + */ + for ( i=0; i<SDL_numcds; ++i ) { + if ( stbuf->st_rdev == SDL_cdmode[i] ) { +#ifdef DEBUG_CDROM + fprintf(stderr, "Duplicate drive detected: %s == %s\n", drive, SDL_cdlist[i]); +#endif + return; + } + } + + /* Add this drive to our list */ + i = SDL_numcds; + SDL_cdlist[i] = SDL_strdup(drive); + if ( SDL_cdlist[i] == NULL ) { + SDL_OutOfMemory(); + return; + } + SDL_cdmode[i] = stbuf->st_rdev; + ++SDL_numcds; +#ifdef DEBUG_CDROM + fprintf(stderr, "Added CD-ROM drive: %s\n", drive); +#endif + } +} + +static void CheckMounts() +{ + char* buffer; + int bufsz; + struct vmount* ptr; + int ret; + + buffer = (char*)SDL_malloc(10); + bufsz = 10; + if ( buffer==NULL ) + { + fprintf(stderr, "Could not allocate 10 bytes in aix/SDL_syscdrom.c:CheckMounts\n" ); + exit ( -10 ); + } + + do + { + /* mntctrl() returns an array of all mounted filesystems */ + ret = mntctl ( MCTL_QUERY, bufsz, buffer ); + if ( ret == 0 ) + { + /* Buffer was too small, realloc. */ + bufsz = *(int*)buffer; /* Required size is in first word. */ + /* (whatever a word is in AIX 4.3.3) */ + /* int seems to be OK in 32bit mode. */ + SDL_free(buffer); + buffer = (char*)SDL_malloc(bufsz); + if ( buffer==NULL ) + { + fprintf(stderr, + "Could not allocate %d bytes in aix/SDL_syscdrom.c:CheckMounts\n", + bufsz ); + exit ( -10 ); + } + } + else if ( ret < 0 ) + { +#ifdef DEBUG_CDROM + fprintf(stderr, "Error reading vmount structures\n"); +#endif + return; + } + } + while ( ret == 0 ); + +#ifdef DEBUG_CDROM + fprintf ( stderr, "Read %d vmount structures\n",ret ); +#endif + ptr = (struct vmount*)buffer; + do + { + switch(ptr->vmt_gfstype) + { + case MNT_CDROM : + { + struct stat stbuf; + char* text; + + text = (char*)ptr + ptr->vmt_data[VMT_OBJECT].vmt_off; +#ifdef DEBUG_CDROM + fprintf(stderr, "Checking mount path: %s mounted on %s\n", + text, (char*)ptr + ptr->vmt_data[VMT_STUB].vmt_off ); +#endif + if ( CheckDrive( text, &stbuf) > 0) + { + AddDrive( text, &stbuf); + } + } + break; + default : + break; + } + ptr = (struct vmount*)((char*)ptr + ptr->vmt_length); + ret--; + } + while ( ret > 0 ); + + free ( buffer ); +} + +static int CheckNonmounts() +{ +#ifdef _THREAD_SAFE + AFILE_t fsFile = NULL; + int passNo = 0; + int ret; + struct fstab entry; + struct stat stbuf; + + ret = setfsent_r( &fsFile, &passNo ); + if ( ret != 0 ) return -1; + do + { + ret = getfsent_r ( &entry, &fsFile, &passNo ); + if ( ret == 0 ) { + char* l = SDL_strrchr(entry.fs_spec,'/'); + if ( l != NULL ) { + if ( !SDL_strncmp("cd",++l,2) ) { +#ifdef DEBUG_CDROM + fprintf(stderr, + "Found unmounted CD ROM drive with device name %s\n", + entry.fs_spec); +#endif + if ( CheckDrive( entry.fs_spec, &stbuf) > 0) + { + AddDrive( entry.fs_spec, &stbuf); + } + } + } + } + } + while ( ret == 0 ); + ret = endfsent_r ( &fsFile ); + if ( ret != 0 ) return -1; + return 0; +#else + struct fstab* entry; + struct stat stbuf; + + setfsent(); + do + { + entry = getfsent(); + if ( entry != NULL ) { + char* l = SDL_strrchr(entry->fs_spec,'/'); + if ( l != NULL ) { + if ( !SDL_strncmp("cd",++l,2) ) { +#ifdef DEBUG_CDROM + fprintf(stderr,"Found unmounted CD ROM drive with device name %s", entry->fs_spec); +#endif + if ( CheckDrive( entry->fs_spec, &stbuf) > 0) + { + AddDrive( entry->fs_spec, &stbuf); + } + } + } + } + } + while ( entry != NULL ); + endfsent(); +#endif +} + +int SDL_SYS_CDInit(void) +{ + char *SDLcdrom; + struct stat stbuf; + + /* Fill in our driver capabilities */ + SDL_CDcaps.Name = SDL_SYS_CDName; + SDL_CDcaps.Open = SDL_SYS_CDOpen; + SDL_CDcaps.GetTOC = SDL_SYS_CDGetTOC; + SDL_CDcaps.Status = SDL_SYS_CDStatus; + SDL_CDcaps.Play = SDL_SYS_CDPlay; + SDL_CDcaps.Pause = SDL_SYS_CDPause; + SDL_CDcaps.Resume = SDL_SYS_CDResume; + SDL_CDcaps.Stop = SDL_SYS_CDStop; + SDL_CDcaps.Eject = SDL_SYS_CDEject; + SDL_CDcaps.Close = SDL_SYS_CDClose; + + /* Look in the environment for our CD-ROM drive list */ + SDLcdrom = SDL_getenv("SDL_CDROM"); /* ':' separated list of devices */ + if ( SDLcdrom != NULL ) { + char *cdpath, *delim; + size_t len = SDL_strlen(SDLcdrom)+1; + cdpath = SDL_stack_alloc(char, len); + if ( cdpath != NULL ) { + SDL_strlcpy(cdpath, SDLcdrom, len); + SDLcdrom = cdpath; + do { + delim = SDL_strchr(SDLcdrom, ':'); + if ( delim ) { + *delim++ = '\0'; + } +#ifdef DEBUG_CDROM + fprintf(stderr, "Checking CD-ROM drive from SDL_CDROM: %s\n", SDLcdrom); +#endif + if ( CheckDrive(SDLcdrom, &stbuf) > 0 ) { + AddDrive(SDLcdrom, &stbuf); + } + if ( delim ) { + SDLcdrom = delim; + } else { + SDLcdrom = NULL; + } + } while ( SDLcdrom ); + SDL_stack_free(cdpath); + } + + /* If we found our drives, there's nothing left to do */ + if ( SDL_numcds > 0 ) { + return(0); + } + } + + CheckMounts(); + CheckNonmounts(); + + return 0; +} + +/* General ioctl() CD-ROM command function */ +static int SDL_SYS_CDioctl(int id, int command, void *arg) +{ + int retval; + + retval = ioctl(id, command, arg); + if ( retval < 0 ) { + SDL_SetError("ioctl() error: %s", strerror(errno)); + } + return retval; +} + +static const char *SDL_SYS_CDName(int drive) +{ + return(SDL_cdlist[drive]); +} + +static int SDL_SYS_CDOpen(int drive) +{ + int fd; + char* lastsl; + char* cdromname; + size_t len; + + /* + * We found /dev/cd? drives and that is in our list. But we can + * open only the /dev/rcd? versions of those devices for Audio CD. + */ + len = SDL_strlen(SDL_cdlist[drive])+2; + cdromname = (char*)SDL_malloc(len); + SDL_strlcpy(cdromname,SDL_cdlist[drive],len); + lastsl = SDL_strrchr(cdromname,'/'); + if (lastsl) { + *lastsl = 0; + SDL_strlcat(cdromname,"/r",len); + lastsl = SDL_strrchr(SDL_cdlist[drive],'/'); + if (lastsl) { + lastsl++; + SDL_strlcat(cdromname,lastsl,len); + } + } + +#ifdef DEBUG_CDROM + fprintf(stderr, "Should open drive %s, opening %s\n", SDL_cdlist[drive], cdromname); +#endif + + /* + * Use exclusive access. Don't use SC_DIAGNOSTICS as xmcd does because they + * require root priviledges, and we don't want that. SC_SINGLE provides + * exclusive access with less trouble. + */ + fd = openx(cdromname, O_RDONLY, NULL, SC_SINGLE); + if ( fd < 0 ) + { +#ifdef DEBUG_CDROM + fprintf(stderr, "Could not open drive %s (%s)\n", cdromname, strerror(errno)); +#endif + } + else + { + struct mode_form_op cdMode; + int ret; +#ifdef DEBUG_CDROM + cdMode.action = CD_GET_MODE; + ret = SDL_SYS_CDioctl(fd, DK_CD_MODE, &cdMode); + if ( ret < 0 ) { + fprintf(stderr, + "Could not get drive mode for %s (%s)\n", + cdromname, strerror(errno)); + } else { + switch(cdMode.cd_mode_form) { + case CD_MODE1 : + fprintf(stderr, + "Drive mode for %s is %s\n", + cdromname, "CD-ROM Data Mode 1"); + break; + case CD_MODE2_FORM1 : + fprintf(stderr, + "Drive mode for %s is %s\n", + cdromname, "CD-ROM XA Data Mode 2 Form 1"); + break; + case CD_MODE2_FORM2 : + fprintf(stderr, + "Drive mode for %s is %s\n", + cdromname, "CD-ROM XA Data Mode 2 Form 2"); + break; + case CD_DA : + fprintf(stderr, + "Drive mode for %s is %s\n", + cdromname, "CD-DA"); + break; + default : + fprintf(stderr, + "Drive mode for %s is %s\n", + cdromname, "unknown"); + break; + } + } +#endif + + cdMode.action = CD_CHG_MODE; + cdMode.cd_mode_form = CD_DA; + ret = SDL_SYS_CDioctl(fd, DK_CD_MODE, &cdMode); + if ( ret < 0 ) { +#ifdef DEBUG_CDROM + fprintf(stderr, + "Could not set drive mode for %s (%s)\n", + cdromname, strerror(errno)); +#endif + SDL_SetError("ioctl() error: Could not set CD drive mode, %s", + strerror(errno)); + } else { +#ifdef DEBUG_CDROM + fprintf(stderr, + "Drive mode for %s set to CD_DA\n", + cdromname); +#endif + } + } + SDL_free(cdromname); + return fd; +} + +static int SDL_SYS_CDGetTOC(SDL_CD *cdrom) +{ + struct cd_audio_cmd cmd; + struct cd_audio_cmd entry; + int i; + int okay; + + cmd.audio_cmds = CD_TRK_INFO_AUDIO; + cmd.msf_flag = FALSE; + if ( SDL_SYS_CDioctl(cdrom->id, DKAUDIO, &cmd) < 0 ) { + return -1; + } + + okay = 0; + cdrom->numtracks = cmd.indexing.track_index.last_track + - cmd.indexing.track_index.first_track+1; + if ( cdrom->numtracks > SDL_MAX_TRACKS ) { + cdrom->numtracks = SDL_MAX_TRACKS; + } + + /* Read all the track TOC entries */ + for ( i=0; i<=cdrom->numtracks; ++i ) { + if ( i == cdrom->numtracks ) { + cdrom->track[i].id = 0xAA;; + } else { + cdrom->track[i].id = cmd.indexing.track_index.first_track+i; + } + entry.audio_cmds = CD_GET_TRK_MSF; + entry.indexing.track_msf.track = cdrom->track[i].id; + if ( SDL_SYS_CDioctl(cdrom->id, DKAUDIO, &entry) < 0 ) { + break; + } else { + cdrom->track[i].type = 0; /* don't know how to detect 0x04 data track */ + cdrom->track[i].offset = MSF_TO_FRAMES( + entry.indexing.track_msf.mins, + entry.indexing.track_msf.secs, + entry.indexing.track_msf.frames); + cdrom->track[i].length = 0; + if ( i > 0 ) { + cdrom->track[i-1].length = cdrom->track[i].offset + - cdrom->track[i-1].offset; + } + } + } + if ( i == (cdrom->numtracks+1) ) { + okay = 1; + } + return(okay ? 0 : -1); +} + +/* Get CD-ROM status */ +static CDstatus SDL_SYS_CDStatus(SDL_CD *cdrom, int *position) +{ + CDstatus status; + struct cd_audio_cmd cmd; + cmd.audio_cmds = CD_INFO_AUDIO; + + if ( SDL_SYS_CDioctl(cdrom->id, DKAUDIO, &cmd) < 0 ) { +#ifdef DEBUG_CDROM + fprintf(stderr, "ioctl failed in SDL_SYS_CDStatus (%s)\n", SDL_GetError()); +#endif + status = CD_ERROR; + } else { + switch (cmd.status) { + case CD_NO_AUDIO: + case CD_COMPLETED: + status = CD_STOPPED; + break; + case CD_PLAY_AUDIO: + status = CD_PLAYING; + break; + case CD_PAUSE_AUDIO: + status = CD_PAUSED; + break; + case CD_NOT_VALID: +#ifdef DEBUG_CDROM + fprintf(stderr, "cdStatus failed with CD_NOT_VALID\n"); +#endif + status = CD_ERROR; + break; + case CD_STATUS_ERROR: +#ifdef DEBUG_CDROM + fprintf(stderr, "cdStatus failed with CD_STATUS_ERROR\n"); +#endif + status = CD_ERROR; + break; + default: +#ifdef DEBUG_CDROM + fprintf(stderr, "cdStatus failed with unknown error\n"); +#endif + status = CD_ERROR; + break; + } + } + if ( position ) { + if ( status == CD_PLAYING || (status == CD_PAUSED) ) { + *position = MSF_TO_FRAMES( cmd.indexing.info_audio.current_mins, + cmd.indexing.info_audio.current_secs, + cmd.indexing.info_audio.current_frames); + } else { + *position = 0; + } + } + return status; +} + +/* Start play */ +static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length) +{ + struct cd_audio_cmd cmd; + + /* + * My CD Rom is muted by default. I think I read that this is new with + * AIX 4.3. SDL does not change the volume, so I need a kludge. Maybe + * its better to do this elsewhere? + */ + cmd.audio_cmds = CD_PLAY_AUDIO | CD_SET_VOLUME; + cmd.msf_flag = TRUE; + FRAMES_TO_MSF(start, + &cmd.indexing.msf.first_mins, + &cmd.indexing.msf.first_secs, + &cmd.indexing.msf.first_frames); + FRAMES_TO_MSF(start+length, + &cmd.indexing.msf.last_mins, + &cmd.indexing.msf.last_secs, + &cmd.indexing.msf.last_frames); + cmd.volume_type = CD_VOLUME_ALL; + cmd.all_channel_vol = 255; /* This is a uchar. What is a good value? No docu! */ + cmd.out_port_0_sel = CD_AUDIO_CHNL_0; + cmd.out_port_1_sel = CD_AUDIO_CHNL_1; + cmd.out_port_2_sel = CD_AUDIO_CHNL_2; + cmd.out_port_3_sel = CD_AUDIO_CHNL_3; + +#ifdef DEBUG_CDROM + fprintf(stderr, "Trying to play from %d:%d:%d to %d:%d:%d\n", + cmd.indexing.msf.first_mins, + cmd.indexing.msf.first_secs, + cmd.indexing.msf.first_frames, + cmd.indexing.msf.last_mins, + cmd.indexing.msf.last_secs, + cmd.indexing.msf.last_frames); +#endif + return(SDL_SYS_CDioctl(cdrom->id, DKAUDIO, &cmd)); +} + +/* Pause play */ +static int SDL_SYS_CDPause(SDL_CD *cdrom) +{ + struct cd_audio_cmd cmd; + cmd.audio_cmds = CD_PAUSE_AUDIO; + return(SDL_SYS_CDioctl(cdrom->id, DKAUDIO, &cmd)); +} + +/* Resume play */ +static int SDL_SYS_CDResume(SDL_CD *cdrom) +{ + struct cd_audio_cmd cmd; + cmd.audio_cmds = CD_RESUME_AUDIO; + return(SDL_SYS_CDioctl(cdrom->id, DKAUDIO, &cmd)); +} + +/* Stop play */ +static int SDL_SYS_CDStop(SDL_CD *cdrom) +{ + struct cd_audio_cmd cmd; + cmd.audio_cmds = CD_STOP_AUDIO; + return(SDL_SYS_CDioctl(cdrom->id, DKAUDIO, &cmd)); +} + +/* Eject the CD-ROM */ +static int SDL_SYS_CDEject(SDL_CD *cdrom) +{ + return(SDL_SYS_CDioctl(cdrom->id, DKEJECT, 0)); +} + +/* Close the CD-ROM handle */ +static void SDL_SYS_CDClose(SDL_CD *cdrom) +{ + close(cdrom->id); +} + +void SDL_SYS_CDQuit(void) +{ + int i; + + if ( SDL_numcds > 0 ) { + for ( i=0; i<SDL_numcds; ++i ) { + SDL_free(SDL_cdlist[i]); + } + SDL_numcds = 0; + } +} + +#endif /* SDL_CDROM_AIX */ diff --git a/src/sdl_cdrom/SDL_syscdrom_beos.cc b/src/sdl_cdrom/SDL_syscdrom_beos.cc new file mode 100644 index 000000000..5f3fdaaae --- /dev/null +++ b/src/sdl_cdrom/SDL_syscdrom_beos.cc @@ -0,0 +1,414 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2012 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "config.h" +#define BEOS 1 +#if (C_COMPAT_SDL_CDROM_PLATFORM == BEOS) + +#include "SDL_config.h" + +/*#ifdef SDL_CDROM_BEOS*/ + +/* Functions for system-level CD-ROM audio control on BeOS + (not completely implemented yet) + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> + +#include <scsi.h> +#include <Directory.h> +#include <Entry.h> +#include <Path.h> + +#include "compat_SDL_cdrom.h" +extern "C" { +#include "SDL_syscdrom.h" +} + +/* Constants to help us get at the SCSI table-of-contents info */ +#define CD_NUMTRACKS(toc) toc.toc_data[3] +#define CD_TRACK(toc, track) (&toc.toc_data[6+(track)*8]) +#define CD_TRACK_N(toc, track) CD_TRACK(toc, track)[0] +#define CD_TRACK_M(toc, track) CD_TRACK(toc, track)[3] +#define CD_TRACK_S(toc, track) CD_TRACK(toc, track)[4] +#define CD_TRACK_F(toc, track) CD_TRACK(toc, track)[5] + +/* Constants to help us get at the SCSI position info */ +#define POS_TRACK(pos) pos.position[6] +#define POS_ABS_M(pos) pos.position[9] +#define POS_ABS_S(pos) pos.position[10] +#define POS_ABS_F(pos) pos.position[11] +#define POS_REL_M(pos) pos.position[13] +#define POS_REL_S(pos) pos.position[14] +#define POS_REL_F(pos) pos.position[15] + +/* The maximum number of CD-ROM drives we'll detect */ +#define MAX_DRIVES 16 + +/* A list of available CD-ROM drives */ +static char *SDL_cdlist[MAX_DRIVES]; + +/* The system-dependent CD control functions */ +static const char *SDL_SYS_CDName(int drive); +static int SDL_SYS_CDOpen(int drive); +static int SDL_SYS_CDGetTOC(SDL_CD *cdrom); +static CDstatus SDL_SYS_CDStatus(SDL_CD *cdrom, int *position); +static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length); +static int SDL_SYS_CDPause(SDL_CD *cdrom); +static int SDL_SYS_CDResume(SDL_CD *cdrom); +static int SDL_SYS_CDStop(SDL_CD *cdrom); +static int SDL_SYS_CDEject(SDL_CD *cdrom); +static void SDL_SYS_CDClose(SDL_CD *cdrom); +int try_dir(const char *directory); + + +/* Check a drive to see if it is a CD-ROM */ +static int CheckDrive(char *drive) +{ + struct stat stbuf; + int is_cd, cdfd; + device_geometry info; + + /* If it doesn't exist, return -1 */ + if ( stat(drive, &stbuf) < 0 ) { + return(-1); + } + + /* If it does exist, verify that it's an available CD-ROM */ + is_cd = 0; + cdfd = open(drive, 0); + if ( cdfd >= 0 ) { + if ( ioctl(cdfd, B_GET_GEOMETRY, &info) == B_NO_ERROR ) { + if ( info.device_type == B_CD ) { + is_cd = 1; + } + } + close(cdfd); + } else { + /* This can happen when the drive is open .. (?) */; + is_cd = 1; + } + return(is_cd); +} + +/* Add a CD-ROM drive to our list of valid drives */ +static void AddDrive(char *drive) +{ + int i; + size_t len; + + if ( SDL_numcds < MAX_DRIVES ) { + /* Add this drive to our list */ + i = SDL_numcds; + len = SDL_strlen(drive)+1; + SDL_cdlist[i] = (char *)SDL_malloc(len); + if ( SDL_cdlist[i] == NULL ) { + SDL_OutOfMemory(); + return; + } + SDL_strlcpy(SDL_cdlist[i], drive, len); + ++SDL_numcds; +#ifdef CDROM_DEBUG + fprintf(stderr, "Added CD-ROM drive: %s\n", drive); +#endif + } +} + +/* IDE bus scanning magic */ +enum { + IDE_GET_DEVICES_INFO = B_DEVICE_OP_CODES_END + 50, +}; +struct ide_ctrl_info { + bool ide_0_present; + bool ide_0_master_present; + bool ide_0_slave_present; + int ide_0_master_type; + int ide_0_slave_type; + bool ide_1_present; + bool ide_1_master_present; + bool ide_1_slave_present; + int ide_1_master_type; + int ide_1_slave_type; +}; + +int SDL_SYS_CDInit(void) +{ + char *SDLcdrom; + + /* Fill in our driver capabilities */ + SDL_CDcaps.Name = SDL_SYS_CDName; + SDL_CDcaps.Open = SDL_SYS_CDOpen; + SDL_CDcaps.GetTOC = SDL_SYS_CDGetTOC; + SDL_CDcaps.Status = SDL_SYS_CDStatus; + SDL_CDcaps.Play = SDL_SYS_CDPlay; + SDL_CDcaps.Pause = SDL_SYS_CDPause; + SDL_CDcaps.Resume = SDL_SYS_CDResume; + SDL_CDcaps.Stop = SDL_SYS_CDStop; + SDL_CDcaps.Eject = SDL_SYS_CDEject; + SDL_CDcaps.Close = SDL_SYS_CDClose; + + /* Look in the environment for our CD-ROM drive list */ + SDLcdrom = SDL_getenv("SDL_CDROM"); /* ':' separated list of devices */ + if ( SDLcdrom != NULL ) { + char *cdpath, *delim; + size_t len = SDL_strlen(SDLcdrom)+1; + cdpath = SDL_stack_alloc(char, len); + if ( cdpath != NULL ) { + SDL_strlcpy(cdpath, SDLcdrom, len); + SDLcdrom = cdpath; + do { + delim = SDL_strchr(SDLcdrom, ':'); + if ( delim ) { + *delim++ = '\0'; + } + if ( CheckDrive(SDLcdrom) > 0 ) { + AddDrive(SDLcdrom); + } + if ( delim ) { + SDLcdrom = delim; + } else { + SDLcdrom = NULL; + } + } while ( SDLcdrom ); + SDL_stack_free(cdpath); + } + + /* If we found our drives, there's nothing left to do */ + if ( SDL_numcds > 0 ) { + return(0); + } + } + + /* Scan the system for CD-ROM drives */ + try_dir("/dev/disk"); + return 0; +} + + +int try_dir(const char *directory) +{ + BDirectory dir; + dir.SetTo(directory); + if(dir.InitCheck() != B_NO_ERROR) { + return false; + } + dir.Rewind(); + BEntry entry; + while(dir.GetNextEntry(&entry) >= 0) { + BPath path; + const char *name; + entry_ref e; + + if(entry.GetPath(&path) != B_NO_ERROR) + continue; + name = path.Path(); + + if(entry.GetRef(&e) != B_NO_ERROR) + continue; + + if(entry.IsDirectory()) { + if(SDL_strcmp(e.name, "floppy") == 0) + continue; /* ignore floppy (it is not silent) */ + int devfd = try_dir(name); + if(devfd >= 0) + return devfd; + } + else { + int devfd; + device_geometry g; + + if(SDL_strcmp(e.name, "raw") != 0) + continue; /* ignore partitions */ + + devfd = open(name, O_RDONLY); + if(devfd < 0) + continue; + + if(ioctl(devfd, B_GET_GEOMETRY, &g, sizeof(g)) >= 0) { + if(g.device_type == B_CD) + { + AddDrive(strdup(name)); + } + } + close(devfd); + } + } + return B_ERROR; +} + + +/* General ioctl() CD-ROM command function */ +static int SDL_SYS_CDioctl(int index, int command, void *arg) +{ + int okay; + int fd; + + okay = 0; + fd = open(SDL_cdlist[index], 0); + if ( fd >= 0 ) { + if ( ioctl(fd, command, arg) == B_NO_ERROR ) { + okay = 1; + } + close(fd); + } + return(okay ? 0 : -1); +} + +static const char *SDL_SYS_CDName(int drive) +{ + return(SDL_cdlist[drive]); +} + +static int SDL_SYS_CDOpen(int drive) +{ + return(drive); +} + +static int SDL_SYS_CDGetTOC(SDL_CD *cdrom) +{ + int i; + scsi_toc toc; + + if ( SDL_SYS_CDioctl(cdrom->id, B_SCSI_GET_TOC, &toc) == 0 ) { + cdrom->numtracks = CD_NUMTRACKS(toc); + if ( cdrom->numtracks > SDL_MAX_TRACKS ) { + cdrom->numtracks = SDL_MAX_TRACKS; + } + for ( i=0; i<=cdrom->numtracks; ++i ) { + cdrom->track[i].id = CD_TRACK_N(toc, i); + /* FIXME: How do we tell on BeOS? */ + cdrom->track[i].type = SDL_AUDIO_TRACK; + cdrom->track[i].offset = MSF_TO_FRAMES( + CD_TRACK_M(toc, i), + CD_TRACK_S(toc, i), + CD_TRACK_F(toc, i)); + cdrom->track[i].length = 0; + if ( i > 0 ) { + cdrom->track[i-1].length = + cdrom->track[i].offset- + cdrom->track[i-1].offset; + } + } + return(0); + } else { + return(-1); + } +} + +/* Get CD-ROM status */ +static CDstatus SDL_SYS_CDStatus(SDL_CD *cdrom, int *position) +{ + CDstatus status; + int fd; + int cur_frame; + scsi_position pos; + + fd = open(SDL_cdlist[cdrom->id], 0); + cur_frame = 0; + if ( fd >= 0 ) { + if ( ioctl(fd, B_SCSI_GET_POSITION, &pos) == B_NO_ERROR ) { + cur_frame = MSF_TO_FRAMES( + POS_ABS_M(pos), POS_ABS_S(pos), POS_ABS_F(pos)); + } + if ( ! pos.position[1] || (pos.position[1] >= 0x13) || + ((pos.position[1] == 0x12) && (!pos.position[6])) ) { + status = CD_STOPPED; + } else + if ( pos.position[1] == 0x11 ) { + status = CD_PLAYING; + } else { + status = CD_PAUSED; + } + close(fd); + } else { + status = CD_TRAYEMPTY; + } + if ( position ) { + *position = cur_frame; + } + return(status); +} + +/* Start play */ +static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length) +{ + int okay; + int fd; + scsi_play_position pos; + + okay = 0; + fd = open(SDL_cdlist[cdrom->id], 0); + if ( fd >= 0 ) { + FRAMES_TO_MSF(start, &pos.start_m, &pos.start_s, &pos.start_f); + FRAMES_TO_MSF(start+length, &pos.end_m, &pos.end_s, &pos.end_f); + if ( ioctl(fd, B_SCSI_PLAY_POSITION, &pos) == B_NO_ERROR ) { + okay = 1; + } + close(fd); + } + return(okay ? 0 : -1); +} + +/* Pause play */ +static int SDL_SYS_CDPause(SDL_CD *cdrom) +{ + return(SDL_SYS_CDioctl(cdrom->id, B_SCSI_PAUSE_AUDIO, 0)); +} + +/* Resume play */ +static int SDL_SYS_CDResume(SDL_CD *cdrom) +{ + return(SDL_SYS_CDioctl(cdrom->id, B_SCSI_RESUME_AUDIO, 0)); +} + +/* Stop play */ +static int SDL_SYS_CDStop(SDL_CD *cdrom) +{ + return(SDL_SYS_CDioctl(cdrom->id, B_SCSI_STOP_AUDIO, 0)); +} + +/* Eject the CD-ROM */ +static int SDL_SYS_CDEject(SDL_CD *cdrom) +{ + return(SDL_SYS_CDioctl(cdrom->id, B_SCSI_EJECT, 0)); +} + +/* Close the CD-ROM handle */ +static void SDL_SYS_CDClose(SDL_CD *cdrom) +{ + close(cdrom->id); +} + +void SDL_SYS_CDQuit(void) +{ + int i; + + if ( SDL_numcds > 0 ) { + for ( i=0; i<SDL_numcds; ++i ) { + SDL_free(SDL_cdlist[i]); + } + SDL_numcds = 0; + } +} + +#endif /* SDL_CDROM_BEOS */ diff --git a/src/sdl_cdrom/SDL_syscdrom_bsdi.c b/src/sdl_cdrom/SDL_syscdrom_bsdi.c new file mode 100644 index 000000000..da5c99c8f --- /dev/null +++ b/src/sdl_cdrom/SDL_syscdrom_bsdi.c @@ -0,0 +1,547 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2012 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "config.h" +#define COMPAT_SDL_CDROM_PLATFORM_BSDI 1 + +#if (C_COMPAT_SDL_CDROM_PLATFORM == COMPAT_SDL_CDROM_PLATFORM_BSDI) + +#include "SDL_config.h" + +/*#ifdef SDL_CDROM_BSDI*/ + +/* + * Functions for system-level CD-ROM audio control for BSD/OS 4.x + * This started life out as a copy of the freebsd/SDL_cdrom.c file but was + * heavily modified. Works for standard (MMC) SCSI and ATAPI CDrom drives. + * + * Steven Schultz - sms@to.gd-es.com +*/ + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <err.h> +#include <unistd.h> +#include <sys/ioctl.h> +#include </sys/dev/scsi/scsi.h> +#include </sys/dev/scsi/scsi_ioctl.h> + +#include "compat_SDL_cdrom.h" +#include "SDL_syscdrom.h" + +/* + * The msf_to_frame and frame_to_msf were yanked from libcdrom and inlined + * here so that -lcdrom doesn't have to be dragged in for something so simple. +*/ + +#define FRAMES_PER_SECOND 75 +#define FRAMES_PER_MINUTE (FRAMES_PER_SECOND * 60) + +int +msf_to_frame(int minute, int second, int frame) + { + return(minute * FRAMES_PER_MINUTE + second * FRAMES_PER_SECOND + frame); + } + +void +frame_to_msf(int frame, int *minp, int *secp, int *framep) + { + *minp = frame / FRAMES_PER_MINUTE; + *secp = (frame % FRAMES_PER_MINUTE) / FRAMES_PER_SECOND; + *framep = frame % FRAMES_PER_SECOND; + } + +/* The maximum number of CD-ROM drives we'll detect */ +#define MAX_DRIVES 16 + +/* A list of available CD-ROM drives */ +static char *SDL_cdlist[MAX_DRIVES]; +static dev_t SDL_cdmode[MAX_DRIVES]; + +/* The system-dependent CD control functions */ +static const char *SDL_SYS_CDName(int drive); +static int SDL_SYS_CDOpen(int drive); +static int SDL_SYS_CDGetTOC(SDL_CD *cdrom); +static CDstatus SDL_SYS_CDStatus(SDL_CD *cdrom, int *position); +static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length); +static int SDL_SYS_CDPause(SDL_CD *cdrom); +static int SDL_SYS_CDResume(SDL_CD *cdrom); +static int SDL_SYS_CDStop(SDL_CD *cdrom); +static int SDL_SYS_CDEject(SDL_CD *cdrom); +static void SDL_SYS_CDClose(SDL_CD *cdrom); + +typedef struct scsi_cdb cdb_t; + +static int scsi_cmd(int fd, + struct scsi_cdb *cdb, + int cdblen, + int rw, + caddr_t data, + int datalen, + struct scsi_user_cdb *sus) + { + int scsistatus; + unsigned char *cp; + struct scsi_user_cdb suc; + + /* safety checks */ + if (!cdb) return(-1); + if (rw != SUC_READ && rw != SUC_WRITE) return(-1); + + suc.suc_flags = rw; + suc.suc_cdblen = cdblen; + bcopy(cdb, suc.suc_cdb, cdblen); + suc.suc_datalen = datalen; + suc.suc_data = data; + suc.suc_timeout = 10; /* 10 secs max for TUR or SENSE */ + if (ioctl(fd, SCSIRAWCDB, &suc) == -1) + return(-11); + scsistatus = suc.suc_sus.sus_status; + cp = suc.suc_sus.sus_sense; + +/* + * If a place to copy the sense data back to has been provided then the + * caller is responsible for checking the errors and printing any information + * out if the status was not successful. +*/ + if (scsistatus != 0 && !sus) + { + fprintf(stderr,"scsistatus = %x cmd = %x\n", + scsistatus, cdb[0]); + fprintf(stderr, "sense %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x\n", + cp[0], cp[1], cp[2], cp[3], cp[4], cp[5], + cp[6], cp[7], cp[8], cp[9], cp[10], cp[11], + cp[12], cp[13], cp[14], cp[15]); + return(1); + } + if (sus) + bcopy(&suc, sus, sizeof (struct scsi_user_cdb)); + if (scsistatus) + return(1); /* Return non-zero for unsuccessful status */ + return(0); + } + +/* request vendor brand and model */ +unsigned char *Inquiry(int fd) + { + static struct scsi_cdb6 cdb = + { + 0x12, + 0, 0, 0, + 56, + 0 + }; + static unsigned char Inqbuffer[56]; + + if (scsi_cmd(fd, (cdb_t *)&cdb, 6, SUC_READ, Inqbuffer, + sizeof(Inqbuffer), 0)) + return("\377"); + return(Inqbuffer); + } + +#define ADD_SENSECODE 12 +#define ADD_SC_QUALIFIER 13 + +int TestForMedium(int fd) + { + int sts, asc, ascq; + struct scsi_user_cdb sus; + static struct scsi_cdb6 cdb = + { + CMD_TEST_UNIT_READY, /* command */ + 0, /* reserved */ + 0, /* reserved */ + 0, /* reserved */ + 0, /* reserved */ + 0 /* reserved */ + }; + +again: sts = scsi_cmd(fd, (cdb_t *)&cdb, 6, SUC_READ, 0, 0, &sus); + asc = sus.suc_sus.sus_sense[ADD_SENSECODE]; + ascq = sus.suc_sus.sus_sense[ADD_SC_QUALIFIER]; + if (asc == 0x3a && ascq == 0x0) /* no medium */ + return(0); + if (asc == 0x28 && ascq == 0x0) /* medium changed */ + goto again; + if (asc == 0x4 && ascq == 0x1 ) /* coming ready */ + { + sleep(2); + goto again; + } + return(1); + } + +/* Check a drive to see if it is a CD-ROM */ +static int CheckDrive(char *drive, struct stat *stbuf) +{ + int is_cd = 0, cdfd; + char *p; + + /* If it doesn't exist, return -1 */ + if ( stat(drive, stbuf) < 0 ) { + return(-1); + } + + /* If it does exist, verify that it's an available CD-ROM */ + cdfd = open(drive, (O_RDONLY|O_EXCL|O_NONBLOCK), 0); + if ( cdfd >= 0 ) { + p = Inquiry(cdfd); + if (*p == TYPE_ROM) + is_cd = 1; + close(cdfd); + } + return(is_cd); +} + +/* Add a CD-ROM drive to our list of valid drives */ +static void AddDrive(char *drive, struct stat *stbuf) +{ + int i; + + if ( SDL_numcds < MAX_DRIVES ) { + /* Check to make sure it's not already in our list. + This can happen when we see a drive via symbolic link. + */ + for ( i=0; i<SDL_numcds; ++i ) { + if ( stbuf->st_rdev == SDL_cdmode[i] ) { +#ifdef DEBUG_CDROM + fprintf(stderr, "Duplicate drive detected: %s == %s\n", drive, SDL_cdlist[i]); +#endif + return; + } + } + + /* Add this drive to our list */ + i = SDL_numcds; + SDL_cdlist[i] = SDL_strdup(drive); + if ( SDL_cdlist[i] == NULL ) { + SDL_OutOfMemory(); + return; + } + SDL_cdmode[i] = stbuf->st_rdev; + ++SDL_numcds; +#ifdef DEBUG_CDROM + fprintf(stderr, "Added CD-ROM drive: %s\n", drive); +#endif + } +} + +int SDL_SYS_CDInit(void) +{ + /* checklist: /dev/rsr?c */ + static char *checklist[] = { + "?0 rsr?", NULL + }; + char *SDLcdrom; + int i, j, exists; + char drive[32]; + struct stat stbuf; + + /* Fill in our driver capabilities */ + SDL_CDcaps.Name = SDL_SYS_CDName; + SDL_CDcaps.Open = SDL_SYS_CDOpen; + SDL_CDcaps.GetTOC = SDL_SYS_CDGetTOC; + SDL_CDcaps.Status = SDL_SYS_CDStatus; + SDL_CDcaps.Play = SDL_SYS_CDPlay; + SDL_CDcaps.Pause = SDL_SYS_CDPause; + SDL_CDcaps.Resume = SDL_SYS_CDResume; + SDL_CDcaps.Stop = SDL_SYS_CDStop; + SDL_CDcaps.Eject = SDL_SYS_CDEject; + SDL_CDcaps.Close = SDL_SYS_CDClose; + + /* Look in the environment for our CD-ROM drive list */ + SDLcdrom = SDL_getenv("SDL_CDROM"); /* ':' separated list of devices */ + if ( SDLcdrom != NULL ) { + char *cdpath, *delim; + size_t len = SDL_strlen(SDLcdrom)+1; + cdpath = SDL_stack_alloc(char, len); + if ( cdpath != NULL ) { + SDL_strlcpy(cdpath, SDLcdrom, len); + SDLcdrom = cdpath; + do { + delim = SDL_strchr(SDLcdrom, ':'); + if ( delim ) { + *delim++ = '\0'; + } + if ( CheckDrive(SDLcdrom, &stbuf) > 0 ) { + AddDrive(SDLcdrom, &stbuf); + } + if ( delim ) { + SDLcdrom = delim; + } else { + SDLcdrom = NULL; + } + } while ( SDLcdrom ); + SDL_stack_free(cdpath); + } + + /* If we found our drives, there's nothing left to do */ + if ( SDL_numcds > 0 ) { + return(0); + } + } + + /* Scan the system for CD-ROM drives */ + for ( i=0; checklist[i]; ++i ) { + if ( checklist[i][0] == '?' ) { + char *insert; + exists = 1; + for ( j=checklist[i][1]; exists; ++j ) { + SDL_snprintf(drive, SDL_arraysize(drive), "/dev/%sc", &checklist[i][3]); + insert = SDL_strchr(drive, '?'); + if ( insert != NULL ) { + *insert = j; + } + switch (CheckDrive(drive, &stbuf)) { + /* Drive exists and is a CD-ROM */ + case 1: + AddDrive(drive, &stbuf); + break; + /* Drive exists, but isn't a CD-ROM */ + case 0: + break; + /* Drive doesn't exist */ + case -1: + exists = 0; + break; + } + } + } else { + SDL_snprintf(drive, SDL_arraysize(drive), "/dev/%s", checklist[i]); + if ( CheckDrive(drive, &stbuf) > 0 ) { + AddDrive(drive, &stbuf); + } + } + } + return(0); +} + +static const char *SDL_SYS_CDName(int drive) +{ + return(SDL_cdlist[drive]); +} + +static int SDL_SYS_CDOpen(int drive) +{ + return(open(SDL_cdlist[drive], O_RDONLY | O_NONBLOCK | O_EXCL, 0)); +} + +static int SDL_SYS_CDGetTOC(SDL_CD *cdrom) + { + u_char cdb[10], buf[4], *p, *toc; + struct scsi_user_cdb sus; + int i, sts, first_track, last_track, ntracks, toc_size; + + bzero(cdb, sizeof (cdb)); + cdb[0] = 0x43; /* Read TOC */ + cdb[1] = 0x2; /* MSF */ + cdb[8] = 4; /* size TOC header */ + sts = scsi_cmd(cdrom->id, (cdb_t *)cdb, 10, SUC_READ, buf, 4, &sus); + if (sts < 0) + return(-1); + first_track = buf[2]; + last_track = buf[3]; + ntracks = last_track - first_track + 1; + cdrom->numtracks = ntracks; + toc_size = 4 + (ntracks + 1) * 8; + toc = (u_char *)SDL_malloc(toc_size); + if (toc == NULL) + return(-1); + bzero(cdb, sizeof (cdb)); + cdb[0] = 0x43; + cdb[1] = 0x2; + cdb[6] = first_track; + cdb[7] = toc_size >> 8; + cdb[8] = toc_size & 0xff; + sts = scsi_cmd(cdrom->id, (cdb_t *)cdb, 10, SUC_READ, toc, toc_size, + &sus); + if (sts < 0) + { + SDL_free(toc); + return(-1); + } + + for (i = 0, p = toc+4; i <= ntracks; i++, p+= 8) + { + if (i == ntracks) + cdrom->track[i].id = 0xAA; /* Leadout */ + else + cdrom->track[i].id = first_track + i; + if (p[1] & 0x20) + cdrom->track[i].type = SDL_DATA_TRACK; + else + cdrom->track[i].type = SDL_AUDIO_TRACK; + cdrom->track[i].offset = msf_to_frame(p[5], p[6], p[7]); + cdrom->track[i].length = 0; + if (i > 0) + cdrom->track[i-1].length = cdrom->track[i].offset - + cdrom->track[i-1].offset; + } + SDL_free(toc); + return(0); + } + +/* Get CD-ROM status */ +static CDstatus SDL_SYS_CDStatus(SDL_CD *cdrom, int *position) + { + CDstatus status; + u_char cdb[10], buf[16]; + int sts; + struct scsi_user_cdb sus; + + bzero(cdb, sizeof (cdb)); + cdb[0] = 0x42; /* read subq */ + cdb[1] = 0x2; /* MSF */ + cdb[2] = 0x40; /* q channel */ + cdb[3] = 1; /* current pos */ + cdb[7] = sizeof (buf) >> 8; + cdb[8] = sizeof (buf) & 0xff; + sts = scsi_cmd(cdrom->id, (cdb_t *)cdb, 10, SUC_READ, buf, sizeof (buf), + &sus); + if (sts < 0) + return(-1); + if (sts) + { + if (TestForMedium(cdrom->id) == 0) + status = CD_TRAYEMPTY; + else + status = CD_ERROR; + } + else + { + switch (buf[1]) + { + case 0x11: + status = CD_PLAYING; + break; + case 0x12: + status = CD_PAUSED; + break; + case 0x13: + case 0x14: + case 0x15: + status = CD_STOPPED; + break; + default: + status = CD_ERROR; + break; + } + } + if (position) + { + if ( status == CD_PLAYING || (status == CD_PAUSED) ) + *position = msf_to_frame(buf[9], buf[10], buf[11]); + else + *position = 0; + } + return(status); + } + +/* Start play */ +static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length) + { + u_char cdb[10]; + int sts, minute, second, frame, eminute, esecond, eframe; + struct scsi_user_cdb sus; + + bzero(cdb, sizeof(cdb)); + cdb[0] = 0x47; /* Play */ + frame_to_msf(start, &minute, &second, &frame); + frame_to_msf(start + length, &eminute, &esecond, &eframe); + cdb[3] = minute; + cdb[4] = second; + cdb[5] = frame; + cdb[6] = eminute; + cdb[7] = esecond; + cdb[8] = eframe; + sts = scsi_cmd(cdrom->id, (cdb_t *)cdb, 10, SUC_READ, 0, 0, &sus); + return(sts); + } + +static int +pauseresume(SDL_CD *cdrom, int flag) + { + u_char cdb[10]; + struct scsi_user_cdb sus; + + bzero(cdb, sizeof (cdb)); + cdb[0] = 0x4b; + cdb[8] = flag & 0x1; + return(scsi_cmd(cdrom->id, (cdb_t *)cdb, 10, SUC_READ, 0, 0, &sus)); + } + +/* Pause play */ +static int SDL_SYS_CDPause(SDL_CD *cdrom) +{ + return(pauseresume(cdrom, 0)); +} + +/* Resume play */ +static int SDL_SYS_CDResume(SDL_CD *cdrom) +{ + return(pauseresume(cdrom, 1)); +} + +/* Stop play */ +static int SDL_SYS_CDStop(SDL_CD *cdrom) +{ + u_char cdb[6]; + struct scsi_user_cdb sus; + + bzero(cdb, sizeof (cdb)); + cdb[0] = 0x1b; /* stop */ + cdb[1] = 1; /* immediate */ + return(scsi_cmd(cdrom->id, (cdb_t *)cdb, 6, SUC_READ, 0, 0, &sus)); +} + +/* Eject the CD-ROM */ +static int SDL_SYS_CDEject(SDL_CD *cdrom) +{ + u_char cdb[6]; + struct scsi_user_cdb sus; + + bzero(cdb, sizeof (cdb)); + cdb[0] = 0x1b; /* stop */ + cdb[1] = 1; /* immediate */ + cdb[4] = 2; /* eject */ + return(scsi_cmd(cdrom->id, (cdb_t *)cdb, 6, SUC_READ, 0, 0, &sus)); +} + +/* Close the CD-ROM handle */ +static void SDL_SYS_CDClose(SDL_CD *cdrom) + { + close(cdrom->id); + } + +void SDL_SYS_CDQuit(void) +{ + int i; + + if ( SDL_numcds > 0 ) { + for ( i=0; i<SDL_numcds; ++i ) { + SDL_free(SDL_cdlist[i]); + } + } + SDL_numcds = 0; +} + +#endif /* SDL_CDROM_BSDI */ diff --git a/src/sdl_cdrom/SDL_syscdrom_dc.c b/src/sdl_cdrom/SDL_syscdrom_dc.c new file mode 100644 index 000000000..d4d6283f5 --- /dev/null +++ b/src/sdl_cdrom/SDL_syscdrom_dc.c @@ -0,0 +1,172 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2012 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "config.h" +#define COMPAT_SDL_CDROM_PLATFORM_DC 1 + +#if (C_COMPAT_SDL_CDROM_PLATFORM == COMPAT_SDL_CDROM_PLATFORM_DC) + +#include "SDL_config.h" + +/*#ifdef SDL_CDROM_DC*/ + +/* Functions for system-level CD-ROM audio control */ + +#include <dc/cdrom.h> +#include <dc/spu.h> + +#include "compat_SDL_cdrom.h" +#include "SDL_syscdrom.h" + +/* The system-dependent CD control functions */ +static const char *SDL_SYS_CDName(int drive); +static int SDL_SYS_CDOpen(int drive); +static int SDL_SYS_CDGetTOC(SDL_CD *cdrom); +static CDstatus SDL_SYS_CDStatus(SDL_CD *cdrom, int *position); +static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length); +static int SDL_SYS_CDPause(SDL_CD *cdrom); +static int SDL_SYS_CDResume(SDL_CD *cdrom); +static int SDL_SYS_CDStop(SDL_CD *cdrom); +static int SDL_SYS_CDEject(SDL_CD *cdrom); +static void SDL_SYS_CDClose(SDL_CD *cdrom); + + +int SDL_SYS_CDInit(void) +{ + /* Fill in our driver capabilities */ + SDL_CDcaps.Name = SDL_SYS_CDName; + SDL_CDcaps.Open = SDL_SYS_CDOpen; + SDL_CDcaps.GetTOC = SDL_SYS_CDGetTOC; + SDL_CDcaps.Status = SDL_SYS_CDStatus; + SDL_CDcaps.Play = SDL_SYS_CDPlay; + SDL_CDcaps.Pause = SDL_SYS_CDPause; + SDL_CDcaps.Resume = SDL_SYS_CDResume; + SDL_CDcaps.Stop = SDL_SYS_CDStop; + SDL_CDcaps.Eject = SDL_SYS_CDEject; + SDL_CDcaps.Close = SDL_SYS_CDClose; + + return(0); +} + +static const char *SDL_SYS_CDName(int drive) +{ + return "/cd"; +} + +static int SDL_SYS_CDOpen(int drive) +{ + return(drive); +} + +#define TRACK_CDDA 0 +static int SDL_SYS_CDGetTOC(SDL_CD *cdrom) +{ + CDROM_TOC toc; + int ret,i; + + ret = cdrom_read_toc(&toc,0); + if (ret!=ERR_OK) { + return -1; + } + + cdrom->numtracks = TOC_TRACK(toc.last)-TOC_TRACK(toc.first)+1; + for(i=0;i<cdrom->numtracks;i++) { + unsigned long entry = toc.entry[i]; + cdrom->track[i].id = i+1; + cdrom->track[i].type = (TOC_CTRL(toc.entry[i])==TRACK_CDDA)?SDL_AUDIO_TRACK:SDL_DATA_TRACK; + cdrom->track[i].offset = TOC_LBA(entry)-150; + cdrom->track[i].length = TOC_LBA((i+1<toc.last)?toc.entry[i+1]:toc.leadout_sector)-TOC_LBA(entry); + } + + return 0; +} + +/* Get CD-ROM status */ +static CDstatus SDL_SYS_CDStatus(SDL_CD *cdrom, int *position) +{ + int ret,dc_status,disc_type; + + ret = cdrom_get_status(&dc_status,&disc_type); + if (ret!=ERR_OK) return CD_ERROR; + + switch(dc_status) { +// case CD_STATUS_BUSY: + case CD_STATUS_PAUSED: + return CD_PAUSED; + case CD_STATUS_STANDBY: + return CD_STOPPED; + case CD_STATUS_PLAYING: + return CD_PLAYING; +// case CD_STATUS_SEEKING: +// case CD_STATUS_SCANING: + case CD_STATUS_OPEN: + case CD_STATUS_NO_DISC: + return CD_TRAYEMPTY; + default: + return CD_ERROR; + } +} + +/* Start play */ +static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length) +{ + int ret = cdrom_cdda_play(start-150,start-150+length,1,CDDA_SECTORS); + return ret==ERR_OK?0:-1; +} + +/* Pause play */ +static int SDL_SYS_CDPause(SDL_CD *cdrom) +{ + int ret=cdrom_cdda_pause(); + return ret==ERR_OK?0:-1; +} + +/* Resume play */ +static int SDL_SYS_CDResume(SDL_CD *cdrom) +{ + int ret=cdrom_cdda_resume(); + return ret==ERR_OK?0:-1; +} + +/* Stop play */ +static int SDL_SYS_CDStop(SDL_CD *cdrom) +{ + int ret=cdrom_spin_down(); + return ret==ERR_OK?0:-1; +} + +/* Eject the CD-ROM */ +static int SDL_SYS_CDEject(SDL_CD *cdrom) +{ + return -1; +} + +/* Close the CD-ROM handle */ +static void SDL_SYS_CDClose(SDL_CD *cdrom) +{ +} + +void SDL_SYS_CDQuit(void) +{ + +} + +#endif /* SDL_CDROM_DC */ diff --git a/src/sdl_cdrom/SDL_syscdrom_dummy.c b/src/sdl_cdrom/SDL_syscdrom_dummy.c new file mode 100644 index 000000000..b32f9a8c8 --- /dev/null +++ b/src/sdl_cdrom/SDL_syscdrom_dummy.c @@ -0,0 +1,48 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2012 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "config.h" +#define COMPAT_SDL_CDROM_PLATFORM_DUMMY 1 + +#if (C_COMPAT_SDL_CDROM_PLATFORM == COMPAT_SDL_CDROM_PLATFORM_DUMMY) + +#include "SDL_config.h" + +/*#if defined(SDL_CDROM_DUMMY) || defined(SDL_CDROM_DISABLED)*/ + +/* Stub functions for system-level CD-ROM audio control */ + +/* +#include "compat_SDL_cdrom.h" +#include "SDL_syscdrom.h" +*/ + +int SDL_SYS_CDInit(void) +{ + return(0); +} + +void SDL_SYS_CDQuit(void) +{ + return; +} + +#endif /* SDL_CDROM_DUMMY || SDL_CDROM_DISABLED */ diff --git a/src/sdl_cdrom/SDL_syscdrom_freebsd.c b/src/sdl_cdrom/SDL_syscdrom_freebsd.c new file mode 100644 index 000000000..0fa2c45f8 --- /dev/null +++ b/src/sdl_cdrom/SDL_syscdrom_freebsd.c @@ -0,0 +1,411 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2012 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "config.h" +#define COMPAT_SDL_CDROM_PLATFORM_FREEBSD 1 + +#if (C_COMPAT_SDL_CDROM_PLATFORM == COMPAT_SDL_CDROM_PLATFORM_FREEBSD) + +#include "SDL_config.h" + +/*#ifdef SDL_CDROM_FREEBSD*/ + +/* Functions for system-level CD-ROM audio control */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <unistd.h> +#include <sys/cdio.h> + +#include "compat_SDL_cdrom.h" +#include "SDL_syscdrom.h" + + +/* The maximum number of CD-ROM drives we'll detect */ +#define MAX_DRIVES 16 + +/* A list of available CD-ROM drives */ +static char *SDL_cdlist[MAX_DRIVES]; +static dev_t SDL_cdmode[MAX_DRIVES]; + +/* The system-dependent CD control functions */ +static const char *SDL_SYS_CDName(int drive); +static int SDL_SYS_CDOpen(int drive); +static int SDL_SYS_CDGetTOC(SDL_CD *cdrom); +static CDstatus SDL_SYS_CDStatus(SDL_CD *cdrom, int *position); +static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length); +static int SDL_SYS_CDPause(SDL_CD *cdrom); +static int SDL_SYS_CDResume(SDL_CD *cdrom); +static int SDL_SYS_CDStop(SDL_CD *cdrom); +static int SDL_SYS_CDEject(SDL_CD *cdrom); +static void SDL_SYS_CDClose(SDL_CD *cdrom); + +/* Some ioctl() errno values which occur when the tray is empty */ +#define ERRNO_TRAYEMPTY(errno) \ + ((errno == EIO) || (errno == ENOENT) || (errno == EINVAL)) + +/* Check a drive to see if it is a CD-ROM */ +static int CheckDrive(char *drive, struct stat *stbuf) +{ + int is_cd, cdfd; + struct ioc_read_subchannel info; + + /* If it doesn't exist, return -1 */ + if ( stat(drive, stbuf) < 0 ) { + return(-1); + } + + /* If it does exist, verify that it's an available CD-ROM */ + is_cd = 0; + if ( S_ISCHR(stbuf->st_mode) || S_ISBLK(stbuf->st_mode) ) { + cdfd = open(drive, (O_RDONLY|O_EXCL|O_NONBLOCK), 0); + if ( cdfd >= 0 ) { + info.address_format = CD_MSF_FORMAT; + info.data_format = CD_CURRENT_POSITION; + info.data_len = 0; + info.data = NULL; + /* Under Linux, EIO occurs when a disk is not present. + This isn't 100% reliable, so we use the USE_MNTENT + code above instead. + */ + if ( (ioctl(cdfd, CDIOCREADSUBCHANNEL, &info) == 0) || + ERRNO_TRAYEMPTY(errno) ) { + is_cd = 1; + } + close(cdfd); + } + } + return(is_cd); +} + +/* Add a CD-ROM drive to our list of valid drives */ +static void AddDrive(char *drive, struct stat *stbuf) +{ + int i; + + if ( SDL_numcds < MAX_DRIVES ) { + /* Check to make sure it's not already in our list. + This can happen when we see a drive via symbolic link. + */ + for ( i=0; i<SDL_numcds; ++i ) { + if ( stbuf->st_rdev == SDL_cdmode[i] ) { +#ifdef DEBUG_CDROM + fprintf(stderr, "Duplicate drive detected: %s == %s\n", drive, SDL_cdlist[i]); +#endif + return; + } + } + + /* Add this drive to our list */ + i = SDL_numcds; + SDL_cdlist[i] = SDL_strdup(drive); + if ( SDL_cdlist[i] == NULL ) { + SDL_OutOfMemory(); + return; + } + SDL_cdmode[i] = stbuf->st_rdev; + ++SDL_numcds; +#ifdef DEBUG_CDROM + fprintf(stderr, "Added CD-ROM drive: %s\n", drive); +#endif + } +} + +int SDL_SYS_CDInit(void) +{ + /* checklist: /dev/cdrom,/dev/cd?c /dev/acd?c + /dev/matcd?c /dev/mcd?c /dev/scd?c */ + static char *checklist[] = { + "cdrom", "?0 cd?", "?0 acd?", "?0 matcd?", "?0 mcd?", "?0 scd?",NULL + }; + char *SDLcdrom; + int i, j, exists; + char drive[32]; + struct stat stbuf; + + /* Fill in our driver capabilities */ + SDL_CDcaps.Name = SDL_SYS_CDName; + SDL_CDcaps.Open = SDL_SYS_CDOpen; + SDL_CDcaps.GetTOC = SDL_SYS_CDGetTOC; + SDL_CDcaps.Status = SDL_SYS_CDStatus; + SDL_CDcaps.Play = SDL_SYS_CDPlay; + SDL_CDcaps.Pause = SDL_SYS_CDPause; + SDL_CDcaps.Resume = SDL_SYS_CDResume; + SDL_CDcaps.Stop = SDL_SYS_CDStop; + SDL_CDcaps.Eject = SDL_SYS_CDEject; + SDL_CDcaps.Close = SDL_SYS_CDClose; + + /* Look in the environment for our CD-ROM drive list */ + SDLcdrom = SDL_getenv("SDL_CDROM"); /* ':' separated list of devices */ + if ( SDLcdrom != NULL ) { + char *cdpath, *delim; + size_t len = SDL_strlen(SDLcdrom)+1; + cdpath = SDL_stack_alloc(char, len); + if ( cdpath != NULL ) { + SDL_strlcpy(cdpath, SDLcdrom, len); + SDLcdrom = cdpath; + do { + delim = SDL_strchr(SDLcdrom, ':'); + if ( delim ) { + *delim++ = '\0'; + } + if ( CheckDrive(SDLcdrom, &stbuf) > 0 ) { + AddDrive(SDLcdrom, &stbuf); + } + if ( delim ) { + SDLcdrom = delim; + } else { + SDLcdrom = NULL; + } + } while ( SDLcdrom ); + SDL_stack_free(cdpath); + } + + /* If we found our drives, there's nothing left to do */ + if ( SDL_numcds > 0 ) { + return(0); + } + } + + /* Scan the system for CD-ROM drives */ + for ( i=0; checklist[i]; ++i ) { + if ( checklist[i][0] == '?' ) { + char *insert; + exists = 1; + for ( j=checklist[i][1]; exists; ++j ) { + SDL_snprintf(drive, SDL_arraysize(drive), "/dev/%s", &checklist[i][3]); + insert = SDL_strchr(drive, '?'); + if ( insert != NULL ) { + *insert = j; + } + switch (CheckDrive(drive, &stbuf)) { + /* Drive exists and is a CD-ROM */ + case 1: + AddDrive(drive, &stbuf); + break; + /* Drive exists, but isn't a CD-ROM */ + case 0: + break; + /* Drive doesn't exist */ + case -1: + exists = 0; + break; + } + } + } else { + SDL_snprintf(drive, SDL_arraysize(drive), "/dev/%s", checklist[i]); + if ( CheckDrive(drive, &stbuf) > 0 ) { + AddDrive(drive, &stbuf); + } + } + } + return(0); +} + +/* General ioctl() CD-ROM command function */ +static int SDL_SYS_CDioctl(int id, int command, void *arg) +{ + int retval; + + retval = ioctl(id, command, arg); + if ( retval < 0 ) { + SDL_SetError("ioctl() error: %s", strerror(errno)); + } + return(retval); +} + +static const char *SDL_SYS_CDName(int drive) +{ + return(SDL_cdlist[drive]); +} + +static int SDL_SYS_CDOpen(int drive) +{ + return(open(SDL_cdlist[drive], (O_RDONLY|O_EXCL|O_NONBLOCK), 0)); +} + +static int SDL_SYS_CDGetTOC(SDL_CD *cdrom) +{ + struct ioc_toc_header toc; + int i, okay; + struct ioc_read_toc_entry entry; + struct cd_toc_entry data; + + okay = 0; + if ( SDL_SYS_CDioctl(cdrom->id, CDIOREADTOCHEADER, &toc) == 0 ) { + cdrom->numtracks = toc.ending_track-toc.starting_track+1; + if ( cdrom->numtracks > SDL_MAX_TRACKS ) { + cdrom->numtracks = SDL_MAX_TRACKS; + } + /* Read all the track TOC entries */ + for ( i=0; i<=cdrom->numtracks; ++i ) { + if ( i == cdrom->numtracks ) { + cdrom->track[i].id = 0xAA; /* CDROM_LEADOUT */ + } else { + cdrom->track[i].id = toc.starting_track+i; + } + entry.starting_track = cdrom->track[i].id; + entry.address_format = CD_MSF_FORMAT; + entry.data_len = sizeof(data); + entry.data = &data; + if ( SDL_SYS_CDioctl(cdrom->id, CDIOREADTOCENTRYS, + &entry) < 0 ) { + break; + } else { + cdrom->track[i].type = data.control; + cdrom->track[i].offset = MSF_TO_FRAMES( + data.addr.msf.minute, + data.addr.msf.second, + data.addr.msf.frame); + cdrom->track[i].length = 0; + if ( i > 0 ) { + cdrom->track[i-1].length = + cdrom->track[i].offset- + cdrom->track[i-1].offset; + } + } + } + if ( i == (cdrom->numtracks+1) ) { + okay = 1; + } + } + return(okay ? 0 : -1); +} + +/* Get CD-ROM status */ +static CDstatus SDL_SYS_CDStatus(SDL_CD *cdrom, int *position) +{ + CDstatus status; + struct ioc_toc_header toc; + struct ioc_read_subchannel info; + struct cd_sub_channel_info data; + + info.address_format = CD_MSF_FORMAT; + info.data_format = CD_CURRENT_POSITION; + info.track = 0; + info.data_len = sizeof(data); + info.data = &data; + if ( ioctl(cdrom->id, CDIOCREADSUBCHANNEL, &info) < 0 ) { + if ( ERRNO_TRAYEMPTY(errno) ) { + status = CD_TRAYEMPTY; + } else { + status = CD_ERROR; + } + } else { + switch (data.header.audio_status) { + case CD_AS_AUDIO_INVALID: + case CD_AS_NO_STATUS: + /* Try to determine if there's a CD available */ + if (ioctl(cdrom->id,CDIOREADTOCHEADER,&toc)==0) + status = CD_STOPPED; + else + status = CD_TRAYEMPTY; + break; + case CD_AS_PLAY_COMPLETED: + status = CD_STOPPED; + break; + case CD_AS_PLAY_IN_PROGRESS: + status = CD_PLAYING; + break; + case CD_AS_PLAY_PAUSED: + status = CD_PAUSED; + break; + default: + status = CD_ERROR; + break; + } + } + if ( position ) { + if ( status == CD_PLAYING || (status == CD_PAUSED) ) { + *position = MSF_TO_FRAMES( + data.what.position.absaddr.msf.minute, + data.what.position.absaddr.msf.second, + data.what.position.absaddr.msf.frame); + } else { + *position = 0; + } + } + return(status); +} + +/* Start play */ +static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length) +{ + struct ioc_play_msf playtime; + + FRAMES_TO_MSF(start, + &playtime.start_m, &playtime.start_s, &playtime.start_f); + FRAMES_TO_MSF(start+length, + &playtime.end_m, &playtime.end_s, &playtime.end_f); +#ifdef DEBUG_CDROM + fprintf(stderr, "Trying to play from %d:%d:%d to %d:%d:%d\n", + playtime.cdmsf_min0, playtime.cdmsf_sec0, playtime.cdmsf_frame0, + playtime.cdmsf_min1, playtime.cdmsf_sec1, playtime.cdmsf_frame1); +#endif + ioctl(cdrom->id, CDIOCSTART, 0); + return(SDL_SYS_CDioctl(cdrom->id, CDIOCPLAYMSF, &playtime)); +} + +/* Pause play */ +static int SDL_SYS_CDPause(SDL_CD *cdrom) +{ + return(SDL_SYS_CDioctl(cdrom->id, CDIOCPAUSE, 0)); +} + +/* Resume play */ +static int SDL_SYS_CDResume(SDL_CD *cdrom) +{ + return(SDL_SYS_CDioctl(cdrom->id, CDIOCRESUME, 0)); +} + +/* Stop play */ +static int SDL_SYS_CDStop(SDL_CD *cdrom) +{ + return(SDL_SYS_CDioctl(cdrom->id, CDIOCSTOP, 0)); +} + +/* Eject the CD-ROM */ +static int SDL_SYS_CDEject(SDL_CD *cdrom) +{ + return(SDL_SYS_CDioctl(cdrom->id, CDIOCEJECT, 0)); +} + +/* Close the CD-ROM handle */ +static void SDL_SYS_CDClose(SDL_CD *cdrom) +{ + close(cdrom->id); +} + +void SDL_SYS_CDQuit(void) +{ + int i; + + if ( SDL_numcds > 0 ) { + for ( i=0; i<SDL_numcds; ++i ) { + SDL_free(SDL_cdlist[i]); + } + SDL_numcds = 0; + } +} + +#endif /* SDL_CDROM_FREEBSD */ diff --git a/src/sdl_cdrom/SDL_syscdrom_linux.c b/src/sdl_cdrom/SDL_syscdrom_linux.c new file mode 100644 index 000000000..f05ea376f --- /dev/null +++ b/src/sdl_cdrom/SDL_syscdrom_linux.c @@ -0,0 +1,569 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2012 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "config.h" +#define COMPAT_SDL_CDROM_PLATFORM_LINUX 1 + +#if (C_COMPAT_SDL_CDROM_PLATFORM == COMPAT_SDL_CDROM_PLATFORM_LINUX) + +#include "SDL_config.h" + +/*#ifdef SDL_CDROM_LINUX*/ + +/* Functions for system-level CD-ROM audio control */ + +#include <string.h> /* For strerror() */ +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/ioctl.h> +#include <fcntl.h> +#include <errno.h> +#include <unistd.h> +#ifdef __LINUX__ +#ifdef HAVE_LINUX_VERSION_H +/* linux 2.6.9 workaround */ +#include <linux/version.h> +#if LINUX_VERSION_CODE == KERNEL_VERSION(2,6,9) +#include <asm/types.h> +#define __le64 __u64 +#define __le32 __u32 +#define __le16 __u16 +#define __be64 __u64 +#define __be32 __u32 +#define __be16 __u16 +#endif /* linux 2.6.9 workaround */ +#endif /* HAVE_LINUX_VERSION_H */ +#include <linux/cdrom.h> +#endif +#ifdef __SVR4 +#include <sys/cdio.h> +#endif + +/* Define this to use the alternative getmntent() code */ +#ifndef __SVR4 +#define USE_MNTENT +#endif + +#ifdef USE_MNTENT +#if defined(__USLC__) +#include <sys/mntent.h> +#else +#include <mntent.h> +#endif + +#ifndef _PATH_MNTTAB +#ifdef MNTTAB +#define _PATH_MNTTAB MNTTAB +#else +#define _PATH_MNTTAB "/etc/fstab" +#endif +#endif /* !_PATH_MNTTAB */ + +#ifndef _PATH_MOUNTED +#define _PATH_MOUNTED "/etc/mtab" +#endif /* !_PATH_MOUNTED */ + +#ifndef MNTTYPE_CDROM +#define MNTTYPE_CDROM "iso9660" +#endif +#ifndef MNTTYPE_SUPER +#define MNTTYPE_SUPER "supermount" +#endif +#endif /* USE_MNTENT */ + +#include "compat_SDL_cdrom.h" +#include "SDL_syscdrom.h" + + +/* The maximum number of CD-ROM drives we'll detect */ +#define MAX_DRIVES 16 + +/* A list of available CD-ROM drives */ +static char *SDL_cdlist[MAX_DRIVES]; +static dev_t SDL_cdmode[MAX_DRIVES]; + +/* The system-dependent CD control functions */ +static const char *SDL_SYS_CDName(int drive); +static int SDL_SYS_CDOpen(int drive); +static int SDL_SYS_CDGetTOC(SDL_CD *cdrom); +static CDstatus SDL_SYS_CDStatus(SDL_CD *cdrom, int *position); +static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length); +static int SDL_SYS_CDPause(SDL_CD *cdrom); +static int SDL_SYS_CDResume(SDL_CD *cdrom); +static int SDL_SYS_CDStop(SDL_CD *cdrom); +static int SDL_SYS_CDEject(SDL_CD *cdrom); +static void SDL_SYS_CDClose(SDL_CD *cdrom); + +/* Some ioctl() errno values which occur when the tray is empty */ +#ifndef ENOMEDIUM +#define ENOMEDIUM ENOENT +#endif +#define ERRNO_TRAYEMPTY(errno) \ + ((errno == EIO) || (errno == ENOENT) || \ + (errno == EINVAL) || (errno == ENOMEDIUM)) + +/* Check a drive to see if it is a CD-ROM */ +static int CheckDrive(char *drive, char *mnttype, struct stat *stbuf) +{ + int is_cd, cdfd; + struct cdrom_subchnl info; + + /* If it doesn't exist, return -1 */ + if ( stat(drive, stbuf) < 0 ) { + return(-1); + } + + /* If it does exist, verify that it's an available CD-ROM */ + is_cd = 0; + if ( S_ISCHR(stbuf->st_mode) || S_ISBLK(stbuf->st_mode) ) { + cdfd = open(drive, (O_RDONLY|O_NONBLOCK), 0); + if ( cdfd >= 0 ) { + info.cdsc_format = CDROM_MSF; + /* Under Linux, EIO occurs when a disk is not present. + */ + if ( (ioctl(cdfd, CDROMSUBCHNL, &info) == 0) || + ERRNO_TRAYEMPTY(errno) ) { + is_cd = 1; + } + close(cdfd); + } +#ifdef USE_MNTENT + /* Even if we can't read it, it might be mounted */ + else if ( mnttype && (SDL_strcmp(mnttype, MNTTYPE_CDROM) == 0) ) { + is_cd = 1; + } +#endif + } + return(is_cd); +} + +/* Add a CD-ROM drive to our list of valid drives */ +static void AddDrive(char *drive, struct stat *stbuf) +{ + int i; + + if ( SDL_numcds < MAX_DRIVES ) { + /* Check to make sure it's not already in our list. + This can happen when we see a drive via symbolic link. + */ + for ( i=0; i<SDL_numcds; ++i ) { + if ( stbuf->st_rdev == SDL_cdmode[i] ) { +#ifdef DEBUG_CDROM + fprintf(stderr, "Duplicate drive detected: %s == %s\n", drive, SDL_cdlist[i]); +#endif + return; + } + } + + /* Add this drive to our list */ + i = SDL_numcds; + SDL_cdlist[i] = SDL_strdup(drive); + if ( SDL_cdlist[i] == NULL ) { + SDL_OutOfMemory(); + return; + } + SDL_cdmode[i] = stbuf->st_rdev; + ++SDL_numcds; +#ifdef DEBUG_CDROM + fprintf(stderr, "Added CD-ROM drive: %s\n", drive); +#endif + } +} + +#ifdef USE_MNTENT +static void CheckMounts(const char *mtab) +{ + FILE *mntfp; + struct mntent *mntent; + struct stat stbuf; + + mntfp = setmntent(mtab, "r"); + if ( mntfp != NULL ) { + char *tmp; + char *mnt_type; + size_t mnt_type_len; + char *mnt_dev; + size_t mnt_dev_len; + + while ( (mntent=getmntent(mntfp)) != NULL ) { + mnt_type_len = SDL_strlen(mntent->mnt_type) + 1; + mnt_type = SDL_stack_alloc(char, mnt_type_len); + if (mnt_type == NULL) + continue; /* maybe you'll get lucky next time. */ + + mnt_dev_len = SDL_strlen(mntent->mnt_fsname) + 1; + mnt_dev = SDL_stack_alloc(char, mnt_dev_len); + if (mnt_dev == NULL) { + SDL_stack_free(mnt_type); + continue; + } + + SDL_strlcpy(mnt_type, mntent->mnt_type, mnt_type_len); + SDL_strlcpy(mnt_dev, mntent->mnt_fsname, mnt_dev_len); + + /* Handle "supermount" filesystem mounts */ + if ( SDL_strcmp(mnt_type, MNTTYPE_SUPER) == 0 ) { + tmp = SDL_strstr(mntent->mnt_opts, "fs="); + if ( tmp ) { + SDL_stack_free(mnt_type); + mnt_type = SDL_strdup(tmp + SDL_strlen("fs=")); + if ( mnt_type ) { + tmp = SDL_strchr(mnt_type, ','); + if ( tmp ) { + *tmp = '\0'; + } + } + } + tmp = SDL_strstr(mntent->mnt_opts, "dev="); + if ( tmp ) { + SDL_stack_free(mnt_dev); + mnt_dev = SDL_strdup(tmp + SDL_strlen("dev=")); + if ( mnt_dev ) { + tmp = SDL_strchr(mnt_dev, ','); + if ( tmp ) { + *tmp = '\0'; + } + } + } + } + if ( SDL_strcmp(mnt_type, MNTTYPE_CDROM) == 0 ) { +#ifdef DEBUG_CDROM + fprintf(stderr, "Checking mount path from %s: %s mounted on %s of %s\n", + mtab, mnt_dev, mntent->mnt_dir, mnt_type); +#endif + if (CheckDrive(mnt_dev, mnt_type, &stbuf) > 0) { + AddDrive(mnt_dev, &stbuf); + } + } + SDL_stack_free(mnt_dev); + SDL_stack_free(mnt_type); + } + endmntent(mntfp); + } +} +#endif /* USE_MNTENT */ + +int SDL_SYS_CDInit(void) +{ + /* checklist: /dev/cdrom, /dev/hd?, /dev/scd? /dev/sr? */ + static char *checklist[] = { + "cdrom", "?a hd?", "?0 scd?", "?0 sr?", NULL + }; + char *SDLcdrom; + int i, j, exists; + char drive[32]; + struct stat stbuf; + + /* Fill in our driver capabilities */ + SDL_CDcaps.Name = SDL_SYS_CDName; + SDL_CDcaps.Open = SDL_SYS_CDOpen; + SDL_CDcaps.GetTOC = SDL_SYS_CDGetTOC; + SDL_CDcaps.Status = SDL_SYS_CDStatus; + SDL_CDcaps.Play = SDL_SYS_CDPlay; + SDL_CDcaps.Pause = SDL_SYS_CDPause; + SDL_CDcaps.Resume = SDL_SYS_CDResume; + SDL_CDcaps.Stop = SDL_SYS_CDStop; + SDL_CDcaps.Eject = SDL_SYS_CDEject; + SDL_CDcaps.Close = SDL_SYS_CDClose; + + /* Look in the environment for our CD-ROM drive list */ + SDLcdrom = SDL_getenv("SDL_CDROM"); /* ':' separated list of devices */ + if ( SDLcdrom != NULL ) { + char *cdpath, *delim; + size_t len = SDL_strlen(SDLcdrom)+1; + cdpath = SDL_stack_alloc(char, len); + if ( cdpath != NULL ) { + SDL_strlcpy(cdpath, SDLcdrom, len); + SDLcdrom = cdpath; + do { + delim = SDL_strchr(SDLcdrom, ':'); + if ( delim ) { + *delim++ = '\0'; + } +#ifdef DEBUG_CDROM + fprintf(stderr, "Checking CD-ROM drive from SDL_CDROM: %s\n", SDLcdrom); +#endif + if ( CheckDrive(SDLcdrom, NULL, &stbuf) > 0 ) { + AddDrive(SDLcdrom, &stbuf); + } + if ( delim ) { + SDLcdrom = delim; + } else { + SDLcdrom = NULL; + } + } while ( SDLcdrom ); + SDL_stack_free(cdpath); + } + + /* If we found our drives, there's nothing left to do */ + if ( SDL_numcds > 0 ) { + return(0); + } + } + +#ifdef USE_MNTENT + /* Check /dev/cdrom first :-) */ + if (CheckDrive("/dev/cdrom", NULL, &stbuf) > 0) { + AddDrive("/dev/cdrom", &stbuf); + } + + /* Now check the currently mounted CD drives */ + CheckMounts(_PATH_MOUNTED); + + /* Finally check possible mountable drives in /etc/fstab */ + CheckMounts(_PATH_MNTTAB); + + /* If we found our drives, there's nothing left to do */ + if ( SDL_numcds > 0 ) { + return(0); + } +#endif /* USE_MNTENT */ + + /* Scan the system for CD-ROM drives. + Not always 100% reliable, so use the USE_MNTENT code above first. + */ + for ( i=0; checklist[i]; ++i ) { + if ( checklist[i][0] == '?' ) { + char *insert; + exists = 1; + for ( j=checklist[i][1]; exists; ++j ) { + SDL_snprintf(drive, SDL_arraysize(drive), "/dev/%s", &checklist[i][3]); + insert = SDL_strchr(drive, '?'); + if ( insert != NULL ) { + *insert = j; + } +#ifdef DEBUG_CDROM + fprintf(stderr, "Checking possible CD-ROM drive: %s\n", drive); +#endif + switch (CheckDrive(drive, NULL, &stbuf)) { + /* Drive exists and is a CD-ROM */ + case 1: + AddDrive(drive, &stbuf); + break; + /* Drive exists, but isn't a CD-ROM */ + case 0: + break; + /* Drive doesn't exist */ + case -1: + exists = 0; + break; + } + } + } else { + SDL_snprintf(drive, SDL_arraysize(drive), "/dev/%s", checklist[i]); +#ifdef DEBUG_CDROM + fprintf(stderr, "Checking possible CD-ROM drive: %s\n", drive); +#endif + if ( CheckDrive(drive, NULL, &stbuf) > 0 ) { + AddDrive(drive, &stbuf); + } + } + } + return(0); +} + +/* General ioctl() CD-ROM command function */ +static int SDL_SYS_CDioctl(int id, int command, void *arg) +{ + int retval; + + retval = ioctl(id, command, arg); + if ( retval < 0 ) { + SDL_SetError("ioctl() error: %s", strerror(errno)); + } + return(retval); +} + +static const char *SDL_SYS_CDName(int drive) +{ + return(SDL_cdlist[drive]); +} + +static int SDL_SYS_CDOpen(int drive) +{ + return(open(SDL_cdlist[drive], (O_RDONLY|O_NONBLOCK), 0)); +} + +static int SDL_SYS_CDGetTOC(SDL_CD *cdrom) +{ + struct cdrom_tochdr toc; + int i, okay; + struct cdrom_tocentry entry; + + okay = 0; + if ( SDL_SYS_CDioctl(cdrom->id, CDROMREADTOCHDR, &toc) == 0 ) { + cdrom->numtracks = toc.cdth_trk1-toc.cdth_trk0+1; + if ( cdrom->numtracks > SDL_MAX_TRACKS ) { + cdrom->numtracks = SDL_MAX_TRACKS; + } + /* Read all the track TOC entries */ + for ( i=0; i<=cdrom->numtracks; ++i ) { + if ( i == cdrom->numtracks ) { + cdrom->track[i].id = CDROM_LEADOUT; + } else { + cdrom->track[i].id = toc.cdth_trk0+i; + } + entry.cdte_track = cdrom->track[i].id; + entry.cdte_format = CDROM_MSF; + if ( SDL_SYS_CDioctl(cdrom->id, CDROMREADTOCENTRY, + &entry) < 0 ) { + break; + } else { + if ( entry.cdte_ctrl & CDROM_DATA_TRACK ) { + cdrom->track[i].type = SDL_DATA_TRACK; + } else { + cdrom->track[i].type = SDL_AUDIO_TRACK; + } + cdrom->track[i].offset = MSF_TO_FRAMES( + entry.cdte_addr.msf.minute, + entry.cdte_addr.msf.second, + entry.cdte_addr.msf.frame); + cdrom->track[i].length = 0; + if ( i > 0 ) { + cdrom->track[i-1].length = + cdrom->track[i].offset- + cdrom->track[i-1].offset; + } + } + } + if ( i == (cdrom->numtracks+1) ) { + okay = 1; + } + } + return(okay ? 0 : -1); +} + +/* Get CD-ROM status */ +static CDstatus SDL_SYS_CDStatus(SDL_CD *cdrom, int *position) +{ + CDstatus status; + struct cdrom_tochdr toc; + struct cdrom_subchnl info; + + info.cdsc_format = CDROM_MSF; + if ( ioctl(cdrom->id, CDROMSUBCHNL, &info) < 0 ) { + if ( ERRNO_TRAYEMPTY(errno) ) { + status = CD_TRAYEMPTY; + } else { + status = CD_ERROR; + } + } else { + switch (info.cdsc_audiostatus) { + case CDROM_AUDIO_INVALID: + case CDROM_AUDIO_NO_STATUS: + /* Try to determine if there's a CD available */ + if (ioctl(cdrom->id, CDROMREADTOCHDR, &toc)==0) + status = CD_STOPPED; + else + status = CD_TRAYEMPTY; + break; + case CDROM_AUDIO_COMPLETED: + status = CD_STOPPED; + break; + case CDROM_AUDIO_PLAY: + status = CD_PLAYING; + break; + case CDROM_AUDIO_PAUSED: + /* Workaround buggy CD-ROM drive */ + if ( info.cdsc_trk == CDROM_LEADOUT ) { + status = CD_STOPPED; + } else { + status = CD_PAUSED; + } + break; + default: + status = CD_ERROR; + break; + } + } + if ( position ) { + if ( status == CD_PLAYING || (status == CD_PAUSED) ) { + *position = MSF_TO_FRAMES( + info.cdsc_absaddr.msf.minute, + info.cdsc_absaddr.msf.second, + info.cdsc_absaddr.msf.frame); + } else { + *position = 0; + } + } + return(status); +} + +/* Start play */ +static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length) +{ + struct cdrom_msf playtime; + + FRAMES_TO_MSF(start, + &playtime.cdmsf_min0, &playtime.cdmsf_sec0, &playtime.cdmsf_frame0); + FRAMES_TO_MSF(start+length, + &playtime.cdmsf_min1, &playtime.cdmsf_sec1, &playtime.cdmsf_frame1); +#ifdef DEBUG_CDROM + fprintf(stderr, "Trying to play from %d:%d:%d to %d:%d:%d\n", + playtime.cdmsf_min0, playtime.cdmsf_sec0, playtime.cdmsf_frame0, + playtime.cdmsf_min1, playtime.cdmsf_sec1, playtime.cdmsf_frame1); +#endif + return(SDL_SYS_CDioctl(cdrom->id, CDROMPLAYMSF, &playtime)); +} + +/* Pause play */ +static int SDL_SYS_CDPause(SDL_CD *cdrom) +{ + return(SDL_SYS_CDioctl(cdrom->id, CDROMPAUSE, 0)); +} + +/* Resume play */ +static int SDL_SYS_CDResume(SDL_CD *cdrom) +{ + return(SDL_SYS_CDioctl(cdrom->id, CDROMRESUME, 0)); +} + +/* Stop play */ +static int SDL_SYS_CDStop(SDL_CD *cdrom) +{ + return(SDL_SYS_CDioctl(cdrom->id, CDROMSTOP, 0)); +} + +/* Eject the CD-ROM */ +static int SDL_SYS_CDEject(SDL_CD *cdrom) +{ + return(SDL_SYS_CDioctl(cdrom->id, CDROMEJECT, 0)); +} + +/* Close the CD-ROM handle */ +static void SDL_SYS_CDClose(SDL_CD *cdrom) +{ + close(cdrom->id); +} + +void SDL_SYS_CDQuit(void) +{ + int i; + + if ( SDL_numcds > 0 ) { + for ( i=0; i<SDL_numcds; ++i ) { + SDL_free(SDL_cdlist[i]); + } + SDL_numcds = 0; + } +} + +#endif /* SDL_CDROM_LINUX */ diff --git a/src/sdl_cdrom/SDL_syscdrom_mint.c b/src/sdl_cdrom/SDL_syscdrom_mint.c new file mode 100644 index 000000000..48496d61f --- /dev/null +++ b/src/sdl_cdrom/SDL_syscdrom_mint.c @@ -0,0 +1,324 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2012 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "config.h" +#define COMPAT_SDL_CDROM_PLATFORM_MINT 1 + +#if (C_COMPAT_SDL_CDROM_PLATFORM == COMPAT_SDL_CDROM_PLATFORM_MINT) + +#include "SDL_config.h" +#include "SDL_stdinc.h" + +/*#ifdef SDL_CDROM_MINT*/ + +/* + Atari MetaDOS CD-ROM functions + + Patrice Mandin +*/ + +#include <errno.h> + +#include <mint/cdromio.h> +#include <mint/metados.h> + +#include "compat_SDL_cdrom.h" +#include "SDL_syscdrom.h" + +/* Some ioctl() errno values which occur when the tray is empty */ +#ifndef ENOMEDIUM +#define ENOMEDIUM ENOENT +#endif +#define ERRNO_TRAYEMPTY(errno) \ + ((errno == EIO) || (errno == ENOENT) || \ + (errno == EINVAL) || (errno == ENOMEDIUM)) + +/* The maximum number of CD-ROM drives we'll detect */ +#define MAX_DRIVES 32 + +typedef struct { + char device[3]; /* Physical device letter + ':' + '\0' */ + metaopen_t metaopen; /* Infos on opened drive */ +} metados_drive_t; + +static metados_drive_t metados_drives[MAX_DRIVES]; + +/* The system-dependent CD control functions */ +static const char *SDL_SYS_CDName(int drive); +static int SDL_SYS_CDOpen(int drive); +static void SDL_SYS_CDClose(SDL_CD *cdrom); +static int SDL_SYS_CDioctl(int id, int command, void *arg); +static int SDL_SYS_CDGetTOC(SDL_CD *cdrom); +static CDstatus SDL_SYS_CDStatus(SDL_CD *cdrom, int *position); +static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length); +static int SDL_SYS_CDPause(SDL_CD *cdrom); +static int SDL_SYS_CDResume(SDL_CD *cdrom); +static int SDL_SYS_CDStop(SDL_CD *cdrom); +static int SDL_SYS_CDEject(SDL_CD *cdrom); + +int SDL_SYS_CDInit(void) +{ + metainit_t metainit={0,0,0,0}; + metaopen_t metaopen; + int i, handle; + struct cdrom_subchnl info; + + SDL_numcds = 0; + SDL_memset(metados_drives, 0, sizeof(metados_drives)); + + Metainit(&metainit); + if (metainit.version == NULL) { +#ifdef DEBUG_CDROM + fprintf(stderr, "MetaDOS not installed\n"); +#endif + return 0; + } + + if (metainit.drives_map == 0) { +#ifdef DEBUG_CDROM + fprintf(stderr, "No MetaDOS devices present\n"); +#endif + return 0; + } + + for (i='A'; i<='Z'; i++) { + metados_drives[SDL_numcds].device[0] = 0; + metados_drives[SDL_numcds].device[1] = ':'; + metados_drives[SDL_numcds].device[2] = 0; + + if (metainit.drives_map & (1<<(i-'A'))) { + handle = Metaopen(i, &metaopen); + if (handle == 0) { + + info.cdsc_format = CDROM_MSF; + if ( (Metaioctl(i, METADOS_IOCTL_MAGIC, CDROMSUBCHNL, &info) == 0) || ERRNO_TRAYEMPTY(errno) ) { + metados_drives[SDL_numcds].device[0] = i; + ++SDL_numcds; + } + + Metaclose(i); + } + } + } + + /* Fill in our driver capabilities */ + SDL_CDcaps.Name = SDL_SYS_CDName; + SDL_CDcaps.Open = SDL_SYS_CDOpen; + SDL_CDcaps.Close = SDL_SYS_CDClose; + + SDL_CDcaps.GetTOC = SDL_SYS_CDGetTOC; + SDL_CDcaps.Status = SDL_SYS_CDStatus; + SDL_CDcaps.Play = SDL_SYS_CDPlay; + SDL_CDcaps.Pause = SDL_SYS_CDPause; + SDL_CDcaps.Resume = SDL_SYS_CDResume; + SDL_CDcaps.Stop = SDL_SYS_CDStop; + SDL_CDcaps.Eject = SDL_SYS_CDEject; + + return 0; +} + +void SDL_SYS_CDQuit(void) +{ + SDL_numcds = 0; +} + +static const char *SDL_SYS_CDName(int drive) +{ + return(metados_drives[drive].device); +} + +static int SDL_SYS_CDOpen(int drive) +{ + int handle; + + handle = Metaopen(metados_drives[drive].device[0], &(metados_drives[drive].metaopen)); + if (handle == 0) { + return drive; + } + + return -1; +} + +static void SDL_SYS_CDClose(SDL_CD *cdrom) +{ + Metaclose(metados_drives[cdrom->id].device[0]); +} + +static int SDL_SYS_CDioctl(int id, int command, void *arg) +{ + int retval; + + retval = Metaioctl(metados_drives[id].device[0], METADOS_IOCTL_MAGIC, command, arg); + if ( retval < 0 ) { + SDL_SetError("ioctl() error: %s", strerror(errno)); + } + return(retval); +} + +static int SDL_SYS_CDGetTOC(SDL_CD *cdrom) +{ + int i,okay; + struct cdrom_tochdr toc; + struct cdrom_tocentry entry; + + /* Use standard ioctl() */ + if (SDL_SYS_CDioctl(cdrom->id, CDROMREADTOCHDR, &toc)<0) { + return -1; + } + + cdrom->numtracks = toc.cdth_trk1-toc.cdth_trk0+1; + if ( cdrom->numtracks > SDL_MAX_TRACKS ) { + cdrom->numtracks = SDL_MAX_TRACKS; + } + + /* Read all the track TOC entries */ + okay=1; + for ( i=0; i<=cdrom->numtracks; ++i ) { + if ( i == cdrom->numtracks ) { + cdrom->track[i].id = CDROM_LEADOUT; + } else { + cdrom->track[i].id = toc.cdth_trk0+i; + } + entry.cdte_track = cdrom->track[i].id; + entry.cdte_format = CDROM_MSF; + if ( SDL_SYS_CDioctl(cdrom->id, CDROMREADTOCENTRY, &entry) < 0 ) { + okay=0; + break; + } else { + if ( entry.cdte_ctrl & CDROM_DATA_TRACK ) { + cdrom->track[i].type = SDL_DATA_TRACK; + } else { + cdrom->track[i].type = SDL_AUDIO_TRACK; + } + cdrom->track[i].offset = MSF_TO_FRAMES( + entry.cdte_addr.msf.minute, + entry.cdte_addr.msf.second, + entry.cdte_addr.msf.frame); + cdrom->track[i].length = 0; + if ( i > 0 ) { + cdrom->track[i-1].length = cdrom->track[i].offset-cdrom->track[i-1].offset; + } + } + } + + return(okay ? 0 : -1); +} + +/* Get CD-ROM status */ +static CDstatus SDL_SYS_CDStatus(SDL_CD *cdrom, int *position) +{ + CDstatus status; + struct cdrom_tochdr toc; + struct cdrom_subchnl info; + + info.cdsc_format = CDROM_MSF; + if ( SDL_SYS_CDioctl(cdrom->id, CDROMSUBCHNL, &info) < 0 ) { + if ( ERRNO_TRAYEMPTY(errno) ) { + status = CD_TRAYEMPTY; + } else { + status = CD_ERROR; + } + } else { + switch (info.cdsc_audiostatus) { + case CDROM_AUDIO_INVALID: + case CDROM_AUDIO_NO_STATUS: + /* Try to determine if there's a CD available */ + if (SDL_SYS_CDioctl(cdrom->id, CDROMREADTOCHDR, &toc)==0) { + status = CD_STOPPED; + } else { + status = CD_TRAYEMPTY; + } + break; + case CDROM_AUDIO_COMPLETED: + status = CD_STOPPED; + break; + case CDROM_AUDIO_PLAY: + status = CD_PLAYING; + break; + case CDROM_AUDIO_PAUSED: + /* Workaround buggy CD-ROM drive */ + if ( info.cdsc_trk == CDROM_LEADOUT ) { + status = CD_STOPPED; + } else { + status = CD_PAUSED; + } + break; + default: + status = CD_ERROR; + break; + } + } + if ( position ) { + if ( status == CD_PLAYING || (status == CD_PAUSED) ) { + *position = MSF_TO_FRAMES( + info.cdsc_absaddr.msf.minute, + info.cdsc_absaddr.msf.second, + info.cdsc_absaddr.msf.frame); + } else { + *position = 0; + } + } + return(status); +} + +/* Start play */ +static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length) +{ + struct cdrom_msf playtime; + + FRAMES_TO_MSF(start, + &playtime.cdmsf_min0, &playtime.cdmsf_sec0, &playtime.cdmsf_frame0); + FRAMES_TO_MSF(start+length, + &playtime.cdmsf_min1, &playtime.cdmsf_sec1, &playtime.cdmsf_frame1); +#ifdef DEBUG_CDROM + fprintf(stderr, "Trying to play from %d:%d:%d to %d:%d:%d\n", + playtime.cdmsf_min0, playtime.cdmsf_sec0, playtime.cdmsf_frame0, + playtime.cdmsf_min1, playtime.cdmsf_sec1, playtime.cdmsf_frame1); +#endif + + return SDL_SYS_CDioctl(cdrom->id, CDROMPLAYMSF, &playtime); +} + +/* Pause play */ +static int SDL_SYS_CDPause(SDL_CD *cdrom) +{ + return SDL_SYS_CDioctl(cdrom->id, CDROMPAUSE, 0); +} + +/* Resume play */ +static int SDL_SYS_CDResume(SDL_CD *cdrom) +{ + return SDL_SYS_CDioctl(cdrom->id, CDROMRESUME, 0); +} + +/* Stop play */ +static int SDL_SYS_CDStop(SDL_CD *cdrom) +{ + return SDL_SYS_CDioctl(cdrom->id, CDROMSTOP, 0); +} + +/* Eject the CD-ROM */ +static int SDL_SYS_CDEject(SDL_CD *cdrom) +{ + return SDL_SYS_CDioctl(cdrom->id, CDROMEJECT, 0); +} + +#endif /* SDL_CDROM_MINT */ diff --git a/src/sdl_cdrom/SDL_syscdrom_openbsd.c b/src/sdl_cdrom/SDL_syscdrom_openbsd.c new file mode 100644 index 000000000..44f36813e --- /dev/null +++ b/src/sdl_cdrom/SDL_syscdrom_openbsd.c @@ -0,0 +1,421 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2012 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "config.h" +#define COMPAT_SDL_CDROM_PLATFORM_OPENBSD 1 + +#if (C_COMPAT_SDL_CDROM_PLATFORM == COMPAT_SDL_CDROM_PLATFORM_OPENBSD) + +#include "SDL_config.h" + +/*#ifdef SDL_CDROM_OPENBSD*/ + +/* Functions for system-level CD-ROM audio control */ + +#include <sys/types.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <unistd.h> +#include <sys/ioctl.h> +#include <sys/cdio.h> + +#include "compat_SDL_cdrom.h" +#include "SDL_syscdrom.h" + + +/* The maximum number of CD-ROM drives we'll detect */ +#define MAX_DRIVES 16 + +/* A list of available CD-ROM drives */ +static char *SDL_cdlist[MAX_DRIVES]; +static dev_t SDL_cdmode[MAX_DRIVES]; + +/* The system-dependent CD control functions */ +static const char *SDL_SYS_CDName(int drive); +static int SDL_SYS_CDOpen(int drive); +static int SDL_SYS_CDGetTOC(SDL_CD *cdrom); +static CDstatus SDL_SYS_CDStatus(SDL_CD *cdrom, int *position); +static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length); +static int SDL_SYS_CDPause(SDL_CD *cdrom); +static int SDL_SYS_CDResume(SDL_CD *cdrom); +static int SDL_SYS_CDStop(SDL_CD *cdrom); +static int SDL_SYS_CDEject(SDL_CD *cdrom); +static void SDL_SYS_CDClose(SDL_CD *cdrom); + +/* Some ioctl() errno values which occur when the tray is empty */ +#define ERRNO_TRAYEMPTY(errno) \ + ((errno == EIO) || (errno == ENOENT) || (errno == EINVAL) || \ + (errno == ENODEV)) + +/* Check a drive to see if it is a CD-ROM */ +static int CheckDrive(char *drive, struct stat *stbuf) +{ + int is_cd, cdfd; + struct ioc_read_subchannel info; + + /* If it doesn't exist, return -1 */ + if ( stat(drive, stbuf) < 0 ) { + return(-1); + } + + /* If it does exist, verify that it's an available CD-ROM */ + is_cd = 0; + if ( S_ISCHR(stbuf->st_mode) || S_ISBLK(stbuf->st_mode) ) { + cdfd = open(drive, (O_RDONLY|O_EXCL|O_NONBLOCK), 0); + if ( cdfd >= 0 ) { + info.address_format = CD_MSF_FORMAT; + info.data_format = CD_CURRENT_POSITION; + info.data_len = 0; + info.data = NULL; + /* Under Linux, EIO occurs when a disk is not present. + This isn't 100% reliable, so we use the USE_MNTENT + code above instead. + */ + if ( (ioctl(cdfd, CDIOCREADSUBCHANNEL, &info) == 0) || + ERRNO_TRAYEMPTY(errno) ) { + is_cd = 1; + } + close(cdfd); + } + else if (ERRNO_TRAYEMPTY(errno)) + is_cd = 1; + } + return(is_cd); +} + +/* Add a CD-ROM drive to our list of valid drives */ +static void AddDrive(char *drive, struct stat *stbuf) +{ + int i; + + if ( SDL_numcds < MAX_DRIVES ) { + /* Check to make sure it's not already in our list. + This can happen when we see a drive via symbolic link. + */ + for ( i=0; i<SDL_numcds; ++i ) { + if ( stbuf->st_rdev == SDL_cdmode[i] ) { +#ifdef DEBUG_CDROM + fprintf(stderr, "Duplicate drive detected: %s == %s\n", drive, SDL_cdlist[i]); +#endif + return; + } + } + + /* Add this drive to our list */ + i = SDL_numcds; + SDL_cdlist[i] = SDL_strdup(drive); + if ( SDL_cdlist[i] == NULL ) { + SDL_OutOfMemory(); + return; + } + SDL_cdmode[i] = stbuf->st_rdev; + ++SDL_numcds; +#ifdef DEBUG_CDROM + fprintf(stderr, "Added CD-ROM drive: %s\n", drive); +#endif + } +} + +int SDL_SYS_CDInit(void) +{ + static char *checklist[] = { +#if defined(__OPENBSD__) + "?0 cd?c", "cdrom", NULL +#elif defined(__NETBSD__) + "?0 cd?d", "?0 cd?c", "cdrom", NULL +#else + "?0 cd?c", "?0 acd?c", "cdrom", NULL +#endif + }; + char *SDLcdrom; + int i, j, exists; + char drive[32]; + struct stat stbuf; + + /* Fill in our driver capabilities */ + SDL_CDcaps.Name = SDL_SYS_CDName; + SDL_CDcaps.Open = SDL_SYS_CDOpen; + SDL_CDcaps.GetTOC = SDL_SYS_CDGetTOC; + SDL_CDcaps.Status = SDL_SYS_CDStatus; + SDL_CDcaps.Play = SDL_SYS_CDPlay; + SDL_CDcaps.Pause = SDL_SYS_CDPause; + SDL_CDcaps.Resume = SDL_SYS_CDResume; + SDL_CDcaps.Stop = SDL_SYS_CDStop; + SDL_CDcaps.Eject = SDL_SYS_CDEject; + SDL_CDcaps.Close = SDL_SYS_CDClose; + + /* Look in the environment for our CD-ROM drive list */ + SDLcdrom = SDL_getenv("SDL_CDROM"); /* ':' separated list of devices */ + if ( SDLcdrom != NULL ) { + char *cdpath, *delim; + size_t len = SDL_strlen(SDLcdrom)+1; + cdpath = SDL_stack_alloc(char, len); + if ( cdpath != NULL ) { + SDL_strlcpy(cdpath, SDLcdrom, len); + SDLcdrom = cdpath; + do { + delim = SDL_strchr(SDLcdrom, ':'); + if ( delim ) { + *delim++ = '\0'; + } + if ( CheckDrive(SDLcdrom, &stbuf) > 0 ) { + AddDrive(SDLcdrom, &stbuf); + } + if ( delim ) { + SDLcdrom = delim; + } else { + SDLcdrom = NULL; + } + } while ( SDLcdrom ); + SDL_stack_free(cdpath); + } + + /* If we found our drives, there's nothing left to do */ + if ( SDL_numcds > 0 ) { + return(0); + } + } + + /* Scan the system for CD-ROM drives */ + for ( i=0; checklist[i]; ++i ) { + if ( checklist[i][0] == '?' ) { + char *insert; + exists = 1; + for ( j=checklist[i][1]; exists; ++j ) { + SDL_snprintf(drive, SDL_arraysize(drive), "/dev/%s", &checklist[i][3]); + insert = SDL_strchr(drive, '?'); + if ( insert != NULL ) { + *insert = j; + } + switch (CheckDrive(drive, &stbuf)) { + /* Drive exists and is a CD-ROM */ + case 1: + AddDrive(drive, &stbuf); + break; + /* Drive exists, but isn't a CD-ROM */ + case 0: + break; + /* Drive doesn't exist */ + case -1: + exists = 0; + break; + } + } + } else { + SDL_snprintf(drive, SDL_arraysize(drive), "/dev/%s", checklist[i]); + if ( CheckDrive(drive, &stbuf) > 0 ) { + AddDrive(drive, &stbuf); + } + } + } + return(0); +} + +/* General ioctl() CD-ROM command function */ +static int SDL_SYS_CDioctl(int id, int command, void *arg) +{ + int retval; + + retval = ioctl(id, command, arg); + if ( retval < 0 ) { + SDL_SetError("ioctl() error: %s", strerror(errno)); + } + return(retval); +} + +static const char *SDL_SYS_CDName(int drive) +{ + return(SDL_cdlist[drive]); +} + +static int SDL_SYS_CDOpen(int drive) +{ + return(open(SDL_cdlist[drive], (O_RDONLY|O_EXCL|O_NONBLOCK), 0)); +} + +static int SDL_SYS_CDGetTOC(SDL_CD *cdrom) +{ + struct ioc_toc_header toc; + int i, okay; + struct ioc_read_toc_entry entry; + struct cd_toc_entry data; + + okay = 0; + if ( SDL_SYS_CDioctl(cdrom->id, CDIOREADTOCHEADER, &toc) == 0 ) { + cdrom->numtracks = toc.ending_track-toc.starting_track+1; + if ( cdrom->numtracks > SDL_MAX_TRACKS ) { + cdrom->numtracks = SDL_MAX_TRACKS; + } + /* Read all the track TOC entries */ + for ( i=0; i<=cdrom->numtracks; ++i ) { + if ( i == cdrom->numtracks ) { + cdrom->track[i].id = 0xAA; /* CDROM_LEADOUT */ + } else { + cdrom->track[i].id = toc.starting_track+i; + } + entry.starting_track = cdrom->track[i].id; + entry.address_format = CD_MSF_FORMAT; + entry.data_len = sizeof(data); + entry.data = &data; + if ( SDL_SYS_CDioctl(cdrom->id, CDIOREADTOCENTRYS, + &entry) < 0 ) { + break; + } else { + cdrom->track[i].type = data.control; + cdrom->track[i].offset = MSF_TO_FRAMES( + data.addr.msf.minute, + data.addr.msf.second, + data.addr.msf.frame); + cdrom->track[i].length = 0; + if ( i > 0 ) { + cdrom->track[i-1].length = + cdrom->track[i].offset- + cdrom->track[i-1].offset; + } + } + } + if ( i == (cdrom->numtracks+1) ) { + okay = 1; + } + } + return(okay ? 0 : -1); +} + +/* Get CD-ROM status */ +static CDstatus SDL_SYS_CDStatus(SDL_CD *cdrom, int *position) +{ + CDstatus status; + struct ioc_toc_header toc; + struct ioc_read_subchannel info; + struct cd_sub_channel_info data; + + info.address_format = CD_MSF_FORMAT; + info.data_format = CD_CURRENT_POSITION; + info.track = 0; + info.data_len = sizeof(data); + info.data = &data; + if ( ioctl(cdrom->id, CDIOCREADSUBCHANNEL, &info) < 0 ) { + if ( ERRNO_TRAYEMPTY(errno) ) { + status = CD_TRAYEMPTY; + } else { + status = CD_ERROR; + } + } else { + switch (data.header.audio_status) { + case CD_AS_AUDIO_INVALID: + case CD_AS_NO_STATUS: + /* Try to determine if there's a CD available */ + if (ioctl(cdrom->id,CDIOREADTOCHEADER,&toc)==0) + status = CD_STOPPED; + else + status = CD_TRAYEMPTY; + break; + case CD_AS_PLAY_COMPLETED: + status = CD_STOPPED; + break; + case CD_AS_PLAY_IN_PROGRESS: + status = CD_PLAYING; + break; + case CD_AS_PLAY_PAUSED: + status = CD_PAUSED; + break; + default: + status = CD_ERROR; + break; + } + } + if ( position ) { + if ( status == CD_PLAYING || (status == CD_PAUSED) ) { + *position = MSF_TO_FRAMES( + data.what.position.absaddr.msf.minute, + data.what.position.absaddr.msf.second, + data.what.position.absaddr.msf.frame); + } else { + *position = 0; + } + } + return(status); +} + +/* Start play */ +static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length) +{ + struct ioc_play_msf playtime; + + FRAMES_TO_MSF(start, + &playtime.start_m, &playtime.start_s, &playtime.start_f); + FRAMES_TO_MSF(start+length, + &playtime.end_m, &playtime.end_s, &playtime.end_f); +#ifdef DEBUG_CDROM + fprintf(stderr, "Trying to play from %d:%d:%d to %d:%d:%d\n", + playtime.start_m, playtime.start_s, playtime.start_f, + playtime.end_m, playtime.end_s, playtime.end_f); +#endif + ioctl(cdrom->id, CDIOCSTART, 0); + return(SDL_SYS_CDioctl(cdrom->id, CDIOCPLAYMSF, &playtime)); +} + +/* Pause play */ +static int SDL_SYS_CDPause(SDL_CD *cdrom) +{ + return(SDL_SYS_CDioctl(cdrom->id, CDIOCPAUSE, 0)); +} + +/* Resume play */ +static int SDL_SYS_CDResume(SDL_CD *cdrom) +{ + return(SDL_SYS_CDioctl(cdrom->id, CDIOCRESUME, 0)); +} + +/* Stop play */ +static int SDL_SYS_CDStop(SDL_CD *cdrom) +{ + return(SDL_SYS_CDioctl(cdrom->id, CDIOCSTOP, 0)); +} + +/* Eject the CD-ROM */ +static int SDL_SYS_CDEject(SDL_CD *cdrom) +{ + SDL_SYS_CDioctl(cdrom->id, CDIOCALLOW, 0); + return(SDL_SYS_CDioctl(cdrom->id, CDIOCEJECT, 0)); +} + +/* Close the CD-ROM handle */ +static void SDL_SYS_CDClose(SDL_CD *cdrom) +{ + close(cdrom->id); +} + +void SDL_SYS_CDQuit(void) +{ + int i; + + if ( SDL_numcds > 0 ) { + for ( i=0; i<SDL_numcds; ++i ) { + SDL_free(SDL_cdlist[i]); + } + SDL_numcds = 0; + } +} + +#endif /* SDL_CDROM_OPENBSD */ diff --git a/src/sdl_cdrom/SDL_syscdrom_os2.c b/src/sdl_cdrom/SDL_syscdrom_os2.c new file mode 100644 index 000000000..aa69ef837 --- /dev/null +++ b/src/sdl_cdrom/SDL_syscdrom_os2.c @@ -0,0 +1,398 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2012 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "config.h" +#define COMPAT_SDL_CDROM_PLATFORM_OS2 1 + +#if (C_COMPAT_SDL_CDROM_PLATFORM == COMPAT_SDL_CDROM_PLATFORM_OS2) + +#include "SDL_config.h" + +/*#ifdef SDL_CDROM_OS2*/ + +/* Functions for system-level CD-ROM audio control */ + +#define INCL_MCIOS2 +#include <os2.h> +#include <os2me.h> + +#include "compat_SDL_cdrom.h" +#include "SDL_syscdrom.h" + +/* Size of MCI result buffer (in bytes) */ +#define MCI_CMDRETBUFSIZE 128 + +/* The maximum number of CD-ROM drives we'll detect */ +#define MAX_DRIVES 16 + +/* A list of available CD-ROM drives */ +static char *SDL_cdlist[MAX_DRIVES]; +//static dev_t SDL_cdmode[MAX_DRIVES]; + +/* The system-dependent CD control functions */ +static const char *SDL_SYS_CDName(int drive); +static int SDL_SYS_CDOpen(int drive); +static int SDL_SYS_CDGetTOC(SDL_CD *cdrom); +static CDstatus SDL_SYS_CDStatus(SDL_CD *cdrom, int *position); +static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length); +static int SDL_SYS_CDPause(SDL_CD *cdrom); +static int SDL_SYS_CDResume(SDL_CD *cdrom); +static int SDL_SYS_CDStop(SDL_CD *cdrom); +static int SDL_SYS_CDEject(SDL_CD *cdrom); +static void SDL_SYS_CDClose(SDL_CD *cdrom); + +/* MCI Timing Functions */ +#define MCI_MMTIMEPERSECOND 3000 +#define FRAMESFROMMM(mmtime) (((mmtime)*CD_FPS)/MCI_MMTIMEPERSECOND) + + +/* Ready for MCI CDAudio Devices */ +int SDL_SYS_CDInit(void) +{ +int i; /* generig counter */ +MCI_SYSINFO_PARMS msp; /* Structure to MCI SysInfo parameters */ +CHAR SysInfoRet[MCI_CMDRETBUFSIZE]; /* Buffer for MCI Command result */ + +/* Fill in our driver capabilities */ +SDL_CDcaps.Name = SDL_SYS_CDName; +SDL_CDcaps.Open = SDL_SYS_CDOpen; +SDL_CDcaps.GetTOC = SDL_SYS_CDGetTOC; +SDL_CDcaps.Status = SDL_SYS_CDStatus; +SDL_CDcaps.Play = SDL_SYS_CDPlay; +SDL_CDcaps.Pause = SDL_SYS_CDPause; +SDL_CDcaps.Resume = SDL_SYS_CDResume; +SDL_CDcaps.Stop = SDL_SYS_CDStop; +SDL_CDcaps.Eject = SDL_SYS_CDEject; +SDL_CDcaps.Close = SDL_SYS_CDClose; + +/* Get the number of CD ROMs in the System */ +/* Clean SysInfo structure */ +SDL_memset(&msp, 0x00, sizeof(MCI_SYSINFO_PARMS)); +/* Prepare structure to Ask Numer of Audio CDs */ +msp.usDeviceType = MCI_DEVTYPE_CD_AUDIO; /* CD Audio Type */ +msp.pszReturn = (PSZ)&SysInfoRet; /* Return Structure */ +msp.ulRetSize = MCI_CMDRETBUFSIZE; /* Size of ret struct */ +if (LOUSHORT(mciSendCommand(0,MCI_SYSINFO, MCI_SYSINFO_QUANTITY | MCI_WAIT, (PVOID)&msp, 0)) != MCIERR_SUCCESS) return(CD_ERROR); +SDL_numcds = atoi(SysInfoRet); +if (SDL_numcds > MAX_DRIVES) SDL_numcds = MAX_DRIVES; /* Limit maximum CD number */ + +/* Get and Add their system name to the SDL_cdlist */ +msp.pszReturn = (PSZ)&SysInfoRet; /* Return Structure */ +msp.ulRetSize = MCI_CMDRETBUFSIZE; /* Size of ret struct */ +msp.usDeviceType = MCI_DEVTYPE_CD_AUDIO; /* CD Audio Type */ +for (i=0; i<SDL_numcds; i++) + { + msp.ulNumber = i+1; + mciSendCommand(0,MCI_SYSINFO, MCI_SYSINFO_NAME | MCI_WAIT,&msp, 0); + SDL_cdlist[i] = SDL_strdup(SysInfoRet); + if ( SDL_cdlist[i] == NULL ) + { + SDL_OutOfMemory(); + return(-1); + } + } +return(0); +} + +/* Return CDAudio System Dependent Device Name - Ready for MCI*/ +static const char *SDL_SYS_CDName(int drive) +{ +return(SDL_cdlist[drive]); +} + +/* Open CDAudio Device - Ready for MCI */ +static int SDL_SYS_CDOpen(int drive) +{ +MCI_OPEN_PARMS mop; +MCI_SET_PARMS msp; +MCI_GENERIC_PARMS mgp; + +/* Open the device */ +mop.hwndCallback = (HWND)NULL; // None +mop.usDeviceID = (USHORT)NULL; // Will be returned. +mop.pszDeviceType = (PSZ)SDL_cdlist[drive]; // CDAudio Device +if (LOUSHORT(mciSendCommand(0,MCI_OPEN,MCI_WAIT,&mop, 0)) != MCIERR_SUCCESS) return(CD_ERROR); +/* Set time format */ +msp.hwndCallback = (HWND)NULL; // None +msp.ulTimeFormat = MCI_FORMAT_MSF; // Minute : Second : Frame structure +msp.ulSpeedFormat = (ULONG)NULL; // No change +msp.ulAudio = (ULONG)NULL; // No Channel +msp.ulLevel = (ULONG)NULL; // No Volume +msp.ulOver = (ULONG)NULL; // No Delay +msp.ulItem = (ULONG)NULL; // No item +msp.ulValue = (ULONG)NULL; // No value for item flag +if (LOUSHORT(mciSendCommand(mop.usDeviceID,MCI_SET,MCI_WAIT | MCI_SET_TIME_FORMAT,&msp, 0)) == MCIERR_SUCCESS) return (mop.usDeviceID); +/* Error setting time format? - Close opened device */ +mgp.hwndCallback = (HWND)NULL; // None +mciSendCommand(mop.usDeviceID,MCI_CLOSE,MCI_WAIT,&mgp, 0); +return(CD_ERROR); +} + +/* Get CD Table Of Contents - Ready for MCI */ +static int SDL_SYS_CDGetTOC(SDL_CD *cdrom) +{ +MCI_TOC_PARMS mtp; +MCI_STATUS_PARMS msp; +MCI_TOC_REC * mtr; +INT i; + +/* Correction because MCI cannot read TOC while CD is playing (it'll stop!) */ +if (cdrom->status == CD_PLAYING || cdrom->status == CD_PAUSED) return 0; + +/* Get Number of Tracks */ +msp.hwndCallback = (HWND)NULL; /* None */ +msp.ulReturn = (ULONG)NULL; /* We want this information */ +msp.ulItem = MCI_STATUS_NUMBER_OF_TRACKS; +msp.ulValue = (ULONG)NULL; /* No additional information */ +if (LOUSHORT(mciSendCommand(cdrom->id,MCI_STATUS,MCI_WAIT | MCI_STATUS_ITEM,&msp, 0)) != MCIERR_SUCCESS) return(CD_ERROR); +cdrom->numtracks = msp.ulReturn; +if ( cdrom->numtracks > SDL_MAX_TRACKS ) + { + cdrom->numtracks = SDL_MAX_TRACKS; + } +/* Alocate space for TOC data */ +mtr = (MCI_TOC_REC *)SDL_malloc(cdrom->numtracks*sizeof(MCI_TOC_REC)); +if ( mtr == NULL ) + { + SDL_OutOfMemory(); + return(-1); + } +/* Get TOC from CD */ +mtp.pBuf = mtr; +mtp.ulBufSize = cdrom->numtracks*sizeof(MCI_TOC_REC); +if (LOUSHORT(mciSendCommand(cdrom->id,MCI_GETTOC,MCI_WAIT,&mtp, 0)) != MCIERR_SUCCESS) + { + SDL_OutOfMemory(); + SDL_free(mtr); + return(CD_ERROR); + } +/* Fill SDL Tracks Structure */ +for (i=0; i<cdrom->numtracks; i++) + { + /* Set Track ID */ + cdrom->track[i].id = (mtr+i)->TrackNum; + /* Set Track Type */ + msp.hwndCallback = (HWND)NULL; /* None */ + msp.ulReturn = (ULONG)NULL; /* We want this information */ + msp.ulItem = MCI_CD_STATUS_TRACK_TYPE; + msp.ulValue = (ULONG)((mtr+i)->TrackNum); /* Track Number? */ + if (LOUSHORT(mciSendCommand(cdrom->id,MCI_STATUS,MCI_WAIT | MCI_TRACK | MCI_STATUS_ITEM,&msp, 0)) != MCIERR_SUCCESS) + { + SDL_free(mtr); + return (CD_ERROR); + } + if (msp.ulReturn==MCI_CD_TRACK_AUDIO) cdrom->track[i].type = SDL_AUDIO_TRACK; + else cdrom->track[i].type = SDL_DATA_TRACK; + /* Set Track Length - values from MCI are in MMTIMEs - 3000 MMTIME = 1 second */ + cdrom->track[i].length = FRAMESFROMMM((mtr+i)->ulEndAddr - (mtr+i)->ulStartAddr); + /* Set Track Offset */ + cdrom->track[i].offset = FRAMESFROMMM((mtr+i)->ulStartAddr); + } +SDL_free(mtr); +return(0); +} + + +/* Get CD-ROM status - Ready for MCI */ +static CDstatus SDL_SYS_CDStatus(SDL_CD *cdrom, int *position) +{ +CDstatus status; +MCI_STATUS_PARMS msp; + +/* Get Status from MCI */ +msp.hwndCallback = (HWND)NULL; /* None */ +msp.ulReturn = (ULONG)NULL; /* We want this information */ +msp.ulItem = MCI_STATUS_MODE; +msp.ulValue = (ULONG)NULL; /* No additional information */ +if (LOUSHORT(mciSendCommand(cdrom->id,MCI_STATUS,MCI_WAIT | MCI_STATUS_ITEM,&msp, 0)) != MCIERR_SUCCESS) status = CD_ERROR; +else + { + switch(msp.ulReturn) + { + case MCI_MODE_NOT_READY: + status = CD_TRAYEMPTY; + break; + case MCI_MODE_PAUSE: + status = CD_PAUSED; + break; + case MCI_MODE_PLAY: + status = CD_PLAYING; + break; + case MCI_MODE_STOP: + status = CD_STOPPED; + break; + /* These cases should not occour */ + case MCI_MODE_RECORD: + case MCI_MODE_SEEK: + default: + status = CD_ERROR; + break; + } + } + +/* Determine position */ +if (position != NULL) /* The SDL $&$&%# CDROM call sends NULL pointer here! */ + { + if ((status == CD_PLAYING) || (status == CD_PAUSED)) + { + /* Get Position */ + msp.hwndCallback = (HWND)NULL; /* None */ + msp.ulReturn = (ULONG)NULL; /* We want this information */ + msp.ulItem = MCI_STATUS_POSITION; + msp.ulValue = (ULONG)NULL; /* No additiona info */ + if (LOUSHORT(mciSendCommand(cdrom->id,MCI_STATUS,MCI_WAIT | MCI_STATUS_ITEM,&msp, 0)) != MCIERR_SUCCESS) return (CD_ERROR); + /* Convert from MSF (format selected in the Open process) to Frames (format that will be returned) */ + *position = MSF_TO_FRAMES(MSF_MINUTE(msp.ulReturn),MSF_SECOND(msp.ulReturn),MSF_FRAME(msp.ulReturn)); + } + else *position = 0; + } +return(status); +} + +/* Start play - Ready for MCI */ +static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length) +{ +MCI_GENERIC_PARMS mgp; +MCI_STATUS_PARMS msp; +MCI_PLAY_PARMS mpp; +ULONG min,sec,frm; + +/* Start MSF */ +FRAMES_TO_MSF(start, &min, &sec, &frm); +MSF_MINUTE(mpp.ulFrom) = min; +MSF_SECOND(mpp.ulFrom) = sec; +MSF_FRAME(mpp.ulFrom) = frm; +/* End MSF */ +FRAMES_TO_MSF(start+length, &min, &sec, &frm); +MSF_MINUTE(mpp.ulTo) = min; +MSF_SECOND(mpp.ulTo) = sec; +MSF_FRAME(mpp.ulTo) = frm; +#ifdef DEBUG_CDROM + fprintf(stderr, "Trying to play from %d:%d:%d to %d:%d:%d\n", + playtime.cdmsf_min0, playtime.cdmsf_sec0, playtime.cdmsf_frame0, + playtime.cdmsf_min1, playtime.cdmsf_sec1, playtime.cdmsf_frame1); +#endif +/* Verifies if it is paused first... and if it is, unpause before stopping it. */ +msp.hwndCallback = (HWND)NULL; /* None */ +msp.ulReturn = (ULONG)NULL; /* We want this information */ +msp.ulItem = MCI_STATUS_MODE; +msp.ulValue = (ULONG)NULL; /* No additional information */ +if (LOUSHORT(mciSendCommand(cdrom->id,MCI_STATUS,MCI_WAIT | MCI_STATUS_ITEM,&msp, 0)) == MCIERR_SUCCESS) + { + if (msp.ulReturn == MCI_MODE_PAUSE) + { + mgp.hwndCallback = (HWND)NULL; // None + mciSendCommand(cdrom->id,MCI_RESUME,0,&mgp, 0); + } + } +/* Now play it. */ +mpp.hwndCallback = (HWND)NULL; // We do not want the info. temp +if (LOUSHORT(mciSendCommand(cdrom->id,MCI_PLAY,MCI_FROM | MCI_TO,&mpp, 0)) == MCIERR_SUCCESS) return 0; +return (CD_ERROR); +} + +/* Pause play - Ready for MCI */ +static int SDL_SYS_CDPause(SDL_CD *cdrom) +{ +MCI_GENERIC_PARMS mgp; + +mgp.hwndCallback = (HWND)NULL; // None +if (LOUSHORT(mciSendCommand(cdrom->id,MCI_PAUSE,MCI_WAIT,&mgp, 0)) == MCIERR_SUCCESS) return 0; +return(CD_ERROR); +} + +/* Resume play - Ready for MCI */ +static int SDL_SYS_CDResume(SDL_CD *cdrom) +{ +MCI_GENERIC_PARMS mgp; + +mgp.hwndCallback = (HWND)NULL; // None +if (LOUSHORT(mciSendCommand(cdrom->id,MCI_RESUME,MCI_WAIT,&mgp, 0)) == MCIERR_SUCCESS) return 0; +return(CD_ERROR); +} + +/* Stop play - Ready for MCI */ +static int SDL_SYS_CDStop(SDL_CD *cdrom) +{ +MCI_GENERIC_PARMS mgp; +MCI_STATUS_PARMS msp; + +/* Verifies if it is paused first... and if it is, unpause before stopping it. */ +msp.hwndCallback = (HWND)NULL; /* None */ +msp.ulReturn = (ULONG)NULL; /* We want this information */ +msp.ulItem = MCI_STATUS_MODE; +msp.ulValue = (ULONG)NULL; /* No additional information */ +if (LOUSHORT(mciSendCommand(cdrom->id,MCI_STATUS,MCI_WAIT | MCI_STATUS_ITEM,&msp, 0)) == MCIERR_SUCCESS) + { + if (msp.ulReturn == MCI_MODE_PAUSE) + { + mgp.hwndCallback = (HWND)NULL; // None + mciSendCommand(cdrom->id,MCI_RESUME,0,&mgp, 0); + } + } +/* Now stops the media */ +mgp.hwndCallback = (HWND)NULL; // None +if (LOUSHORT(mciSendCommand(cdrom->id,MCI_STOP,MCI_WAIT,&mgp, 0)) == MCIERR_SUCCESS) return 0; +return(CD_ERROR); +} + +/* Eject the CD-ROM - Ready for MCI */ +static int SDL_SYS_CDEject(SDL_CD *cdrom) +{ +MCI_SET_PARMS msp; + +msp.hwndCallback = (HWND)NULL; // None +msp.ulTimeFormat = (ULONG)NULL; // No change +msp.ulSpeedFormat = (ULONG)NULL; // No change +msp.ulAudio = (ULONG)NULL; // No Channel +msp.ulLevel = (ULONG)NULL; // No Volume +msp.ulOver = (ULONG)NULL; // No Delay +msp.ulItem = (ULONG)NULL; // No item +msp.ulValue = (ULONG)NULL; // No value for item flag +if (LOUSHORT(mciSendCommand(cdrom->id,MCI_SET,MCI_WAIT | MCI_SET_DOOR_OPEN,&msp, 0)) == MCIERR_SUCCESS) return 0; +return(CD_ERROR); +} + +/* Close the CD-ROM handle - Ready for MCI */ +static void SDL_SYS_CDClose(SDL_CD *cdrom) +{ +MCI_GENERIC_PARMS mgp; + +mgp.hwndCallback = (HWND)NULL; // None +mciSendCommand(cdrom->id,MCI_CLOSE,MCI_WAIT,&mgp, 0); +} + +/* Finalize CDROM Subsystem - Ready for MCI */ +void SDL_SYS_CDQuit(void) +{ +int i; + +if ( SDL_numcds > 0 ) + { + for ( i=0; i<SDL_numcds; ++i ) + { + SDL_free(SDL_cdlist[i]); + } + SDL_numcds = 0; + } +} + +#endif /* SDL_CDROM_OS2 */ diff --git a/src/sdl_cdrom/SDL_syscdrom_osf.c b/src/sdl_cdrom/SDL_syscdrom_osf.c new file mode 100644 index 000000000..fb281f9ad --- /dev/null +++ b/src/sdl_cdrom/SDL_syscdrom_osf.c @@ -0,0 +1,449 @@ +/* + Tru64 audio module for SDL (Simple DirectMedia Layer) + Copyright (C) 2003 + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +*/ +#include "config.h" +#define COMPAT_SDL_CDROM_PLATFORM_OSF 1 + +#if (C_COMPAT_SDL_CDROM_PLATFORM == COMPAT_SDL_CDROM_PLATFORM_OSF) + +#include "SDL_config.h" + +/*#ifdef SDL_CDROM_OSF*/ + +/* Functions for system-level CD-ROM audio control */ + +/* #define DEBUG_CDROM 1 */ + +#include <sys/types.h> +#include <dirent.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <io/cam/cdrom.h> +#include <io/cam/rzdisk.h> +#include <io/common/devgetinfo.h> + +#include "compat_SDL_cdrom.h" +#include "SDL_syscdrom.h" + +/* The maximum number of CD-ROM drives we'll detect */ +#define MAX_DRIVES 16 + +/* A list of available CD-ROM drives */ +static char *SDL_cdlist[MAX_DRIVES]; +static dev_t SDL_cdmode[MAX_DRIVES]; + +/* The system-dependent CD control functions */ +static const char *SDL_SYS_CDName(int drive); +static int SDL_SYS_CDOpen(int drive); +static int SDL_SYS_CDGetTOC(SDL_CD *cdrom); +static CDstatus SDL_SYS_CDStatus(SDL_CD *cdrom, int *position); +static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length); +static int SDL_SYS_CDPause(SDL_CD *cdrom); +static int SDL_SYS_CDResume(SDL_CD *cdrom); +static int SDL_SYS_CDStop(SDL_CD *cdrom); +static int SDL_SYS_CDEject(SDL_CD *cdrom); +static void SDL_SYS_CDClose(SDL_CD *cdrom); + +/* Check a drive to see if it is a CD-ROM */ +/* Caution!! Not tested. */ +static int CheckDrive(char *drive, struct stat *stbuf) +{ + int cdfd, is_cd = 0; + struct mode_sel_sns_params msp; + struct inquiry_info inq; + +#ifdef DEBUG_CDROM + char *devtype[] = {"Disk", "Tape", "Printer", "Processor", "WORM", + "CD-ROM", "Scanner", "Optical", "Changer", "Comm", "Unknown"}; +#endif + + bzero(&msp, sizeof(msp)); + bzero(&inq, sizeof(inq)); + + /* If it doesn't exist, return -1 */ + if ( stat(drive, stbuf) < 0 ) { + return(-1); + } + + if ( (cdfd = open(drive, (O_RDWR|O_NDELAY), 0)) >= 0 ) { + msp.msp_addr = (caddr_t) &inq; + msp.msp_pgcode = 0; + msp.msp_pgctrl = 0; + msp.msp_length = sizeof(inq); + msp.msp_setps = 0; + + if ( ioctl(cdfd, SCSI_GET_INQUIRY_DATA, &msp) ) + return (0); + +#ifdef DEBUG_CDROM + fprintf(stderr, "Device Type: %s\n", devtype[inq.perfdt]); + fprintf(stderr, "Vendor: %.8s\n", inq.vndrid); + fprintf(stderr, "Product: %.8s\n", inq.prodid); + fprintf(stderr, "Revision: %.8s\n", inq.revlvl); +#endif + if ( inq.perfdt == DTYPE_RODIRECT ) + is_cd = 1; + } + + return(is_cd); +} + +/* Add a CD-ROM drive to our list of valid drives */ +static void AddDrive(char *drive, struct stat *stbuf) +{ + int i; + + if ( SDL_numcds < MAX_DRIVES ) { + /* Check to make sure it's not already in our list. + * This can happen when we see a drive via symbolic link. + * + */ + for ( i=0; i<SDL_numcds; ++i ) { + if ( stbuf->st_rdev == SDL_cdmode[i] ) { +#ifdef DEBUG_CDROM + fprintf(stderr, "Duplicate drive detected: %s == %s\n", drive, SDL_cdlist[i]); +#endif + return; + } + } + + /* Add this drive to our list */ + i = SDL_numcds; + SDL_cdlist[i] = SDL_strdup(drive); + if ( SDL_cdlist[i] == NULL ) { + SDL_OutOfMemory(); + return; + } + SDL_cdmode[i] = stbuf->st_rdev; + ++SDL_numcds; +#ifdef DEBUG_CDROM + fprintf(stderr, "Added CD-ROM drive: %s\n", drive); +#endif + } +} + +int SDL_SYS_CDInit(void) +{ + /* checklist: + * + * Tru64 5.X (/dev/rdisk/cdrom?c) + * dir: /dev/rdisk, name: cdrom + * + * Digital UNIX 4.0X (/dev/rrz?c) + * dir: /dev, name: rrz + * + */ + struct { + char *dir; + char *name; + } checklist[] = { + {"/dev/rdisk", "cdrom"}, + {"/dev", "rrz"}, + {NULL, NULL}}; + char drive[32]; + char *SDLcdrom; + int i, j, exists; + struct stat stbuf; + + /* Fill in our driver capabilities */ + SDL_CDcaps.Name = SDL_SYS_CDName; + SDL_CDcaps.Open = SDL_SYS_CDOpen; + SDL_CDcaps.GetTOC = SDL_SYS_CDGetTOC; + SDL_CDcaps.Status = SDL_SYS_CDStatus; + SDL_CDcaps.Play = SDL_SYS_CDPlay; + SDL_CDcaps.Pause = SDL_SYS_CDPause; + SDL_CDcaps.Resume = SDL_SYS_CDResume; + SDL_CDcaps.Stop = SDL_SYS_CDStop; + SDL_CDcaps.Eject = SDL_SYS_CDEject; + SDL_CDcaps.Close = SDL_SYS_CDClose; + + + /* Look in the environment for our CD-ROM drive list */ + SDLcdrom = SDL_getenv("SDL_CDROM"); /* ':' separated list of devices */ + if ( SDLcdrom != NULL ) { + char *cdpath, *delim; + size_t len = SDL_strlen(SDLcdrom)+1; + cdpath = SDL_stack_alloc(char, len); + if ( cdpath != NULL ) { + SDL_strlcpy(cdpath, SDLcdrom, len); + SDLcdrom = cdpath; + do { + delim = SDL_strchr(SDLcdrom, ':'); + if ( delim ) { + *delim++ = '\0'; + } + if ( CheckDrive(SDLcdrom, &stbuf) > 0 ) { + AddDrive(SDLcdrom, &stbuf); + } + if ( delim ) { + SDLcdrom = delim; + } else { + SDLcdrom = NULL; + } + } while ( SDLcdrom ); + SDL_stack_free(cdpath); + } + + /* If we found our drives, there's nothing left to do */ + if ( SDL_numcds > 0 ) { + return(0); + } + } + /* Scan the system for CD-ROM drives */ + for ( i = 0; checklist[i].dir; ++i) { + DIR *devdir; + struct dirent *devent; + int name_len; + + devdir = opendir(checklist[i].dir); + if (devdir) { + name_len = SDL_strlen(checklist[i].name); + while (devent = readdir(devdir)) + if (SDL_memcmp(checklist[i].name, devent->d_name, name_len) == 0) + if (devent->d_name[devent->d_namlen-1] == 'c') { + SDL_snprintf(drive, SDL_arraysize(drive), "%s/%s", checklist[i].dir, devent->d_name); +#ifdef DEBUG_CDROM + fprintf(stderr, "Try to add drive: %s\n", drive); +#endif + if ( CheckDrive(drive, &stbuf) > 0 ) + AddDrive(drive, &stbuf); + } + closedir(devdir); + } else { +#ifdef DEBUG_CDROM + fprintf(stderr, "cannot open dir: %s\n", checklist[i].dir); +#endif + } + } + return (0); +} + +static const char *SDL_SYS_CDName(int drive) +{ + return(SDL_cdlist[drive]); +} + +static int SDL_SYS_CDOpen(int drive) +{ + /* O_RDWR: To use ioctl(fd, SCSI_STOP_UNIT) */ + return(open(SDL_cdlist[drive], (O_RDWR|O_NDELAY), 0)); +} + +static int SDL_SYS_CDGetTOC(SDL_CD *cdrom) +{ + struct cd_toc toc; + struct cd_toc_header hdr; + struct cd_toc_entry *cdte; + int i; + int okay = 0; + if ( ioctl(cdrom->id, CDROM_TOC_HEADER, &hdr) ) { + fprintf(stderr,"ioctl error CDROM_TOC_HEADER\n"); + return -1; + } + cdrom->numtracks = hdr.th_ending_track - hdr.th_starting_track + 1; + if ( cdrom->numtracks > SDL_MAX_TRACKS ) { + cdrom->numtracks = SDL_MAX_TRACKS; + } +#ifdef DEBUG_CDROM + fprintf(stderr,"hdr.th_data_len1 = %d\n", hdr.th_data_len1); + fprintf(stderr,"hdr.th_data_len0 = %d\n", hdr.th_data_len0); + fprintf(stderr,"hdr.th_starting_track = %d\n", hdr.th_starting_track); + fprintf(stderr,"hdr.th_ending_track = %d\n", hdr.th_ending_track); + fprintf(stderr,"cdrom->numtracks = %d\n", cdrom->numtracks); +#endif + toc.toc_address_format = CDROM_LBA_FORMAT; + toc.toc_starting_track = 0; + toc.toc_alloc_length = (hdr.th_data_len1 << 8) + + hdr.th_data_len0 + sizeof(hdr); + if ( (toc.toc_buffer = alloca(toc.toc_alloc_length)) == NULL) { + fprintf(stderr,"cannot allocate toc.toc_buffer\n"); + return -1; + } + + bzero (toc.toc_buffer, toc.toc_alloc_length); + if (ioctl(cdrom->id, CDROM_TOC_ENTRYS, &toc)) { + fprintf(stderr,"ioctl error CDROM_TOC_ENTRYS\n"); + return -1; + } + + cdte =(struct cd_toc_entry *) ((char *) toc.toc_buffer + sizeof(hdr)); + for (i=0; i <= cdrom->numtracks; ++i) { + if (i == cdrom->numtracks ) { + cdrom->track[i].id = 0xAA;; + } else { + cdrom->track[i].id = hdr.th_starting_track + i; + } + + cdrom->track[i].type = + cdte[i].te_control & CDROM_DATA_TRACK; + cdrom->track[i].offset = + cdte[i].te_absaddr.lba.addr3 << 24 | + cdte[i].te_absaddr.lba.addr2 << 16 | + cdte[i].te_absaddr.lba.addr1 << 8 | + cdte[i].te_absaddr.lba.addr0; + cdrom->track[i].length = 0; + if ( i > 0 ) { + cdrom->track[i - 1].length = + cdrom->track[i].offset - + cdrom->track[i - 1].offset; + } + } +#ifdef DEBUG_CDROM + for (i = 0; i <= cdrom->numtracks; i++) { + fprintf(stderr,"toc_entry[%d].te_track_number = %d\n", + i,cdte[i].te_track_number); + fprintf(stderr,"cdrom->track[%d].id = %d\n", i,cdrom->track[i].id); + fprintf(stderr,"cdrom->track[%d].type = %x\n", i,cdrom->track[i].type); + fprintf(stderr,"cdrom->track[%d].offset = %d\n", i,cdrom->track[i].offset); + fprintf(stderr,"cdrom->track[%d].length = %d\n", i,cdrom->track[i].length); + } +#endif + if ( i == (cdrom->numtracks+1) ) { + okay = 1; + } + + return(okay ? 0 : -1); +} + +/* Get CD-ROM status */ +static CDstatus SDL_SYS_CDStatus(SDL_CD *cdrom, int *position) +{ + CDstatus status; + struct cd_sub_channel sc; + struct cd_subc_channel_data scd; + + sc.sch_address_format = CDROM_LBA_FORMAT; + sc.sch_data_format = CDROM_CURRENT_POSITION; + sc.sch_track_number = 0; + sc.sch_alloc_length = sizeof(scd); + sc.sch_buffer = (caddr_t)&scd; + if ( ioctl(cdrom->id, CDROM_READ_SUBCHANNEL, &sc) ) { + status = CD_ERROR; + fprintf(stderr,"ioctl error CDROM_READ_SUBCHANNEL \n"); + } else { + switch (scd.scd_header.sh_audio_status) { + case AS_AUDIO_INVALID: + status = CD_STOPPED; + break; + case AS_PLAY_IN_PROGRESS: + status = CD_PLAYING; + break; + case AS_PLAY_PAUSED: + status = CD_PAUSED; + break; + case AS_PLAY_COMPLETED: + status = CD_STOPPED; + break; + case AS_PLAY_ERROR: + status = CD_ERROR; + break; + case AS_NO_STATUS: + status = CD_STOPPED; + break; + default: + status = CD_ERROR; + break; + } +#ifdef DEBUG_CDROM + fprintf(stderr,"scd.scd_header.sh_audio_status = %x\n", + scd.scd_header.sh_audio_status); +#endif + } + if (position) { + if (status == CD_PLAYING || (status == CD_PAUSED) ) { + *position = + scd.scd_position_data.scp_absaddr.lba.addr3 << 24 | + scd.scd_position_data.scp_absaddr.lba.addr2 << 16 | + scd.scd_position_data.scp_absaddr.lba.addr1 << 8 | + scd.scd_position_data.scp_absaddr.lba.addr0; + } else { + *position = 0; + } + } + + return status; +} + +/* Start play */ +static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length) +{ +/* + * Play MSF + */ + struct cd_play_audio_msf msf; + int end; + + bzero(&msf, sizeof(msf)); + end = start +length; + FRAMES_TO_MSF(start + 150, /* LBA = 4500*M + 75*S + F - 150 */ + &msf.msf_starting_M_unit, + &msf.msf_starting_S_unit, + &msf.msf_starting_F_unit); + FRAMES_TO_MSF(end + 150, /* LBA = 4500*M + 75*S + F - 150 */ + &msf.msf_ending_M_unit, + &msf.msf_ending_S_unit, + &msf.msf_ending_F_unit); + + return(ioctl(cdrom->id, CDROM_PLAY_AUDIO_MSF, &msf)); +} + +/* Pause play */ +static int SDL_SYS_CDPause(SDL_CD *cdrom) +{ + return(ioctl(cdrom->id, CDROM_PAUSE_PLAY)); +} + +/* Resume play */ +static int SDL_SYS_CDResume(SDL_CD *cdrom) +{ + return(ioctl(cdrom->id, CDROM_RESUME_PLAY)); +} + +/* Stop play */ +static int SDL_SYS_CDStop(SDL_CD *cdrom) +{ + return(ioctl(cdrom->id, SCSI_STOP_UNIT)); +} + +/* Eject the CD-ROM */ +static int SDL_SYS_CDEject(SDL_CD *cdrom) +{ + return(ioctl(cdrom->id, CDROM_EJECT_CADDY)); +} + +/* Close the CD-ROM handle */ +static void SDL_SYS_CDClose(SDL_CD *cdrom) +{ + close(cdrom->id); +} + +void SDL_SYS_CDQuit(void) +{ + int i; + + if ( SDL_numcds > 0 ) { + for ( i=0; i<SDL_numcds; ++i ) { + SDL_free(SDL_cdlist[i]); + } + SDL_numcds = 0; + } +} + +#endif /* SDL_CDROM_OSF */ diff --git a/src/sdl_cdrom/SDL_syscdrom_qnx.c b/src/sdl_cdrom/SDL_syscdrom_qnx.c new file mode 100644 index 000000000..c656a7809 --- /dev/null +++ b/src/sdl_cdrom/SDL_syscdrom_qnx.c @@ -0,0 +1,556 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2012 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "config.h" +#define COMPAT_SDL_CDROM_PLATFORM_QNX 1 + +#if (C_COMPAT_SDL_CDROM_PLATFORM == COMPAT_SDL_CDROM_PLATFORM_QNX) + +#include "SDL_config.h" + +/*#ifdef SDL_CDROM_QNX*/ + +/* Functions for system-level CD-ROM audio control */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/ioctl.h> +#include <fcntl.h> +#include <errno.h> +#include <unistd.h> +#include <sys/cdrom.h> +#include <sys/dcmd_cam.h> + +#include "SDL_timer.h" +#include "compat_SDL_cdrom.h" +#include "SDL_syscdrom.h" + +/* The maximum number of CD-ROM drives we'll detect */ +#define MAX_DRIVES 16 + +#define QNX_CD_OPENMODE O_RDONLY | O_EXCL + +/* A list of available CD-ROM drives */ +static char *SDL_cdlist[MAX_DRIVES]; +static dev_t SDL_cdmode[MAX_DRIVES]; +static int SDL_cdopen[MAX_DRIVES]; + +/* The system-dependent CD control functions */ +static const char *SDL_SYS_CDName(int drive); +static int SDL_SYS_CDOpen(int drive); +static int SDL_SYS_CDGetTOC(SDL_CD *cdrom); +static CDstatus SDL_SYS_CDStatus(SDL_CD *cdrom, int *position); +static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length); +static int SDL_SYS_CDPause(SDL_CD *cdrom); +static int SDL_SYS_CDResume(SDL_CD *cdrom); +static int SDL_SYS_CDStop(SDL_CD *cdrom); +static int SDL_SYS_CDEject(SDL_CD *cdrom); +static void SDL_SYS_CDClose(SDL_CD *cdrom); + +/* Check a drive to see if it is a CD-ROM */ +static int CheckDrive(char *drive, struct stat *stbuf) +{ + int is_cd, cdfd; + cam_devinfo_t dinfo; + int devctlret=0; + + int atapi; + int removable; + int cdb10; + + /* If it doesn't exist, return -1 */ + if (stat(drive, stbuf) < 0) + { + return(-1); + } + + /* If it does exist, verify that it's an available CD-ROM */ + is_cd = 0; + + if (S_ISCHR(stbuf->st_mode) || S_ISBLK(stbuf->st_mode)) + { + cdfd = open(drive, QNX_CD_OPENMODE); + if ( cdfd >= 0 ) + { + devctlret=devctl(cdfd, DCMD_CAM_DEVINFO, &dinfo, sizeof(cam_devinfo_t), NULL); + + if (devctlret==EOK) + { + atapi=dinfo.flags & DEV_ATAPI; + removable=dinfo.flags & DEV_REMOVABLE; + cdb10=dinfo.flags & DEV_CDB_10; /* I'm not sure about that flag */ + + /* in the near future need to add more checks for splitting cdroms from other devices */ + if ((atapi)&&(removable)) + { + is_cd = 1; + } + } + + close(cdfd); + } + } + return(is_cd); +} + +/* Add a CD-ROM drive to our list of valid drives */ +static void AddDrive(char *drive, struct stat *stbuf) +{ + int i; + + if (SDL_numcds < MAX_DRIVES) + { + /* Check to make sure it's not already in our list. + This can happen when we see a drive via symbolic link. */ + + for (i=0; i<SDL_numcds; ++i) + { + if (stbuf->st_rdev == SDL_cdmode[i]) + { + return; + } + } + + /* Add this drive to our list */ + + i = SDL_numcds; + SDL_cdlist[i] = SDL_strdup(drive); + if (SDL_cdlist[i] == NULL) + { + SDL_OutOfMemory(); + return; + } + SDL_cdmode[i] = stbuf->st_rdev; + ++SDL_numcds; + } +} + +int SDL_SYS_CDInit(void) +{ + /* checklist: /dev/cdrom, /dev/cd?, /dev/scd? */ + static char *checklist[]={"cdrom", "?0 cd?", "?1 cd?", "?0 scd?", NULL}; + + char *SDLcdrom; + int i, j, exists; + char drive[32]; + struct stat stbuf; + + /* Fill in our driver capabilities */ + SDL_CDcaps.Name = SDL_SYS_CDName; + SDL_CDcaps.Open = SDL_SYS_CDOpen; + SDL_CDcaps.GetTOC = SDL_SYS_CDGetTOC; + SDL_CDcaps.Status = SDL_SYS_CDStatus; + SDL_CDcaps.Play = SDL_SYS_CDPlay; + SDL_CDcaps.Pause = SDL_SYS_CDPause; + SDL_CDcaps.Resume = SDL_SYS_CDResume; + SDL_CDcaps.Stop = SDL_SYS_CDStop; + SDL_CDcaps.Eject = SDL_SYS_CDEject; + SDL_CDcaps.Close = SDL_SYS_CDClose; + + /* clearing device open status */ + for (i=0; i<MAX_DRIVES; i++) + { + SDL_cdopen[i]=0; + } + + /* Look in the environment for our CD-ROM drive list */ + SDLcdrom = SDL_getenv("SDL_CDROM"); /* ':' separated list of devices */ + if ( SDLcdrom != NULL ) + { + char *cdpath, *delim; + size_t len = SDL_strlen(SDLcdrom)+1; + cdpath = SDL_stack_alloc(char, len); + if (cdpath != NULL) + { + SDL_strlcpy(cdpath, SDLcdrom, len); + SDLcdrom = cdpath; + do { + delim = SDL_strchr(SDLcdrom, ':'); + if (delim) + { + *delim++ = '\0'; + } + if (CheckDrive(SDLcdrom, &stbuf) > 0) + { + AddDrive(SDLcdrom, &stbuf); + } + if (delim) + { + SDLcdrom = delim; + } + else + { + SDLcdrom = NULL; + } + } while (SDLcdrom); + SDL_stack_free(cdpath); + } + + /* If we found our drives, there's nothing left to do */ + if (SDL_numcds > 0) + { + return(0); + } + } + + /* Scan the system for CD-ROM drives */ + for ( i=0; checklist[i]; ++i ) + { + if (checklist[i][0] == '?') + { + char* insert; + exists = 1; + + for ( j=checklist[i][1]; exists; ++j ) + { + SDL_snprintf(drive, SDL_arraysize(drive), "/dev/%s", &checklist[i][3]); + insert = SDL_strchr(drive, '?'); + if (insert != NULL) + { + *insert = j; + } + switch (CheckDrive(drive, &stbuf)) + { + /* Drive exists and is a CD-ROM */ + case 1: + AddDrive(drive, &stbuf); + break; + /* Drive exists, but isn't a CD-ROM */ + case 0: + break; + /* Drive doesn't exist */ + case -1: + exists = 0; + break; + } + } + } + else + { + SDL_snprintf(drive, SDL_arraysize(drive), "/dev/%s", checklist[i]); + if (CheckDrive(drive, &stbuf) > 0) + { + AddDrive(drive, &stbuf); + } + } + } + return(0); +} + +static const char *SDL_SYS_CDName(int drive) +{ + return(SDL_cdlist[drive]); +} + +static int SDL_SYS_CDOpen(int drive) +{ + int handle; + + handle=open(SDL_cdlist[drive], QNX_CD_OPENMODE); + + if (handle>0) + { + SDL_cdopen[drive]=handle; + } + + return (handle); +} + +static int SDL_SYS_CDGetTOC(SDL_CD *cdrom) +{ + cdrom_read_toc_t toc; + int i, okay; + + okay = 0; + if (devctl(cdrom->id, DCMD_CAM_CDROMREADTOC, &toc, sizeof(toc), NULL) == 0) + { + cdrom->numtracks = toc.last_track - toc.first_track + 1; + if (cdrom->numtracks > SDL_MAX_TRACKS) + { + cdrom->numtracks = SDL_MAX_TRACKS; + } + /* Read all the track TOC entries */ + for (i=0; i<=cdrom->numtracks; ++i) + { + if (i == cdrom->numtracks) + { + cdrom->track[i].id = CDROM_LEADOUT; + } + else + { + cdrom->track[i].id = toc.first_track+i; + } + + cdrom->track[i].type = toc.toc_entry[i].control_adr & 0x0F; + cdrom->track[i].offset = toc.toc_entry[i].addr.lba; + cdrom->track[i].length = 0; + + if (i > 0) + { + cdrom->track[i-1].length = cdrom->track[i].offset-cdrom->track[i-1].offset; + } + } + if (i == (cdrom->numtracks+1)) + { + okay = 1; + } + } + return (okay ? 0 : -1); +} + +/* Get CD-ROM status */ +static CDstatus SDL_SYS_CDStatus(SDL_CD *cdrom, int *position) +{ + CDstatus status; + + cdrom_read_toc_t toc; + cdrom_subch_data_t info; + cam_devinfo_t dinfo; + + int devctlret=0; + int drive=-1; + int i; + int eagaincnt=0; + + /* check media presence before read subchannel call, some cdroms can lockups */ + /* if no media, while calling read subchannel functions. */ + devctlret=devctl(cdrom->id, DCMD_CAM_DEVINFO, &dinfo, sizeof(cam_devinfo_t), NULL); + + if (devctlret==EOK) + { + if ((dinfo.flags & DEV_NO_MEDIA)!=0) + { + status = CD_TRAYEMPTY; + if (position) + { + *position = 0; + } + return (status); + } + } + + /* if media exists, then do other stuff */ + + SDL_memset(&info, 0x00, sizeof(info)); + info.subch_command.data_format = CDROM_SUBCH_CURRENT_POSITION; + + do { + devctlret=devctl(cdrom->id, DCMD_CAM_CDROMSUBCHNL, &info, sizeof(info), NULL); + if (devctlret==EIO) + { + /* big workaround for media change, handle is unusable after that, + that bug was found in QNX 6.2, 6.2.1 is not released yet. */ + + for (i=0; i<MAX_DRIVES; i++) + { + if (SDL_cdopen[i]==cdrom->id) + { + drive=i; + break; + } + } + if (drive==-1) + { + /* that cannot happen, but ... */ + break; + } + close(cdrom->id); + cdrom->id=open(SDL_cdlist[drive], QNX_CD_OPENMODE); + devctlret=EAGAIN; + } + if (devctlret==EAGAIN) + { + eagaincnt++; + } + if (eagaincnt==2) + { + /* workaround for broken cdroms, which can return always EAGAIN when its not ready, */ + /* that mean errornous media or just no media avail */ + devctlret=ENXIO; + break; + } + } while ((devctlret==EAGAIN)||(devctlret==ESTALE)); + + if (devctlret != 0) + { + if (devctlret==ENXIO) + { + status = CD_TRAYEMPTY; + } + else + { + status = CD_ERROR; + } + } + else + { + switch (info.current_position.header.audio_status) + { + case CDROM_AUDIO_INVALID: + case CDROM_AUDIO_NO_STATUS: + /* Try to determine if there's a CD available */ + if (devctl(cdrom->id, DCMD_CAM_CDROMREADTOC, &toc, sizeof(toc), NULL)==0) + status = CD_STOPPED; + else + status = CD_TRAYEMPTY; + break; + case CDROM_AUDIO_COMPLETED: + status = CD_STOPPED; + break; + case CDROM_AUDIO_PLAY: + status = CD_PLAYING; + break; + case CDROM_AUDIO_PAUSED: + /* Workaround buggy CD-ROM drive */ + if (info.current_position.data_format == CDROM_LEADOUT) + { + status = CD_STOPPED; + } + else + { + status = CD_PAUSED; + } + break; + default: + status = CD_ERROR; + break; + } + } + + if (position) + { + if (status==CD_PLAYING || (status==CD_PAUSED)) + { + *position = MSF_TO_FRAMES(info.current_position.addr.msf.minute, + info.current_position.addr.msf.second, + info.current_position.addr.msf.frame); + } + else + { + *position = 0; + } + } + + return (status); +} + +/* Start play */ +static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length) +{ + cdrom_playmsf_t playtime; + + FRAMES_TO_MSF(start, &playtime.start_minute, &playtime.start_second, &playtime.start_frame); + FRAMES_TO_MSF(start+length, &playtime.end_minute, &playtime.end_second, &playtime.end_frame); + + if (devctl(cdrom->id, DCMD_CAM_CDROMPLAYMSF, &playtime, sizeof(playtime), NULL) != 0) + { + return -1; + } + else + { + return 0; + } +} + +/* Pause play */ +static int SDL_SYS_CDPause(SDL_CD *cdrom) +{ + if (devctl(cdrom->id, DCMD_CAM_CDROMPAUSE, NULL, 0, NULL)!=0) + { + return -1; + } + else + { + return 0; + } +} + +/* Resume play */ +static int SDL_SYS_CDResume(SDL_CD *cdrom) +{ + if (devctl(cdrom->id, DCMD_CAM_CDROMRESUME, NULL, 0, NULL)!=0) + { + return -1; + } + else + { + return 0; + } +} + +/* Stop play */ +static int SDL_SYS_CDStop(SDL_CD *cdrom) +{ + if (devctl(cdrom->id, DCMD_CAM_CDROMSTOP, NULL, 0, NULL)!=0) + { + return -1; + } + else + { + return 0; + } +} + +/* Eject the CD-ROM */ +static int SDL_SYS_CDEject(SDL_CD *cdrom) +{ + if (devctl(cdrom->id, DCMD_CAM_EJECT_MEDIA, NULL, 0, NULL)!=0) + { + return -1; + } + else + { + return 0; + } +} + +/* Close the CD-ROM handle */ +static void SDL_SYS_CDClose(SDL_CD *cdrom) +{ + int i; + + for (i=0; i<MAX_DRIVES; i++) + { + if (SDL_cdopen[i]==cdrom->id) + { + SDL_cdopen[i]=0; + break; + } + } + + close(cdrom->id); +} + +void SDL_SYS_CDQuit(void) +{ + int i; + + if (SDL_numcds > 0) + { + for (i=0; i<SDL_numcds; ++i) + { + SDL_free(SDL_cdlist[i]); + } + SDL_numcds = 0; + } +} + +#endif /* SDL_CDROM_QNX */ diff --git a/src/sdl_cdrom/SDL_syscdrom_win32.c b/src/sdl_cdrom/SDL_syscdrom_win32.c new file mode 100644 index 000000000..67c7c6f1e --- /dev/null +++ b/src/sdl_cdrom/SDL_syscdrom_win32.c @@ -0,0 +1,391 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2012 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "config.h" +#define COMPAT_SDL_CDROM_PLATFORM_WIN32 1 + +#if (C_COMPAT_SDL_CDROM_PLATFORM == COMPAT_SDL_CDROM_PLATFORM_WIN32) + +#include "SDL_config.h" + +/*#ifdef SDL_CDROM_WIN32*/ + +/* Functions for system-level CD-ROM audio control */ + +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#include <mmsystem.h> + +#include "compat_SDL_cdrom.h" +#include "SDL_syscdrom.h" + +/* This really broken?? */ +#define BROKEN_MCI_PAUSE /* Pausing actually stops play -- Doh! */ + +/* The maximum number of CD-ROM drives we'll detect (Don't change!) */ +#define MAX_DRIVES 26 + +/* A list of available CD-ROM drives */ +static char *SDL_cdlist[MAX_DRIVES]; +static MCIDEVICEID SDL_mciID[MAX_DRIVES]; +#ifdef BROKEN_MCI_PAUSE +static int SDL_paused[MAX_DRIVES]; +#endif +static int SDL_CD_end_position; + +/* The system-dependent CD control functions */ +static const char *SDL_SYS_CDName(int drive); +static int SDL_SYS_CDOpen(int drive); +static int SDL_SYS_CDGetTOC(SDL_CD *cdrom); +static CDstatus SDL_SYS_CDStatus(SDL_CD *cdrom, int *position); +static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length); +static int SDL_SYS_CDPause(SDL_CD *cdrom); +static int SDL_SYS_CDResume(SDL_CD *cdrom); +static int SDL_SYS_CDStop(SDL_CD *cdrom); +static int SDL_SYS_CDEject(SDL_CD *cdrom); +static void SDL_SYS_CDClose(SDL_CD *cdrom); + + +/* Add a CD-ROM drive to our list of valid drives */ +static void AddDrive(char *drive) +{ + int i; + + if ( SDL_numcds < MAX_DRIVES ) { + /* Add this drive to our list */ + i = SDL_numcds; + SDL_cdlist[i] = SDL_strdup(drive); + if ( SDL_cdlist[i] == NULL ) { + SDL_OutOfMemory(); + return; + } + ++SDL_numcds; +#ifdef CDROM_DEBUG + fprintf(stderr, "Added CD-ROM drive: %s\n", drive); +#endif + } +} + +int SDL_SYS_CDInit(void) +{ + /* checklist: Drive 'A' - 'Z' */ + int i; + char drive[4]; + + /* Fill in our driver capabilities */ + SDL_CDcaps.Name = SDL_SYS_CDName; + SDL_CDcaps.Open = SDL_SYS_CDOpen; + SDL_CDcaps.GetTOC = SDL_SYS_CDGetTOC; + SDL_CDcaps.Status = SDL_SYS_CDStatus; + SDL_CDcaps.Play = SDL_SYS_CDPlay; + SDL_CDcaps.Pause = SDL_SYS_CDPause; + SDL_CDcaps.Resume = SDL_SYS_CDResume; + SDL_CDcaps.Stop = SDL_SYS_CDStop; + SDL_CDcaps.Eject = SDL_SYS_CDEject; + SDL_CDcaps.Close = SDL_SYS_CDClose; + + /* Scan the system for CD-ROM drives */ + for ( i='A'; i<='Z'; ++i ) { + SDL_snprintf(drive, SDL_arraysize(drive), "%c:\\", i); + if ( GetDriveType(drive) == DRIVE_CDROM ) { + AddDrive(drive); + } + } + SDL_memset(SDL_mciID, 0, sizeof(SDL_mciID)); + return(0); +} + +/* General ioctl() CD-ROM command function */ +static int SDL_SYS_CDioctl(int id, UINT msg, DWORD flags, void *arg) +{ + MCIERROR mci_error; + + mci_error = mciSendCommand(SDL_mciID[id], msg, flags, (DWORD_PTR)arg); + if ( mci_error ) { + char error[256]; + + mciGetErrorString(mci_error, error, 256); + SDL_SetError("mciSendCommand() error: %s", error); + } + return(!mci_error ? 0 : -1); +} + +static const char *SDL_SYS_CDName(int drive) +{ + return(SDL_cdlist[drive]); +} + +static int SDL_SYS_CDOpen(int drive) +{ + MCI_OPEN_PARMS mci_open; + MCI_SET_PARMS mci_set; + char device[3]; + DWORD flags; + + /* Open the requested device */ + mci_open.lpstrDeviceType = (LPCSTR) MCI_DEVTYPE_CD_AUDIO; + device[0] = *SDL_cdlist[drive]; + device[1] = ':'; + device[2] = '\0'; + mci_open.lpstrElementName = device; + flags = + (MCI_OPEN_TYPE|MCI_OPEN_SHAREABLE|MCI_OPEN_TYPE_ID|MCI_OPEN_ELEMENT); + if ( SDL_SYS_CDioctl(0, MCI_OPEN, flags, &mci_open) < 0 ) { + flags &= ~MCI_OPEN_SHAREABLE; + if ( SDL_SYS_CDioctl(0, MCI_OPEN, flags, &mci_open) < 0 ) { + return(-1); + } + } + SDL_mciID[drive] = mci_open.wDeviceID; + + /* Set the minute-second-frame time format */ + mci_set.dwTimeFormat = MCI_FORMAT_MSF; + SDL_SYS_CDioctl(drive, MCI_SET, MCI_SET_TIME_FORMAT, &mci_set); + +#ifdef BROKEN_MCI_PAUSE + SDL_paused[drive] = 0; +#endif + return(drive); +} + +static int SDL_SYS_CDGetTOC(SDL_CD *cdrom) +{ + MCI_STATUS_PARMS mci_status; + int i, okay; + DWORD flags; + + okay = 0; + mci_status.dwItem = MCI_STATUS_NUMBER_OF_TRACKS; + flags = MCI_STATUS_ITEM | MCI_WAIT; + if ( SDL_SYS_CDioctl(cdrom->id, MCI_STATUS, flags, &mci_status) == 0 ) { + cdrom->numtracks = mci_status.dwReturn; + if ( cdrom->numtracks > SDL_MAX_TRACKS ) { + cdrom->numtracks = SDL_MAX_TRACKS; + } + /* Read all the track TOC entries */ + flags = MCI_STATUS_ITEM | MCI_TRACK | MCI_WAIT; + for ( i=0; i<cdrom->numtracks; ++i ) { + cdrom->track[i].id = i+1; + mci_status.dwTrack = cdrom->track[i].id; +#ifdef MCI_CDA_STATUS_TYPE_TRACK + mci_status.dwItem = MCI_CDA_STATUS_TYPE_TRACK; + if ( SDL_SYS_CDioctl(cdrom->id, MCI_STATUS, flags, + &mci_status) < 0 ) { + break; + } + if ( mci_status.dwReturn == MCI_CDA_TRACK_AUDIO ) { + cdrom->track[i].type = SDL_AUDIO_TRACK; + } else { + cdrom->track[i].type = SDL_DATA_TRACK; + } +#else + cdrom->track[i].type = SDL_AUDIO_TRACK; +#endif + mci_status.dwItem = MCI_STATUS_POSITION; + if ( SDL_SYS_CDioctl(cdrom->id, MCI_STATUS, flags, + &mci_status) < 0 ) { + break; + } + cdrom->track[i].offset = MSF_TO_FRAMES( + MCI_MSF_MINUTE(mci_status.dwReturn), + MCI_MSF_SECOND(mci_status.dwReturn), + MCI_MSF_FRAME(mci_status.dwReturn)); + cdrom->track[i].length = 0; + if ( i > 0 ) { + cdrom->track[i-1].length = + cdrom->track[i].offset- + cdrom->track[i-1].offset; + } + } + if ( i == cdrom->numtracks ) { + mci_status.dwTrack = cdrom->track[i - 1].id; + mci_status.dwItem = MCI_STATUS_LENGTH; + if ( SDL_SYS_CDioctl(cdrom->id, MCI_STATUS, flags, + &mci_status) == 0 ) { + cdrom->track[i - 1].length = MSF_TO_FRAMES( + MCI_MSF_MINUTE(mci_status.dwReturn), + MCI_MSF_SECOND(mci_status.dwReturn), + MCI_MSF_FRAME(mci_status.dwReturn)); + /* compute lead-out offset */ + cdrom->track[i].offset = cdrom->track[i - 1].offset + + cdrom->track[i - 1].length; + cdrom->track[i].length = 0; + okay = 1; + } + } + } + return(okay ? 0 : -1); +} + +/* Get CD-ROM status */ +static CDstatus SDL_SYS_CDStatus(SDL_CD *cdrom, int *position) +{ + CDstatus status; + MCI_STATUS_PARMS mci_status; + DWORD flags; + + flags = MCI_STATUS_ITEM | MCI_WAIT; + mci_status.dwItem = MCI_STATUS_MODE; + if ( SDL_SYS_CDioctl(cdrom->id, MCI_STATUS, flags, &mci_status) < 0 ) { + status = CD_ERROR; + } else { + switch (mci_status.dwReturn) { + case MCI_MODE_NOT_READY: + case MCI_MODE_OPEN: + status = CD_TRAYEMPTY; + break; + case MCI_MODE_STOP: +#ifdef BROKEN_MCI_PAUSE + if ( SDL_paused[cdrom->id] ) { + status = CD_PAUSED; + } else { + status = CD_STOPPED; + } +#else + status = CD_STOPPED; +#endif /* BROKEN_MCI_PAUSE */ + break; + case MCI_MODE_PLAY: +#ifdef BROKEN_MCI_PAUSE + if ( SDL_paused[cdrom->id] ) { + status = CD_PAUSED; + } else { + status = CD_PLAYING; + } +#else + status = CD_PLAYING; +#endif /* BROKEN_MCI_PAUSE */ + break; + case MCI_MODE_PAUSE: + status = CD_PAUSED; + break; + default: + status = CD_ERROR; + break; + } + } + if ( position ) { + if ( status == CD_PLAYING || (status == CD_PAUSED) ) { + mci_status.dwItem = MCI_STATUS_POSITION; + if ( SDL_SYS_CDioctl(cdrom->id, MCI_STATUS, flags, + &mci_status) == 0 ) { + *position = MSF_TO_FRAMES( + MCI_MSF_MINUTE(mci_status.dwReturn), + MCI_MSF_SECOND(mci_status.dwReturn), + MCI_MSF_FRAME(mci_status.dwReturn)); + } else { + *position = 0; + } + } else { + *position = 0; + } + } + return(status); +} + +/* Start play */ +static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length) +{ + MCI_PLAY_PARMS mci_play; + int m, s, f; + DWORD flags; + + flags = MCI_FROM | MCI_TO | MCI_NOTIFY; + mci_play.dwCallback = 0; + FRAMES_TO_MSF(start, &m, &s, &f); + mci_play.dwFrom = MCI_MAKE_MSF(m, s, f); + FRAMES_TO_MSF(start+length, &m, &s, &f); + mci_play.dwTo = MCI_MAKE_MSF(m, s, f); + SDL_CD_end_position = mci_play.dwTo; + return(SDL_SYS_CDioctl(cdrom->id, MCI_PLAY, flags, &mci_play)); +} + +/* Pause play */ +static int SDL_SYS_CDPause(SDL_CD *cdrom) +{ +#ifdef BROKEN_MCI_PAUSE + SDL_paused[cdrom->id] = 1; +#endif + return(SDL_SYS_CDioctl(cdrom->id, MCI_PAUSE, MCI_WAIT, NULL)); +} + +/* Resume play */ +static int SDL_SYS_CDResume(SDL_CD *cdrom) +{ +#ifdef BROKEN_MCI_PAUSE + MCI_STATUS_PARMS mci_status; + int okay; + int flags; + + okay = 0; + /* Play from the current play position to the end position set earlier */ + flags = MCI_STATUS_ITEM | MCI_WAIT; + mci_status.dwItem = MCI_STATUS_POSITION; + if ( SDL_SYS_CDioctl(cdrom->id, MCI_STATUS, flags, &mci_status) == 0 ) { + MCI_PLAY_PARMS mci_play; + + flags = MCI_FROM | MCI_TO | MCI_NOTIFY; + mci_play.dwCallback = 0; + mci_play.dwFrom = mci_status.dwReturn; + mci_play.dwTo = SDL_CD_end_position; + if (SDL_SYS_CDioctl(cdrom->id,MCI_PLAY,flags,&mci_play) == 0) { + okay = 1; + SDL_paused[cdrom->id] = 0; + } + } + return(okay ? 0 : -1); +#else + return(SDL_SYS_CDioctl(cdrom->id, MCI_RESUME, MCI_WAIT, NULL)); +#endif /* BROKEN_MCI_PAUSE */ +} + +/* Stop play */ +static int SDL_SYS_CDStop(SDL_CD *cdrom) +{ + return(SDL_SYS_CDioctl(cdrom->id, MCI_STOP, MCI_WAIT, NULL)); +} + +/* Eject the CD-ROM */ +static int SDL_SYS_CDEject(SDL_CD *cdrom) +{ + return(SDL_SYS_CDioctl(cdrom->id, MCI_SET, MCI_SET_DOOR_OPEN, NULL)); +} + +/* Close the CD-ROM handle */ +static void SDL_SYS_CDClose(SDL_CD *cdrom) +{ + SDL_SYS_CDioctl(cdrom->id, MCI_CLOSE, MCI_WAIT, NULL); +} + +void SDL_SYS_CDQuit(void) +{ + int i; + + if ( SDL_numcds > 0 ) { + for ( i=0; i<SDL_numcds; ++i ) { + SDL_free(SDL_cdlist[i]); + SDL_cdlist[i] = NULL; + } + SDL_numcds = 0; + } +} + +#endif /* SDL_CDROM_WIN32 */ diff --git a/src/sdl_cdrom/compat_SDL_cdrom.c b/src/sdl_cdrom/compat_SDL_cdrom.c new file mode 100644 index 000000000..97f76597f --- /dev/null +++ b/src/sdl_cdrom/compat_SDL_cdrom.c @@ -0,0 +1,347 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2012 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ +/*#include "SDL_config.h"*/ + +/* This is the CD-audio control API for Simple DirectMedia Layer */ + +#include "config.h" + +#ifdef C_COMPAT_SDL_CDROM_PLATFORM + +#include "compat_SDL_cdrom.h" +#include "SDL_syscdrom.h" + +#if !defined(__MACOS__) +#define CLIP_FRAMES 10 /* Some CD-ROMs won't go all the way */ +#endif + +static int SDL_cdinitted = 0; +static SDL_CD *default_cdrom; + +/* The system level CD-ROM control functions */ +struct CDcaps SDL_CDcaps = { + NULL, /* Name */ + NULL, /* Open */ + NULL, /* GetTOC */ + NULL, /* Status */ + NULL, /* Play */ + NULL, /* Pause */ + NULL, /* Resume */ + NULL, /* Stop */ + NULL, /* Eject */ + NULL, /* Close */ +}; +int SDL_numcds; + +int Compat_SDL_CDROMInit(void) +{ + int retval; + + SDL_numcds = 0; + retval = SDL_SYS_CDInit(); + if ( retval == 0 ) { + SDL_cdinitted = 1; + } + default_cdrom = NULL; + return(retval); +} + +/* Check to see if the CD-ROM subsystem has been initialized */ +static int CheckInit(int check_cdrom, SDL_CD **cdrom) +{ + int okay; + + okay = SDL_cdinitted; + if ( check_cdrom && (*cdrom == NULL) ) { + *cdrom = default_cdrom; + if ( *cdrom == NULL ) { + SDL_SetError("CD-ROM not opened"); + okay = 0; + } + } + if ( ! SDL_cdinitted ) { + SDL_SetError("CD-ROM subsystem not initialized"); + } + return(okay); +} + +int SDL_CDNumDrives(void) +{ + if ( ! CheckInit(0, NULL) ) { + return(-1); + } + return(SDL_numcds); +} + +const char *SDL_CDName(int drive) +{ + if ( ! CheckInit(0, NULL) ) { + return(NULL); + } + if ( drive >= SDL_numcds ) { + SDL_SetError("Invalid CD-ROM drive index"); + return(NULL); + } + if ( SDL_CDcaps.Name ) { + return(SDL_CDcaps.Name(drive)); + } else { + return(""); + } +} + +SDL_CD *SDL_CDOpen(int drive) +{ + struct SDL_CD *cdrom; + + if ( ! CheckInit(0, NULL) ) { + return(NULL); + } + if ( drive >= SDL_numcds ) { + SDL_SetError("Invalid CD-ROM drive index"); + return(NULL); + } + cdrom = (SDL_CD *)SDL_malloc(sizeof(*cdrom)); + if ( cdrom == NULL ) { + SDL_OutOfMemory(); + return(NULL); + } + SDL_memset(cdrom, 0, sizeof(*cdrom)); + cdrom->id = SDL_CDcaps.Open(drive); + if ( cdrom->id < 0 ) { + SDL_free(cdrom); + return(NULL); + } + default_cdrom = cdrom; + return(cdrom); +} + +CDstatus SDL_CDStatus(SDL_CD *cdrom) +{ + CDstatus status; + int i; + Uint32 position; + + /* Check if the CD-ROM subsystem has been initialized */ + if ( ! CheckInit(1, &cdrom) ) { + return(CD_ERROR); + } + + /* Get the current status of the drive */ + cdrom->numtracks = 0; + cdrom->cur_track = 0; + cdrom->cur_frame = 0; + status = SDL_CDcaps.Status(cdrom, &i); + position = (Uint32)i; + cdrom->status = status; + + /* Get the table of contents, if there's a CD available */ + if ( CD_INDRIVE(status) ) { + if ( SDL_CDcaps.GetTOC(cdrom) < 0 ) { + status = CD_ERROR; + } + /* If the drive is playing, get current play position */ + if ( (status == CD_PLAYING) || (status == CD_PAUSED) ) { + for ( i=1; cdrom->track[i].offset <= position; ++i ) { + /* Keep looking */; + } +#ifdef DEBUG_CDROM + fprintf(stderr, "Current position: %d, track = %d (offset is %d)\n", + position, i-1, cdrom->track[i-1].offset); +#endif + cdrom->cur_track = i-1; + position -= cdrom->track[cdrom->cur_track].offset; + cdrom->cur_frame = position; + } + } + return(status); +} + +int SDL_CDPlayTracks(SDL_CD *cdrom, + int strack, int sframe, int ntracks, int nframes) +{ + int etrack, eframe; + int start, length; + + /* Check if the CD-ROM subsystem has been initialized */ + if ( ! CheckInit(1, &cdrom) ) { + return(CD_ERROR); + } + + /* Determine the starting and ending tracks */ + if ( (strack < 0) || (strack >= cdrom->numtracks) ) { + SDL_SetError("Invalid starting track"); + return(CD_ERROR); + } + if ( ! ntracks && ! nframes ) { + etrack = cdrom->numtracks; + eframe = 0; + } else { + etrack = strack+ntracks; + if ( etrack == strack ) { + eframe = sframe + nframes; + } else { + eframe = nframes; + } + } + if ( etrack > cdrom->numtracks ) { + SDL_SetError("Invalid play length"); + return(CD_ERROR); + } + + /* Skip data tracks and verify frame offsets */ + while ( (strack <= etrack) && + (cdrom->track[strack].type == SDL_DATA_TRACK) ) { + ++strack; + } + if ( sframe >= (int)cdrom->track[strack].length ) { + SDL_SetError("Invalid starting frame for track %d", strack); + return(CD_ERROR); + } + while ( (etrack > strack) && + (cdrom->track[etrack-1].type == SDL_DATA_TRACK) ) { + --etrack; + } + if ( eframe > (int)cdrom->track[etrack].length ) { + SDL_SetError("Invalid ending frame for track %d", etrack); + return(CD_ERROR); + } + + /* Determine start frame and play length */ + start = (cdrom->track[strack].offset+sframe); + length = (cdrom->track[etrack].offset+eframe)-start; +#ifdef CLIP_FRAMES + /* I've never seen this necessary, but xmcd does it.. */ + length -= CLIP_FRAMES; /* CLIP_FRAMES == 10 */ +#endif + if ( length < 0 ) { + return(0); + } + + /* Play! */ +#ifdef DEBUG_CDROM + fprintf(stderr, "Playing %d frames at offset %d\n", length, start); +#endif + return(SDL_CDcaps.Play(cdrom, start, length)); +} + +int SDL_CDPlay(SDL_CD *cdrom, int sframe, int length) +{ + /* Check if the CD-ROM subsystem has been initialized */ + if ( ! CheckInit(1, &cdrom) ) { + return(CD_ERROR); + } + + return(SDL_CDcaps.Play(cdrom, sframe, length)); +} + +int SDL_CDPause(SDL_CD *cdrom) +{ + CDstatus status; + int retval; + + /* Check if the CD-ROM subsystem has been initialized */ + if ( ! CheckInit(1, &cdrom) ) { + return(CD_ERROR); + } + + status = SDL_CDcaps.Status(cdrom, NULL); + switch (status) { + case CD_PLAYING: + retval = SDL_CDcaps.Pause(cdrom); + break; + default: + retval = 0; + break; + } + return(retval); +} + +int SDL_CDResume(SDL_CD *cdrom) +{ + CDstatus status; + int retval; + + /* Check if the CD-ROM subsystem has been initialized */ + if ( ! CheckInit(1, &cdrom) ) { + return(CD_ERROR); + } + + status = SDL_CDcaps.Status(cdrom, NULL); + switch (status) { + case CD_PAUSED: + retval = SDL_CDcaps.Resume(cdrom); + default: + retval = 0; + break; + } + return(retval); +} + +int SDL_CDStop(SDL_CD *cdrom) +{ + CDstatus status; + int retval; + + /* Check if the CD-ROM subsystem has been initialized */ + if ( ! CheckInit(1, &cdrom) ) { + return(CD_ERROR); + } + + status = SDL_CDcaps.Status(cdrom, NULL); + switch (status) { + case CD_PLAYING: + case CD_PAUSED: + retval = SDL_CDcaps.Stop(cdrom); + default: + retval = 0; + break; + } + return(retval); +} + +int SDL_CDEject(SDL_CD *cdrom) +{ + /* Check if the CD-ROM subsystem has been initialized */ + if ( ! CheckInit(1, &cdrom) ) { + return(CD_ERROR); + } + return(SDL_CDcaps.Eject(cdrom)); +} + +void SDL_CDClose(SDL_CD *cdrom) +{ + /* Check if the CD-ROM subsystem has been initialized */ + if ( ! CheckInit(1, &cdrom) ) { + return; + } + SDL_CDcaps.Close(cdrom); + SDL_free(cdrom); + default_cdrom = NULL; +} + +void Compat_SDL_CDROMQuit(void) +{ + SDL_SYS_CDQuit(); + SDL_cdinitted = 0; +} + +#endif /* SDL_CDROM */ diff --git a/src/sdl_cdrom/compat_SDL_cdrom.h b/src/sdl_cdrom/compat_SDL_cdrom.h new file mode 100644 index 000000000..fa78410fe --- /dev/null +++ b/src/sdl_cdrom/compat_SDL_cdrom.h @@ -0,0 +1,206 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2012 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ + +/** + * @file SDL_cdrom.h + * This is the CD-audio control API for Simple DirectMedia Layer + */ + +#ifndef _SDL_cdrom_h +#define _SDL_cdrom_h + +#include "SDL_stdinc.h" +#include "SDL_error.h" + +/*#include "begin_code.h"*/ +/* Set up for C function definitions, even when using C++ */ +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @file SDL_cdrom.h + * In order to use these functions (original SDL 1.2 revision), SDL_Init() + * must have been called with the SDL_INIT_CDROM flag. This causes SDL to + * scan the system for CD-ROM drives, and load appropriate drivers. + */ + +/** The maximum number of CD-ROM tracks on a disk */ +#define SDL_MAX_TRACKS 99 + +/** @name Track Types + * The types of CD-ROM track possible + */ +/*@{*/ +#define SDL_AUDIO_TRACK 0x00 +#define SDL_DATA_TRACK 0x04 +/*@}*/ + +/** The possible states which a CD-ROM drive can be in. */ +typedef enum { + CD_TRAYEMPTY, + CD_STOPPED, + CD_PLAYING, + CD_PAUSED, + CD_ERROR = -1 +} CDstatus; + +/** Given a status, returns true if there's a disk in the drive */ +#define CD_INDRIVE(status) ((int)(status) > 0) + +typedef struct SDL_CDtrack { + Uint8 id; /**< Track number */ + Uint8 type; /**< Data or audio track */ + Uint16 unused; + Uint32 length; /**< Length, in frames, of this track */ + Uint32 offset; /**< Offset, in frames, from start of disk */ +} SDL_CDtrack; + +/** This structure is only current as of the last call to SDL_CDStatus() */ +typedef struct SDL_CD { + int id; /**< Private drive identifier */ + CDstatus status; /**< Current drive status */ + + /** The rest of this structure is only valid if there's a CD in drive */ + /*@{*/ + int numtracks; /**< Number of tracks on disk */ + int cur_track; /**< Current track position */ + int cur_frame; /**< Current frame offset within current track */ + SDL_CDtrack track[SDL_MAX_TRACKS+1]; + /*@}*/ +} SDL_CD; + +/** @name Frames / MSF Conversion Functions + * Conversion functions from frames to Minute/Second/Frames and vice versa + */ +/*@{*/ +#define CD_FPS 75 +#define FRAMES_TO_MSF(f, M,S,F) { \ + int value = f; \ + *(F) = value%CD_FPS; \ + value /= CD_FPS; \ + *(S) = value%60; \ + value /= 60; \ + *(M) = value; \ +} +#define MSF_TO_FRAMES(M, S, F) ((M)*60*CD_FPS+(S)*CD_FPS+(F)) +/*@}*/ + +/* Compatibility functions (used separately from the SDL_Init+Quit rountines) */ +extern int Compat_SDL_CDROMInit(void); +extern void Compat_SDL_CDROMQuit(void); + +/* CD-audio API functions: */ + +/** + * Returns the number of CD-ROM drives on the system, or -1 if + * SDL_Init() has not been called with the SDL_INIT_CDROM flag. + */ +extern /*DECLSPEC*/ int /*SDLCALL*/ SDL_CDNumDrives(void); + +/** + * Returns a human-readable, system-dependent identifier for the CD-ROM. + * Example: + * - "/dev/cdrom" + * - "E:" + * - "/dev/disk/ide/1/master" + */ +extern /*DECLSPEC*/ const char * /*SDLCALL*/ SDL_CDName(int drive); + +/** + * Opens a CD-ROM drive for access. It returns a drive handle on success, + * or NULL if the drive was invalid or busy. This newly opened CD-ROM + * becomes the default CD used when other CD functions are passed a NULL + * CD-ROM handle. + * Drives are numbered starting with 0. Drive 0 is the system default CD-ROM. + */ +extern /*DECLSPEC*/ SDL_CD * /*SDLCALL*/ SDL_CDOpen(int drive); + +/** + * This function returns the current status of the given drive. + * If the drive has a CD in it, the table of contents of the CD and current + * play position of the CD will be stored in the SDL_CD structure. + */ +extern /*DECLSPEC*/ CDstatus /*SDLCALL*/ SDL_CDStatus(SDL_CD *cdrom); + +/** + * Play the given CD starting at 'start_track' and 'start_frame' for 'ntracks' + * tracks and 'nframes' frames. If both 'ntrack' and 'nframe' are 0, play + * until the end of the CD. This function will skip data tracks. + * This function should only be called after calling SDL_CDStatus() to + * get track information about the CD. + * For example: + * @code + * // Play entire CD: + * if ( CD_INDRIVE(SDL_CDStatus(cdrom)) ) + * SDL_CDPlayTracks(cdrom, 0, 0, 0, 0); + * // Play last track: + * if ( CD_INDRIVE(SDL_CDStatus(cdrom)) ) { + * SDL_CDPlayTracks(cdrom, cdrom->numtracks-1, 0, 0, 0); + * } + * // Play first and second track and 10 seconds of third track: + * if ( CD_INDRIVE(SDL_CDStatus(cdrom)) ) + * SDL_CDPlayTracks(cdrom, 0, 0, 2, 10); + * @endcode + * + * @return This function returns 0, or -1 if there was an error. + */ +extern /*DECLSPEC*/ int /*SDLCALL*/ SDL_CDPlayTracks(SDL_CD *cdrom, + int start_track, int start_frame, int ntracks, int nframes); + +/** + * Play the given CD starting at 'start' frame for 'length' frames. + * @return It returns 0, or -1 if there was an error. + */ +extern /*DECLSPEC*/ int /*SDLCALL*/ SDL_CDPlay(SDL_CD *cdrom, int start, int length); + +/** Pause play + * @return returns 0, or -1 on error + */ +extern /*DECLSPEC*/ int /*SDLCALL*/ SDL_CDPause(SDL_CD *cdrom); + +/** Resume play + * @return returns 0, or -1 on error + */ +extern DECLSPEC int /*SDLCALL*/ SDL_CDResume(SDL_CD *cdrom); + +/** Stop play + * @return returns 0, or -1 on error + */ +extern /*DECLSPEC*/ int /*SDLCALL*/ SDL_CDStop(SDL_CD *cdrom); + +/** Eject CD-ROM + * @return returns 0, or -1 on error + */ +extern /*DECLSPEC*/ int /*SDLCALL*/ SDL_CDEject(SDL_CD *cdrom); + +/** Closes the handle for the CD-ROM drive */ +extern /*DECLSPEC*/ void /*SDLCALL*/ SDL_CDClose(SDL_CD *cdrom); + + +/* Ends C function definitions when using C++ */ +#ifdef __cplusplus +} +#endif +/*#include "close_code.h"*/ + +#endif /* _SDL_video_h */ diff --git a/src/sdl_cdrom/macos/Makefile.am b/src/sdl_cdrom/macos/Makefile.am new file mode 100644 index 000000000..1d1fe5dd8 --- /dev/null +++ b/src/sdl_cdrom/macos/Makefile.am @@ -0,0 +1,5 @@ +AM_CPPFLAGS = -I$(top_srcdir)/include + +noinst_LIBRARIES = libcsdlcdrommacos.a +EXTRA_DIST = DL_syscdrom_c.h +libcsdlcdrommacos_a_SOURCES = SDL_syscdrom.c diff --git a/src/sdl_cdrom/macos/SDL_syscdrom.c b/src/sdl_cdrom/macos/SDL_syscdrom.c new file mode 100644 index 000000000..229d0648c --- /dev/null +++ b/src/sdl_cdrom/macos/SDL_syscdrom.c @@ -0,0 +1,530 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2012 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "config.h" +#define COMPAT_SDL_CDROM_PLATFORM_MACOS 1 + +#if (C_COMPAT_SDL_CDROM_PLATFORM == COMPAT_SDL_CDROM_PLATFORM_MACOS) + +#include "SDL_config.h" + +/*#ifdef SDL_CDROM_MACOS*/ + +/* MacOS functions for system-level CD-ROM audio control */ + +#include <Devices.h> +#include <Files.h> +#include <LowMem.h> /* Use entry table macros, not functions in InterfaceLib */ + +#include "../compat_SDL_cdrom.h" +#include "../SDL_syscdrom.h" +#include "SDL_syscdrom_c.h" + +/* Added by Matt Slot */ +#if !defined(LMGetUnitTableEntryCount) + #define LMGetUnitTableEntryCount() *(short *)0x01D2 +#endif + +/* The maximum number of CD-ROM drives we'll detect */ +#define MAX_DRIVES 26 + +/* A list of available CD-ROM drives */ +static long SDL_cdversion = 0; +static struct { + short dRefNum; + short driveNum; + long frames; + char name[256]; + Boolean hasAudio; + } SDL_cdlist[MAX_DRIVES]; +static StringPtr gDriverName = "\p.AppleCD"; + +/* The system-dependent CD control functions */ +static const char *SDL_SYS_CDName(int drive); +static int SDL_SYS_CDOpen(int drive); +static int SDL_SYS_CDGetTOC(SDL_CD *cdrom); +static CDstatus SDL_SYS_CDStatus(SDL_CD *cdrom, int *position); +static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length); +static int SDL_SYS_CDPause(SDL_CD *cdrom); +static int SDL_SYS_CDResume(SDL_CD *cdrom); +static int SDL_SYS_CDStop(SDL_CD *cdrom); +static int SDL_SYS_CDEject(SDL_CD *cdrom); +static void SDL_SYS_CDClose(SDL_CD *cdrom); + +static short SDL_SYS_ShortToBCD(short value) +{ + return((value % 10) + (value / 10) * 0x10); /* Convert value to BCD */ +} + +static short SDL_SYS_BCDToShort(short value) +{ + return((value % 0x10) + (value / 0x10) * 10); /* Convert value from BCD */ +} + +int SDL_SYS_CDInit(void) +{ + SInt16 dRefNum = 0; + SInt16 first, last; + + SDL_numcds = 0; + + /* Check that the software is available */ + if (Gestalt(kGestaltAudioCDSelector, &SDL_cdversion) || + !SDL_cdversion) return(0); + + /* Fill in our driver capabilities */ + SDL_CDcaps.Name = SDL_SYS_CDName; + SDL_CDcaps.Open = SDL_SYS_CDOpen; + SDL_CDcaps.GetTOC = SDL_SYS_CDGetTOC; + SDL_CDcaps.Status = SDL_SYS_CDStatus; + SDL_CDcaps.Play = SDL_SYS_CDPlay; + SDL_CDcaps.Pause = SDL_SYS_CDPause; + SDL_CDcaps.Resume = SDL_SYS_CDResume; + SDL_CDcaps.Stop = SDL_SYS_CDStop; + SDL_CDcaps.Eject = SDL_SYS_CDEject; + SDL_CDcaps.Close = SDL_SYS_CDClose; + + /* Walk the list, count each AudioCD driver, and save the refnums */ + first = -1; + last = 0 - LMGetUnitTableEntryCount(); + for(dRefNum = first; dRefNum >= last; dRefNum--) { + Str255 driverName; + StringPtr namePtr; + DCtlHandle deviceEntry; + + deviceEntry = GetDCtlEntry(dRefNum); + if (! deviceEntry) continue; + + /* Is this an .AppleCD ? */ + namePtr = (*deviceEntry)->dCtlFlags & (1L << dRAMBased) ? + ((StringPtr) ((DCtlPtr) deviceEntry)->dCtlDriver + 18) : + ((StringPtr) (*deviceEntry)->dCtlDriver + 18); + BlockMoveData(namePtr, driverName, namePtr[0]+1); + if (driverName[0] > gDriverName[0]) driverName[0] = gDriverName[0]; + if (! EqualString(driverName, gDriverName, false, false)) continue; + + /* Record the basic info for each drive */ + SDL_cdlist[SDL_numcds].dRefNum = dRefNum; + BlockMoveData(namePtr + 1, SDL_cdlist[SDL_numcds].name, namePtr[0]); + SDL_cdlist[SDL_numcds].name[namePtr[0]] = 0; + SDL_cdlist[SDL_numcds].hasAudio = false; + SDL_numcds++; + } + return(0); +} + +static const char *SDL_SYS_CDName(int drive) +{ + return(SDL_cdlist[drive].name); +} + +static int get_drivenum(int drive) +{ + QHdr *driveQ = GetDrvQHdr(); + DrvQEl *driveElem; + + /* Update the drive number */ + SDL_cdlist[drive].driveNum = 0; + if ( driveQ->qTail ) { + driveQ->qTail->qLink = 0; + } + for ( driveElem=(DrvQEl *)driveQ->qHead; driveElem; + driveElem = (DrvQEl *)driveElem->qLink ) { + if ( driveElem->dQRefNum == SDL_cdlist[drive].dRefNum ) { + SDL_cdlist[drive].driveNum = driveElem->dQDrive; + break; + } + } + return(SDL_cdlist[drive].driveNum); +} + +static int SDL_SYS_CDOpen(int drive) +{ + return(drive); +} + +static int SDL_SYS_CDGetTOC(SDL_CD *cdrom) +{ + CDCntrlParam cdpb; + CDTrackData tracks[SDL_MAX_TRACKS]; + long i, leadout; + + /* Get the number of tracks on the CD by examining the TOC */ + SDL_memset(&cdpb, 0, sizeof(cdpb)); + cdpb.ioVRefNum = SDL_cdlist[cdrom->id].driveNum; + cdpb.ioCRefNum = SDL_cdlist[cdrom->id].dRefNum; + cdpb.csCode = kReadTOC; + cdpb.csParam.words[0] = kGetTrackRange; + if ( PBControlSync((ParmBlkPtr)&cdpb) != noErr ) { + SDL_SetError("PBControlSync() failed"); + return(-1); + } + + cdrom->numtracks = + SDL_SYS_BCDToShort(cdpb.csParam.bytes[1]) - + SDL_SYS_BCDToShort(cdpb.csParam.bytes[0]) + 1; + if ( cdrom->numtracks > SDL_MAX_TRACKS ) + cdrom->numtracks = SDL_MAX_TRACKS; + cdrom->status = CD_STOPPED; + cdrom->cur_track = 0; /* Apparently these are set elsewhere */ + cdrom->cur_frame = 0; /* Apparently these are set elsewhere */ + + + /* Get the lead out area of the CD by examining the TOC */ + SDL_memset(&cdpb, 0, sizeof(cdpb)); + cdpb.ioVRefNum = SDL_cdlist[cdrom->id].driveNum; + cdpb.ioCRefNum = SDL_cdlist[cdrom->id].dRefNum; + cdpb.csCode = kReadTOC; + cdpb.csParam.words[0] = kGetLeadOutArea; + if ( PBControlSync((ParmBlkPtr)&cdpb) != noErr ) { + SDL_SetError("PBControlSync() failed"); + return(-1); + } + + leadout = MSF_TO_FRAMES( + SDL_SYS_BCDToShort(cdpb.csParam.bytes[0]), + SDL_SYS_BCDToShort(cdpb.csParam.bytes[1]), + SDL_SYS_BCDToShort(cdpb.csParam.bytes[2])); + + /* Get an array of track locations by examining the TOC */ + SDL_memset(tracks, 0, sizeof(tracks)); + SDL_memset(&cdpb, 0, sizeof(cdpb)); + cdpb.ioVRefNum = SDL_cdlist[cdrom->id].driveNum; + cdpb.ioCRefNum = SDL_cdlist[cdrom->id].dRefNum; + cdpb.csCode = kReadTOC; + cdpb.csParam.words[0] = kGetTrackEntries; /* Type of Query */ + * ((long *) (cdpb.csParam.words+1)) = (long) tracks; + cdpb.csParam.words[3] = cdrom->numtracks * sizeof(tracks[0]); + * ((char *) (cdpb.csParam.words+4)) = 1; /* First track */ + if ( PBControlSync((ParmBlkPtr)&cdpb) != noErr ) { + SDL_SetError("PBControlSync() failed"); + return(-1); + } + + /* Read all the track TOC entries */ + SDL_cdlist[cdrom->id].hasAudio = false; + for ( i=0; i<cdrom->numtracks; ++i ) + { + cdrom->track[i].id = i+1; + if (tracks[i].entry.control & kDataTrackMask) + cdrom->track[i].type = SDL_DATA_TRACK; + else + { + cdrom->track[i].type = SDL_AUDIO_TRACK; + SDL_cdlist[SDL_numcds].hasAudio = true; + } + + cdrom->track[i].offset = MSF_TO_FRAMES( + SDL_SYS_BCDToShort(tracks[i].entry.min), + SDL_SYS_BCDToShort(tracks[i].entry.min), + SDL_SYS_BCDToShort(tracks[i].entry.frame)); + cdrom->track[i].length = MSF_TO_FRAMES( + SDL_SYS_BCDToShort(tracks[i+1].entry.min), + SDL_SYS_BCDToShort(tracks[i+1].entry.min), + SDL_SYS_BCDToShort(tracks[i+1].entry.frame)) - + cdrom->track[i].offset; + } + + /* Apparently SDL wants a fake last entry */ + cdrom->track[i].offset = leadout; + cdrom->track[i].length = 0; + + return(0); +} + +/* Get CD-ROM status */ +static CDstatus SDL_SYS_CDStatus(SDL_CD *cdrom, int *position) +{ + CDCntrlParam cdpb; + CDstatus status = CD_ERROR; + Boolean spinning = false; + + if (position) *position = 0; + + /* Get the number of tracks on the CD by examining the TOC */ + if ( ! get_drivenum(cdrom->id) ) { + return(CD_TRAYEMPTY); + } + SDL_memset(&cdpb, 0, sizeof(cdpb)); + cdpb.ioVRefNum = SDL_cdlist[cdrom->id].driveNum; + cdpb.ioCRefNum = SDL_cdlist[cdrom->id].dRefNum; + cdpb.csCode = kReadTOC; + cdpb.csParam.words[0] = kGetTrackRange; + if ( PBControlSync((ParmBlkPtr)&cdpb) != noErr ) { + SDL_SetError("PBControlSync() failed"); + return(CD_ERROR); + } + + cdrom->numtracks = + SDL_SYS_BCDToShort(cdpb.csParam.bytes[1]) - + SDL_SYS_BCDToShort(cdpb.csParam.bytes[0]) + 1; + if ( cdrom->numtracks > SDL_MAX_TRACKS ) + cdrom->numtracks = SDL_MAX_TRACKS; + cdrom->cur_track = 0; /* Apparently these are set elsewhere */ + cdrom->cur_frame = 0; /* Apparently these are set elsewhere */ + + + if (1 || SDL_cdlist[cdrom->id].hasAudio) { + /* Get the current playback status */ + SDL_memset(&cdpb, 0, sizeof(cdpb)); + cdpb.ioVRefNum = SDL_cdlist[cdrom->id].driveNum; + cdpb.ioCRefNum = SDL_cdlist[cdrom->id].dRefNum; + cdpb.csCode = kAudioStatus; + if ( PBControlSync((ParmBlkPtr)&cdpb) != noErr ) { + SDL_SetError("PBControlSync() failed"); + return(-1); + } + + switch(cdpb.csParam.cd.status) { + case kStatusPlaying: + status = CD_PLAYING; + spinning = true; + break; + case kStatusPaused: + status = CD_PAUSED; + spinning = true; + break; + case kStatusMuted: + status = CD_PLAYING; /* What should I do here? */ + spinning = true; + break; + case kStatusDone: + status = CD_STOPPED; + spinning = true; + break; + case kStatusStopped: + status = CD_STOPPED; + spinning = false; + break; + case kStatusError: + default: + status = CD_ERROR; + spinning = false; + break; + } + + if (spinning && position) *position = MSF_TO_FRAMES( + SDL_SYS_BCDToShort(cdpb.csParam.cd.minute), + SDL_SYS_BCDToShort(cdpb.csParam.cd.second), + SDL_SYS_BCDToShort(cdpb.csParam.cd.frame)); + } + else + status = CD_ERROR; /* What should I do here? */ + + return(status); +} + +/* Start play */ +static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length) +{ + CDCntrlParam cdpb; + + /* Pause the current audio playback to avoid audible artifacts */ + if ( SDL_SYS_CDPause(cdrom) < 0 ) { + return(-1); + } + + /* Specify the AudioCD playback mode */ + SDL_memset(&cdpb, 0, sizeof(cdpb)); + cdpb.ioVRefNum = SDL_cdlist[cdrom->id].driveNum; + cdpb.ioCRefNum = SDL_cdlist[cdrom->id].dRefNum; + cdpb.csCode = kSetPlayMode; + cdpb.csParam.bytes[0] = false; /* Repeat? */ + cdpb.csParam.bytes[1] = kPlayModeSequential; /* Play mode */ + /* \A5\A5\A5\CATreat as soft error, NEC Drive doesnt support this call \A5\A5\A5 */ + PBControlSync((ParmBlkPtr) &cdpb); + +#if 1 + /* Specify the end of audio playback */ + SDL_memset(&cdpb, 0, sizeof(cdpb)); + cdpb.ioVRefNum = SDL_cdlist[cdrom->id].driveNum; + cdpb.ioCRefNum = SDL_cdlist[cdrom->id].dRefNum; + cdpb.csCode = kAudioStop; + cdpb.csParam.words[0] = kBlockPosition; /* Position Mode */ + *(long *) (cdpb.csParam.words + 1) = start+length-1; /* Search Address */ + if ( PBControlSync((ParmBlkPtr)&cdpb) != noErr ) { + SDL_SetError("PBControlSync() failed"); + return(-1); + } + + /* Specify the start of audio playback, and start it */ + SDL_memset(&cdpb, 0, sizeof(cdpb)); + cdpb.ioVRefNum = SDL_cdlist[cdrom->id].driveNum; + cdpb.ioCRefNum = SDL_cdlist[cdrom->id].dRefNum; + cdpb.csCode = kAudioPlay; + cdpb.csParam.words[0] = kBlockPosition; /* Position Mode */ + *(long *) (cdpb.csParam.words + 1) = start+1; /* Search Address */ + cdpb.csParam.words[3] = false; /* Stop address? */ + cdpb.csParam.words[4] = kStereoPlayMode; /* Audio Play Mode */ + if ( PBControlSync((ParmBlkPtr)&cdpb) != noErr ) { + SDL_SetError("PBControlSync() failed"); + return(-1); + } +#else + /* Specify the end of audio playback */ + FRAMES_TO_MSF(start+length, &m, &s, &f); + SDL_memset(&cdpb, 0, sizeof(cdpb)); + cdpb.ioVRefNum = SDL_cdlist[cdrom->id].driveNum; + cdpb.ioCRefNum = SDL_cdlist[cdrom->id].dRefNum; + cdpb.csCode = kAudioStop; + cdpb.csParam.words[0] = kTrackPosition; /* Position Mode */ + cdpb.csParam.words[1] = 0; /* Search Address (hiword)*/ + cdpb.csParam.words[2] = /* Search Address (loword)*/ + SDL_SYS_ShortToBCD(cdrom->numtracks); + if ( PBControlSync((ParmBlkPtr)&cdpb) != noErr ) { + SDL_SetError("PBControlSync() failed"); + return(-1); + } + + /* Specify the start of audio playback, and start it */ + FRAMES_TO_MSF(start, &m, &s, &f); + SDL_memset(&cdpb, 0, sizeof(cdpb)); + cdpb.ioVRefNum = SDL_cdlist[cdrom->id].driveNum; + cdpb.ioCRefNum = SDL_cdlist[cdrom->id].dRefNum; + cdpb.csCode = kAudioPlay; + cdpb.csParam.words[0] = kTrackPosition; /* Position Mode */ + cdpb.csParam.words[1] = 0; /* Search Address (hiword)*/ + cdpb.csParam.words[2] = SDL_SYS_ShortToBCD(1); /* Search Address (loword)*/ + cdpb.csParam.words[3] = false; /* Stop address? */ + cdpb.csParam.words[4] = kStereoPlayMode; /* Audio Play Mode */ + if ( PBControlSync((ParmBlkPtr)&cdpb) != noErr ) { + SDL_SetError("PBControlSync() failed"); + return(-1); + } +#endif + + return(0); +} + +/* Pause play */ +static int SDL_SYS_CDPause(SDL_CD *cdrom) +{ + CDCntrlParam cdpb; + + SDL_memset(&cdpb, 0, sizeof(cdpb)); + cdpb.ioVRefNum = SDL_cdlist[cdrom->id].driveNum; + cdpb.ioCRefNum = SDL_cdlist[cdrom->id].dRefNum; + cdpb.csCode = kAudioPause; + cdpb.csParam.words[0] = 0; /* Pause/Continue Flag (hiword) */ + cdpb.csParam.words[1] = 1; /* Pause/Continue Flag (loword) */ + if ( PBControlSync((ParmBlkPtr)&cdpb) != noErr ) { + SDL_SetError("PBControlSync() failed"); + return(-1); + } + return(0); +} + +/* Resume play */ +static int SDL_SYS_CDResume(SDL_CD *cdrom) +{ + CDCntrlParam cdpb; + + SDL_memset(&cdpb, 0, sizeof(cdpb)); + cdpb.ioVRefNum = SDL_cdlist[cdrom->id].driveNum; + cdpb.ioCRefNum = SDL_cdlist[cdrom->id].dRefNum; + cdpb.csCode = kAudioPause; + cdpb.csParam.words[0] = 0; /* Pause/Continue Flag (hiword) */ + cdpb.csParam.words[1] = 0; /* Pause/Continue Flag (loword) */ + if ( PBControlSync((ParmBlkPtr)&cdpb) != noErr ) { + SDL_SetError("PBControlSync() failed"); + return(-1); + } + return(0); +} + +/* Stop play */ +static int SDL_SYS_CDStop(SDL_CD *cdrom) +{ + CDCntrlParam cdpb; + + SDL_memset(&cdpb, 0, sizeof(cdpb)); + cdpb.ioVRefNum = SDL_cdlist[cdrom->id].driveNum; + cdpb.ioCRefNum = SDL_cdlist[cdrom->id].dRefNum; + cdpb.csCode = kAudioStop; + cdpb.csParam.words[0] = 0; /* Position Mode */ + cdpb.csParam.words[1] = 0; /* Search Address (hiword) */ + cdpb.csParam.words[2] = 0; /* Search Address (loword) */ + if ( PBControlSync((ParmBlkPtr)&cdpb) != noErr ) { + SDL_SetError("PBControlSync() failed"); + return(-1); + } + return(0); +} + +/* Eject the CD-ROM */ +static int SDL_SYS_CDEject(SDL_CD *cdrom) +{ + Boolean disk = false; + QHdr *driveQ = GetDrvQHdr(); + DrvQEl *driveElem; + HParamBlockRec hpb; + ParamBlockRec cpb; + + for ( driveElem = (DrvQEl *) driveQ->qHead; driveElem; driveElem = + (driveElem) ? ((DrvQEl *) driveElem->qLink) : + ((DrvQEl *) driveQ->qHead) ) { + if ( driveQ->qTail ) { + driveQ->qTail->qLink = 0; + } + if ( driveElem->dQRefNum != SDL_cdlist[cdrom->id].dRefNum ) { + continue; + } + + /* Does drive contain mounted volume? If not, skip */ + SDL_memset(&hpb, 0, sizeof(hpb)); + hpb.volumeParam.ioVRefNum = driveElem->dQDrive; + if ( PBHGetVInfoSync(&hpb) != noErr ) { + continue; + } + if ( (UnmountVol(0, driveElem->dQDrive) == noErr) && + (Eject(0, driveElem->dQDrive) == noErr) ) { + driveElem = 0; /* Clear pointer to reset our loop */ + disk = true; + } + } + + /* If no disk is present, just eject the tray */ + if (! disk) { + SDL_memset(&cpb, 0, sizeof(cpb)); + cpb.cntrlParam.ioVRefNum = 0; /* No Drive */ + cpb.cntrlParam.ioCRefNum = SDL_cdlist[cdrom->id].dRefNum; + cpb.cntrlParam.csCode = kEjectTheDisc; + if ( PBControlSync((ParmBlkPtr)&cpb) != noErr ) { + SDL_SetError("PBControlSync() failed"); + return(-1); + } + } + return(0); +} + +/* Close the CD-ROM handle */ +static void SDL_SYS_CDClose(SDL_CD *cdrom) +{ + return; +} + +void SDL_SYS_CDQuit(void) +{ + while(SDL_numcds--) + SDL_memset(SDL_cdlist + SDL_numcds, 0, sizeof(SDL_cdlist[0])); +} + +#endif /* SDL_CDROM_MACOS */ diff --git a/src/sdl_cdrom/macos/SDL_syscdrom_c.h b/src/sdl_cdrom/macos/SDL_syscdrom_c.h new file mode 100644 index 000000000..e715a256d --- /dev/null +++ b/src/sdl_cdrom/macos/SDL_syscdrom_c.h @@ -0,0 +1,140 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2012 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "SDL_config.h" + +/* This is the MacOS specific header for the SDL CD-ROM API + Contributed by Matt Slot + */ + +/* AppleCD Control calls */ +#define kVerifyTheDisc 5 /* Returns noErr if there is disc inserted */ +#define kEjectTheDisc 7 /* Eject disc from drive */ +#define kUserEject 80 /* Enable/disable the CD-ROM eject button */ +#define kReadTOC 100 /* Extract various TOC information from the disc */ +#define kReadQ 101 /* Extract Q subcode info for the current track */ +#define kAudioTrackSearch 103 /* Start playback from the indicated position */ +#define kAudioPlay 104 /* Start playback from the indicated position */ +#define kAudioPause 105 /* Pause/continue the playback */ +#define kAudioStop 106 /* Stop playback at the indicated position */ +#define kAudioStatus 107 /* Return audio play status */ +#define kAudioControl 109 /* Set the output volume for the audio channels */ +#define kReadAudioVolume 112 /* Get the output volume for the audio channels */ +#define kSetTrackList 122 /* Set the track program for the audio CD to play */ +#define kGetTrackList 123 /* Get the track program the audio CD is playing */ +#define kGetTrackIndex 124 /* Get the track index the audio CD is playing */ +#define kSetPlayMode 125 /* Set the audio tracks play mode */ +#define kGetPlayMode 126 /* Get the audio tracks play mode */ + +/* AppleCD Status calls */ +#define kGetDriveType 96 /* Get the type of the physical CD-ROM drive */ +#define kWhoIsThere 97 /* Get a bitmap of SCSI IDs the driver controls */ +#define kGetBlockSize 98 /* Get current block size of the CD-ROM drive */ + +/* AppleCD other constants */ +#define kBlockPosition 0 /* Position at the specified logical block number */ +#define kAbsMSFPosition 1 /* Position at the specified Min/Sec/Frame (in BCD) */ +#define kTrackPosition 2 /* Position at the specified track number (in BCD) */ +#define kIndexPosition 3 /* Position at the nth track in program (in BCD) */ + +#define kMutedPlayMode 0 /* Play the audio track with no output */ +#define kStereoPlayMode 9 /* Play the audio track in normal stereo */ + +#define kControlFieldMask 0x0D /* Bits 3,2,0 in the nibble */ +#define kDataTrackMask 0x04 /* Indicates Data Track */ + +#define kGetTrackRange 1 /* Query TOC for track numbers */ +#define kGetLeadOutArea 2 /* Query TOC for "Lead Out" end of audio data */ +#define kGetTrackEntries 3 /* Query TOC for track starts and data types */ + +#define kStatusPlaying 0 /* Audio Play operation in progress */ +#define kStatusPaused 1 /* CD-ROM device in Hold Track ("Pause") state */ +#define kStatusMuted 2 /* MUTING-ON operation in progress */ +#define kStatusDone 3 /* Audio Play completed */ +#define kStatusError 4 /* Error occurred during audio play operation */ +#define kStatusStopped 5 /* Audio play operation not requested */ + +#define kPlayModeSequential 0 /* Play tracks in order */ +#define kPlayModeShuffled 1 /* Play tracks randomly */ +#define kPlayModeProgrammed 2 /* Use custom playlist */ + +/* AppleCD Gestalt selectors */ +#define kGestaltAudioCDSelector 'aucd' +#define kDriverVersion52 0x00000520 +#define kDriverVersion51 0x00000510 +#define kDriverVersion50 0x00000500 + +/* Drive type constants */ +#define kDriveAppleCD_SC 1 +#define kDriveAppleCD_SCPlus_or_150 2 +#define kDriveAppleCD_300_or_300Plus 3 + +/* Misc constants */ +#define kFirstSCSIDevice -33 +#define kLastSCSIDevice -40 + +#if PRAGMA_STRUCT_ALIGN + #pragma options align=mac68k +#endif + +/* AppleCD driver parameter block */ +typedef struct CDCntrlParam { + QElemPtr qLink; + short qType; + short ioTrap; + Ptr ioCmdAddr; + IOCompletionUPP ioCompletion; + OSErr ioResult; + StringPtr ioNamePtr; + short ioVRefNum; + short ioCRefNum; + short csCode; + + union { + long longs[6]; + short words[11]; + unsigned char bytes[22]; + struct { + unsigned char status; + unsigned char play; + unsigned char control; + unsigned char minute; + unsigned char second; + unsigned char frame; + } cd; + } csParam; + + } CDCntrlParam, *CDCntrlParamPtr; + +typedef union CDTrackData { + long value; /* Treat as a longword value */ + struct { + unsigned char reserved : 4; /* Unused by AppleCD driver */ + unsigned char control : 4; /* Track flags (data track?) */ + unsigned char min; /* Start of track (BCD) */ + unsigned char sec; /* Start of track (BCD) */ + unsigned char frame; /* Start of track (BCD) */ + } entry; /* Broken into fields */ + } CDTrackData, *CDTrackPtr; + +#if PRAGMA_STRUCT_ALIGN + #pragma options align=reset +#endif diff --git a/src/sdl_cdrom/macosx/AudioFilePlayer.c b/src/sdl_cdrom/macosx/AudioFilePlayer.c new file mode 100644 index 000000000..d702053c5 --- /dev/null +++ b/src/sdl_cdrom/macosx/AudioFilePlayer.c @@ -0,0 +1,366 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2012 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Sam Lantinga + slouken@libsdl.org + + This file based on Apple sample code. We haven't changed the file name, + so if you want to see the original search for it on apple.com/developer +*/ +#include "config.h" +#define COMPAT_SDL_CDROM_PLATFORM_MACOSX 1 + +#if (C_COMPAT_SDL_CDROM_PLATFORM == COMPAT_SDL_CDROM_PLATFORM_MACOSX) + +#include "SDL_config.h" +#include "SDL_endian.h" + +/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + AudioFilePlayer.cpp +*/ +#include "AudioFilePlayer.h" + +/* +void ThrowResult (OSStatus result, const char* str) +{ + SDL_SetError ("Error: %s %d", str, result); + throw result; +} +*/ + +#if DEBUG +static void PrintStreamDesc (AudioStreamBasicDescription *inDesc) +{ + if (!inDesc) { + printf ("Can't print a NULL desc!\n"); + return; + } + + printf ("- - - - - - - - - - - - - - - - - - - -\n"); + printf (" Sample Rate:%f\n", inDesc->mSampleRate); + printf (" Format ID:%s\n", (char*)&inDesc->mFormatID); + printf (" Format Flags:%lX\n", inDesc->mFormatFlags); + printf (" Bytes per Packet:%ld\n", inDesc->mBytesPerPacket); + printf (" Frames per Packet:%ld\n", inDesc->mFramesPerPacket); + printf (" Bytes per Frame:%ld\n", inDesc->mBytesPerFrame); + printf (" Channels per Frame:%ld\n", inDesc->mChannelsPerFrame); + printf (" Bits per Channel:%ld\n", inDesc->mBitsPerChannel); + printf ("- - - - - - - - - - - - - - - - - - - -\n"); +} +#endif + + +static int AudioFilePlayer_SetDestination (AudioFilePlayer *afp, AudioUnit *inDestUnit) +{ + /*if (afp->mConnected) throw static_cast<OSStatus>(-1);*/ /* can't set dest if already engaged */ + if (afp->mConnected) + return 0 ; + + SDL_memcpy(&afp->mPlayUnit, inDestUnit, sizeof (afp->mPlayUnit)); + + OSStatus result = noErr; + + + /* we can "down" cast a component instance to a component */ + ComponentDescription desc; + result = GetComponentInfo ((Component)*inDestUnit, &desc, 0, 0, 0); + if (result) return 0; /*THROW_RESULT("GetComponentInfo")*/ + + /* we're going to use this to know which convert routine to call + a v1 audio unit will have a type of 'aunt' + a v2 audio unit will have one of several different types. */ + if (desc.componentType != kAudioUnitType_Output) { + result = badComponentInstance; + /*THROW_RESULT("BAD COMPONENT")*/ + if (result) return 0; + } + + /* Set the input format of the audio unit. */ + result = AudioUnitSetProperty (*inDestUnit, + kAudioUnitProperty_StreamFormat, + kAudioUnitScope_Input, + 0, + &afp->mFileDescription, + sizeof (afp->mFileDescription)); + /*THROW_RESULT("AudioUnitSetProperty")*/ + if (result) return 0; + return 1; +} + +static void AudioFilePlayer_SetNotifier(AudioFilePlayer *afp, AudioFilePlayNotifier inNotifier, void *inRefCon) +{ + afp->mNotifier = inNotifier; + afp->mRefCon = inRefCon; +} + +static int AudioFilePlayer_IsConnected(AudioFilePlayer *afp) +{ + return afp->mConnected; +} + +static AudioUnit AudioFilePlayer_GetDestUnit(AudioFilePlayer *afp) +{ + return afp->mPlayUnit; +} + +static void AudioFilePlayer_Print(AudioFilePlayer *afp) +{ +#if DEBUG + printf ("Is Connected:%s\n", (IsConnected() ? "true" : "false")); + printf ("- - - - - - - - - - - - - - \n"); +#endif +} + +static void AudioFilePlayer_SetStartFrame (AudioFilePlayer *afp, int frame) +{ + SInt64 position = frame * 2352; + + afp->mStartFrame = frame; + afp->mAudioFileManager->SetPosition (afp->mAudioFileManager, position); +} + + +static int AudioFilePlayer_GetCurrentFrame (AudioFilePlayer *afp) +{ + return afp->mStartFrame + (afp->mAudioFileManager->GetByteCounter(afp->mAudioFileManager) / 2352); +} + +static void AudioFilePlayer_SetStopFrame (AudioFilePlayer *afp, int frame) +{ + SInt64 position = frame * 2352; + + afp->mAudioFileManager->SetEndOfFile (afp->mAudioFileManager, position); +} + +void delete_AudioFilePlayer(AudioFilePlayer *afp) +{ + if (afp != NULL) + { + afp->Disconnect(afp); + + if (afp->mAudioFileManager) { + delete_AudioFileManager(afp->mAudioFileManager); + afp->mAudioFileManager = 0; + } + + if (afp->mForkRefNum) { + FSCloseFork (afp->mForkRefNum); + afp->mForkRefNum = 0; + } + SDL_free(afp); + } +} + +static int AudioFilePlayer_Connect(AudioFilePlayer *afp) +{ +#if DEBUG + printf ("Connect:%x, engaged=%d\n", (int)afp->mPlayUnit, (afp->mConnected ? 1 : 0)); +#endif + if (!afp->mConnected) + { + if (!afp->mAudioFileManager->DoConnect(afp->mAudioFileManager)) + return 0; + + /* set the render callback for the file data to be supplied to the sound converter AU */ + afp->mInputCallback.inputProc = afp->mAudioFileManager->FileInputProc; + afp->mInputCallback.inputProcRefCon = afp->mAudioFileManager; + + OSStatus result = AudioUnitSetProperty (afp->mPlayUnit, + kAudioUnitProperty_SetRenderCallback, + kAudioUnitScope_Input, + 0, + &afp->mInputCallback, + sizeof(afp->mInputCallback)); + if (result) return 0; /*THROW_RESULT("AudioUnitSetProperty")*/ + afp->mConnected = 1; + } + + return 1; +} + +/* warning noted, now please go away ;-) */ +/* #warning This should redirect the calling of notification code to some other thread */ +static void AudioFilePlayer_DoNotification (AudioFilePlayer *afp, OSStatus inStatus) +{ + if (afp->mNotifier) { + (*afp->mNotifier) (afp->mRefCon, inStatus); + } else { + SDL_SetError ("Notification posted with no notifier in place"); + + if (inStatus == kAudioFilePlay_FileIsFinished) + afp->Disconnect(afp); + else if (inStatus != kAudioFilePlayErr_FilePlayUnderrun) + afp->Disconnect(afp); + } +} + +static void AudioFilePlayer_Disconnect (AudioFilePlayer *afp) +{ +#if DEBUG + printf ("Disconnect:%x,%ld, engaged=%d\n", (int)afp->mPlayUnit, 0, (afp->mConnected ? 1 : 0)); +#endif + if (afp->mConnected) + { + afp->mConnected = 0; + + afp->mInputCallback.inputProc = 0; + afp->mInputCallback.inputProcRefCon = 0; + OSStatus result = AudioUnitSetProperty (afp->mPlayUnit, + kAudioUnitProperty_SetRenderCallback, + kAudioUnitScope_Input, + 0, + &afp->mInputCallback, + sizeof(afp->mInputCallback)); + if (result) + SDL_SetError ("AudioUnitSetProperty:RemoveInputCallback:%ld", result); + + afp->mAudioFileManager->Disconnect(afp->mAudioFileManager); + } +} + +typedef struct { + UInt32 offset; + UInt32 blockSize; +} SSNDData; + +static int AudioFilePlayer_OpenFile (AudioFilePlayer *afp, const FSRef *inRef, SInt64 *outFileDataSize) +{ + ContainerChunk chunkHeader; + ChunkHeader chunk; + SSNDData ssndData; + + OSErr result; + HFSUniStr255 dfName; + ByteCount actual; + SInt64 offset; + + /* Open the data fork of the input file */ + result = FSGetDataForkName(&dfName); + if (result) return 0; /*THROW_RESULT("AudioFilePlayer::OpenFile(): FSGetDataForkName")*/ + + result = FSOpenFork(inRef, dfName.length, dfName.unicode, fsRdPerm, &afp->mForkRefNum); + if (result) return 0; /*THROW_RESULT("AudioFilePlayer::OpenFile(): FSOpenFork")*/ + + /* Read the file header, and check if it's indeed an AIFC file */ + result = FSReadFork(afp->mForkRefNum, fsAtMark, 0, sizeof(chunkHeader), &chunkHeader, &actual); + if (result) return 0; /*THROW_RESULT("AudioFilePlayer::OpenFile(): FSReadFork")*/ + + if (SDL_SwapBE32(chunkHeader.ckID) != 'FORM') { + result = -1; + if (result) return 0; /*THROW_RESULT("AudioFilePlayer::OpenFile(): chunk id is not 'FORM'");*/ + } + + if (SDL_SwapBE32(chunkHeader.formType) != 'AIFC') { + result = -1; + if (result) return 0; /*THROW_RESULT("AudioFilePlayer::OpenFile(): file format is not 'AIFC'");*/ + } + + /* Search for the SSND chunk. We ignore all compression etc. information + in other chunks. Of course that is kind of evil, but for now we are lazy + and rely on the cdfs to always give us the same fixed format. + TODO: Parse the COMM chunk we currently skip to fill in mFileDescription. + */ + offset = 0; + do { + result = FSReadFork(afp->mForkRefNum, fsFromMark, offset, sizeof(chunk), &chunk, &actual); + if (result) return 0; /*THROW_RESULT("AudioFilePlayer::OpenFile(): FSReadFork")*/ + + chunk.ckID = SDL_SwapBE32(chunk.ckID); + chunk.ckSize = SDL_SwapBE32(chunk.ckSize); + + /* Skip the chunk data */ + offset = chunk.ckSize; + } while (chunk.ckID != 'SSND'); + + /* Read the header of the SSND chunk. After this, we are positioned right + at the start of the audio data. */ + result = FSReadFork(afp->mForkRefNum, fsAtMark, 0, sizeof(ssndData), &ssndData, &actual); + if (result) return 0; /*THROW_RESULT("AudioFilePlayer::OpenFile(): FSReadFork")*/ + + ssndData.offset = SDL_SwapBE32(ssndData.offset); + + result = FSSetForkPosition(afp->mForkRefNum, fsFromMark, ssndData.offset); + if (result) return 0; /*THROW_RESULT("AudioFilePlayer::OpenFile(): FSSetForkPosition")*/ + + /* Data size */ + *outFileDataSize = chunk.ckSize - ssndData.offset - 8; + + /* File format */ + afp->mFileDescription.mSampleRate = 44100; + afp->mFileDescription.mFormatID = kAudioFormatLinearPCM; + afp->mFileDescription.mFormatFlags = kLinearPCMFormatFlagIsPacked | kLinearPCMFormatFlagIsSignedInteger; + afp->mFileDescription.mBytesPerPacket = 4; + afp->mFileDescription.mFramesPerPacket = 1; + afp->mFileDescription.mBytesPerFrame = 4; + afp->mFileDescription.mChannelsPerFrame = 2; + afp->mFileDescription.mBitsPerChannel = 16; + + return 1; +} + +AudioFilePlayer *new_AudioFilePlayer (const FSRef *inFileRef) +{ + SInt64 fileDataSize = 0; + + AudioFilePlayer *afp = (AudioFilePlayer *) SDL_malloc(sizeof (AudioFilePlayer)); + if (afp == NULL) + return NULL; + SDL_memset(afp, '\0', sizeof (*afp)); + + #define SET_AUDIOFILEPLAYER_METHOD(m) afp->m = AudioFilePlayer_##m + SET_AUDIOFILEPLAYER_METHOD(SetDestination); + SET_AUDIOFILEPLAYER_METHOD(SetNotifier); + SET_AUDIOFILEPLAYER_METHOD(SetStartFrame); + SET_AUDIOFILEPLAYER_METHOD(GetCurrentFrame); + SET_AUDIOFILEPLAYER_METHOD(SetStopFrame); + SET_AUDIOFILEPLAYER_METHOD(Connect); + SET_AUDIOFILEPLAYER_METHOD(Disconnect); + SET_AUDIOFILEPLAYER_METHOD(DoNotification); + SET_AUDIOFILEPLAYER_METHOD(IsConnected); + SET_AUDIOFILEPLAYER_METHOD(GetDestUnit); + SET_AUDIOFILEPLAYER_METHOD(Print); + SET_AUDIOFILEPLAYER_METHOD(OpenFile); + #undef SET_AUDIOFILEPLAYER_METHOD + + if (!afp->OpenFile (afp, inFileRef, &fileDataSize)) + { + SDL_free(afp); + return NULL; + } + + /* we want about 4 seconds worth of data for the buffer */ + int bytesPerSecond = (UInt32) (4 * afp->mFileDescription.mSampleRate * afp->mFileDescription.mBytesPerFrame); + +#if DEBUG + printf("File format:\n"); + PrintStreamDesc (&afp->mFileDescription); +#endif + + afp->mAudioFileManager = new_AudioFileManager(afp, afp->mForkRefNum, + fileDataSize, + bytesPerSecond); + if (afp->mAudioFileManager == NULL) + { + delete_AudioFilePlayer(afp); + return NULL; + } + + return afp; +} + +#endif /* SDL_CDROM_MACOSX */ diff --git a/src/sdl_cdrom/macosx/AudioFilePlayer.h b/src/sdl_cdrom/macosx/AudioFilePlayer.h new file mode 100644 index 000000000..886d017a5 --- /dev/null +++ b/src/sdl_cdrom/macosx/AudioFilePlayer.h @@ -0,0 +1,178 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2012 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Sam Lantinga + slouken@libsdl.org + + This file based on Apple sample code. We haven't changed the file name, + so if you want to see the original search for it on apple.com/developer +*/ +#include "SDL_config.h" + +/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + AudioFilePlayer.h +*/ +#ifndef __AudioFilePlayer_H__ +#define __AudioFilePlayer_H__ + +#include <CoreServices/CoreServices.h> + +#include <AudioUnit/AudioUnit.h> +#if MAC_OS_X_VERSION_MAX_ALLOWED <= 1050 +#include <AudioUnit/AUNTComponent.h> +#endif + +#if (MAC_OS_X_VERSION_MAX_ALLOWED < 1050) +typedef SInt16 FSIORefNum; +#endif + +#include "SDL_error.h" + +const char* AudioFilePlayerErrorStr (OSStatus error); + +/* +void ThrowResult (OSStatus result, const char *str); + +#define THROW_RESULT(str) \ + if (result) { \ + ThrowResult (result, str); \ + } +*/ + +typedef void (*AudioFilePlayNotifier)(void *inRefCon, + OSStatus inStatus); + +enum { + kAudioFilePlayErr_FilePlayUnderrun = -10000, + kAudioFilePlay_FileIsFinished = -10001, + kAudioFilePlay_PlayerIsUninitialized = -10002 +}; + + +struct S_AudioFileManager; + +#pragma mark __________ AudioFilePlayer +typedef struct S_AudioFilePlayer +{ +/*public:*/ + int (*SetDestination)(struct S_AudioFilePlayer *afp, AudioUnit *inDestUnit); + void (*SetNotifier)(struct S_AudioFilePlayer *afp, AudioFilePlayNotifier inNotifier, void *inRefCon); + void (*SetStartFrame)(struct S_AudioFilePlayer *afp, int frame); /* seek in the file */ + int (*GetCurrentFrame)(struct S_AudioFilePlayer *afp); /* get the current frame position */ + void (*SetStopFrame)(struct S_AudioFilePlayer *afp, int frame); /* set limit in the file */ + int (*Connect)(struct S_AudioFilePlayer *afp); + void (*Disconnect)(struct S_AudioFilePlayer *afp); + void (*DoNotification)(struct S_AudioFilePlayer *afp, OSStatus inError); + int (*IsConnected)(struct S_AudioFilePlayer *afp); + AudioUnit (*GetDestUnit)(struct S_AudioFilePlayer *afp); + void (*Print)(struct S_AudioFilePlayer *afp); + +/*private:*/ + AudioUnit mPlayUnit; + FSIORefNum mForkRefNum; + + AURenderCallbackStruct mInputCallback; + + AudioStreamBasicDescription mFileDescription; + + int mConnected; + + struct S_AudioFileManager* mAudioFileManager; + + AudioFilePlayNotifier mNotifier; + void* mRefCon; + + int mStartFrame; + +#pragma mark __________ Private_Methods + + int (*OpenFile)(struct S_AudioFilePlayer *afp, const FSRef *inRef, SInt64 *outFileSize); +} AudioFilePlayer; + + +AudioFilePlayer *new_AudioFilePlayer(const FSRef *inFileRef); +void delete_AudioFilePlayer(AudioFilePlayer *afp); + + + +#pragma mark __________ AudioFileManager +typedef struct S_AudioFileManager +{ +/*public:*/ + /* this method should NOT be called by an object of this class + as it is called by the parent's Disconnect() method */ + void (*Disconnect)(struct S_AudioFileManager *afm); + int (*DoConnect)(struct S_AudioFileManager *afm); + OSStatus (*Read)(struct S_AudioFileManager *afm, char *buffer, ByteCount *len); + const char* (*GetFileBuffer)(struct S_AudioFileManager *afm); + const AudioFilePlayer *(*GetParent)(struct S_AudioFileManager *afm); + void (*SetPosition)(struct S_AudioFileManager *afm, SInt64 pos); /* seek/rewind in the file */ + int (*GetByteCounter)(struct S_AudioFileManager *afm); /* return actual bytes streamed to audio hardware */ + void (*SetEndOfFile)(struct S_AudioFileManager *afm, SInt64 pos); /* set the "EOF" (will behave just like it reached eof) */ + +/*protected:*/ + AudioFilePlayer* mParent; + SInt16 mForkRefNum; + SInt64 mAudioDataOffset; + + char* mFileBuffer; + + int mByteCounter; + + int mReadFromFirstBuffer; + int mLockUnsuccessful; + int mIsEngaged; + + int mNumTimesAskedSinceFinished; + + + void* mTmpBuffer; + UInt32 mBufferSize; + UInt32 mBufferOffset; +/*public:*/ + UInt32 mChunkSize; + SInt64 mFileLength; + SInt64 mReadFilePosition; + int mWriteToFirstBuffer; + int mFinishedReadingData; + +/*protected:*/ + OSStatus (*Render)(struct S_AudioFileManager *afm, AudioBufferList *ioData); + OSStatus (*GetFileData)(struct S_AudioFileManager *afm, void** inOutData, UInt32 *inOutDataSize); + void (*AfterRender)(struct S_AudioFileManager *afm); + +/*public:*/ + /*static*/ + OSStatus (*FileInputProc)(void *inRefCon, + AudioUnitRenderActionFlags *ioActionFlags, + const AudioTimeStamp *inTimeStamp, + UInt32 inBusNumber, + UInt32 inNumberFrames, + AudioBufferList *ioData); +} AudioFileManager; + + +AudioFileManager *new_AudioFileManager (AudioFilePlayer *inParent, + SInt16 inForkRefNum, + SInt64 inFileLength, + UInt32 inChunkSize); + +void delete_AudioFileManager(AudioFileManager *afm); + +#endif + diff --git a/src/sdl_cdrom/macosx/AudioFileReaderThread.c b/src/sdl_cdrom/macosx/AudioFileReaderThread.c new file mode 100644 index 000000000..1b48d6404 --- /dev/null +++ b/src/sdl_cdrom/macosx/AudioFileReaderThread.c @@ -0,0 +1,616 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2012 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Sam Lantinga + slouken@libsdl.org + + This file based on Apple sample code. We haven't changed the file name, + so if you want to see the original search for it on apple.com/developer +*/ +#include "config.h" +#define COMPAT_SDL_CDROM_PLATFORM_MACOSX 1 + +#if (C_COMPAT_SDL_CDROM_PLATFORM == COMPAT_SDL_CDROM_PLATFORM_MACOSX) + +#include "SDL_config.h" + +/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + AudioFileManager.cpp +*/ +#include "AudioFilePlayer.h" +#include <mach/mach.h> /* used for setting policy of thread */ +#include "SDLOSXCAGuard.h" +#include <pthread.h> + +/*#include <list>*/ + +/*typedef void *FileData;*/ +typedef struct S_FileData +{ + AudioFileManager *obj; + struct S_FileData *next; +} FileData; + + +typedef struct S_FileReaderThread { +/*public:*/ + SDLOSXCAGuard* (*GetGuard)(struct S_FileReaderThread *frt); + void (*AddReader)(struct S_FileReaderThread *frt); + void (*RemoveReader)(struct S_FileReaderThread *frt, AudioFileManager* inItem); + int (*TryNextRead)(struct S_FileReaderThread *frt, AudioFileManager* inItem); + + int mThreadShouldDie; + +/*private:*/ + /*typedef std::list<AudioFileManager*> FileData;*/ + + SDLOSXCAGuard *mGuard; + UInt32 mThreadPriority; + + int mNumReaders; + FileData *mFileData; + + + void (*ReadNextChunk)(struct S_FileReaderThread *frt); + int (*StartFixedPriorityThread)(struct S_FileReaderThread *frt); + /*static*/ + UInt32 (*GetThreadBasePriority)(pthread_t inThread); + /*static*/ + void* (*DiskReaderEntry)(void *inRefCon); +} FileReaderThread; + + +static SDLOSXCAGuard* FileReaderThread_GetGuard(FileReaderThread *frt) +{ + return frt->mGuard; +} + +/* returns 1 if succeeded */ +static int FileReaderThread_TryNextRead (FileReaderThread *frt, AudioFileManager* inItem) +{ + int didLock = 0; + int succeeded = 0; + if (frt->mGuard->Try(frt->mGuard, &didLock)) + { + /*frt->mFileData.push_back (inItem);*/ + /* !!! FIXME: this could be faster with a "tail" member. --ryan. */ + FileData *i = frt->mFileData; + FileData *prev = NULL; + + FileData *newfd = (FileData *) SDL_malloc(sizeof (FileData)); + newfd->obj = inItem; + newfd->next = NULL; + + while (i != NULL) { prev = i; i = i->next; } + if (prev == NULL) + frt->mFileData = newfd; + else + prev->next = newfd; + + frt->mGuard->Notify(frt->mGuard); + succeeded = 1; + + if (didLock) + frt->mGuard->Unlock(frt->mGuard); + } + + return succeeded; +} + +static void FileReaderThread_AddReader(FileReaderThread *frt) +{ + if (frt->mNumReaders == 0) + { + frt->mThreadShouldDie = 0; + frt->StartFixedPriorityThread (frt); + } + frt->mNumReaders++; +} + +static void FileReaderThread_RemoveReader (FileReaderThread *frt, AudioFileManager* inItem) +{ + if (frt->mNumReaders > 0) + { + int bNeedsRelease = frt->mGuard->Lock(frt->mGuard); + + /*frt->mFileData.remove (inItem);*/ + FileData *i = frt->mFileData; + FileData *prev = NULL; + while (i != NULL) + { + FileData *next = i->next; + if (i->obj != inItem) + prev = i; + else + { + if (prev == NULL) + frt->mFileData = next; + else + prev->next = next; + SDL_free(i); + } + i = next; + } + + if (--frt->mNumReaders == 0) { + frt->mThreadShouldDie = 1; + frt->mGuard->Notify(frt->mGuard); /* wake up thread so it will quit */ + frt->mGuard->Wait(frt->mGuard); /* wait for thread to die */ + } + + if (bNeedsRelease) frt->mGuard->Unlock(frt->mGuard); + } +} + +static int FileReaderThread_StartFixedPriorityThread (FileReaderThread *frt) +{ + pthread_attr_t theThreadAttrs; + pthread_t pThread; + + OSStatus result = pthread_attr_init(&theThreadAttrs); + if (result) return 0; /*THROW_RESULT("pthread_attr_init - Thread attributes could not be created.")*/ + + result = pthread_attr_setdetachstate(&theThreadAttrs, PTHREAD_CREATE_DETACHED); + if (result) return 0; /*THROW_RESULT("pthread_attr_setdetachstate - Thread attributes could not be detached.")*/ + + result = pthread_create (&pThread, &theThreadAttrs, frt->DiskReaderEntry, frt); + if (result) return 0; /*THROW_RESULT("pthread_create - Create and start the thread.")*/ + + pthread_attr_destroy(&theThreadAttrs); + + /* we've now created the thread and started it + we'll now set the priority of the thread to the nominated priority + and we'll also make the thread fixed */ + thread_extended_policy_data_t theFixedPolicy; + thread_precedence_policy_data_t thePrecedencePolicy; + SInt32 relativePriority; + + /* make thread fixed */ + theFixedPolicy.timeshare = 0; /* set to 1 for a non-fixed thread */ + result = thread_policy_set (pthread_mach_thread_np(pThread), THREAD_EXTENDED_POLICY, (thread_policy_t)&theFixedPolicy, THREAD_EXTENDED_POLICY_COUNT); + if (result) return 0; /*THROW_RESULT("thread_policy - Couldn't set thread as fixed priority.")*/ + /* set priority */ + /* precedency policy's "importance" value is relative to spawning thread's priority */ + relativePriority = frt->mThreadPriority - frt->GetThreadBasePriority(pthread_self()); + + thePrecedencePolicy.importance = relativePriority; + result = thread_policy_set (pthread_mach_thread_np(pThread), THREAD_PRECEDENCE_POLICY, (thread_policy_t)&thePrecedencePolicy, THREAD_PRECEDENCE_POLICY_COUNT); + if (result) return 0; /*THROW_RESULT("thread_policy - Couldn't set thread priority.")*/ + + return 1; +} + +static UInt32 FileReaderThread_GetThreadBasePriority (pthread_t inThread) +{ + thread_basic_info_data_t threadInfo; + policy_info_data_t thePolicyInfo; + unsigned int count; + + /* get basic info */ + count = THREAD_BASIC_INFO_COUNT; + thread_info (pthread_mach_thread_np (inThread), THREAD_BASIC_INFO, (integer_t*)&threadInfo, &count); + + switch (threadInfo.policy) { + case POLICY_TIMESHARE: + count = POLICY_TIMESHARE_INFO_COUNT; + thread_info(pthread_mach_thread_np (inThread), THREAD_SCHED_TIMESHARE_INFO, (integer_t*)&(thePolicyInfo.ts), &count); + return thePolicyInfo.ts.base_priority; + break; + + case POLICY_FIFO: + count = POLICY_FIFO_INFO_COUNT; + thread_info(pthread_mach_thread_np (inThread), THREAD_SCHED_FIFO_INFO, (integer_t*)&(thePolicyInfo.fifo), &count); + if (thePolicyInfo.fifo.depressed) { + return thePolicyInfo.fifo.depress_priority; + } else { + return thePolicyInfo.fifo.base_priority; + } + break; + + case POLICY_RR: + count = POLICY_RR_INFO_COUNT; + thread_info(pthread_mach_thread_np (inThread), THREAD_SCHED_RR_INFO, (integer_t*)&(thePolicyInfo.rr), &count); + if (thePolicyInfo.rr.depressed) { + return thePolicyInfo.rr.depress_priority; + } else { + return thePolicyInfo.rr.base_priority; + } + break; + } + + return 0; +} + +static void *FileReaderThread_DiskReaderEntry (void *inRefCon) +{ + FileReaderThread *frt = (FileReaderThread *)inRefCon; + frt->ReadNextChunk(frt); + #if DEBUG + printf ("finished with reading file\n"); + #endif + + return 0; +} + +static void FileReaderThread_ReadNextChunk (FileReaderThread *frt) +{ + OSStatus result; + ByteCount dataChunkSize; + AudioFileManager* theItem = 0; + + for (;;) + { + { /* this is a scoped based lock */ + int bNeedsRelease = frt->mGuard->Lock(frt->mGuard); + + if (frt->mThreadShouldDie) { + frt->mGuard->Notify(frt->mGuard); + if (bNeedsRelease) frt->mGuard->Unlock(frt->mGuard); + return; + } + + /*if (frt->mFileData.empty())*/ + if (frt->mFileData == NULL) + { + frt->mGuard->Wait(frt->mGuard); + } + + /* kill thread */ + if (frt->mThreadShouldDie) { + + frt->mGuard->Notify(frt->mGuard); + if (bNeedsRelease) frt->mGuard->Unlock(frt->mGuard); + return; + } + + /*theItem = frt->mFileData.front();*/ + /*frt->mFileData.pop_front();*/ + theItem = NULL; + if (frt->mFileData != NULL) + { + FileData *next = frt->mFileData->next; + theItem = frt->mFileData->obj; + SDL_free(frt->mFileData); + frt->mFileData = next; + } + + if (bNeedsRelease) frt->mGuard->Unlock(frt->mGuard); + } + + if ((theItem->mFileLength - theItem->mReadFilePosition) < theItem->mChunkSize) + dataChunkSize = theItem->mFileLength - theItem->mReadFilePosition; + else + dataChunkSize = theItem->mChunkSize; + + /* this is the exit condition for the thread */ + if (dataChunkSize <= 0) { + theItem->mFinishedReadingData = 1; + continue; + } + /* construct pointer */ + char* writePtr = (char *) (theItem->GetFileBuffer(theItem) + + (theItem->mWriteToFirstBuffer ? 0 : theItem->mChunkSize)); + + /* read data */ + result = theItem->Read(theItem, writePtr, &dataChunkSize); + if (result != noErr && result != eofErr) { + AudioFilePlayer *afp = (AudioFilePlayer *) theItem->GetParent(theItem); + afp->DoNotification(afp, result); + continue; + } + + if (dataChunkSize != theItem->mChunkSize) + { + writePtr += dataChunkSize; + + /* can't exit yet.. we still have to pass the partial buffer back */ + SDL_memset(writePtr, 0, (theItem->mChunkSize - dataChunkSize)); + } + + theItem->mWriteToFirstBuffer = !theItem->mWriteToFirstBuffer; /* switch buffers */ + + if (result == eofErr) + theItem->mReadFilePosition = theItem->mFileLength; + else + theItem->mReadFilePosition += dataChunkSize; /* increment count */ + } +} + +void delete_FileReaderThread(FileReaderThread *frt) +{ + if (frt != NULL) + { + delete_SDLOSXCAGuard(frt->mGuard); + SDL_free(frt); + } +} + +FileReaderThread *new_FileReaderThread () +{ + FileReaderThread *frt = (FileReaderThread *) SDL_malloc(sizeof (FileReaderThread)); + if (frt == NULL) + return NULL; + SDL_memset(frt, '\0', sizeof (*frt)); + + frt->mGuard = new_SDLOSXCAGuard(); + if (frt->mGuard == NULL) + { + SDL_free(frt); + return NULL; + } + + #define SET_FILEREADERTHREAD_METHOD(m) frt->m = FileReaderThread_##m + SET_FILEREADERTHREAD_METHOD(GetGuard); + SET_FILEREADERTHREAD_METHOD(AddReader); + SET_FILEREADERTHREAD_METHOD(RemoveReader); + SET_FILEREADERTHREAD_METHOD(TryNextRead); + SET_FILEREADERTHREAD_METHOD(ReadNextChunk); + SET_FILEREADERTHREAD_METHOD(StartFixedPriorityThread); + SET_FILEREADERTHREAD_METHOD(GetThreadBasePriority); + SET_FILEREADERTHREAD_METHOD(DiskReaderEntry); + #undef SET_FILEREADERTHREAD_METHOD + + frt->mThreadPriority = 62; + return frt; +} + + +static FileReaderThread *sReaderThread; + + +static int AudioFileManager_DoConnect (AudioFileManager *afm) +{ + if (!afm->mIsEngaged) + { + OSStatus result; + + /*afm->mReadFilePosition = 0;*/ + afm->mFinishedReadingData = 0; + + afm->mNumTimesAskedSinceFinished = 0; + afm->mLockUnsuccessful = 0; + + ByteCount dataChunkSize; + + if ((afm->mFileLength - afm->mReadFilePosition) < afm->mChunkSize) + dataChunkSize = afm->mFileLength - afm->mReadFilePosition; + else + dataChunkSize = afm->mChunkSize; + + result = afm->Read(afm, afm->mFileBuffer, &dataChunkSize); + if (result) return 0; /*THROW_RESULT("AudioFileManager::DoConnect(): Read")*/ + + afm->mReadFilePosition += dataChunkSize; + + afm->mWriteToFirstBuffer = 0; + afm->mReadFromFirstBuffer = 1; + + sReaderThread->AddReader(sReaderThread); + + afm->mIsEngaged = 1; + } + /* + else + throw static_cast<OSStatus>(-1); */ /* thread has already been started */ + + return 1; +} + +static void AudioFileManager_Disconnect (AudioFileManager *afm) +{ + if (afm->mIsEngaged) + { + sReaderThread->RemoveReader (sReaderThread, afm); + afm->mIsEngaged = 0; + } +} + +static OSStatus AudioFileManager_Read(AudioFileManager *afm, char *buffer, ByteCount *len) +{ + return FSReadFork (afm->mForkRefNum, + fsFromStart, + afm->mReadFilePosition + afm->mAudioDataOffset, + *len, + buffer, + len); +} + +static OSStatus AudioFileManager_GetFileData (AudioFileManager *afm, void** inOutData, UInt32 *inOutDataSize) +{ + if (afm->mFinishedReadingData) + { + ++afm->mNumTimesAskedSinceFinished; + *inOutDataSize = 0; + *inOutData = 0; + return noErr; + } + + if (afm->mReadFromFirstBuffer == afm->mWriteToFirstBuffer) { + #if DEBUG + printf ("* * * * * * * Can't keep up with reading file\n"); + #endif + + afm->mParent->DoNotification (afm->mParent, kAudioFilePlayErr_FilePlayUnderrun); + *inOutDataSize = 0; + *inOutData = 0; + } else { + *inOutDataSize = afm->mChunkSize; + *inOutData = afm->mReadFromFirstBuffer ? afm->mFileBuffer : (afm->mFileBuffer + afm->mChunkSize); + } + + afm->mLockUnsuccessful = !sReaderThread->TryNextRead (sReaderThread, afm); + + afm->mReadFromFirstBuffer = !afm->mReadFromFirstBuffer; + + return noErr; +} + +static void AudioFileManager_AfterRender (AudioFileManager *afm) +{ + if (afm->mNumTimesAskedSinceFinished > 0) + { + int didLock = 0; + SDLOSXCAGuard *guard = sReaderThread->GetGuard(sReaderThread); + if (guard->Try(guard, &didLock)) { + afm->mParent->DoNotification (afm->mParent, kAudioFilePlay_FileIsFinished); + if (didLock) + guard->Unlock(guard); + } + } + + if (afm->mLockUnsuccessful) + afm->mLockUnsuccessful = !sReaderThread->TryNextRead (sReaderThread, afm); +} + +static void AudioFileManager_SetPosition (AudioFileManager *afm, SInt64 pos) +{ + if (pos < 0 || pos >= afm->mFileLength) { + SDL_SetError ("AudioFileManager::SetPosition - position invalid: %d filelen=%d\n", + (unsigned int)pos, (unsigned int)afm->mFileLength); + pos = 0; + } + + afm->mReadFilePosition = pos; +} + +static void AudioFileManager_SetEndOfFile (AudioFileManager *afm, SInt64 pos) +{ + if (pos <= 0 || pos > afm->mFileLength) { + SDL_SetError ("AudioFileManager::SetEndOfFile - position beyond actual eof\n"); + pos = afm->mFileLength; + } + + afm->mFileLength = pos; +} + +static const char *AudioFileManager_GetFileBuffer(AudioFileManager *afm) +{ + return afm->mFileBuffer; +} + +const AudioFilePlayer *AudioFileManager_GetParent(AudioFileManager *afm) +{ + return afm->mParent; +} + +static int AudioFileManager_GetByteCounter(AudioFileManager *afm) +{ + return afm->mByteCounter; +} + +static OSStatus AudioFileManager_FileInputProc (void *inRefCon, + AudioUnitRenderActionFlags *ioActionFlags, + const AudioTimeStamp *inTimeStamp, + UInt32 inBusNumber, + UInt32 inNumberFrames, + AudioBufferList *ioData) +{ + AudioFileManager* afm = (AudioFileManager*)inRefCon; + return afm->Render(afm, ioData); +} + +static OSStatus AudioFileManager_Render (AudioFileManager *afm, AudioBufferList *ioData) +{ + OSStatus result = noErr; + AudioBuffer *abuf; + UInt32 i; + + for (i = 0; i < ioData->mNumberBuffers; i++) { + abuf = &ioData->mBuffers[i]; + if (afm->mBufferOffset >= afm->mBufferSize) { + result = afm->GetFileData(afm, &afm->mTmpBuffer, &afm->mBufferSize); + if (result) { + SDL_SetError ("AudioConverterFillBuffer:%ld\n", result); + afm->mParent->DoNotification(afm->mParent, result); + return result; + } + + afm->mBufferOffset = 0; + } + + if (abuf->mDataByteSize > afm->mBufferSize - afm->mBufferOffset) + abuf->mDataByteSize = afm->mBufferSize - afm->mBufferOffset; + abuf->mData = (char *)afm->mTmpBuffer + afm->mBufferOffset; + afm->mBufferOffset += abuf->mDataByteSize; + + afm->mByteCounter += abuf->mDataByteSize; + afm->AfterRender(afm); + } + return result; +} + + +void delete_AudioFileManager (AudioFileManager *afm) +{ + if (afm != NULL) { + if (afm->mFileBuffer) { + free(afm->mFileBuffer); + } + + SDL_free(afm); + } +} + + +AudioFileManager *new_AudioFileManager(AudioFilePlayer *inParent, + SInt16 inForkRefNum, + SInt64 inFileLength, + UInt32 inChunkSize) +{ + AudioFileManager *afm; + + if (sReaderThread == NULL) + { + sReaderThread = new_FileReaderThread(); + if (sReaderThread == NULL) + return NULL; + } + + afm = (AudioFileManager *) SDL_malloc(sizeof (AudioFileManager)); + if (afm == NULL) + return NULL; + SDL_memset(afm, '\0', sizeof (*afm)); + + #define SET_AUDIOFILEMANAGER_METHOD(m) afm->m = AudioFileManager_##m + SET_AUDIOFILEMANAGER_METHOD(Disconnect); + SET_AUDIOFILEMANAGER_METHOD(DoConnect); + SET_AUDIOFILEMANAGER_METHOD(Read); + SET_AUDIOFILEMANAGER_METHOD(GetFileBuffer); + SET_AUDIOFILEMANAGER_METHOD(GetParent); + SET_AUDIOFILEMANAGER_METHOD(SetPosition); + SET_AUDIOFILEMANAGER_METHOD(GetByteCounter); + SET_AUDIOFILEMANAGER_METHOD(SetEndOfFile); + SET_AUDIOFILEMANAGER_METHOD(Render); + SET_AUDIOFILEMANAGER_METHOD(GetFileData); + SET_AUDIOFILEMANAGER_METHOD(AfterRender); + SET_AUDIOFILEMANAGER_METHOD(FileInputProc); + #undef SET_AUDIOFILEMANAGER_METHOD + + afm->mParent = inParent; + afm->mForkRefNum = inForkRefNum; + afm->mBufferSize = inChunkSize; + afm->mBufferOffset = inChunkSize; + afm->mChunkSize = inChunkSize; + afm->mFileLength = inFileLength; + afm->mFileBuffer = (char*) SDL_malloc(afm->mChunkSize * 2); + FSGetForkPosition(afm->mForkRefNum, &afm->mAudioDataOffset); + assert (afm->mFileBuffer != NULL); + return afm; +} + +#endif /* SDL_CDROM_MACOSX */ diff --git a/src/sdl_cdrom/macosx/CDPlayer.c b/src/sdl_cdrom/macosx/CDPlayer.c new file mode 100644 index 000000000..f86973c4e --- /dev/null +++ b/src/sdl_cdrom/macosx/CDPlayer.c @@ -0,0 +1,643 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2012 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "config.h" +#define COMPAT_SDL_CDROM_PLATFORM_MACOSX 1 + +#if (C_COMPAT_SDL_CDROM_PLATFORM == COMPAT_SDL_CDROM_PLATFORM_MACOSX) + +#include "SDL_config.h" + +#include "CDPlayer.h" +#include "AudioFilePlayer.h" +#include "SDLOSXCAGuard.h" + +/* we're exporting these functions into C land for SDL_syscdrom.c */ +/*extern "C" {*/ + +/*/////////////////////////////////////////////////////////////////////////// + Constants + //////////////////////////////////////////////////////////////////////////*/ + +#define kAudioCDFilesystemID (UInt16)(('J' << 8) | 'H') /* 'JH'; this avoids compiler warning */ + +/* XML PList keys */ +#define kRawTOCDataString "Format 0x02 TOC Data" +#define kSessionsString "Sessions" +#define kSessionTypeString "Session Type" +#define kTrackArrayString "Track Array" +#define kFirstTrackInSessionString "First Track" +#define kLastTrackInSessionString "Last Track" +#define kLeadoutBlockString "Leadout Block" +#define kDataKeyString "Data" +#define kPointKeyString "Point" +#define kSessionNumberKeyString "Session Number" +#define kStartBlockKeyString "Start Block" + +/*/////////////////////////////////////////////////////////////////////////// + Globals + //////////////////////////////////////////////////////////////////////////*/ + +#pragma mark -- Globals -- + +static int playBackWasInit = 0; +static AudioUnit theUnit; +static AudioFilePlayer* thePlayer = NULL; +static CDPlayerCompletionProc completionProc = NULL; +static SDL_mutex *apiMutex = NULL; +static SDL_sem *callbackSem; +static SDL_CD* theCDROM; + +/*/////////////////////////////////////////////////////////////////////////// + Prototypes + //////////////////////////////////////////////////////////////////////////*/ + +#pragma mark -- Prototypes -- + +static OSStatus CheckInit (); + +static void FilePlayNotificationHandler (void* inRefCon, OSStatus inStatus); + +static int RunCallBackThread (void* inRefCon); + + +#pragma mark -- Public Functions -- + +void Lock () +{ + if (!apiMutex) { + apiMutex = SDL_CreateMutex(); + } + SDL_mutexP(apiMutex); +} + +void Unlock () +{ + SDL_mutexV(apiMutex); +} + +int DetectAudioCDVolumes(FSVolumeRefNum *volumes, int numVolumes) +{ + int volumeIndex; + int cdVolumeCount = 0; + OSStatus result = noErr; + + for (volumeIndex = 1; result == noErr || result != nsvErr; volumeIndex++) + { + FSVolumeRefNum actualVolume; + FSVolumeInfo volumeInfo; + + memset (&volumeInfo, 0, sizeof(volumeInfo)); + + result = FSGetVolumeInfo (kFSInvalidVolumeRefNum, + volumeIndex, + &actualVolume, + kFSVolInfoFSInfo, + &volumeInfo, + NULL, + NULL); + + if (result == noErr) + { + if (volumeInfo.filesystemID == kAudioCDFilesystemID) /* It's an audio CD */ + { + if (volumes != NULL && cdVolumeCount < numVolumes) + volumes[cdVolumeCount] = actualVolume; + + cdVolumeCount++; + } + } + else + { + /* I'm commenting this out because it seems to be harmless */ + /*SDL_SetError ("DetectAudioCDVolumes: FSGetVolumeInfo returned %d", result);*/ + } + } + + return cdVolumeCount; +} + +int ReadTOCData (FSVolumeRefNum theVolume, SDL_CD *theCD) +{ + HFSUniStr255 dataForkName; + OSStatus theErr; + FSIORefNum forkRefNum; + SInt64 forkSize; + Ptr forkData = 0; + ByteCount actualRead; + CFDataRef dataRef = 0; + CFPropertyListRef propertyListRef = 0; + FSRefParam fsRefPB; + FSRef tocPlistFSRef; + FSRef rootRef; + const char* error = "Unspecified Error"; + const UniChar uniName[] = { '.','T','O','C','.','p','l','i','s','t' }; + + theErr = FSGetVolumeInfo(theVolume, 0, 0, kFSVolInfoNone, 0, 0, &rootRef); + if(theErr != noErr) { + error = "FSGetVolumeInfo"; + goto bail; + } + + SDL_memset(&fsRefPB, '\0', sizeof (fsRefPB)); + + /* get stuff from .TOC.plist */ + fsRefPB.ref = &rootRef; + fsRefPB.newRef = &tocPlistFSRef; + fsRefPB.nameLength = sizeof (uniName) / sizeof (uniName[0]); + fsRefPB.name = uniName; + fsRefPB.textEncodingHint = kTextEncodingUnknown; + + theErr = PBMakeFSRefUnicodeSync (&fsRefPB); + if(theErr != noErr) { + error = "PBMakeFSRefUnicodeSync"; + goto bail; + } + + /* Load and parse the TOC XML data */ + + theErr = FSGetDataForkName (&dataForkName); + if (theErr != noErr) { + error = "FSGetDataForkName"; + goto bail; + } + + theErr = FSOpenFork (&tocPlistFSRef, dataForkName.length, dataForkName.unicode, fsRdPerm, &forkRefNum); + if (theErr != noErr) { + error = "FSOpenFork"; + goto bail; + } + + theErr = FSGetForkSize (forkRefNum, &forkSize); + if (theErr != noErr) { + error = "FSGetForkSize"; + goto bail; + } + + /* Allocate some memory for the XML data */ + forkData = NewPtr (forkSize); + if(forkData == NULL) { + error = "NewPtr"; + goto bail; + } + + theErr = FSReadFork (forkRefNum, fsFromStart, 0 /* offset location */, forkSize, forkData, &actualRead); + if(theErr != noErr) { + error = "FSReadFork"; + goto bail; + } + + dataRef = CFDataCreate (kCFAllocatorDefault, (UInt8 *)forkData, forkSize); + if(dataRef == 0) { + error = "CFDataCreate"; + goto bail; + } + + propertyListRef = CFPropertyListCreateFromXMLData (kCFAllocatorDefault, + dataRef, + kCFPropertyListImmutable, + NULL); + if (propertyListRef == NULL) { + error = "CFPropertyListCreateFromXMLData"; + goto bail; + } + + /* Now we got the Property List in memory. Parse it. */ + + /* First, make sure the root item is a CFDictionary. If not, release and bail. */ + if(CFGetTypeID(propertyListRef)== CFDictionaryGetTypeID()) + { + CFDictionaryRef dictRef = (CFDictionaryRef)propertyListRef; + + CFDataRef theRawTOCDataRef; + CFArrayRef theSessionArrayRef; + CFIndex numSessions; + CFIndex index; + + /* This is how we get the Raw TOC Data */ + theRawTOCDataRef = (CFDataRef)CFDictionaryGetValue (dictRef, CFSTR(kRawTOCDataString)); + + /* Get the session array info. */ + theSessionArrayRef = (CFArrayRef)CFDictionaryGetValue (dictRef, CFSTR(kSessionsString)); + + /* Find out how many sessions there are. */ + numSessions = CFArrayGetCount (theSessionArrayRef); + + /* Initialize the total number of tracks to 0 */ + theCD->numtracks = 0; + + /* Iterate over all sessions, collecting the track data */ + for(index = 0; index < numSessions; index++) + { + CFDictionaryRef theSessionDict; + CFNumberRef leadoutBlock; + CFArrayRef trackArray; + CFIndex numTracks; + CFIndex trackIndex; + UInt32 value = 0; + + theSessionDict = (CFDictionaryRef) CFArrayGetValueAtIndex (theSessionArrayRef, index); + leadoutBlock = (CFNumberRef) CFDictionaryGetValue (theSessionDict, CFSTR(kLeadoutBlockString)); + + trackArray = (CFArrayRef)CFDictionaryGetValue (theSessionDict, CFSTR(kTrackArrayString)); + + numTracks = CFArrayGetCount (trackArray); + + for(trackIndex = 0; trackIndex < numTracks; trackIndex++) { + + CFDictionaryRef theTrackDict; + CFNumberRef trackNumber; + CFNumberRef sessionNumber; + CFNumberRef startBlock; + CFBooleanRef isDataTrack; + UInt32 value; + + theTrackDict = (CFDictionaryRef) CFArrayGetValueAtIndex (trackArray, trackIndex); + + trackNumber = (CFNumberRef) CFDictionaryGetValue (theTrackDict, CFSTR(kPointKeyString)); + sessionNumber = (CFNumberRef) CFDictionaryGetValue (theTrackDict, CFSTR(kSessionNumberKeyString)); + startBlock = (CFNumberRef) CFDictionaryGetValue (theTrackDict, CFSTR(kStartBlockKeyString)); + isDataTrack = (CFBooleanRef) CFDictionaryGetValue (theTrackDict, CFSTR(kDataKeyString)); + + /* Fill in the SDL_CD struct */ + int idx = theCD->numtracks++; + + CFNumberGetValue (trackNumber, kCFNumberSInt32Type, &value); + theCD->track[idx].id = value; + + CFNumberGetValue (startBlock, kCFNumberSInt32Type, &value); + theCD->track[idx].offset = value; + + theCD->track[idx].type = (isDataTrack == kCFBooleanTrue) ? SDL_DATA_TRACK : SDL_AUDIO_TRACK; + + /* Since the track lengths are not stored in .TOC.plist we compute them. */ + if (trackIndex > 0) { + theCD->track[idx-1].length = theCD->track[idx].offset - theCD->track[idx-1].offset; + } + } + + /* Compute the length of the last track */ + CFNumberGetValue (leadoutBlock, kCFNumberSInt32Type, &value); + + theCD->track[theCD->numtracks-1].length = + value - theCD->track[theCD->numtracks-1].offset; + + /* Set offset to leadout track */ + theCD->track[theCD->numtracks].offset = value; + } + + } + + theErr = 0; + goto cleanup; +bail: + SDL_SetError ("ReadTOCData: %s returned %d", error, theErr); + theErr = -1; +cleanup: + + if (propertyListRef != NULL) + CFRelease(propertyListRef); + if (dataRef != NULL) + CFRelease(dataRef); + if (forkData != NULL) + DisposePtr(forkData); + + FSCloseFork (forkRefNum); + + return theErr; +} + +int ListTrackFiles (FSVolumeRefNum theVolume, FSRef *trackFiles, int numTracks) +{ + OSStatus result = -1; + FSIterator iterator; + ItemCount actualObjects; + FSRef rootDirectory; + FSRef ref; + HFSUniStr255 nameStr; + + result = FSGetVolumeInfo (theVolume, + 0, + NULL, + kFSVolInfoFSInfo, + NULL, + NULL, + &rootDirectory); + + if (result != noErr) { + SDL_SetError ("ListTrackFiles: FSGetVolumeInfo returned %d", result); + return result; + } + + result = FSOpenIterator (&rootDirectory, kFSIterateFlat, &iterator); + if (result == noErr) { + do + { + result = FSGetCatalogInfoBulk (iterator, 1, &actualObjects, + NULL, kFSCatInfoNone, NULL, &ref, NULL, &nameStr); + if (result == noErr) { + + CFStringRef name; + name = CFStringCreateWithCharacters (NULL, nameStr.unicode, nameStr.length); + + /* Look for .aiff extension */ + if (CFStringHasSuffix (name, CFSTR(".aiff")) || + CFStringHasSuffix (name, CFSTR(".cdda"))) { + + /* Extract the track id from the filename */ + int trackID = 0, i = 0; + while (i < nameStr.length && !isdigit(nameStr.unicode[i])) { + ++i; + } + while (i < nameStr.length && isdigit(nameStr.unicode[i])) { + trackID = 10 * trackID +(nameStr.unicode[i] - '0'); + ++i; + } + + #if DEBUG_CDROM + printf("Found AIFF for track %d: '%s'\n", trackID, + CFStringGetCStringPtr (name, CFStringGetSystemEncoding())); + #endif + + /* Track ID's start at 1, but we want to start at 0 */ + trackID--; + + assert(0 <= trackID && trackID <= SDL_MAX_TRACKS); + + if (trackID < numTracks) + memcpy (&trackFiles[trackID], &ref, sizeof(FSRef)); + } + CFRelease (name); + } + } while(noErr == result); + FSCloseIterator (iterator); + } + + return 0; +} + +int LoadFile (const FSRef *ref, int startFrame, int stopFrame) +{ + int error = -1; + + if (CheckInit () < 0) + goto bail; + + /* release any currently playing file */ + if (ReleaseFile () < 0) + goto bail; + + #if DEBUG_CDROM + printf ("LoadFile: %d %d\n", startFrame, stopFrame); + #endif + + /*try {*/ + + /* create a new player, and attach to the audio unit */ + + thePlayer = new_AudioFilePlayer(ref); + if (thePlayer == NULL) { + SDL_SetError ("LoadFile: Could not create player"); + return -3; /*throw (-3);*/ + } + + if (!thePlayer->SetDestination(thePlayer, &theUnit)) + goto bail; + + if (startFrame >= 0) + thePlayer->SetStartFrame (thePlayer, startFrame); + + if (stopFrame >= 0 && stopFrame > startFrame) + thePlayer->SetStopFrame (thePlayer, stopFrame); + + /* we set the notifier later */ + /*thePlayer->SetNotifier(thePlayer, FilePlayNotificationHandler, NULL);*/ + + if (!thePlayer->Connect(thePlayer)) + goto bail; + + #if DEBUG_CDROM + thePlayer->Print(thePlayer); + fflush (stdout); + #endif + /*} + catch (...) + { + goto bail; + }*/ + + error = 0; + + bail: + return error; +} + +int ReleaseFile () +{ + int error = -1; + + /* (Don't see any way that the original C++ code could throw here.) --ryan. */ + /*try {*/ + if (thePlayer != NULL) { + + thePlayer->Disconnect(thePlayer); + + delete_AudioFilePlayer(thePlayer); + + thePlayer = NULL; + } + /*} + catch (...) + { + goto bail; + }*/ + + error = 0; + +/* bail: */ + return error; +} + +int PlayFile () +{ + OSStatus result = -1; + + if (CheckInit () < 0) + goto bail; + + /*try {*/ + + // start processing of the audio unit + result = AudioOutputUnitStart (theUnit); + if (result) goto bail; //THROW_RESULT("PlayFile: AudioOutputUnitStart") + + /*} + catch (...) + { + goto bail; + }*/ + + result = 0; + +bail: + return result; +} + +int PauseFile () +{ + OSStatus result = -1; + + if (CheckInit () < 0) + goto bail; + + /*try {*/ + + /* stop processing the audio unit */ + result = AudioOutputUnitStop (theUnit); + if (result) goto bail; /*THROW_RESULT("PauseFile: AudioOutputUnitStop")*/ + /*} + catch (...) + { + goto bail; + }*/ + + result = 0; +bail: + return result; +} + +void SetCompletionProc (CDPlayerCompletionProc proc, SDL_CD *cdrom) +{ + assert(thePlayer != NULL); + + theCDROM = cdrom; + completionProc = proc; + thePlayer->SetNotifier (thePlayer, FilePlayNotificationHandler, cdrom); +} + +int GetCurrentFrame () +{ + int frame; + + if (thePlayer == NULL) + frame = 0; + else + frame = thePlayer->GetCurrentFrame (thePlayer); + + return frame; +} + + +#pragma mark -- Private Functions -- + +static OSStatus CheckInit () +{ + if (playBackWasInit) + return 0; + + OSStatus result = noErr; + + /* Create the callback semaphore */ + callbackSem = SDL_CreateSemaphore(0); + + /* Start callback thread */ + SDL_CreateThread(RunCallBackThread, NULL); + + { /*try {*/ + ComponentDescription desc; + + desc.componentType = kAudioUnitType_Output; + desc.componentSubType = kAudioUnitSubType_DefaultOutput; + desc.componentManufacturer = kAudioUnitManufacturer_Apple; + desc.componentFlags = 0; + desc.componentFlagsMask = 0; + + Component comp = FindNextComponent (NULL, &desc); + if (comp == NULL) { + SDL_SetError ("CheckInit: FindNextComponent returned NULL"); + if (result) return -1; //throw(internalComponentErr); + } + + result = OpenAComponent (comp, &theUnit); + if (result) return -1; //THROW_RESULT("CheckInit: OpenAComponent") + + // you need to initialize the output unit before you set it as a destination + result = AudioUnitInitialize (theUnit); + if (result) return -1; //THROW_RESULT("CheckInit: AudioUnitInitialize") + + + playBackWasInit = true; + } + /*catch (...) + { + return -1; + }*/ + + return 0; +} + +static void FilePlayNotificationHandler(void * inRefCon, OSStatus inStatus) +{ + if (inStatus == kAudioFilePlay_FileIsFinished) { + + /* notify non-CA thread to perform the callback */ + SDL_SemPost(callbackSem); + + } else if (inStatus == kAudioFilePlayErr_FilePlayUnderrun) { + + SDL_SetError ("CDPlayer Notification: buffer underrun"); + } else if (inStatus == kAudioFilePlay_PlayerIsUninitialized) { + + SDL_SetError ("CDPlayer Notification: player is uninitialized"); + } else { + + SDL_SetError ("CDPlayer Notification: unknown error %ld", inStatus); + } +} + +static int RunCallBackThread (void *param) +{ + for (;;) { + + SDL_SemWait(callbackSem); + + if (completionProc && theCDROM) { + #if DEBUG_CDROM + printf ("callback!\n"); + #endif + (*completionProc)(theCDROM); + } else { + #if DEBUG_CDROM + printf ("callback?\n"); + #endif + } + } + + #if DEBUG_CDROM + printf ("thread dying now...\n"); + #endif + + return 0; +} + +#endif /* SDL_CDROM_MACOSX */ + +/*}; // extern "C" */ diff --git a/src/sdl_cdrom/macosx/CDPlayer.h b/src/sdl_cdrom/macosx/CDPlayer.h new file mode 100644 index 000000000..be1ac1826 --- /dev/null +++ b/src/sdl_cdrom/macosx/CDPlayer.h @@ -0,0 +1,69 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2012 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "SDL_config.h" + +#ifndef __CDPlayer__H__ +#define __CDPlayer__H__ 1 + +#include <string.h> + +#include <Carbon/Carbon.h> +#include <CoreFoundation/CoreFoundation.h> +#include <AudioUnit/AudioUnit.h> + +#include "SDL.h" +#include "SDL_thread.h" +#include "SDL_mutex.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void (*CDPlayerCompletionProc)(SDL_CD *cdrom) ; + +void Lock (); + +void Unlock(); + +int LoadFile (const FSRef *ref, int startFrame, int endFrame); /* pass -1 to do nothing */ + +int ReleaseFile (); + +int PlayFile (); + +int PauseFile (); + +void SetCompletionProc (CDPlayerCompletionProc proc, SDL_CD *cdrom); + +int ReadTOCData (FSVolumeRefNum theVolume, SDL_CD *theCD); + +int ListTrackFiles (FSVolumeRefNum theVolume, FSRef *trackFiles, int numTracks); + +int DetectAudioCDVolumes (FSVolumeRefNum *volumes, int numVolumes); + +int GetCurrentFrame (); + +#ifdef __cplusplus +}; +#endif + +#endif /* __CD_Player__H__ */ diff --git a/src/sdl_cdrom/macosx/Makefile.am b/src/sdl_cdrom/macosx/Makefile.am new file mode 100644 index 000000000..f92f83c52 --- /dev/null +++ b/src/sdl_cdrom/macosx/Makefile.am @@ -0,0 +1,5 @@ +AM_CPPFLAGS = -I$(top_srcdir)/include + +noinst_LIBRARIES = libcsdlcdrommacosx.a +EXTRA_DIST = AudioFilePlayer.h CDPlayer.h SDLOSXCAGuard.h SDL_syscdrom_c.h +libcsdlcdrommacosx_a_SOURCES = AudioFilePlayer.c AudioFileReaderThread.c CDPlayer.c SDLOSXCAGuard.c SDL_syscdrom.c diff --git a/src/sdl_cdrom/macosx/SDLOSXCAGuard.c b/src/sdl_cdrom/macosx/SDLOSXCAGuard.c new file mode 100644 index 000000000..df120b7a6 --- /dev/null +++ b/src/sdl_cdrom/macosx/SDLOSXCAGuard.c @@ -0,0 +1,205 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2012 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "config.h" +#define COMPAT_SDL_CDROM_PLATFORM_MACOSX 1 + +#if (C_COMPAT_SDL_CDROM_PLATFORM == COMPAT_SDL_CDROM_PLATFORM_MACOSX) + +#include "SDL_config.h" + +/* + Note: This file hasn't been modified so technically we have to keep the disclaimer :-( + + Copyright: \A9 Copyright 2002 Apple Computer, Inc. All rights reserved. + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. + ("Apple") in consideration of your agreement to the following terms, and your + use, installation, modification or redistribution of this Apple software + constitutes acceptance of these terms. If you do not agree with these terms, + please do not use, install, modify or redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and subject + to these terms, Apple grants you a personal, non-exclusive license, under Apple\D5s + copyrights in this original Apple software (the "Apple Software"), to use, + reproduce, modify and redistribute the Apple Software, with or without + modifications, in source and/or binary forms; provided that if you redistribute + the Apple Software in its entirety and without modifications, you must retain + this notice and the following text and disclaimers in all such redistributions of + the Apple Software. Neither the name, trademarks, service marks or logos of + Apple Computer, Inc. may be used to endorse or promote products derived from the + Apple Software without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or implied, + are granted by Apple herein, including but not limited to any patent rights that + may be infringed by your derivative works or by other works in which the Apple + Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO + WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED + WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN + COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION + OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT + (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN + ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/*============================================================================= + CAGuard.cp + +=============================================================================*/ + +/*============================================================================= + Includes + =============================================================================*/ + +/* +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +*/ +#include "SDL_stdinc.h" + +/*#define NDEBUG 1*/ +/* +#include <assert.h> +*/ +#define assert(X) + + +#include "SDLOSXCAGuard.h" + +/*#warning Need a try-based Locker too*/ +/*============================================================================= + SDLOSXCAGuard + =============================================================================*/ + +static int SDLOSXCAGuard_Lock(SDLOSXCAGuard *cag) +{ + int theAnswer = 0; + + if(pthread_self() != cag->mOwner) + { + OSStatus theError = pthread_mutex_lock(&cag->mMutex); + (void)theError; + assert(theError == 0); + cag->mOwner = pthread_self(); + theAnswer = 1; + } + + return theAnswer; +} + +static void SDLOSXCAGuard_Unlock(SDLOSXCAGuard *cag) +{ + OSStatus theError; + assert(pthread_self() == cag->mOwner); + + cag->mOwner = 0; + theError = pthread_mutex_unlock(&cag->mMutex); + (void)theError; + assert(theError == 0); +} + +static int SDLOSXCAGuard_Try (SDLOSXCAGuard *cag, int *outWasLocked) +{ + int theAnswer = 0; + *outWasLocked = 0; + + if (pthread_self() == cag->mOwner) { + theAnswer = 1; + *outWasLocked = 0; + } else { + OSStatus theError = pthread_mutex_trylock(&cag->mMutex); + if (theError == 0) { + cag->mOwner = pthread_self(); + theAnswer = 1; + *outWasLocked = 1; + } + } + + return theAnswer; +} + +static void SDLOSXCAGuard_Wait(SDLOSXCAGuard *cag) +{ + OSStatus theError; + assert(pthread_self() == cag->mOwner); + + cag->mOwner = 0; + + theError = pthread_cond_wait(&cag->mCondVar, &cag->mMutex); + (void)theError; + assert(theError == 0); + cag->mOwner = pthread_self(); +} + +static void SDLOSXCAGuard_Notify(SDLOSXCAGuard *cag) +{ + OSStatus theError = pthread_cond_signal(&cag->mCondVar); + (void)theError; + assert(theError == 0); +} + + +SDLOSXCAGuard *new_SDLOSXCAGuard(void) +{ + OSStatus theError; + SDLOSXCAGuard *cag = (SDLOSXCAGuard *) SDL_malloc(sizeof (SDLOSXCAGuard)); + if (cag == NULL) + return NULL; + SDL_memset(cag, '\0', sizeof (*cag)); + + #define SET_SDLOSXCAGUARD_METHOD(m) cag->m = SDLOSXCAGuard_##m + SET_SDLOSXCAGUARD_METHOD(Lock); + SET_SDLOSXCAGUARD_METHOD(Unlock); + SET_SDLOSXCAGUARD_METHOD(Try); + SET_SDLOSXCAGUARD_METHOD(Wait); + SET_SDLOSXCAGUARD_METHOD(Notify); + #undef SET_SDLOSXCAGUARD_METHOD + + theError = pthread_mutex_init(&cag->mMutex, NULL); + (void)theError; + assert(theError == 0); + + theError = pthread_cond_init(&cag->mCondVar, NULL); + (void)theError; + assert(theError == 0); + + cag->mOwner = 0; + return cag; +} + +void delete_SDLOSXCAGuard(SDLOSXCAGuard *cag) +{ + if (cag != NULL) + { + pthread_mutex_destroy(&cag->mMutex); + pthread_cond_destroy(&cag->mCondVar); + SDL_free(cag); + } +} + +#endif /* SDL_CDROM_MACOSX */ diff --git a/src/sdl_cdrom/macosx/SDLOSXCAGuard.h b/src/sdl_cdrom/macosx/SDLOSXCAGuard.h new file mode 100644 index 000000000..08604ed09 --- /dev/null +++ b/src/sdl_cdrom/macosx/SDLOSXCAGuard.h @@ -0,0 +1,116 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2012 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "SDL_config.h" + +/* + Note: This file hasn't been modified so technically we have to keep the disclaimer :-( + + + Copyright: \A9 Copyright 2002 Apple Computer, Inc. All rights reserved. + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. + ("Apple") in consideration of your agreement to the following terms, and your + use, installation, modification or redistribution of this Apple software + constitutes acceptance of these terms. If you do not agree with these terms, + please do not use, install, modify or redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and subject + to these terms, Apple grants you a personal, non-exclusive license, under Apple\D5s + copyrights in this original Apple software (the "Apple Software"), to use, + reproduce, modify and redistribute the Apple Software, with or without + modifications, in source and/or binary forms; provided that if you redistribute + the Apple Software in its entirety and without modifications, you must retain + this notice and the following text and disclaimers in all such redistributions of + the Apple Software. Neither the name, trademarks, service marks or logos of + Apple Computer, Inc. may be used to endorse or promote products derived from the + Apple Software without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or implied, + are granted by Apple herein, including but not limited to any patent rights that + may be infringed by your derivative works or by other works in which the Apple + Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO + WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED + WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN + COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION + OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT + (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN + ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/*============================================================================= + CAGuard.h + +=============================================================================*/ +#if !defined(__CAGuard_h__) +#define __CAGuard_h__ + +/*============================================================================= + Includes + =============================================================================*/ + +#include <CoreAudio/CoreAudioTypes.h> +#include <pthread.h> + + +/*============================================================================= + CAGuard + + This is your typical mutex with signalling implemented via pthreads. + Lock() will return true if and only if the guard is locked on that call. + A thread that already has the guard will receive 'false' if it locks it + again. Use of the stack-based CAGuard::Locker class is highly recommended + to properly manage the recursive nesting. The Wait calls with timeouts + will return true if and only if the timeout period expired. They will + return false if they receive notification any other way. + =============================================================================*/ + +typedef struct S_SDLOSXCAGuard +{ + +/* Construction/Destruction */ +/*public:*/ +/* Actions */ +/*public:*/ + int (*Lock)(struct S_SDLOSXCAGuard *cag); + void (*Unlock)(struct S_SDLOSXCAGuard *cag); + int (*Try)(struct S_SDLOSXCAGuard *cag, int *outWasLocked); /* returns true if lock is free, false if not */ + void (*Wait)(struct S_SDLOSXCAGuard *cag); + void (*Notify)(struct S_SDLOSXCAGuard *cag); + +/* Implementation */ +/*protected:*/ + pthread_mutex_t mMutex; + pthread_cond_t mCondVar; + pthread_t mOwner; +} SDLOSXCAGuard; + +SDLOSXCAGuard *new_SDLOSXCAGuard(void); +void delete_SDLOSXCAGuard(SDLOSXCAGuard *cag); + +#endif + diff --git a/src/sdl_cdrom/macosx/SDL_syscdrom.c b/src/sdl_cdrom/macosx/SDL_syscdrom.c new file mode 100644 index 000000000..43023304f --- /dev/null +++ b/src/sdl_cdrom/macosx/SDL_syscdrom.c @@ -0,0 +1,519 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2012 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "config.h" +#define COMPAT_SDL_CDROM_PLATFORM_MACOSX 1 + +#if (C_COMPAT_SDL_CDROM_PLATFORM == COMPAT_SDL_CDROM_PLATFORM_MACOSX) + +#include "SDL_config.h" + +/*#ifdef SDL_CDROM_MACOSX*/ + +#include "SDL_syscdrom_c.h" + +#pragma mark -- Globals -- + +static FSRef** tracks; +static FSVolumeRefNum* volumes; +static CDstatus status; +static int nextTrackFrame; +static int nextTrackFramesRemaining; +static int fakeCD; +static int currentTrack; +static int didReadTOC; +static int cacheTOCNumTracks; +static int currentDrive; /* Only allow 1 drive in use at a time */ + +#pragma mark -- Prototypes -- + +static const char *SDL_SYS_CDName (int drive); +static int SDL_SYS_CDOpen (int drive); +static int SDL_SYS_CDGetTOC (SDL_CD *cdrom); +static CDstatus SDL_SYS_CDStatus (SDL_CD *cdrom, int *position); +static int SDL_SYS_CDPlay (SDL_CD *cdrom, int start, int length); +static int SDL_SYS_CDPause (SDL_CD *cdrom); +static int SDL_SYS_CDResume (SDL_CD *cdrom); +static int SDL_SYS_CDStop (SDL_CD *cdrom); +static int SDL_SYS_CDEject (SDL_CD *cdrom); +static void SDL_SYS_CDClose (SDL_CD *cdrom); + +#pragma mark -- Helper Functions -- + +/* Read a list of tracks from the volume */ +static int LoadTracks (SDL_CD *cdrom) +{ + /* Check if tracks are already loaded */ + if ( tracks[cdrom->id] != NULL ) + return 0; + + /* Allocate memory for tracks */ + tracks[cdrom->id] = (FSRef*) SDL_calloc (1, sizeof(**tracks) * cdrom->numtracks); + if (tracks[cdrom->id] == NULL) { + SDL_OutOfMemory (); + return -1; + } + + /* Load tracks */ + if (ListTrackFiles (volumes[cdrom->id], tracks[cdrom->id], cdrom->numtracks) < 0) + return -1; + + return 0; +} + +/* Find a file for a given start frame and length */ +static FSRef* GetFileForOffset (SDL_CD *cdrom, int start, int length, int *outStartFrame, int *outStopFrame) +{ + int i; + + for (i = 0; i < cdrom->numtracks; i++) { + + if (cdrom->track[i].offset <= start && + start < (cdrom->track[i].offset + cdrom->track[i].length)) + break; + } + + if (i == cdrom->numtracks) + return NULL; + + currentTrack = i; + + *outStartFrame = start - cdrom->track[i].offset; + + if ((*outStartFrame + length) < cdrom->track[i].length) { + *outStopFrame = *outStartFrame + length; + length = 0; + nextTrackFrame = -1; + nextTrackFramesRemaining = -1; + } + else { + *outStopFrame = -1; + length -= cdrom->track[i].length - *outStartFrame; + nextTrackFrame = cdrom->track[i+1].offset; + nextTrackFramesRemaining = length; + } + + return &tracks[cdrom->id][i]; +} + +/* Setup another file for playback, or stop playback (called from another thread) */ +static void CompletionProc (SDL_CD *cdrom) +{ + + Lock (); + + if (nextTrackFrame > 0 && nextTrackFramesRemaining > 0) { + + /* Load the next file to play */ + int startFrame, stopFrame; + FSRef *file; + + PauseFile (); + ReleaseFile (); + + file = GetFileForOffset (cdrom, nextTrackFrame, + nextTrackFramesRemaining, &startFrame, &stopFrame); + + if (file == NULL) { + status = CD_STOPPED; + Unlock (); + return; + } + + LoadFile (file, startFrame, stopFrame); + + SetCompletionProc (CompletionProc, cdrom); + + PlayFile (); + } + else { + + /* Release the current file */ + PauseFile (); + ReleaseFile (); + status = CD_STOPPED; + } + + Unlock (); +} + + +#pragma mark -- Driver Functions -- + +/* Initialize */ +int SDL_SYS_CDInit (void) +{ + /* Initialize globals */ + volumes = NULL; + tracks = NULL; + status = CD_STOPPED; + nextTrackFrame = -1; + nextTrackFramesRemaining = -1; + fakeCD = SDL_FALSE; + currentTrack = -1; + didReadTOC = SDL_FALSE; + cacheTOCNumTracks = -1; + currentDrive = -1; + + /* Fill in function pointers */ + SDL_CDcaps.Name = SDL_SYS_CDName; + SDL_CDcaps.Open = SDL_SYS_CDOpen; + SDL_CDcaps.GetTOC = SDL_SYS_CDGetTOC; + SDL_CDcaps.Status = SDL_SYS_CDStatus; + SDL_CDcaps.Play = SDL_SYS_CDPlay; + SDL_CDcaps.Pause = SDL_SYS_CDPause; + SDL_CDcaps.Resume = SDL_SYS_CDResume; + SDL_CDcaps.Stop = SDL_SYS_CDStop; + SDL_CDcaps.Eject = SDL_SYS_CDEject; + SDL_CDcaps.Close = SDL_SYS_CDClose; + + /* + Read the list of "drives" + + This is currently a hack that infers drives from + mounted audio CD volumes, rather than + actual CD-ROM devices - which means it may not + act as expected sometimes. + */ + + /* Find out how many cd volumes are mounted */ + SDL_numcds = DetectAudioCDVolumes (NULL, 0); + + /* + If there are no volumes, fake a cd device + so tray empty can be reported. + */ + if (SDL_numcds == 0) { + + fakeCD = SDL_TRUE; + SDL_numcds = 1; + status = CD_TRAYEMPTY; + + return 0; + } + + /* Allocate space for volumes */ + volumes = (FSVolumeRefNum*) SDL_calloc (1, sizeof(*volumes) * SDL_numcds); + if (volumes == NULL) { + SDL_OutOfMemory (); + return -1; + } + + /* Allocate space for tracks */ + tracks = (FSRef**) SDL_calloc (1, sizeof(*tracks) * (SDL_numcds + 1)); + if (tracks == NULL) { + SDL_OutOfMemory (); + return -1; + } + + /* Mark the end of the tracks array */ + tracks[ SDL_numcds ] = (FSRef*)-1; + + /* + Redetect, now save all volumes for later + Update SDL_numcds just in case it changed + */ + { + int numVolumes = SDL_numcds; + + SDL_numcds = DetectAudioCDVolumes (volumes, numVolumes); + + /* If more cds suddenly show up, ignore them */ + if (SDL_numcds > numVolumes) { + SDL_SetError ("Some CD's were added but they will be ignored"); + SDL_numcds = numVolumes; + } + } + + return 0; +} + +/* Shutdown and cleanup */ +void SDL_SYS_CDQuit(void) +{ + ReleaseFile(); + + if (volumes != NULL) + free (volumes); + + if (tracks != NULL) { + + FSRef **ptr; + for (ptr = tracks; *ptr != (FSRef*)-1; ptr++) + if (*ptr != NULL) + free (*ptr); + + free (tracks); + } +} + +/* Get the Unix disk name of the volume */ +static const char *SDL_SYS_CDName (int drive) +{ + /* + * !!! FIXME: PBHGetVolParmsSync() is gone in 10.6, + * !!! FIXME: replaced with FSGetVolumeParms(), which + * !!! FIXME: isn't available before 10.5. :/ + */ + return "Mac OS X CD-ROM Device"; + +#if 0 + OSStatus err = noErr; + HParamBlockRec pb; + GetVolParmsInfoBuffer volParmsInfo; + + if (fakeCD) + return "Fake CD-ROM Device"; + + pb.ioParam.ioNamePtr = NULL; + pb.ioParam.ioVRefNum = volumes[drive]; + pb.ioParam.ioBuffer = (Ptr)&volParmsInfo; + pb.ioParam.ioReqCount = (SInt32)sizeof(volParmsInfo); + err = PBHGetVolParmsSync(&pb); + + if (err != noErr) { + SDL_SetError ("PBHGetVolParmsSync returned %d", err); + return NULL; + } + + return volParmsInfo.vMDeviceID; +#endif +} + +/* Open the "device" */ +static int SDL_SYS_CDOpen (int drive) +{ + /* Only allow 1 device to be open */ + if (currentDrive >= 0) { + SDL_SetError ("Only one cdrom is supported"); + return -1; + } + else + currentDrive = drive; + + return drive; +} + +/* Get the table of contents */ +static int SDL_SYS_CDGetTOC (SDL_CD *cdrom) +{ + if (fakeCD) { + SDL_SetError (kErrorFakeDevice); + return -1; + } + + if (didReadTOC) { + cdrom->numtracks = cacheTOCNumTracks; + return 0; + } + + + ReadTOCData (volumes[cdrom->id], cdrom); + didReadTOC = SDL_TRUE; + cacheTOCNumTracks = cdrom->numtracks; + + return 0; +} + +/* Get CD-ROM status */ +static CDstatus SDL_SYS_CDStatus (SDL_CD *cdrom, int *position) +{ + if (position) { + int trackFrame; + + Lock (); + trackFrame = GetCurrentFrame (); + Unlock (); + + *position = cdrom->track[currentTrack].offset + trackFrame; + } + + return status; +} + +/* Start playback */ +static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length) +{ + int startFrame, stopFrame; + FSRef *ref; + + if (fakeCD) { + SDL_SetError (kErrorFakeDevice); + return -1; + } + + Lock(); + + if (LoadTracks (cdrom) < 0) + return -2; + + if (PauseFile () < 0) + return -3; + + if (ReleaseFile () < 0) + return -4; + + ref = GetFileForOffset (cdrom, start, length, &startFrame, &stopFrame); + if (ref == NULL) { + SDL_SetError ("SDL_SYS_CDPlay: No file for start=%d, length=%d", start, length); + return -5; + } + + if (LoadFile (ref, startFrame, stopFrame) < 0) + return -6; + + SetCompletionProc (CompletionProc, cdrom); + + if (PlayFile () < 0) + return -7; + + status = CD_PLAYING; + + Unlock(); + + return 0; +} + +/* Pause playback */ +static int SDL_SYS_CDPause(SDL_CD *cdrom) +{ + if (fakeCD) { + SDL_SetError (kErrorFakeDevice); + return -1; + } + + Lock (); + + if (PauseFile () < 0) { + Unlock (); + return -2; + } + + status = CD_PAUSED; + + Unlock (); + + return 0; +} + +/* Resume playback */ +static int SDL_SYS_CDResume(SDL_CD *cdrom) +{ + if (fakeCD) { + SDL_SetError (kErrorFakeDevice); + return -1; + } + + Lock (); + + if (PlayFile () < 0) { + Unlock (); + return -2; + } + + status = CD_PLAYING; + + Unlock (); + + return 0; +} + +/* Stop playback */ +static int SDL_SYS_CDStop(SDL_CD *cdrom) +{ + if (fakeCD) { + SDL_SetError (kErrorFakeDevice); + return -1; + } + + Lock (); + + if (PauseFile () < 0) { + Unlock (); + return -2; + } + + if (ReleaseFile () < 0) { + Unlock (); + return -3; + } + + status = CD_STOPPED; + + Unlock (); + + return 0; +} + +/* Eject the CD-ROM (Unmount the volume) */ +static int SDL_SYS_CDEject(SDL_CD *cdrom) +{ + OSStatus err; + pid_t dissenter; + + if (fakeCD) { + SDL_SetError (kErrorFakeDevice); + return -1; + } + + Lock (); + + if (PauseFile () < 0) { + Unlock (); + return -2; + } + + if (ReleaseFile () < 0) { + Unlock (); + return -3; + } + + status = CD_STOPPED; + + /* Eject the volume */ + err = FSEjectVolumeSync(volumes[cdrom->id], kNilOptions, &dissenter); + + if (err != noErr) { + Unlock (); + SDL_SetError ("PBUnmountVol returned %d", err); + return -4; + } + + status = CD_TRAYEMPTY; + + /* Invalidate volume and track info */ + volumes[cdrom->id] = 0; + free (tracks[cdrom->id]); + tracks[cdrom->id] = NULL; + + Unlock (); + + return 0; +} + +/* Close the CD-ROM */ +static void SDL_SYS_CDClose(SDL_CD *cdrom) +{ + currentDrive = -1; + return; +} + +#endif /* SDL_CDROM_MACOSX */ diff --git a/src/sdl_cdrom/macosx/SDL_syscdrom_c.h b/src/sdl_cdrom/macosx/SDL_syscdrom_c.h new file mode 100644 index 000000000..ae3d9144f --- /dev/null +++ b/src/sdl_cdrom/macosx/SDL_syscdrom_c.h @@ -0,0 +1,136 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2012 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "SDL_config.h" + +/* This is the Mac OS X / CoreAudio specific header for the SDL CD-ROM API + Contributed by Darrell Walisser and Max Horn + */ + +/*********************************************************************************** + Implementation Notes + ********************* + + This code has several limitations currently (all of which are proabaly fixable): + + 1. A CD-ROM device is inferred from a mounted cdfs volume, so device 0 is + not necessarily the first CD-ROM device on the system. (Somewhat easy to fix + by useing the device name from the volume id's to reorder the volumes) + + 2. You can only open and control 1 CD-ROM device at a time. (Challenging to fix, + due to extensive code restructuring) + + 3. The status reported by SDL_CDStatus only changes to from CD_PLAYING to CD_STOPPED in + 1-second intervals (because the audio is buffered in 1-second chunks) If + the audio data is less than 1 second, the remainder is filled with silence. + + If you need to play sequences back-to-back that are less that 1 second long, + use the frame position to determine when to play the next sequence, instead + of SDL_CDStatus. + + This may be possible to fix with a clever usage of the AudioUnit API. + + 4. When new volumes are inserted, our volume information is not updated. The only way + to refresh this information is to reinit the CD-ROM subsystem of SDL. To fix this, + one would probably have to fix point 1 above first, then figure out how to register + for a notification when new media is mounted in order to perform an automatic + rescan for cdfs volumes. + + + + So, here comes a description of how this all works. + + < Initializing > + + To get things rolling, we have to locate mounted volumes that contain + audio (since nearly all Macs don't have analog audio-in on the sound card). + That's easy, since these volumes have a flag that indicates this special + filesystem. See DetectAudioCDVolumes() in CDPlayer.cpp for this code. + + Next, we parse the invisible .TOC.plist in the root of the volume, which gets us + the track information (number, offset, length, leadout, etc). See ReadTOCData() in + CDPlayer.cpp for the skinny on this. + + + < The Playback Loop > + + Now come the tricky parts. Let's start with basic audio playback. When a frame + range to play is requested, we must first find the .aiff files on the volume, + hopefully in the right order. Since these files all begin with a number "1 Audio Track", + etc, this is used to determine the correct track order. + + Once all files are determined, we have to find what file corresponds to the start + and length parameter to SDL_SYS_CDPlay(). Again, this is quite simple by walking the + cdrom's track list. At this point, we also save the offset to the next track and frames + remaining, if we're going to have to play another file after the first one. See + GetFileForOffset() for this code. + + At this point we have all info needed to start playback, so we hand off to the LoadFile() + function, which proceeds to do its magic and plays back the file. + + When the file is finished playing, CompletionProc() is invoked, at which time we can + play the next file if the previously saved next track and frames remaining + indicates that we should. + + + < Magic > + + OK, so it's not really magic, but since I don't fully understand all the hidden details it + seems like it to me ;-) The API's involved are the AudioUnit and AudioFile API's. These + appear to be an extension of CoreAudio for creating modular playback and f/x entities. + The important thing is that CPU usage is very low and reliability is very high. You'd + be hard-pressed to find a way to stutter the playback with other CPU-intensive tasks. + + One part of this magic is that it uses multiple threads, which carries the usual potential + for disaster if not handled carefully. Playback currently requires 4 additional threads: + 1. The coreaudio runloop thread + 2. The coreaudio device i/o thread + 3. The file streaming thread + 4. The notification/callback thread + + The first 2 threads are necessary evil - CoreAudio creates this no matter what the situation + is (even the SDL sound implementation creates theses suckers). The last two are are created + by us. + + The file is streamed from disk using a threaded double-buffer approach. + This way, the high latency operation of reading from disk can be performed without interrupting + the real-time device thread (which amounts to avoiding dropouts). The device thread grabs the + buffer that isn't being read and sends it to the CoreAudio mixer where it eventually gets + to the sound card. + + The device thread posts a notification when the file streaming thread is out of data. This + notification must be handled in a separate thread to avoid potential deadlock in the + device thread. That's where the notification thread comes in. This thread is signaled + whenever a notification needs to be processed, so another file can be played back if need be. + + The API in CDPlayer.cpp contains synchronization because otherwise both the notification thread + and main thread (or another other thread using the SDL CD api) can potentially call it at the same time. + +************************************************************************************/ + + +#include "../compat_SDL_cdrom.h" +#include "../SDL_syscdrom.h" + +#include "CDPlayer.h" + +#define kErrorFakeDevice "Error: Cannot proceed since we're faking a CD-ROM device. Reinit the CD-ROM subsystem to scan for new volumes." + |