aboutsummaryrefslogtreecommitdiff
path: root/backends/platform/ios7
diff options
context:
space:
mode:
Diffstat (limited to 'backends/platform/ios7')
-rw-r--r--backends/platform/ios7/README.md150
-rw-r--r--backends/platform/ios7/iOS7AppDelegate.h15
-rw-r--r--backends/platform/ios7/iOS7AppDelegate.mm95
-rw-r--r--backends/platform/ios7/iOS7ScummVMViewController.h28
-rw-r--r--backends/platform/ios7/iOS7ScummVMViewController.mm32
-rw-r--r--backends/platform/ios7/ios7_common.h128
-rw-r--r--backends/platform/ios7/ios7_keyboard.h44
-rw-r--r--backends/platform/ios7/ios7_keyboard.mm98
-rw-r--r--backends/platform/ios7/ios7_main.mm47
-rw-r--r--backends/platform/ios7/ios7_osys_events.cpp576
-rw-r--r--backends/platform/ios7/ios7_osys_main.cpp366
-rw-r--r--backends/platform/ios7/ios7_osys_main.h233
-rw-r--r--backends/platform/ios7/ios7_osys_sound.cpp105
-rw-r--r--backends/platform/ios7/ios7_osys_video.mm545
-rw-r--r--backends/platform/ios7/ios7_video.h129
-rw-r--r--backends/platform/ios7/ios7_video.mm996
-rw-r--r--backends/platform/ios7/module.mk17
17 files changed, 3604 insertions, 0 deletions
diff --git a/backends/platform/ios7/README.md b/backends/platform/ios7/README.md
new file mode 100644
index 0000000000..1f34c15cc6
--- /dev/null
+++ b/backends/platform/ios7/README.md
@@ -0,0 +1,150 @@
+# ScummVM for iOS 7.1+ #
+
+This is a quick fix of the latest ScummVM (1.8.0) for iOS 7.1. It has been tested on real iPhone 6S+, and iPad Pro, and also on all the available Xcode simulators.
+
+I tried to use all the latest iOS features to replace the old code. For instance, it uses gesture recognizers most of the time, it supports the new iPhones 6 / 6+ / 6s / 6s+ resolution, and you can copy your game files using iTunes.
+
+## Compilation ##
+
+First, clone the repository:
+```
+$ git clone -b ios-fix --recursive https://github.com/bSr43/scummvm.git
+```
+
+### Compilation from Xcode ###
+
+This is the recommended way to compile ScummVM, and the only one which makes it possible to run ScummVM on a non-jailbroken device!
+
+The next step is to compile the **create_project** tool. Open the Xcode project you'll found in the **devtools/create\_project/xcode/** directory. Once compiled, copy the binary somewhere in your *PATH*, and create a **build** directory somewhere on your harddisk. It is recommended to create this directory next to the cloned repository (they share the same parent).
+
+Execute the following commands in a terminal:
+```
+$ cd path_to_the_build_directory
+$ create_project path_to_scummvm_repository --xcode --enable-fluidsynth --disable-jpeg --disable-bink --disable-16bit --disable-mt32emu --disable-nasm --disable-opengl --disable-theora --disable-taskbar
+```
+
+This will create an Xcode project for ScummVM, for both the OS X, and the iOS target.
+
+Now, download the external libraries from http://bsr43.free.fr/scummvm/ScummVM-iOS-libraries.zip. Unzip the archive in your **build** directory. Please make sure that the **lib**, and **include** directories are at the root of the **build** directory, not in a subdirectory.
+
+Now, your **build** directory should contain:
+* a generated **engines** directory,
+* a generated **scummvm.xcodeproj** project,
+* an **include** directory,
+* a **lib** directory.
+
+You are ready to compile ScummVM: open the **scummvm.xcodeproj** project, and build it.
+
+### Compilation from command line ###
+
+For jailbroken devices, it is also possible to compile the project from command line. You'll need a working toolchain, and some tools, like **ldid**, to fake the code signature.
+
+Here is a script to download, and compile all the required tools. This script has been wrote for Debian 8.2, and should be run as root.
+
+```
+#!/bin/bash
+
+if [ $UID -ne 0 ]; then
+ echo "This script should be run by the root user"
+ exit 1
+fi
+
+# Install the Clang compiler
+apt-get install -y clang-3.4 libclang-3.4-dev llvm-3.4 libtool bison flex automake subversion git pkg-config wget libssl-dev uuid-dev libxml2-dev || exit 1
+
+# Add LLVM to the linker library path
+echo /usr/lib/llvm-3.4/lib > /etc/ld.so.conf.d/libllvm-3.4.conf
+ldconfig
+
+# Add symlinks for the LLVM headers
+ln -s /usr/lib/llvm-3.4/bin/llvm-config /usr/bin/llvm-config || exit 1
+ln -s /usr/include/llvm-3.4/llvm /usr/include/llvm || exit 1
+ln -s /usr/include/llvm-c-3.4/llvm-c /usr/include/llvm-c || exit 1
+ln -s /usr/bin/clang-3.4 /usr/bin/clang || exit 1
+ln -s /usr/bin/clang++-3.4 /usr/bin/clang++ || exit 1
+
+# Build the linker
+svn checkout http://ios-toolchain-based-on-clang-for-linux.googlecode.com/svn/trunk/cctools-porting || exit 1
+cd cctools-porting
+sed -i'' 's/proz -k=20 --no-curses/wget/g' cctools-ld64.sh
+./cctools-ld64.sh || exit 1
+
+cd cctools-855-ld64-236.3
+./autogen.sh || exit 1
+./configure --prefix=/usr/local --target=arm-apple-darwin11 || exit 1
+make || exit 1
+make install || exit 1
+cd ../..
+
+# Install ios-tools
+wget https://ios-toolchain-based-on-clang-for-linux.googlecode.com/files/iphonesdk-utils-2.0.tar.gz || exit 1
+tar xzf iphonesdk-utils-2.0.tar.gz
+cd iphonesdk-utils-2.0
+patch -p0 <<_EOF
+*** genLocalization2/getLocalizedStringFromFile.cpp 2015-04-02 04:45:39.309837816 +0530
+--- genLocalization2/getLocalizedStringFromFile.cpp 2015-04-02 04:45:11.525700021 +0530
+***************
+*** 113,115 ****
+ clang::HeaderSearch headerSearch(headerSearchOptions,
+- fileManager,
+ *pDiagnosticsEngine,
+--- 113,115 ----
+ clang::HeaderSearch headerSearch(headerSearchOptions,
++ sourceManager,
+ *pDiagnosticsEngine,
+***************
+*** 129,134 ****
+ false);
+- clang::HeaderSearch headerSearch(fileManager,
+ *pDiagnosticsEngine,
+ languageOptions,
+- pTargetInfo);
+ ApplyHeaderSearchOptions(headerSearch, headerSearchOptions, languageOptions, pTargetInfo->getTriple());
+--- 129,134 ----
+ false);
++ clang::HeaderSearch headerSearch(fileManager);/*,
+ *pDiagnosticsEngine,
+ languageOptions,
++ pTargetInfo);*/
+ ApplyHeaderSearchOptions(headerSearch, headerSearchOptions, languageOptio
+_EOF
+
+./autogen.sh || exit 1
+CC=clang CXX=clang++ ./configure --prefix=/usr/local || exit 1
+make || exit 1
+make install || exit 1
+
+# Install the iOS SDK 8.1
+mkdir -p /usr/share/ios-sdk
+cd /usr/share/ios-sdk
+wget http://iphone.howett.net/sdks/dl/iPhoneOS8.1.sdk.tbz2 || exit 1
+tar xjf iPhoneOS8.1.sdk.tbz2
+rm iPhoneOS8.1.sdk.tbz2
+```
+
+Now, in order to compile ScummVM, execute the following commands:
+```
+$ export SDKROOT=/usr/share/ios-sdk/iPhoneOS8.1.sdk
+$ export CC=ios-clang
+$ export CXX=ios-clang++
+$ ./configure --host=ios7 --disable-mt32emu --enable-release
+$ make ios7bundle
+```
+
+At the end of the compilation, you'll find a **ScummVM.app** application: copy it over SSH, and reboot your device.
+
+## Usage ##
+
+The game data files can be copied on the iOS device using iTunes. Once done, add your games in ScummVM as usual.
+
+Here is a list of the in-game gestures:
+
+|Gesture|Description|
+|-------|-----------|
+|Two fingers swipe down|Display the ScummVM menu for loading, saving, etc.|
+|Two fingers swipe right|Enable / disable the touchpad mode|
+|Two fingers swipe up|Enable / disable the mouse-click-and-drag mode|
+|Two fingers tap|Simulate a right click. You should tap with one finger, and then tap with another while keeping your first finger on the screen.|
+|Two fingers double-tap|Skip the cinematic / video|
+
+The iOS keyboard is visible when the device is in portrait mode, and hidden in landscape mode.
diff --git a/backends/platform/ios7/iOS7AppDelegate.h b/backends/platform/ios7/iOS7AppDelegate.h
new file mode 100644
index 0000000000..7f903a692d
--- /dev/null
+++ b/backends/platform/ios7/iOS7AppDelegate.h
@@ -0,0 +1,15 @@
+//
+// Created by Vincent Bénony on 07/12/2015.
+//
+
+#import <UIKit/UIKit.h>
+
+@class iPhoneView;
+
+
+@interface iOS7AppDelegate : NSObject<UIApplicationDelegate>
+
++ (iOS7AppDelegate *)iOS7AppDelegate;
++ (iPhoneView *)iPhoneView;
+
+@end
diff --git a/backends/platform/ios7/iOS7AppDelegate.mm b/backends/platform/ios7/iOS7AppDelegate.mm
new file mode 100644
index 0000000000..d664f91ccc
--- /dev/null
+++ b/backends/platform/ios7/iOS7AppDelegate.mm
@@ -0,0 +1,95 @@
+//
+// Created by Vincent Bénony on 07/12/2015.
+//
+
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+#import "iOS7AppDelegate.h"
+#import "iOS7ScummVMViewController.h"
+#import "ios7_video.h"
+
+@implementation iOS7AppDelegate {
+ UIWindow *_window;
+ iOS7ScummVMViewController *_controller;
+ iPhoneView *_view;
+}
+
+- (id)init {
+ if (self = [super init]) {
+ _window = nil;
+ _view = nil;
+ }
+ return self;
+}
+
+- (void)mainLoop:(id)param {
+ @autoreleasepool {
+ iOS7_main(iOS7_argc, iOS7_argv);
+ }
+
+ exit(0);
+}
+
+- (void)applicationDidFinishLaunching:(UIApplication *)application {
+ CGRect rect = [[UIScreen mainScreen] bounds];
+
+ // Create the directory for savegames
+#ifdef IPHONE_OFFICIAL
+ NSFileManager *fm = [NSFileManager defaultManager];
+ NSString *documentPath = [NSString stringWithUTF8String:iOS7_getDocumentsDir()];
+ NSString *savePath = [documentPath stringByAppendingPathComponent:@"Savegames"];
+ if (![fm fileExistsAtPath:savePath]) {
+ [fm createDirectoryAtPath:savePath withIntermediateDirectories:YES attributes:nil error:nil];
+ }
+#endif
+
+ _window = [[UIWindow alloc] initWithFrame:rect];
+ [_window retain];
+
+ _controller = [[iOS7ScummVMViewController alloc] init];
+
+ _view = [[iPhoneView alloc] initWithFrame:rect];
+ _view.multipleTouchEnabled = YES;
+ _controller.view = _view;
+
+ [_window setRootViewController:_controller];
+ [_window makeKeyAndVisible];
+
+ [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
+ [[NSNotificationCenter defaultCenter] addObserver:self
+ selector:@selector(didRotate:)
+ name:@"UIDeviceOrientationDidChangeNotification"
+ object:nil];
+
+ [NSThread detachNewThreadSelector:@selector(mainLoop:) toTarget:self withObject:nil];
+}
+
+- (void)applicationWillResignActive:(UIApplication *)application {
+ [_view applicationSuspend];
+}
+
+- (void)applicationDidBecomeActive:(UIApplication *)application {
+ [_view applicationResume];
+}
+
+- (void)didRotate:(NSNotification *)notification {
+ UIDeviceOrientation screenOrientation = [[UIDevice currentDevice] orientation];
+ [_view deviceOrientationChanged:screenOrientation];
+}
+
++ (iOS7AppDelegate *)iOS7AppDelegate {
+ UIApplication *app = [UIApplication sharedApplication];
+ return (iOS7AppDelegate *) app.delegate;
+}
+
++ (iPhoneView *)iPhoneView {
+ iOS7AppDelegate *appDelegate = [self iOS7AppDelegate];
+ return appDelegate->_view;
+}
+
+@end
+
+const char *iOS7_getDocumentsDir() {
+ NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
+ NSString *documentsDirectory = [paths objectAtIndex:0];
+ return [documentsDirectory UTF8String];
+}
diff --git a/backends/platform/ios7/iOS7ScummVMViewController.h b/backends/platform/ios7/iOS7ScummVMViewController.h
new file mode 100644
index 0000000000..0322bc6f7f
--- /dev/null
+++ b/backends/platform/ios7/iOS7ScummVMViewController.h
@@ -0,0 +1,28 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#import <UIKit/UIKit.h>
+
+
+@interface iOS7ScummVMViewController : UIViewController
+
+@end
diff --git a/backends/platform/ios7/iOS7ScummVMViewController.mm b/backends/platform/ios7/iOS7ScummVMViewController.mm
new file mode 100644
index 0000000000..f92fd0115c
--- /dev/null
+++ b/backends/platform/ios7/iOS7ScummVMViewController.mm
@@ -0,0 +1,32 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#import "iOS7ScummVMViewController.h"
+
+
+@implementation iOS7ScummVMViewController
+
+- (BOOL)prefersStatusBarHidden {
+ return YES;
+}
+
+@end
diff --git a/backends/platform/ios7/ios7_common.h b/backends/platform/ios7/ios7_common.h
new file mode 100644
index 0000000000..b7a47eda8a
--- /dev/null
+++ b/backends/platform/ios7/ios7_common.h
@@ -0,0 +1,128 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef BACKENDS_PLATFORM_IPHONE_IPHONE_COMMON_H
+#define BACKENDS_PLATFORM_IPHONE_IPHONE_COMMON_H
+
+#include "graphics/surface.h"
+
+enum InputEvent {
+ kInputMouseDown,
+ kInputMouseUp,
+ kInputMouseDragged,
+ kInputMouseSecondDragged,
+ kInputMouseSecondDown,
+ kInputMouseSecondUp,
+ kInputOrientationChanged,
+ kInputKeyPressed,
+ kInputApplicationSuspended,
+ kInputApplicationResumed,
+ kInputSwipe,
+ kInputTap
+};
+
+enum ScreenOrientation {
+ kScreenOrientationPortrait,
+ kScreenOrientationLandscape,
+ kScreenOrientationFlippedLandscape
+};
+
+enum UIViewSwipeDirection {
+ kUIViewSwipeUp = 1,
+ kUIViewSwipeDown = 2,
+ kUIViewSwipeLeft = 4,
+ kUIViewSwipeRight = 8
+};
+
+enum UIViewTapDescription {
+ kUIViewTapSingle = 1,
+ kUIViewTapDouble = 2
+};
+
+enum GraphicsModes {
+ kGraphicsModeLinear = 0,
+ kGraphicsModeNone = 1,
+
+ kGraphicsMode2xSaI,
+ kGraphicsModeSuper2xSaI,
+ kGraphicsModeSuperEagle,
+ kGraphicsModeAdvMame2x,
+ kGraphicsModeAdvMame3x,
+ kGraphicsModeHQ2x,
+ kGraphicsModeHQ3x,
+ kGraphicsModeTV2x,
+ kGraphicsModeDotMatrix
+};
+
+struct VideoContext {
+ VideoContext() : asprectRatioCorrection(), screenWidth(), screenHeight(), overlayVisible(false),
+ overlayWidth(), overlayHeight(), mouseX(), mouseY(),
+ mouseHotspotX(), mouseHotspotY(), mouseWidth(), mouseHeight(),
+ mouseIsVisible(), graphicsMode(kGraphicsModeNone), shakeOffsetY() {
+ }
+
+ // Game screen state
+ bool asprectRatioCorrection;
+ uint screenWidth, screenHeight;
+ Graphics::Surface screenTexture;
+
+ // Overlay state
+ bool overlayVisible;
+ uint overlayWidth, overlayHeight;
+ Graphics::Surface overlayTexture;
+
+ // Mouse cursor state
+ uint mouseX, mouseY;
+ int mouseHotspotX, mouseHotspotY;
+ uint mouseWidth, mouseHeight;
+ bool mouseIsVisible;
+ Graphics::Surface mouseTexture;
+
+ // Misc state
+ GraphicsModes graphicsMode;
+ int shakeOffsetY;
+};
+
+struct InternalEvent {
+ InternalEvent() : type(), value1(), value2() {}
+ InternalEvent(InputEvent t, int v1, int v2) : type(t), value1(v1), value2(v2) {}
+
+ InputEvent type;
+ int value1, value2;
+};
+
+// On the ObjC side
+
+extern int iOS7_argc;
+extern char **iOS7_argv;
+
+void iOS7_updateScreen();
+bool iOS7_fetchEvent(InternalEvent *event);
+bool iOS7_isBigDevice();
+
+void iOS7_main(int argc, char **argv);
+const char *iOS7_getDocumentsDir();
+bool iOS7_touchpadModeEnabled();
+
+uint getSizeNextPOT(uint size);
+
+#endif
diff --git a/backends/platform/ios7/ios7_keyboard.h b/backends/platform/ios7/ios7_keyboard.h
new file mode 100644
index 0000000000..ddd8f436e5
--- /dev/null
+++ b/backends/platform/ios7/ios7_keyboard.h
@@ -0,0 +1,44 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef BACKENDS_PLATFORM_IPHONE_IPHONE_KEYBOARD_H
+#define BACKENDS_PLATFORM_IPHONE_IPHONE_KEYBOARD_H
+
+#include <UIKit/UIKit.h>
+#include <UIKit/UITextView.h>
+
+@interface SoftKeyboard : UIView<UITextViewDelegate> {
+ id inputDelegate;
+ UITextView *inputView;
+}
+
+- (id)initWithFrame:(CGRect)frame;
+- (UITextView *)inputView;
+- (void)setInputDelegate:(id)delegate;
+- (void)handleKeyPress:(unichar)c;
+
+- (void)showKeyboard;
+- (void)hideKeyboard;
+
+@end
+
+#endif
diff --git a/backends/platform/ios7/ios7_keyboard.mm b/backends/platform/ios7/ios7_keyboard.mm
new file mode 100644
index 0000000000..7923a1cda9
--- /dev/null
+++ b/backends/platform/ios7/ios7_keyboard.mm
@@ -0,0 +1,98 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "ios7_keyboard.h"
+
+@interface UITextInputTraits
+- (void)setAutocorrectionType:(int)type;
+- (void)setAutocapitalizationType:(int)type;
+- (void)setEnablesReturnKeyAutomatically:(BOOL)val;
+@end
+
+@interface TextInputHandler : UITextView {
+ SoftKeyboard *softKeyboard;
+}
+
+- (id)initWithKeyboard:(SoftKeyboard *)keyboard;
+
+@end
+
+
+@implementation TextInputHandler
+
+- (id)initWithKeyboard:(SoftKeyboard *)keyboard {
+ self = [super initWithFrame:CGRectMake(0.0f, 0.0f, 0.0f, 0.0f)];
+ softKeyboard = keyboard;
+
+ [self setAutocorrectionType:UITextAutocorrectionTypeNo];
+ [self setAutocapitalizationType:UITextAutocapitalizationTypeNone];
+ [self setEnablesReturnKeyAutomatically:NO];
+
+ return self;
+}
+
+@end
+
+
+@implementation SoftKeyboard
+
+- (id)initWithFrame:(CGRect)frame {
+ self = [super initWithFrame:frame];
+ inputDelegate = nil;
+ inputView = [[TextInputHandler alloc] initWithKeyboard:self];
+ inputView.delegate = self;
+ return self;
+}
+
+- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text {
+ unichar c;
+ if (text.length) {
+ c = [text characterAtIndex:0];
+ }
+ else {
+ c = '\b';
+ }
+ [inputDelegate handleKeyPress:c];
+ return YES;
+}
+
+- (UITextView *)inputView {
+ return inputView;
+}
+
+- (void)setInputDelegate:(id)delegate {
+ inputDelegate = delegate;
+}
+
+- (void)handleKeyPress:(unichar)c {
+ [inputDelegate handleKeyPress:c];
+}
+
+- (void)showKeyboard {
+ [inputView becomeFirstResponder];
+}
+
+- (void)hideKeyboard {
+ [inputView endEditing:YES];
+}
+
+@end
diff --git a/backends/platform/ios7/ios7_main.mm b/backends/platform/ios7/ios7_main.mm
new file mode 100644
index 0000000000..96f41f4c02
--- /dev/null
+++ b/backends/platform/ios7/ios7_main.mm
@@ -0,0 +1,47 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+// Disable symbol overrides so that we can use system headers.
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
+#include <UIKit/UIKit.h>
+#include <Foundation/NSThread.h>
+
+#include "ios7_video.h"
+
+
+int iOS7_argc;
+char **iOS7_argv;
+
+int main(int argc, char **argv) {
+ int returnCode;
+
+ @autoreleasepool {
+ iOS7_argc = argc;
+ iOS7_argv = argv;
+
+ returnCode = UIApplicationMain(argc, argv, @"UIApplication", @"iOS7AppDelegate");
+ }
+
+ return returnCode;
+}
+
diff --git a/backends/platform/ios7/ios7_osys_events.cpp b/backends/platform/ios7/ios7_osys_events.cpp
new file mode 100644
index 0000000000..7e805f34b7
--- /dev/null
+++ b/backends/platform/ios7/ios7_osys_events.cpp
@@ -0,0 +1,576 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+// Disable symbol overrides so that we can use system headers.
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
+#include "gui/message.h"
+#include "common/translation.h"
+
+#include "ios7_osys_main.h"
+
+static const int kQueuedInputEventDelay = 50;
+
+bool OSystem_iOS7::pollEvent(Common::Event &event) {
+ //printf("pollEvent()\n");
+
+ long curTime = getMillis();
+
+ if (_timerCallback && (curTime >= _timerCallbackNext)) {
+ _timerCallback(_timerCallbackTimer);
+ _timerCallbackNext = curTime + _timerCallbackTimer;
+ }
+
+ if (_queuedInputEvent.type != Common::EVENT_INVALID && curTime >= _queuedEventTime) {
+ event = _queuedInputEvent;
+ _queuedInputEvent.type = Common::EVENT_INVALID;
+ return true;
+ }
+
+ InternalEvent internalEvent;
+
+ if (iOS7_fetchEvent(&internalEvent)) {
+ switch (internalEvent.type) {
+ case kInputMouseDown:
+ if (!handleEvent_mouseDown(event, internalEvent.value1, internalEvent.value2))
+ return false;
+ break;
+
+ case kInputMouseUp:
+ if (!handleEvent_mouseUp(event, internalEvent.value1, internalEvent.value2))
+ return false;
+ break;
+
+ case kInputMouseDragged:
+ if (!handleEvent_mouseDragged(event, internalEvent.value1, internalEvent.value2))
+ return false;
+ break;
+
+ case kInputOrientationChanged:
+ handleEvent_orientationChanged(internalEvent.value1);
+ return false;
+
+ case kInputApplicationSuspended:
+ handleEvent_applicationSuspended();
+ return false;
+
+ case kInputApplicationResumed:
+ handleEvent_applicationResumed();
+ return false;
+
+ case kInputMouseSecondDragged:
+ if (!handleEvent_mouseSecondDragged(event, internalEvent.value1, internalEvent.value2))
+ return false;
+ break;
+ case kInputMouseSecondDown:
+ _secondaryTapped = true;
+ if (!handleEvent_secondMouseDown(event, internalEvent.value1, internalEvent.value2))
+ return false;
+ break;
+ case kInputMouseSecondUp:
+ _secondaryTapped = false;
+ if (!handleEvent_secondMouseUp(event, internalEvent.value1, internalEvent.value2))
+ return false;
+ break;
+
+ case kInputKeyPressed:
+ handleEvent_keyPressed(event, internalEvent.value1);
+ break;
+
+ case kInputSwipe:
+ if (!handleEvent_swipe(event, internalEvent.value1, internalEvent.value2))
+ return false;
+ break;
+
+ case kInputTap:
+ if (!handleEvent_tap(event, (UIViewTapDescription) internalEvent.value1, internalEvent.value2))
+ return false;
+ break;
+
+ default:
+ break;
+ }
+
+ return true;
+ }
+ return false;
+}
+
+bool OSystem_iOS7::handleEvent_mouseDown(Common::Event &event, int x, int y) {
+ //printf("Mouse down at (%u, %u)\n", x, y);
+
+ // Workaround: kInputMouseSecondToggled isn't always sent when the
+ // secondary finger is lifted. Need to make sure we get out of that mode.
+ _secondaryTapped = false;
+
+ if (_touchpadModeEnabled) {
+ _lastPadX = x;
+ _lastPadY = y;
+ } else
+ warpMouse(x, y);
+
+ if (_mouseClickAndDragEnabled) {
+ event.type = Common::EVENT_LBUTTONDOWN;
+ event.mouse.x = _videoContext->mouseX;
+ event.mouse.y = _videoContext->mouseY;
+ return true;
+ } else {
+ _lastMouseDown = getMillis();
+ }
+ return false;
+}
+
+bool OSystem_iOS7::handleEvent_mouseUp(Common::Event &event, int x, int y) {
+ //printf("Mouse up at (%u, %u)\n", x, y);
+
+ if (_secondaryTapped) {
+ _secondaryTapped = false;
+ if (!handleEvent_secondMouseUp(event, x, y))
+ return false;
+ } else if (_mouseClickAndDragEnabled) {
+ event.type = Common::EVENT_LBUTTONUP;
+ event.mouse.x = _videoContext->mouseX;
+ event.mouse.y = _videoContext->mouseY;
+ } else {
+ if (getMillis() - _lastMouseDown < 250) {
+ event.type = Common::EVENT_LBUTTONDOWN;
+ event.mouse.x = _videoContext->mouseX;
+ event.mouse.y = _videoContext->mouseY;
+
+ _queuedInputEvent.type = Common::EVENT_LBUTTONUP;
+ _queuedInputEvent.mouse.x = _videoContext->mouseX;
+ _queuedInputEvent.mouse.y = _videoContext->mouseY;
+ _lastMouseTap = getMillis();
+ _queuedEventTime = _lastMouseTap + kQueuedInputEventDelay;
+ } else
+ return false;
+ }
+
+ return true;
+}
+
+bool OSystem_iOS7::handleEvent_secondMouseDown(Common::Event &event, int x, int y) {
+ _lastSecondaryDown = getMillis();
+ _gestureStartX = x;
+ _gestureStartY = y;
+
+ if (_mouseClickAndDragEnabled) {
+ event.type = Common::EVENT_LBUTTONUP;
+ event.mouse.x = _videoContext->mouseX;
+ event.mouse.y = _videoContext->mouseY;
+
+ _queuedInputEvent.type = Common::EVENT_RBUTTONDOWN;
+ _queuedInputEvent.mouse.x = _videoContext->mouseX;
+ _queuedInputEvent.mouse.y = _videoContext->mouseY;
+ } else
+ return false;
+
+ return true;
+}
+
+bool OSystem_iOS7::handleEvent_secondMouseUp(Common::Event &event, int x, int y) {
+ int curTime = getMillis();
+
+ if (curTime - _lastSecondaryDown < 400) {
+ //printf("Right tap!\n");
+ if (curTime - _lastSecondaryTap < 400 && !_videoContext->overlayVisible) {
+ //printf("Right escape!\n");
+ event.type = Common::EVENT_KEYDOWN;
+ _queuedInputEvent.type = Common::EVENT_KEYUP;
+
+ event.kbd.flags = _queuedInputEvent.kbd.flags = 0;
+ event.kbd.keycode = _queuedInputEvent.kbd.keycode = Common::KEYCODE_ESCAPE;
+ event.kbd.ascii = _queuedInputEvent.kbd.ascii = Common::ASCII_ESCAPE;
+ _queuedEventTime = curTime + kQueuedInputEventDelay;
+ _lastSecondaryTap = 0;
+ } else if (!_mouseClickAndDragEnabled) {
+ //printf("Rightclick!\n");
+ event.type = Common::EVENT_RBUTTONDOWN;
+ event.mouse.x = _videoContext->mouseX;
+ event.mouse.y = _videoContext->mouseY;
+ _queuedInputEvent.type = Common::EVENT_RBUTTONUP;
+ _queuedInputEvent.mouse.x = _videoContext->mouseX;
+ _queuedInputEvent.mouse.y = _videoContext->mouseY;
+ _lastSecondaryTap = curTime;
+ _queuedEventTime = curTime + kQueuedInputEventDelay;
+ } else {
+ //printf("Right nothing!\n");
+ return false;
+ }
+ }
+ if (_mouseClickAndDragEnabled) {
+ event.type = Common::EVENT_RBUTTONUP;
+ event.mouse.x = _videoContext->mouseX;
+ event.mouse.y = _videoContext->mouseY;
+ }
+
+ return true;
+}
+
+bool OSystem_iOS7::handleEvent_mouseDragged(Common::Event &event, int x, int y) {
+ if (_lastDragPosX == x && _lastDragPosY == y)
+ return false;
+
+ _lastDragPosX = x;
+ _lastDragPosY = y;
+
+ //printf("Mouse dragged at (%u, %u)\n", x, y);
+ int mouseNewPosX;
+ int mouseNewPosY;
+ if (_touchpadModeEnabled) {
+ int deltaX = _lastPadX - x;
+ int deltaY = _lastPadY - y;
+ _lastPadX = x;
+ _lastPadY = y;
+
+ mouseNewPosX = (int)(_videoContext->mouseX - deltaX / 0.5f);
+ mouseNewPosY = (int)(_videoContext->mouseY - deltaY / 0.5f);
+
+ int widthCap = _videoContext->overlayVisible ? _videoContext->overlayWidth : _videoContext->screenWidth;
+ int heightCap = _videoContext->overlayVisible ? _videoContext->overlayHeight : _videoContext->screenHeight;
+
+ if (mouseNewPosX < 0)
+ mouseNewPosX = 0;
+ else if (mouseNewPosX > widthCap)
+ mouseNewPosX = widthCap;
+
+ if (mouseNewPosY < 0)
+ mouseNewPosY = 0;
+ else if (mouseNewPosY > heightCap)
+ mouseNewPosY = heightCap;
+
+ } else {
+ mouseNewPosX = x;
+ mouseNewPosY = y;
+ }
+
+ event.type = Common::EVENT_MOUSEMOVE;
+ event.mouse.x = mouseNewPosX;
+ event.mouse.y = mouseNewPosY;
+ warpMouse(mouseNewPosX, mouseNewPosY);
+
+ return true;
+}
+
+bool OSystem_iOS7::handleEvent_mouseSecondDragged(Common::Event &event, int x, int y) {
+ if (_gestureStartX == -1 || _gestureStartY == -1) {
+ return false;
+ }
+
+ static const int kNeededLength = 100;
+ static const int kMaxDeviation = 20;
+
+ int vecX = (x - _gestureStartX);
+ int vecY = (y - _gestureStartY);
+
+ int absX = abs(vecX);
+ int absY = abs(vecY);
+
+ //printf("(%d, %d)\n", vecX, vecY);
+
+ if (absX >= kNeededLength || absY >= kNeededLength) { // Long enough gesture to react upon.
+ _gestureStartX = -1;
+ _gestureStartY = -1;
+
+ if (absX < kMaxDeviation && vecY >= kNeededLength) {
+ // Swipe down
+ event.type = Common::EVENT_MAINMENU;
+ _queuedInputEvent.type = Common::EVENT_INVALID;
+
+ _queuedEventTime = getMillis() + kQueuedInputEventDelay;
+ return true;
+ }
+
+ if (absX < kMaxDeviation && -vecY >= kNeededLength) {
+ // Swipe up
+ _mouseClickAndDragEnabled = !_mouseClickAndDragEnabled;
+ const char *dialogMsg;
+ if (_mouseClickAndDragEnabled) {
+ _touchpadModeEnabled = false;
+ dialogMsg = _("Mouse-click-and-drag mode enabled.");
+ } else
+ dialogMsg = _("Mouse-click-and-drag mode disabled.");
+ GUI::TimedMessageDialog dialog(dialogMsg, 1500);
+ dialog.runModal();
+ return false;
+ }
+
+ if (absY < kMaxDeviation && vecX >= kNeededLength) {
+ // Swipe right
+ _touchpadModeEnabled = !_touchpadModeEnabled;
+ const char *dialogMsg;
+ if (_touchpadModeEnabled)
+ dialogMsg = _("Touchpad mode enabled.");
+ else
+ dialogMsg = _("Touchpad mode disabled.");
+ GUI::TimedMessageDialog dialog(dialogMsg, 1500);
+ dialog.runModal();
+ return false;
+
+ }
+
+ if (absY < kMaxDeviation && -vecX >= kNeededLength) {
+ // Swipe left
+ return false;
+ }
+ }
+
+ return false;
+}
+
+void OSystem_iOS7::handleEvent_orientationChanged(int orientation) {
+ //printf("Orientation: %i\n", orientation);
+
+ ScreenOrientation newOrientation;
+ switch (orientation) {
+ case 1:
+ newOrientation = kScreenOrientationPortrait;
+ break;
+ case 3:
+ newOrientation = kScreenOrientationLandscape;
+ break;
+ case 4:
+ newOrientation = kScreenOrientationFlippedLandscape;
+ break;
+ default:
+ return;
+ }
+
+ if (_screenOrientation != newOrientation) {
+ _screenOrientation = newOrientation;
+ rebuildSurface();
+ }
+}
+
+void OSystem_iOS7::rebuildSurface() {
+ updateOutputSurface();
+
+ dirtyFullScreen();
+ if (_videoContext->overlayVisible) {
+ dirtyFullOverlayScreen();
+ }
+ updateScreen();
+}
+
+void OSystem_iOS7::handleEvent_applicationSuspended() {
+ suspendLoop();
+}
+
+void OSystem_iOS7::handleEvent_applicationResumed() {
+ rebuildSurface();
+}
+
+void OSystem_iOS7::handleEvent_keyPressed(Common::Event &event, int keyPressed) {
+ int ascii = keyPressed;
+ //printf("key: %i\n", keyPressed);
+
+ // We remap some of the iPhone keyboard keys.
+ // The first ten here are the row of symbols below the numeric keys.
+ switch (keyPressed) {
+ case 45:
+ keyPressed = Common::KEYCODE_F1;
+ ascii = Common::ASCII_F1;
+ break;
+ case 47:
+ keyPressed = Common::KEYCODE_F2;
+ ascii = Common::ASCII_F2;
+ break;
+ case 58:
+ keyPressed = Common::KEYCODE_F3;
+ ascii = Common::ASCII_F3;
+ break;
+ case 59:
+ keyPressed = Common::KEYCODE_F4;
+ ascii = Common::ASCII_F4;
+ break;
+ case 40:
+ keyPressed = Common::KEYCODE_F5;
+ ascii = Common::ASCII_F5;
+ break;
+ case 41:
+ keyPressed = Common::KEYCODE_F6;
+ ascii = Common::ASCII_F6;
+ break;
+ case 36:
+ keyPressed = Common::KEYCODE_F7;
+ ascii = Common::ASCII_F7;
+ break;
+ case 38:
+ keyPressed = Common::KEYCODE_F8;
+ ascii = Common::ASCII_F8;
+ break;
+ case 64:
+ keyPressed = Common::KEYCODE_F9;
+ ascii = Common::ASCII_F9;
+ break;
+ case 34:
+ keyPressed = Common::KEYCODE_F10;
+ ascii = Common::ASCII_F10;
+ break;
+ case 10:
+ keyPressed = Common::KEYCODE_RETURN;
+ ascii = Common::ASCII_RETURN;
+ break;
+ }
+ event.type = Common::EVENT_KEYDOWN;
+ _queuedInputEvent.type = Common::EVENT_KEYUP;
+
+ event.kbd.flags = _queuedInputEvent.kbd.flags = 0;
+ event.kbd.keycode = _queuedInputEvent.kbd.keycode = (Common::KeyCode)keyPressed;
+ event.kbd.ascii = _queuedInputEvent.kbd.ascii = ascii;
+ _queuedEventTime = getMillis() + kQueuedInputEventDelay;
+}
+
+bool OSystem_iOS7::handleEvent_swipe(Common::Event &event, int direction, int touches) {
+ if (touches == 1) {
+ Common::KeyCode keycode = Common::KEYCODE_INVALID;
+ switch (_screenOrientation) {
+ case kScreenOrientationPortrait:
+ switch ((UIViewSwipeDirection)direction) {
+ case kUIViewSwipeUp:
+ keycode = Common::KEYCODE_UP;
+ break;
+ case kUIViewSwipeDown:
+ keycode = Common::KEYCODE_DOWN;
+ break;
+ case kUIViewSwipeLeft:
+ keycode = Common::KEYCODE_LEFT;
+ break;
+ case kUIViewSwipeRight:
+ keycode = Common::KEYCODE_RIGHT;
+ break;
+ default:
+ return false;
+ }
+ break;
+ case kScreenOrientationLandscape:
+ switch ((UIViewSwipeDirection)direction) {
+ case kUIViewSwipeUp:
+ keycode = Common::KEYCODE_LEFT;
+ break;
+ case kUIViewSwipeDown:
+ keycode = Common::KEYCODE_RIGHT;
+ break;
+ case kUIViewSwipeLeft:
+ keycode = Common::KEYCODE_DOWN;
+ break;
+ case kUIViewSwipeRight:
+ keycode = Common::KEYCODE_UP;
+ break;
+ default:
+ return false;
+ }
+ break;
+ case kScreenOrientationFlippedLandscape:
+ switch ((UIViewSwipeDirection)direction) {
+ case kUIViewSwipeUp:
+ keycode = Common::KEYCODE_RIGHT;
+ break;
+ case kUIViewSwipeDown:
+ keycode = Common::KEYCODE_LEFT;
+ break;
+ case kUIViewSwipeLeft:
+ keycode = Common::KEYCODE_UP;
+ break;
+ case kUIViewSwipeRight:
+ keycode = Common::KEYCODE_DOWN;
+ break;
+ default:
+ return false;
+ }
+ break;
+ }
+
+ event.kbd.keycode = _queuedInputEvent.kbd.keycode = keycode;
+ event.kbd.ascii = _queuedInputEvent.kbd.ascii = 0;
+ event.type = Common::EVENT_KEYDOWN;
+ _queuedInputEvent.type = Common::EVENT_KEYUP;
+ event.kbd.flags = _queuedInputEvent.kbd.flags = 0;
+ _queuedEventTime = getMillis() + kQueuedInputEventDelay;
+
+ return true;
+ }
+ else if (touches == 2) {
+ switch ((UIViewSwipeDirection)direction) {
+ case kUIViewSwipeUp: {
+ _mouseClickAndDragEnabled = !_mouseClickAndDragEnabled;
+ const char *dialogMsg;
+ if (_mouseClickAndDragEnabled) {
+ _touchpadModeEnabled = false;
+ dialogMsg = _("Mouse-click-and-drag mode enabled.");
+ } else
+ dialogMsg = _("Mouse-click-and-drag mode disabled.");
+ GUI::TimedMessageDialog dialog(dialogMsg, 1500);
+ dialog.runModal();
+ return false;
+ }
+
+ case kUIViewSwipeDown: {
+ // Swipe down
+ event.type = Common::EVENT_MAINMENU;
+ _queuedInputEvent.type = Common::EVENT_INVALID;
+ _queuedEventTime = getMillis() + kQueuedInputEventDelay;
+ return true;
+ }
+
+ case kUIViewSwipeRight: {
+ // Swipe right
+ _touchpadModeEnabled = !_touchpadModeEnabled;
+ const char *dialogMsg;
+ if (_touchpadModeEnabled)
+ dialogMsg = _("Touchpad mode enabled.");
+ else
+ dialogMsg = _("Touchpad mode disabled.");
+ GUI::TimedMessageDialog dialog(dialogMsg, 1500);
+ dialog.runModal();
+ return false;
+ }
+
+ default:
+ break;
+ }
+ }
+ return false;
+}
+
+bool OSystem_iOS7::handleEvent_tap(Common::Event &event, UIViewTapDescription type, int touches) {
+ if (touches == 1) {
+ if (type == kUIViewTapDouble) {
+ event.type = Common::EVENT_RBUTTONDOWN;
+ _queuedInputEvent.type = Common::EVENT_RBUTTONUP;
+ _queuedEventTime = getMillis() + kQueuedInputEventDelay;
+ return true;
+ }
+ }
+ else if (touches == 2) {
+ if (type == kUIViewTapDouble) {
+ event.kbd.keycode = _queuedInputEvent.kbd.keycode = Common::KEYCODE_ESCAPE;
+ event.kbd.ascii = _queuedInputEvent.kbd.ascii = Common::ASCII_ESCAPE;
+ event.type = Common::EVENT_KEYDOWN;
+ _queuedInputEvent.type = Common::EVENT_KEYUP;
+ event.kbd.flags = _queuedInputEvent.kbd.flags = 0;
+ _queuedEventTime = getMillis() + kQueuedInputEventDelay;
+ return true;
+ }
+ }
+ return false;
+}
diff --git a/backends/platform/ios7/ios7_osys_main.cpp b/backends/platform/ios7/ios7_osys_main.cpp
new file mode 100644
index 0000000000..cd62148ca9
--- /dev/null
+++ b/backends/platform/ios7/ios7_osys_main.cpp
@@ -0,0 +1,366 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+// Disable symbol overrides so that we can use system headers.
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
+#include <unistd.h>
+#include <pthread.h>
+#include <string.h>
+
+#include <sys/time.h>
+#include <QuartzCore/QuartzCore.h>
+
+#include "common/scummsys.h"
+#include "common/util.h"
+#include "common/rect.h"
+#include "common/file.h"
+#include "common/fs.h"
+
+#include "base/main.h"
+
+#include "backends/saves/default/default-saves.h"
+#include "backends/timer/default/default-timer.h"
+#include "backends/fs/chroot/chroot-fs-factory.h"
+#include "backends/fs/posix/posix-fs.h"
+#include "audio/mixer.h"
+#include "audio/mixer_intern.h"
+
+#include "graphics/scaler.h"
+#include "graphics/scaler/aspect.h"
+
+#include "ios7_osys_main.h"
+
+
+const OSystem::GraphicsMode OSystem_iOS7::s_supportedGraphicsModes[] = {
+ { "none", "No filtering", kGraphicsModeNone },
+ { "linear", "Linear filtering", kGraphicsModeLinear },
+#ifdef USE_SCALERS
+// {"2x", "2x", GFX_DOUBLESIZE},
+// {"3x", "3x", GFX_TRIPLESIZE},
+ { "2xsai", "2xSAI", kGraphicsMode2xSaI},
+ {"super2xsai", "Super2xSAI", kGraphicsModeSuper2xSaI},
+ {"supereagle", "SuperEagle", kGraphicsModeSuperEagle},
+ {"advmame2x", "AdvMAME2x", kGraphicsModeAdvMame2x},
+ {"advmame3x", "AdvMAME3x", kGraphicsModeAdvMame3x},
+#ifdef USE_HQ_SCALERS
+ {"hq2x", "HQ2x", kGraphicsModeHQ2x},
+ {"hq3x", "HQ3x", kGraphicsModeHQ3x},
+#endif
+ {"tv2x", "TV2x", kGraphicsModeTV2x},
+ {"dotmatrix", "DotMatrix", kGraphicsModeDotMatrix},
+#endif
+ { 0, 0, 0 }
+};
+
+AQCallbackStruct OSystem_iOS7::s_AudioQueue;
+SoundProc OSystem_iOS7::s_soundCallback = NULL;
+void *OSystem_iOS7::s_soundParam = NULL;
+
+OSystem_iOS7::OSystem_iOS7() :
+ _mixer(NULL), _lastMouseTap(0), _queuedEventTime(0),
+ _mouseNeedTextureUpdate(false), _secondaryTapped(false), _lastSecondaryTap(0),
+ _screenOrientation(kScreenOrientationFlippedLandscape), _mouseClickAndDragEnabled(false),
+ _gestureStartX(-1), _gestureStartY(-1), _fullScreenIsDirty(false), _fullScreenOverlayIsDirty(false),
+ _mouseDirty(false), _timeSuspended(0), _lastDragPosX(-1), _lastDragPosY(-1), _screenChangeCount(0),
+ _lastErrorMessage(NULL), _mouseCursorPaletteEnabled(false), _gfxTransactionError(kTransactionSuccess) {
+ _queuedInputEvent.type = Common::EVENT_INVALID;
+ _touchpadModeEnabled = !iOS7_isBigDevice();
+#ifdef IPHONE_OFFICIAL
+ _fsFactory = new ChRootFilesystemFactory(iOS7_getDocumentsDir());
+#else
+ _fsFactory = new POSIXFilesystemFactory();
+#endif
+ initVideoContext();
+
+ memset(_gamePalette, 0, sizeof(_gamePalette));
+ memset(_gamePaletteRGBA5551, 0, sizeof(_gamePaletteRGBA5551));
+ memset(_mouseCursorPalette, 0, sizeof(_mouseCursorPalette));
+}
+
+OSystem_iOS7::~OSystem_iOS7() {
+ AudioQueueDispose(s_AudioQueue.queue, true);
+
+ delete _mixer;
+ // Prevent accidental freeing of the screen texture here. This needs to be
+ // checked since we might use the screen texture as framebuffer in the case
+ // of hi-color games for example. Otherwise this can lead to a double free.
+ if (_framebuffer.getPixels() != _videoContext->screenTexture.getPixels())
+ _framebuffer.free();
+ _mouseBuffer.free();
+}
+
+bool OSystem_iOS7::touchpadModeEnabled() const {
+ return _touchpadModeEnabled;
+}
+
+int OSystem_iOS7::timerHandler(int t) {
+ DefaultTimerManager *tm = (DefaultTimerManager *)g_system->getTimerManager();
+ tm->handler();
+ return t;
+}
+
+void OSystem_iOS7::initBackend() {
+#ifdef IPHONE_OFFICIAL
+ _savefileManager = new DefaultSaveFileManager("/Savegames");
+#else
+ _savefileManager = new DefaultSaveFileManager(SCUMMVM_SAVE_PATH);
+#endif
+
+ _timerManager = new DefaultTimerManager();
+
+ gettimeofday(&_startTime, NULL);
+
+ setupMixer();
+
+ setTimerCallback(&OSystem_iOS7::timerHandler, 10);
+
+ EventsBaseBackend::initBackend();
+}
+
+bool OSystem_iOS7::hasFeature(Feature f) {
+ switch (f) {
+ case kFeatureCursorPalette:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+void OSystem_iOS7::setFeatureState(Feature f, bool enable) {
+ switch (f) {
+ case kFeatureCursorPalette:
+ if (_mouseCursorPaletteEnabled != enable) {
+ _mouseNeedTextureUpdate = true;
+ _mouseDirty = true;
+ _mouseCursorPaletteEnabled = enable;
+ }
+ break;
+ case kFeatureAspectRatioCorrection:
+ _videoContext->asprectRatioCorrection = enable;
+ break;
+
+ default:
+ break;
+ }
+}
+
+bool OSystem_iOS7::getFeatureState(Feature f) {
+ switch (f) {
+ case kFeatureCursorPalette:
+ return _mouseCursorPaletteEnabled;
+ case kFeatureAspectRatioCorrection:
+ return _videoContext->asprectRatioCorrection;
+
+ default:
+ return false;
+ }
+}
+
+void OSystem_iOS7::suspendLoop() {
+ bool done = false;
+ uint32 startTime = getMillis();
+
+ stopSoundsystem();
+
+ InternalEvent event;
+ while (!done) {
+ if (iOS7_fetchEvent(&event))
+ if (event.type == kInputApplicationResumed)
+ done = true;
+ usleep(100000);
+ }
+
+ startSoundsystem();
+
+ _timeSuspended += getMillis() - startTime;
+}
+
+uint32 OSystem_iOS7::getMillis(bool skipRecord) {
+ CFTimeInterval timeInSeconds = CACurrentMediaTime();
+ return (uint32) (timeInSeconds * 1000.0);
+}
+
+void OSystem_iOS7::delayMillis(uint msecs) {
+ //printf("delayMillis(%d)\n", msecs);
+ usleep(msecs * 1000);
+}
+
+OSystem::MutexRef OSystem_iOS7::createMutex(void) {
+ pthread_mutexattr_t attr;
+ pthread_mutexattr_init(&attr);
+ pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
+
+ pthread_mutex_t *mutex = (pthread_mutex_t *) malloc(sizeof(pthread_mutex_t));
+ if (pthread_mutex_init(mutex, &attr) != 0) {
+ printf("pthread_mutex_init() failed!\n");
+ free(mutex);
+ return NULL;
+ }
+
+ return (MutexRef)mutex;
+}
+
+void OSystem_iOS7::lockMutex(MutexRef mutex) {
+ if (pthread_mutex_lock((pthread_mutex_t *) mutex) != 0) {
+ printf("pthread_mutex_lock() failed!\n");
+ }
+}
+
+void OSystem_iOS7::unlockMutex(MutexRef mutex) {
+ if (pthread_mutex_unlock((pthread_mutex_t *) mutex) != 0) {
+ printf("pthread_mutex_unlock() failed!\n");
+ }
+}
+
+void OSystem_iOS7::deleteMutex(MutexRef mutex) {
+ if (pthread_mutex_destroy((pthread_mutex_t *) mutex) != 0) {
+ printf("pthread_mutex_destroy() failed!\n");
+ } else {
+ free(mutex);
+ }
+}
+
+
+void OSystem_iOS7::setTimerCallback(TimerProc callback, int interval) {
+ //printf("setTimerCallback()\n");
+
+ if (callback != NULL) {
+ _timerCallbackTimer = interval;
+ _timerCallbackNext = getMillis() + interval;
+ _timerCallback = callback;
+ } else
+ _timerCallback = NULL;
+}
+
+void OSystem_iOS7::quit() {
+}
+
+void OSystem_iOS7::getTimeAndDate(TimeDate &td) const {
+ time_t curTime = time(0);
+ struct tm t = *localtime(&curTime);
+ td.tm_sec = t.tm_sec;
+ td.tm_min = t.tm_min;
+ td.tm_hour = t.tm_hour;
+ td.tm_mday = t.tm_mday;
+ td.tm_mon = t.tm_mon;
+ td.tm_year = t.tm_year;
+ td.tm_wday = t.tm_wday;
+}
+
+Audio::Mixer *OSystem_iOS7::getMixer() {
+ assert(_mixer);
+ return _mixer;
+}
+
+OSystem_iOS7 *OSystem_iOS7::sharedInstance() {
+ static OSystem_iOS7 *instance = new OSystem_iOS7();
+ return instance;
+}
+
+Common::String OSystem_iOS7::getDefaultConfigFileName() {
+#ifdef IPHONE_OFFICIAL
+ Common::String path = "/Preferences";
+ return path;
+#else
+ return SCUMMVM_PREFS_PATH;
+#endif
+}
+
+void OSystem_iOS7::addSysArchivesToSearchSet(Common::SearchSet &s, int priority) {
+ // Get URL of the Resource directory of the .app bundle
+ CFURLRef fileUrl = CFBundleCopyResourcesDirectoryURL(CFBundleGetMainBundle());
+ if (fileUrl) {
+ // Try to convert the URL to an absolute path
+ UInt8 buf[MAXPATHLEN];
+ if (CFURLGetFileSystemRepresentation(fileUrl, true, buf, sizeof(buf))) {
+ // Success: Add it to the search path
+ Common::String bundlePath((const char *)buf);
+#ifdef IPHONE_OFFICIAL
+ POSIXFilesystemNode *posixNode = new POSIXFilesystemNode(bundlePath);
+ Common::FSNode *node = new Common::FSNode(posixNode);
+ s.add("__OSX_BUNDLE__", new Common::FSDirectory(*node), priority);
+#else
+ // OS X
+ s.add("__OSX_BUNDLE__", new Common::FSDirectory(bundlePath), priority);
+#endif
+ }
+ CFRelease(fileUrl);
+ }
+}
+
+void OSystem_iOS7::logMessage(LogMessageType::Type type, const char *message) {
+ FILE *output = 0;
+
+ if (type == LogMessageType::kInfo || type == LogMessageType::kDebug)
+ output = stdout;
+ else
+ output = stderr;
+
+ if (type == LogMessageType::kError) {
+ free(_lastErrorMessage);
+ _lastErrorMessage = strdup(message);
+ }
+
+ fputs(message, output);
+ fflush(output);
+}
+
+bool iOS7_touchpadModeEnabled() {
+ OSystem_iOS7 *sys = (OSystem_iOS7 *) g_system;
+ return sys && sys->touchpadModeEnabled();
+}
+
+void iOS7_main(int argc, char **argv) {
+
+ //OSystem_iOS7::migrateApp();
+
+ FILE *newfp = fopen("/var/mobile/.scummvm.log", "a");
+ if (newfp != NULL) {
+ fclose(stdout);
+ fclose(stderr);
+ *stdout = *newfp;
+ *stderr = *newfp;
+ setbuf(stdout, NULL);
+ setbuf(stderr, NULL);
+
+ //extern int gDebugLevel;
+ //gDebugLevel = 10;
+ }
+
+#ifdef IPHONE_OFFICIAL
+ chdir(iOS7_getDocumentsDir());
+#else
+ system("mkdir " SCUMMVM_ROOT_PATH);
+ system("mkdir " SCUMMVM_SAVE_PATH);
+
+ chdir("/var/mobile/");
+#endif
+
+ g_system = OSystem_iOS7::sharedInstance();
+ assert(g_system);
+
+ // Invoke the actual ScummVM main entry point:
+ scummvm_main(argc, (const char *const *) argv);
+ g_system->quit(); // TODO: Consider removing / replacing this!
+}
diff --git a/backends/platform/ios7/ios7_osys_main.h b/backends/platform/ios7/ios7_osys_main.h
new file mode 100644
index 0000000000..eadb49e5ac
--- /dev/null
+++ b/backends/platform/ios7/ios7_osys_main.h
@@ -0,0 +1,233 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef BACKENDS_PLATFORM_IPHONE_OSYS_MAIN_H
+#define BACKENDS_PLATFORM_IPHONE_OSYS_MAIN_H
+
+#include "graphics/surface.h"
+#include "ios7_common.h"
+#include "backends/base-backend.h"
+#include "common/events.h"
+#include "audio/mixer_intern.h"
+#include "backends/fs/posix/posix-fs-factory.h"
+#include "graphics/colormasks.h"
+#include "graphics/palette.h"
+
+#include <AudioToolbox/AudioQueue.h>
+
+#define AUDIO_BUFFERS 3
+#define WAVE_BUFFER_SIZE 2048
+#define AUDIO_SAMPLE_RATE 44100
+
+#define SCUMMVM_ROOT_PATH "/var/mobile/Library/ScummVM"
+#define SCUMMVM_SAVE_PATH SCUMMVM_ROOT_PATH "/Savegames"
+#define SCUMMVM_PREFS_PATH SCUMMVM_ROOT_PATH "/Preferences"
+
+typedef void (*SoundProc)(void *param, byte *buf, int len);
+typedef int (*TimerProc)(int interval);
+
+struct AQCallbackStruct {
+ AudioQueueRef queue;
+ uint32 frameCount;
+ AudioQueueBufferRef buffers[AUDIO_BUFFERS];
+ AudioStreamBasicDescription dataFormat;
+};
+
+class OSystem_iOS7 : public EventsBaseBackend, public PaletteManager {
+protected:
+ static const OSystem::GraphicsMode s_supportedGraphicsModes[];
+ static AQCallbackStruct s_AudioQueue;
+ static SoundProc s_soundCallback;
+ static void *s_soundParam;
+
+ Audio::MixerImpl *_mixer;
+
+ VideoContext *_videoContext;
+
+ Graphics::Surface _framebuffer;
+
+ // For signaling that screen format set up might have failed.
+ TransactionError _gfxTransactionError;
+
+ // For use with the game texture
+ uint16 _gamePalette[256];
+ // For use with the mouse texture
+ uint16 _gamePaletteRGBA5551[256];
+
+ struct timeval _startTime;
+ uint32 _timeSuspended;
+
+ bool _mouseCursorPaletteEnabled;
+ uint16 _mouseCursorPalette[256];
+ Graphics::Surface _mouseBuffer;
+ uint16 _mouseKeyColor;
+ bool _mouseDirty;
+ bool _mouseNeedTextureUpdate;
+
+ long _lastMouseDown;
+ long _lastMouseTap;
+ long _queuedEventTime;
+ Common::Event _queuedInputEvent;
+ bool _secondaryTapped;
+ long _lastSecondaryDown;
+ long _lastSecondaryTap;
+ int _gestureStartX, _gestureStartY;
+ bool _mouseClickAndDragEnabled;
+ bool _touchpadModeEnabled;
+ int _lastPadX;
+ int _lastPadY;
+ int _lastDragPosX;
+ int _lastDragPosY;
+
+ int _timerCallbackNext;
+ int _timerCallbackTimer;
+ TimerProc _timerCallback;
+
+ Common::Array<Common::Rect> _dirtyRects;
+ Common::Array<Common::Rect> _dirtyOverlayRects;
+ ScreenOrientation _screenOrientation;
+ bool _fullScreenIsDirty;
+ bool _fullScreenOverlayIsDirty;
+ int _screenChangeCount;
+
+ char *_lastErrorMessage;
+
+public:
+
+ OSystem_iOS7();
+ virtual ~OSystem_iOS7();
+
+ static OSystem_iOS7 *sharedInstance();
+
+ virtual void initBackend();
+
+ virtual bool hasFeature(Feature f);
+ virtual void setFeatureState(Feature f, bool enable);
+ virtual bool getFeatureState(Feature f);
+ virtual const GraphicsMode *getSupportedGraphicsModes() const;
+ virtual int getDefaultGraphicsMode() const;
+ virtual bool setGraphicsMode(int mode);
+ virtual int getGraphicsMode() const;
+ virtual void initSize(uint width, uint height, const Graphics::PixelFormat *format);
+
+ virtual void beginGFXTransaction();
+ virtual TransactionError endGFXTransaction();
+
+ virtual int16 getHeight();
+ virtual int16 getWidth();
+
+ bool touchpadModeEnabled() const;
+
+#ifdef USE_RGB_COLOR
+ virtual Graphics::PixelFormat getScreenFormat() const { return _framebuffer.format; }
+ virtual Common::List<Graphics::PixelFormat> getSupportedFormats() const;
+#endif
+
+ virtual PaletteManager *getPaletteManager() { return this; }
+protected:
+ // PaletteManager API
+ virtual void setPalette(const byte *colors, uint start, uint num);
+ virtual void grabPalette(byte *colors, uint start, uint num);
+
+public:
+ virtual void copyRectToScreen(const void *buf, int pitch, int x, int y, int w, int h);
+ virtual void updateScreen();
+ virtual Graphics::Surface *lockScreen();
+ virtual void unlockScreen();
+ virtual void setShakePos(int shakeOffset);
+
+ virtual void showOverlay();
+ virtual void hideOverlay();
+ virtual void clearOverlay();
+ virtual void grabOverlay(void *buf, int pitch);
+ virtual void copyRectToOverlay(const void *buf, int pitch, int x, int y, int w, int h);
+ virtual int16 getOverlayHeight();
+ virtual int16 getOverlayWidth();
+ virtual Graphics::PixelFormat getOverlayFormat() const { return Graphics::createPixelFormat<5551>(); }
+
+ virtual bool showMouse(bool visible);
+
+ virtual void warpMouse(int x, int y);
+ virtual void setMouseCursor(const void *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor = 255, bool dontScale = false, const Graphics::PixelFormat *format = NULL);
+ virtual void setCursorPalette(const byte *colors, uint start, uint num);
+
+ virtual bool pollEvent(Common::Event &event);
+ virtual uint32 getMillis(bool skipRecord = false);
+ virtual void delayMillis(uint msecs);
+
+ virtual MutexRef createMutex(void);
+ virtual void lockMutex(MutexRef mutex);
+ virtual void unlockMutex(MutexRef mutex);
+ virtual void deleteMutex(MutexRef mutex);
+
+ static void mixCallback(void *sys, byte *samples, int len);
+ virtual void setupMixer(void);
+ virtual void setTimerCallback(TimerProc callback, int interval);
+ virtual int getScreenChangeID() const { return _screenChangeCount; }
+ virtual void quit();
+
+ virtual void addSysArchivesToSearchSet(Common::SearchSet &s, int priority = 0);
+ virtual void getTimeAndDate(TimeDate &t) const;
+
+ virtual Audio::Mixer *getMixer();
+
+ void startSoundsystem();
+ void stopSoundsystem();
+
+ virtual Common::String getDefaultConfigFileName();
+
+ virtual void logMessage(LogMessageType::Type type, const char *message);
+ virtual void fatalError() override;
+
+protected:
+ void initVideoContext();
+ void updateOutputSurface();
+
+ void internUpdateScreen();
+ void dirtyFullScreen();
+ void dirtyFullOverlayScreen();
+ void suspendLoop();
+ void drawDirtyRect(const Common::Rect &dirtyRect);
+ void updateMouseTexture();
+ static void AQBufferCallback(void *in, AudioQueueRef inQ, AudioQueueBufferRef outQB);
+ static int timerHandler(int t);
+
+ bool handleEvent_swipe(Common::Event &event, int direction, int touches);
+ bool handleEvent_tap(Common::Event &event, UIViewTapDescription type, int touches);
+ void handleEvent_keyPressed(Common::Event &event, int keyPressed);
+ void handleEvent_orientationChanged(int orientation);
+ void handleEvent_applicationSuspended();
+ void handleEvent_applicationResumed();
+
+ bool handleEvent_mouseDown(Common::Event &event, int x, int y);
+ bool handleEvent_mouseUp(Common::Event &event, int x, int y);
+
+ bool handleEvent_secondMouseDown(Common::Event &event, int x, int y);
+ bool handleEvent_secondMouseUp(Common::Event &event, int x, int y);
+
+ bool handleEvent_mouseDragged(Common::Event &event, int x, int y);
+ bool handleEvent_mouseSecondDragged(Common::Event &event, int x, int y);
+
+ void rebuildSurface();
+};
+
+#endif
diff --git a/backends/platform/ios7/ios7_osys_sound.cpp b/backends/platform/ios7/ios7_osys_sound.cpp
new file mode 100644
index 0000000000..0dc81a9385
--- /dev/null
+++ b/backends/platform/ios7/ios7_osys_sound.cpp
@@ -0,0 +1,105 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+// Disable symbol overrides so that we can use system headers.
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
+#include "ios7_osys_main.h"
+
+void OSystem_iOS7::AQBufferCallback(void *in, AudioQueueRef inQ, AudioQueueBufferRef outQB) {
+ //printf("AQBufferCallback()\n");
+ if (s_AudioQueue.frameCount > 0 && s_soundCallback != NULL) {
+ outQB->mAudioDataByteSize = 4 * s_AudioQueue.frameCount;
+ s_soundCallback(s_soundParam, (byte *)outQB->mAudioData, outQB->mAudioDataByteSize);
+ AudioQueueEnqueueBuffer(inQ, outQB, 0, NULL);
+ } else {
+ AudioQueueStop(s_AudioQueue.queue, false);
+ }
+}
+
+void OSystem_iOS7::mixCallback(void *sys, byte *samples, int len) {
+ OSystem_iOS7 *this_ = (OSystem_iOS7 *)sys;
+ assert(this_);
+
+ if (this_->_mixer) {
+ this_->_mixer->mixCallback(samples, len);
+ }
+}
+
+void OSystem_iOS7::setupMixer() {
+ _mixer = new Audio::MixerImpl(this, AUDIO_SAMPLE_RATE);
+
+ s_soundCallback = mixCallback;
+ s_soundParam = this;
+
+ startSoundsystem();
+}
+
+void OSystem_iOS7::startSoundsystem() {
+ s_AudioQueue.dataFormat.mSampleRate = AUDIO_SAMPLE_RATE;
+ s_AudioQueue.dataFormat.mFormatID = kAudioFormatLinearPCM;
+ s_AudioQueue.dataFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
+ s_AudioQueue.dataFormat.mBytesPerPacket = 4;
+ s_AudioQueue.dataFormat.mFramesPerPacket = 1;
+ s_AudioQueue.dataFormat.mBytesPerFrame = 4;
+ s_AudioQueue.dataFormat.mChannelsPerFrame = 2;
+ s_AudioQueue.dataFormat.mBitsPerChannel = 16;
+ s_AudioQueue.frameCount = WAVE_BUFFER_SIZE;
+
+ if (AudioQueueNewOutput(&s_AudioQueue.dataFormat, AQBufferCallback, &s_AudioQueue, 0, kCFRunLoopCommonModes, 0, &s_AudioQueue.queue)) {
+ printf("Couldn't set the AudioQueue callback!\n");
+ _mixer->setReady(false);
+ return;
+ }
+
+ uint32 bufferBytes = s_AudioQueue.frameCount * s_AudioQueue.dataFormat.mBytesPerFrame;
+
+ for (int i = 0; i < AUDIO_BUFFERS; i++) {
+ if (AudioQueueAllocateBuffer(s_AudioQueue.queue, bufferBytes, &s_AudioQueue.buffers[i])) {
+ printf("Error allocating AudioQueue buffer!\n");
+ _mixer->setReady(false);
+ return;
+ }
+
+ AQBufferCallback(&s_AudioQueue, s_AudioQueue.queue, s_AudioQueue.buffers[i]);
+ }
+
+ AudioQueueSetParameter(s_AudioQueue.queue, kAudioQueueParam_Volume, 1.0);
+ if (AudioQueueStart(s_AudioQueue.queue, NULL)) {
+ printf("Error starting the AudioQueue!\n");
+ _mixer->setReady(false);
+ return;
+ }
+
+ _mixer->setReady(true);
+}
+
+void OSystem_iOS7::stopSoundsystem() {
+ AudioQueueStop(s_AudioQueue.queue, true);
+
+ for (int i = 0; i < AUDIO_BUFFERS; i++) {
+ AudioQueueFreeBuffer(s_AudioQueue.queue, s_AudioQueue.buffers[i]);
+ }
+
+ AudioQueueDispose(s_AudioQueue.queue, true);
+ _mixer->setReady(false);
+}
diff --git a/backends/platform/ios7/ios7_osys_video.mm b/backends/platform/ios7/ios7_osys_video.mm
new file mode 100644
index 0000000000..0d183ce834
--- /dev/null
+++ b/backends/platform/ios7/ios7_osys_video.mm
@@ -0,0 +1,545 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+// Disable symbol overrides so that we can use system headers.
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
+#include "ios7_osys_main.h"
+#include "ios7_video.h"
+
+#include "graphics/conversion.h"
+#import "iOS7AppDelegate.h"
+
+@interface iOS7AlertHandler : NSObject<UIAlertViewDelegate>
+@end
+
+@implementation iOS7AlertHandler
+
+- (void)alertView:(UIAlertView *)alertView willDismissWithButtonIndex:(NSInteger)buttonIndex {
+ OSystem_iOS7::sharedInstance()->quit();
+ exit(1);
+}
+
+@end
+
+static void displayAlert(void *ctx) {
+ UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Fatal Error"
+ message:[NSString stringWithCString:(const char *)ctx encoding:NSUTF8StringEncoding]
+ delegate:[[iOS7AlertHandler alloc] init]
+ cancelButtonTitle:@"OK"
+ otherButtonTitles:nil];
+ [alert show];
+ [alert autorelease];
+}
+
+void OSystem_iOS7::fatalError() {
+ if (_lastErrorMessage) {
+ dispatch_async_f(dispatch_get_main_queue(), _lastErrorMessage, displayAlert);
+ for(;;);
+ }
+ else {
+ OSystem::fatalError();
+ }
+}
+
+void OSystem_iOS7::initVideoContext() {
+ _videoContext = [[iOS7AppDelegate iPhoneView] getVideoContext];
+}
+
+const OSystem::GraphicsMode *OSystem_iOS7::getSupportedGraphicsModes() const {
+ return s_supportedGraphicsModes;
+}
+
+int OSystem_iOS7::getDefaultGraphicsMode() const {
+ return kGraphicsModeNone;
+}
+
+bool OSystem_iOS7::setGraphicsMode(int mode) {
+ switch (mode) {
+ case kGraphicsModeNone:
+ case kGraphicsModeLinear:
+ case kGraphicsMode2xSaI:
+ case kGraphicsModeSuper2xSaI:
+ case kGraphicsModeSuperEagle:
+ case kGraphicsModeAdvMame2x:
+ case kGraphicsModeAdvMame3x:
+ case kGraphicsModeHQ2x:
+ case kGraphicsModeHQ3x:
+ case kGraphicsModeTV2x:
+ case kGraphicsModeDotMatrix:
+ _videoContext->graphicsMode = (GraphicsModes)mode;
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+int OSystem_iOS7::getGraphicsMode() const {
+ return _videoContext->graphicsMode;
+}
+
+#ifdef USE_RGB_COLOR
+Common::List<Graphics::PixelFormat> OSystem_iOS7::getSupportedFormats() const {
+ Common::List<Graphics::PixelFormat> list;
+ // RGB565
+ list.push_back(Graphics::createPixelFormat<565>());
+ // CLUT8
+ list.push_back(Graphics::PixelFormat::createFormatCLUT8());
+ return list;
+}
+#endif
+
+void OSystem_iOS7::initSize(uint width, uint height, const Graphics::PixelFormat *format) {
+ //printf("initSize(%u, %u, %p)\n", width, height, (const void *)format);
+
+ _videoContext->screenWidth = width;
+ _videoContext->screenHeight = height;
+ _videoContext->shakeOffsetY = 0;
+
+ // In case we use the screen texture as frame buffer we reset the pixels
+ // pointer here to avoid freeing the screen texture.
+ if (_framebuffer.getPixels() == _videoContext->screenTexture.getPixels())
+ _framebuffer.setPixels(0);
+
+ // Create the screen texture right here. We need to do this here, since
+ // when a game requests hi-color mode, we actually set the framebuffer
+ // to the texture buffer to avoid an additional copy step.
+ [[iOS7AppDelegate iPhoneView] performSelectorOnMainThread:@selector(createScreenTexture) withObject:nil waitUntilDone: YES];
+
+ // In case the client code tries to set up a non supported mode, we will
+ // fall back to CLUT8 and set the transaction error accordingly.
+ if (format && format->bytesPerPixel != 1 && *format != _videoContext->screenTexture.format) {
+ format = 0;
+ _gfxTransactionError = kTransactionFormatNotSupported;
+ }
+
+ if (!format || format->bytesPerPixel == 1) {
+ _framebuffer.create(width, height, Graphics::PixelFormat::createFormatCLUT8());
+ } else {
+#if 0
+ printf("bytesPerPixel: %u RGBAlosses: %u,%u,%u,%u RGBAshifts: %u,%u,%u,%u\n", format->bytesPerPixel,
+ format->rLoss, format->gLoss, format->bLoss, format->aLoss,
+ format->rShift, format->gShift, format->bShift, format->aShift);
+#endif
+ // We directly draw on the screen texture in hi-color mode. Thus
+ // we copy over its settings here and just replace the width and
+ // height to avoid any problems.
+ _framebuffer = _videoContext->screenTexture;
+ _framebuffer.w = width;
+ _framebuffer.h = height;
+ }
+
+ _fullScreenIsDirty = false;
+ dirtyFullScreen();
+ _mouseCursorPaletteEnabled = false;
+}
+
+void OSystem_iOS7::beginGFXTransaction() {
+ _gfxTransactionError = kTransactionSuccess;
+}
+
+OSystem::TransactionError OSystem_iOS7::endGFXTransaction() {
+ _screenChangeCount++;
+ updateOutputSurface();
+ [[iOS7AppDelegate iPhoneView] performSelectorOnMainThread:@selector(setGraphicsMode) withObject:nil waitUntilDone: YES];
+
+ return _gfxTransactionError;
+}
+
+void OSystem_iOS7::updateOutputSurface() {
+ [[iOS7AppDelegate iPhoneView] performSelectorOnMainThread:@selector(initSurface) withObject:nil waitUntilDone: YES];
+}
+
+int16 OSystem_iOS7::getHeight() {
+ return _videoContext->screenHeight;
+}
+
+int16 OSystem_iOS7::getWidth() {
+ return _videoContext->screenWidth;
+}
+
+void OSystem_iOS7::setPalette(const byte *colors, uint start, uint num) {
+ //printf("setPalette(%p, %u, %u)\n", colors, start, num);
+ assert(start + num <= 256);
+ const byte *b = colors;
+
+ for (uint i = start; i < start + num; ++i) {
+ _gamePalette[i] = Graphics::RGBToColor<Graphics::ColorMasks<565> >(b[0], b[1], b[2]);
+ _gamePaletteRGBA5551[i] = Graphics::RGBToColor<Graphics::ColorMasks<5551> >(b[0], b[1], b[2]);
+ b += 3;
+ }
+
+ dirtyFullScreen();
+
+ // Automatically update the mouse texture when the palette changes while the
+ // cursor palette is disabled.
+ if (!_mouseCursorPaletteEnabled && _mouseBuffer.format.bytesPerPixel == 1)
+ _mouseDirty = _mouseNeedTextureUpdate = true;
+}
+
+void OSystem_iOS7::grabPalette(byte *colors, uint start, uint num) {
+ //printf("grabPalette(%p, %u, %u)\n", colors, start, num);
+ assert(start + num <= 256);
+ byte *b = colors;
+
+ for (uint i = start; i < start + num; ++i) {
+ Graphics::colorToRGB<Graphics::ColorMasks<565> >(_gamePalette[i], b[0], b[1], b[2]);
+ b += 3;
+ }
+}
+
+void OSystem_iOS7::copyRectToScreen(const void *buf, int pitch, int x, int y, int w, int h) {
+ //printf("copyRectToScreen(%p, %d, %i, %i, %i, %i)\n", buf, pitch, x, y, w, h);
+ //Clip the coordinates
+ const byte *src = (const byte *)buf;
+ if (x < 0) {
+ w += x;
+ src -= x;
+ x = 0;
+ }
+
+ if (y < 0) {
+ h += y;
+ src -= y * pitch;
+ y = 0;
+ }
+
+ if (w > (int)_framebuffer.w - x) {
+ w = _framebuffer.w - x;
+ }
+
+ if (h > (int)_framebuffer.h - y) {
+ h = _framebuffer.h - y;
+ }
+
+ if (w <= 0 || h <= 0)
+ return;
+
+ if (!_fullScreenIsDirty) {
+ _dirtyRects.push_back(Common::Rect(x, y, x + w, y + h));
+ }
+
+ byte *dst = (byte *)_framebuffer.getBasePtr(x, y);
+ if (_framebuffer.pitch == pitch && _framebuffer.w == w) {
+ memcpy(dst, src, h * pitch);
+ } else {
+ do {
+ memcpy(dst, src, w * _framebuffer.format.bytesPerPixel);
+ src += pitch;
+ dst += _framebuffer.pitch;
+ } while (--h);
+ }
+}
+
+void OSystem_iOS7::updateScreen() {
+ if (_dirtyRects.size() == 0 && _dirtyOverlayRects.size() == 0 && !_mouseDirty)
+ return;
+
+ //printf("updateScreen(): %i dirty rects.\n", _dirtyRects.size());
+
+ internUpdateScreen();
+ _mouseDirty = false;
+ _fullScreenIsDirty = false;
+ _fullScreenOverlayIsDirty = false;
+
+ iOS7_updateScreen();
+}
+
+void OSystem_iOS7::internUpdateScreen() {
+ if (_mouseNeedTextureUpdate) {
+ updateMouseTexture();
+ _mouseNeedTextureUpdate = false;
+ }
+
+ while (_dirtyRects.size()) {
+ Common::Rect dirtyRect = _dirtyRects.remove_at(_dirtyRects.size() - 1);
+
+ //printf("Drawing: (%i, %i) -> (%i, %i)\n", dirtyRect.left, dirtyRect.top, dirtyRect.right, dirtyRect.bottom);
+ drawDirtyRect(dirtyRect);
+ // TODO: Implement dirty rect code
+ //updateHardwareSurfaceForRect(dirtyRect);
+ }
+
+ if (_videoContext->overlayVisible) {
+ // TODO: Implement dirty rect code
+ _dirtyOverlayRects.clear();
+ /*while (_dirtyOverlayRects.size()) {
+ Common::Rect dirtyRect = _dirtyOverlayRects.remove_at(_dirtyOverlayRects.size() - 1);
+
+ //printf("Drawing: (%i, %i) -> (%i, %i)\n", dirtyRect.left, dirtyRect.top, dirtyRect.right, dirtyRect.bottom);
+ drawDirtyOverlayRect(dirtyRect);
+ }*/
+ }
+}
+
+void OSystem_iOS7::drawDirtyRect(const Common::Rect &dirtyRect) {
+ // We only need to do a color look up for CLUT8
+ if (_framebuffer.format.bytesPerPixel != 1)
+ return;
+
+ int h = dirtyRect.bottom - dirtyRect.top;
+ int w = dirtyRect.right - dirtyRect.left;
+
+ const byte *src = (const byte *)_framebuffer.getBasePtr(dirtyRect.left, dirtyRect.top);
+ byte *dstRaw = (byte *)_videoContext->screenTexture.getBasePtr(dirtyRect.left, dirtyRect.top);
+
+ // When we use CLUT8 do a color look up
+ for (int y = h; y > 0; y--) {
+ uint16 *dst = (uint16 *)dstRaw;
+ for (int x = w; x > 0; x--)
+ *dst++ = _gamePalette[*src++];
+
+ dstRaw += _videoContext->screenTexture.pitch;
+ src += _framebuffer.pitch - w;
+ }
+}
+
+Graphics::Surface *OSystem_iOS7::lockScreen() {
+ //printf("lockScreen()\n");
+ return &_framebuffer;
+}
+
+void OSystem_iOS7::unlockScreen() {
+ //printf("unlockScreen()\n");
+ dirtyFullScreen();
+}
+
+void OSystem_iOS7::setShakePos(int shakeOffset) {
+ //printf("setShakePos(%i)\n", shakeOffset);
+ _videoContext->shakeOffsetY = shakeOffset;
+ [[iOS7AppDelegate iPhoneView] performSelectorOnMainThread:@selector(setViewTransformation) withObject:nil waitUntilDone: YES];
+ // HACK: We use this to force a redraw.
+ _mouseDirty = true;
+}
+
+void OSystem_iOS7::showOverlay() {
+ //printf("showOverlay()\n");
+ _videoContext->overlayVisible = true;
+ dirtyFullOverlayScreen();
+ updateScreen();
+ [[iOS7AppDelegate iPhoneView] performSelectorOnMainThread:@selector(updateMouseCursorScaling) withObject:nil waitUntilDone: YES];
+ [[iOS7AppDelegate iPhoneView] performSelectorOnMainThread:@selector(clearColorBuffer) withObject:nil waitUntilDone: YES];
+}
+
+void OSystem_iOS7::hideOverlay() {
+ //printf("hideOverlay()\n");
+ _videoContext->overlayVisible = false;
+ _dirtyOverlayRects.clear();
+ dirtyFullScreen();
+ [[iOS7AppDelegate iPhoneView] performSelectorOnMainThread:@selector(updateMouseCursorScaling) withObject:nil waitUntilDone: YES];
+ [[iOS7AppDelegate iPhoneView] performSelectorOnMainThread:@selector(clearColorBuffer) withObject:nil waitUntilDone: YES];
+}
+
+void OSystem_iOS7::clearOverlay() {
+ //printf("clearOverlay()\n");
+ bzero(_videoContext->overlayTexture.getPixels(), _videoContext->overlayTexture.h * _videoContext->overlayTexture.pitch);
+ dirtyFullOverlayScreen();
+}
+
+void OSystem_iOS7::grabOverlay(void *buf, int pitch) {
+ //printf("grabOverlay()\n");
+ int h = _videoContext->overlayHeight;
+
+ byte *dst = (byte *)buf;
+ const byte *src = (const byte *)_videoContext->overlayTexture.getPixels();
+ do {
+ memcpy(dst, src, _videoContext->overlayWidth * sizeof(uint16));
+ src += _videoContext->overlayTexture.pitch;
+ dst += pitch;
+ } while (--h);
+}
+
+void OSystem_iOS7::copyRectToOverlay(const void *buf, int pitch, int x, int y, int w, int h) {
+ //printf("copyRectToOverlay(%p, pitch=%i, x=%i, y=%i, w=%i, h=%i)\n", (const void *)buf, pitch, x, y, w, h);
+ const byte *src = (const byte *)buf;
+
+ //Clip the coordinates
+ if (x < 0) {
+ w += x;
+ src -= x * sizeof(uint16);
+ x = 0;
+ }
+
+ if (y < 0) {
+ h += y;
+ src -= y * pitch;
+ y = 0;
+ }
+
+ if (w > (int)_videoContext->overlayWidth - x)
+ w = _videoContext->overlayWidth - x;
+
+ if (h > (int)_videoContext->overlayHeight - y)
+ h = _videoContext->overlayHeight - y;
+
+ if (w <= 0 || h <= 0)
+ return;
+
+ if (!_fullScreenOverlayIsDirty) {
+ _dirtyOverlayRects.push_back(Common::Rect(x, y, x + w, y + h));
+ }
+
+ byte *dst = (byte *)_videoContext->overlayTexture.getBasePtr(x, y);
+ do {
+ memcpy(dst, src, w * sizeof(uint16));
+ src += pitch;
+ dst += _videoContext->overlayTexture.pitch;
+ } while (--h);
+}
+
+int16 OSystem_iOS7::getOverlayHeight() {
+ return _videoContext->overlayHeight;
+}
+
+int16 OSystem_iOS7::getOverlayWidth() {
+ return _videoContext->overlayWidth;
+}
+
+bool OSystem_iOS7::showMouse(bool visible) {
+ //printf("showMouse(%d)\n", visible);
+ bool last = _videoContext->mouseIsVisible;
+ _videoContext->mouseIsVisible = visible;
+ _mouseDirty = true;
+
+ return last;
+}
+
+void OSystem_iOS7::warpMouse(int x, int y) {
+ //printf("warpMouse(%d, %d)\n", x, y);
+ _videoContext->mouseX = x;
+ _videoContext->mouseY = y;
+ [[iOS7AppDelegate iPhoneView] performSelectorOnMainThread:@selector(notifyMouseMove) withObject:nil waitUntilDone: YES];
+ _mouseDirty = true;
+}
+
+void OSystem_iOS7::dirtyFullScreen() {
+ if (!_fullScreenIsDirty) {
+ _dirtyRects.clear();
+ _dirtyRects.push_back(Common::Rect(0, 0, _videoContext->screenWidth, _videoContext->screenHeight));
+ _fullScreenIsDirty = true;
+ }
+}
+
+void OSystem_iOS7::dirtyFullOverlayScreen() {
+ if (!_fullScreenOverlayIsDirty) {
+ _dirtyOverlayRects.clear();
+ _dirtyOverlayRects.push_back(Common::Rect(0, 0, _videoContext->overlayWidth, _videoContext->overlayHeight));
+ _fullScreenOverlayIsDirty = true;
+ }
+}
+
+void OSystem_iOS7::setMouseCursor(const void *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, bool dontScale, const Graphics::PixelFormat *format) {
+ //printf("setMouseCursor(%p, %u, %u, %i, %i, %u, %d, %p)\n", (const void *)buf, w, h, hotspotX, hotspotY, keycolor, dontScale, (const void *)format);
+
+ const Graphics::PixelFormat pixelFormat = format ? *format : Graphics::PixelFormat::createFormatCLUT8();
+#if 0
+ printf("bytesPerPixel: %u RGBAlosses: %u,%u,%u,%u RGBAshifts: %u,%u,%u,%u\n", pixelFormat.bytesPerPixel,
+ pixelFormat.rLoss, pixelFormat.gLoss, pixelFormat.bLoss, pixelFormat.aLoss,
+ pixelFormat.rShift, pixelFormat.gShift, pixelFormat.bShift, pixelFormat.aShift);
+#endif
+ assert(pixelFormat.bytesPerPixel == 1 || pixelFormat.bytesPerPixel == 2);
+
+ if (_mouseBuffer.w != w || _mouseBuffer.h != h || _mouseBuffer.format != pixelFormat || !_mouseBuffer.getPixels())
+ _mouseBuffer.create(w, h, pixelFormat);
+
+ _videoContext->mouseWidth = w;
+ _videoContext->mouseHeight = h;
+
+ _videoContext->mouseHotspotX = hotspotX;
+ _videoContext->mouseHotspotY = hotspotY;
+
+ _mouseKeyColor = keycolor;
+
+ memcpy(_mouseBuffer.getPixels(), buf, h * _mouseBuffer.pitch);
+
+ _mouseDirty = true;
+ _mouseNeedTextureUpdate = true;
+}
+
+void OSystem_iOS7::setCursorPalette(const byte *colors, uint start, uint num) {
+ //printf("setCursorPalette(%p, %u, %u)\n", (const void *)colors, start, num);
+ assert(start + num <= 256);
+
+ for (uint i = start; i < start + num; ++i, colors += 3)
+ _mouseCursorPalette[i] = Graphics::RGBToColor<Graphics::ColorMasks<5551> >(colors[0], colors[1], colors[2]);
+
+ // FIXME: This is just stupid, our client code seems to assume that this
+ // automatically enables the cursor palette.
+ _mouseCursorPaletteEnabled = true;
+
+ if (_mouseCursorPaletteEnabled)
+ _mouseDirty = _mouseNeedTextureUpdate = true;
+}
+
+void OSystem_iOS7::updateMouseTexture() {
+ uint texWidth = getSizeNextPOT(_videoContext->mouseWidth);
+ uint texHeight = getSizeNextPOT(_videoContext->mouseHeight);
+
+ Graphics::Surface &mouseTexture = _videoContext->mouseTexture;
+ if (mouseTexture.w != texWidth || mouseTexture.h != texHeight)
+ mouseTexture.create(texWidth, texHeight, Graphics::createPixelFormat<5551>());
+
+ if (_mouseBuffer.format.bytesPerPixel == 1) {
+ const uint16 *palette;
+ if (_mouseCursorPaletteEnabled)
+ palette = _mouseCursorPalette;
+ else
+ palette = _gamePaletteRGBA5551;
+
+ uint16 *mouseBuf = (uint16 *)mouseTexture.getPixels();
+ for (uint x = 0; x < _videoContext->mouseWidth; ++x) {
+ for (uint y = 0; y < _videoContext->mouseHeight; ++y) {
+ const byte color = *(const byte *)_mouseBuffer.getBasePtr(x, y);
+ if (color != _mouseKeyColor)
+ mouseBuf[y * texWidth + x] = palette[color] | 0x1;
+ else
+ mouseBuf[y * texWidth + x] = 0x0;
+ }
+ }
+ } else {
+ if (crossBlit((byte *)mouseTexture.getPixels(), (const byte *)_mouseBuffer.getPixels(), mouseTexture.pitch,
+ _mouseBuffer.pitch, _mouseBuffer.w, _mouseBuffer.h, mouseTexture.format, _mouseBuffer.format)) {
+ if (!_mouseBuffer.format.aBits()) {
+ // Apply color keying since the original cursor had no alpha channel.
+ const uint16 *src = (const uint16 *)_mouseBuffer.getPixels();
+ uint8 *dstRaw = (uint8 *)mouseTexture.getPixels();
+
+ for (uint y = 0; y < _mouseBuffer.h; ++y, dstRaw += mouseTexture.pitch) {
+ uint16 *dst = (uint16 *)dstRaw;
+ for (uint x = 0; x < _mouseBuffer.w; ++x, ++dst) {
+ if (*src++ == _mouseKeyColor)
+ *dst &= ~1;
+ else
+ *dst |= 1;
+ }
+ }
+ }
+ } else {
+ // TODO: Log this!
+ // Make the cursor all transparent... we really need a better fallback ;-).
+ memset(mouseTexture.getPixels(), 0, mouseTexture.h * mouseTexture.pitch);
+ }
+ }
+
+ [[iOS7AppDelegate iPhoneView] performSelectorOnMainThread:@selector(updateMouseCursor) withObject:nil waitUntilDone: YES];
+}
diff --git a/backends/platform/ios7/ios7_video.h b/backends/platform/ios7/ios7_video.h
new file mode 100644
index 0000000000..799cd21a94
--- /dev/null
+++ b/backends/platform/ios7/ios7_video.h
@@ -0,0 +1,129 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef BACKENDS_PLATFORM_IPHONE_IPHONE_VIDEO_H
+#define BACKENDS_PLATFORM_IPHONE_IPHONE_VIDEO_H
+
+#include <UIKit/UIKit.h>
+#include <Foundation/Foundation.h>
+#include <QuartzCore/QuartzCore.h>
+
+#include <OpenGLES/EAGL.h>
+#include <OpenGLES/ES2/gl.h>
+#include <OpenGLES/ES2/glext.h>
+
+#include "ios7_keyboard.h"
+#include "ios7_common.h"
+
+#include "common/list.h"
+#import "graphics/scaler.h"
+
+typedef struct {
+ GLfloat x, y;
+ GLfloat u,v;
+} GLVertex;
+
+@interface iPhoneView : UIView {
+ VideoContext _videoContext;
+
+ Common::List<InternalEvent> _events;
+ NSLock *_eventLock;
+ SoftKeyboard *_keyboardView;
+
+ EAGLContext *_context;
+ GLuint _viewRenderbuffer;
+ GLuint _viewFramebuffer;
+ GLuint _screenTexture;
+ GLuint _overlayTexture;
+ GLuint _mouseCursorTexture;
+
+ GLuint _vertexShader;
+ GLuint _fragmentShader;
+
+ GLuint _vertexBuffer;
+
+ GLuint _screenSizeSlot;
+ GLuint _textureSlot;
+ GLuint _shakeSlot;
+
+ GLuint _positionSlot;
+ GLuint _textureCoordSlot;
+
+ GLint _renderBufferWidth;
+ GLint _renderBufferHeight;
+
+ GLVertex _gameScreenCoords[4];
+ CGRect _gameScreenRect;
+
+ GLVertex _overlayCoords[4];
+ CGRect _overlayRect;
+
+ GLVertex _mouseCoords[4];
+
+ GLint _mouseHotspotX, _mouseHotspotY;
+ GLint _mouseWidth, _mouseHeight;
+ GLfloat _mouseScaleX, _mouseScaleY;
+
+ int _scaledShakeOffsetY;
+
+ UITouch *_firstTouch;
+ UITouch *_secondTouch;
+
+ uint8_t *_scalerMemorySrc;
+ uint8_t *_scalerMemoryDst;
+ size_t _scalerMemorySrcSize;
+ size_t _scalerMemoryDstSize;
+ int _scalerScale;
+ ScalerProc *_scaler;
+}
+
+- (id)initWithFrame:(struct CGRect)frame;
+
+- (VideoContext *)getVideoContext;
+
+- (void)createScreenTexture;
+- (void)initSurface;
+- (void)setViewTransformation;
+
+- (void)setGraphicsMode;
+
+- (void)updateSurface;
+- (void)updateMainSurface;
+- (void)updateOverlaySurface;
+- (void)updateMouseSurface;
+- (void)clearColorBuffer;
+
+- (void)notifyMouseMove;
+- (void)updateMouseCursorScaling;
+- (void)updateMouseCursor;
+
+- (void)deviceOrientationChanged:(UIDeviceOrientation)orientation;
+
+- (void)applicationSuspend;
+
+- (void)applicationResume;
+
+- (bool)fetchEvent:(InternalEvent *)event;
+
+@end
+
+#endif
diff --git a/backends/platform/ios7/ios7_video.mm b/backends/platform/ios7/ios7_video.mm
new file mode 100644
index 0000000000..e3c3edf06b
--- /dev/null
+++ b/backends/platform/ios7/ios7_video.mm
@@ -0,0 +1,996 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+// Disable symbol overrides so that we can use system headers.
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
+#include "ios7_video.h"
+
+#include "graphics/colormasks.h"
+#include "common/system.h"
+#import "iOS7AppDelegate.h"
+
+static int g_fullWidth;
+static int g_fullHeight;
+
+static int g_needsScreenUpdate = 0;
+
+#if 0
+static long g_lastTick = 0;
+static int g_frames = 0;
+#endif
+
+#define printOpenGLError() printOglError(__FILE__, __LINE__)
+
+int printOglError(const char *file, int line) {
+ int retCode = 0;
+
+ // returns 1 if an OpenGL error occurred, 0 otherwise.
+ GLenum glErr = glGetError();
+ while (glErr != GL_NO_ERROR) {
+ fprintf(stderr, "glError: %u (%s: %d)\n", glErr, file, line);
+ retCode = 1;
+ glErr = glGetError();
+ }
+ return retCode;
+}
+
+bool iOS7_isBigDevice() {
+ return UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad;
+}
+
+void iOS7_updateScreen() {
+ //printf("Mouse: (%i, %i)\n", mouseX, mouseY);
+ if (!g_needsScreenUpdate) {
+ g_needsScreenUpdate = 1;
+ [[iOS7AppDelegate iPhoneView] performSelectorOnMainThread:@selector(updateSurface) withObject:nil waitUntilDone: NO];
+ }
+}
+
+bool iOS7_fetchEvent(InternalEvent *event) {
+ return [[iOS7AppDelegate iPhoneView] fetchEvent:event];
+}
+
+uint getSizeNextPOT(uint size) {
+ if ((size & (size - 1)) || !size) {
+ int log = 0;
+
+ while (size >>= 1)
+ ++log;
+
+ size = (2 << log);
+ }
+
+ return size;
+}
+
+@implementation iPhoneView
+
++ (Class)layerClass {
+ return [CAEAGLLayer class];
+}
+
+- (VideoContext *)getVideoContext {
+ return &_videoContext;
+}
+
+- (void)createContext {
+ CAEAGLLayer *eaglLayer = (CAEAGLLayer *)self.layer;
+
+ eaglLayer.opaque = YES;
+ eaglLayer.drawableProperties = @{
+ kEAGLDrawablePropertyRetainedBacking: @NO,
+ kEAGLDrawablePropertyColorFormat: kEAGLColorFormatRGB565
+ };
+
+ _context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
+
+ // In case creating the OpenGL ES context failed, we will error out here.
+ if (_context == nil) {
+ fprintf(stderr, "Could not create OpenGL ES context\n");
+ exit(-1);
+ }
+
+ if ([EAGLContext setCurrentContext:_context]) {
+ // glEnableClientState(GL_TEXTURE_COORD_ARRAY); printOpenGLError();
+ // glEnableClientState(GL_VERTEX_ARRAY); printOpenGLError();
+ [self setupOpenGL];
+ }
+}
+
+- (void)setupOpenGL {
+ [self setupFramebuffer];
+ [self createOverlaySurface];
+ [self compileShaders];
+ [self setupVBOs];
+ [self setupTextures];
+
+ [self finishGLSetup];
+}
+
+- (void)finishGLSetup {
+ glViewport(0, 0, _renderBufferWidth, _renderBufferHeight); printOpenGLError();
+ glClearColor(0.0f, 0.0f, 0.0f, 1.0f); printOpenGLError();
+
+ glUniform2f(_screenSizeSlot, _renderBufferWidth, _renderBufferHeight);
+
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+}
+
+- (void)freeOpenGL {
+ [self deleteTextures];
+ [self deleteVBOs];
+ [self deleteShaders];
+ [self deleteFramebuffer];
+}
+
+- (void)rebuildFrameBuffer {
+ [self deleteFramebuffer];
+ [self setupFramebuffer];
+ [self finishGLSetup];
+}
+
+- (void)setupFramebuffer {
+ glGenRenderbuffers(1, &_viewRenderbuffer);
+ printOpenGLError();
+ glBindRenderbuffer(GL_RENDERBUFFER, _viewRenderbuffer);
+ printOpenGLError();
+ [_context renderbufferStorage:GL_RENDERBUFFER fromDrawable:(id <EAGLDrawable>) self.layer];
+
+ glGenFramebuffers(1, &_viewFramebuffer);
+ printOpenGLError();
+ glBindFramebuffer(GL_FRAMEBUFFER, _viewFramebuffer);
+ printOpenGLError();
+ glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, _viewRenderbuffer);
+ printOpenGLError();
+
+ // Retrieve the render buffer size. This *should* match the frame size,
+ // i.e. g_fullWidth and g_fullHeight.
+ glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &_renderBufferWidth);
+ printOpenGLError();
+ glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &_renderBufferHeight);
+ printOpenGLError();
+
+ if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
+ NSLog(@"Failed to make complete framebuffer object %x.", glCheckFramebufferStatus(GL_FRAMEBUFFER));
+ return;
+ }
+}
+
+- (void)createOverlaySurface {
+ uint overlayWidth = (uint) MAX(_renderBufferWidth, _renderBufferHeight);
+ uint overlayHeight = (uint) MIN(_renderBufferWidth, _renderBufferHeight);
+
+ if (iOS7_isBigDevice()) {
+ // On really big displays, like the iPad Pro, we scale the interface down
+ // so that the controls are not too small..
+ while (overlayHeight > 1024) {
+ overlayWidth /= 2;
+ overlayHeight /= 2;
+ }
+ }
+ else {
+ // On small devices, we force the user interface to use the small theme
+ while (overlayHeight > 480) {
+ overlayWidth /= 2;
+ overlayHeight /= 2;
+ }
+ }
+
+ _videoContext.overlayWidth = overlayWidth;
+ _videoContext.overlayHeight = overlayHeight;
+
+ uint overlayTextureWidthPOT = getSizeNextPOT(overlayWidth);
+ uint overlayTextureHeightPOT = getSizeNextPOT(overlayHeight);
+
+ // Since the overlay size won't change the whole run, we can
+ // precalculate the texture coordinates for the overlay texture here
+ // and just use it later on.
+ GLfloat u = _videoContext.overlayWidth / (GLfloat) overlayTextureWidthPOT;
+ GLfloat v = _videoContext.overlayHeight / (GLfloat) overlayTextureHeightPOT;
+ _overlayCoords[0].x = 0; _overlayCoords[0].y = 0; _overlayCoords[0].u = 0; _overlayCoords[0].v = 0;
+ _overlayCoords[1].x = 0; _overlayCoords[1].y = 0; _overlayCoords[1].u = u; _overlayCoords[1].v = 0;
+ _overlayCoords[2].x = 0; _overlayCoords[2].y = 0; _overlayCoords[2].u = 0; _overlayCoords[2].v = v;
+ _overlayCoords[3].x = 0; _overlayCoords[3].y = 0; _overlayCoords[3].u = u; _overlayCoords[3].v = v;
+
+ _videoContext.overlayTexture.create((uint16) overlayTextureWidthPOT, (uint16) overlayTextureHeightPOT, Graphics::createPixelFormat<5551>());
+}
+
+- (void)deleteFramebuffer {
+ glDeleteRenderbuffers(1, &_viewRenderbuffer);
+ glDeleteFramebuffers(1, &_viewFramebuffer);
+}
+
+- (void)setupVBOs {
+ glGenBuffers(1, &_vertexBuffer);
+ glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer);
+}
+
+- (void)deleteVBOs {
+ glDeleteBuffers(1, &_vertexBuffer);
+}
+
+- (GLuint)compileShader:(const char*)shaderPrg withType:(GLenum)shaderType {
+ GLuint shaderHandle = glCreateShader(shaderType);
+
+ int shaderPrgLength = strlen(shaderPrg);
+ glShaderSource(shaderHandle, 1, &shaderPrg, &shaderPrgLength);
+
+ glCompileShader(shaderHandle);
+
+ GLint compileSuccess;
+ glGetShaderiv(shaderHandle, GL_COMPILE_STATUS, &compileSuccess);
+ if (compileSuccess == GL_FALSE) {
+ GLchar messages[256];
+ glGetShaderInfoLog(shaderHandle, sizeof(messages), 0, &messages[0]);
+ NSString *messageString = [NSString stringWithUTF8String:messages];
+ NSLog(@"%@", messageString);
+ exit(1);
+ }
+
+ return shaderHandle;
+}
+
+- (void)compileShaders {
+ const char *vertexPrg =
+ "uniform vec2 ScreenSize;"
+ "uniform float Shake;"
+ ""
+ "attribute vec2 Position;"
+ "attribute vec2 TexCoord;"
+ ""
+ "varying vec4 DestColor;"
+ "varying vec2 o_TexCoord;"
+ ""
+ "void main(void) {"
+ " DestColor = vec4(Position.x, Position.y, 0, 1);"
+ " o_TexCoord = TexCoord;"
+ " gl_Position = vec4((Position.x / ScreenSize.x) * 2.0 - 1.0, (1.0 - (Position.y + Shake) / ScreenSize.y) * 2.0 - 1.0, 0, 1);"
+ "}";
+
+ const char *fragmentPrg =
+ "uniform sampler2D Texture;"
+ ""
+ "varying lowp vec4 DestColor;"
+ "varying lowp vec2 o_TexCoord;"
+ ""
+ "void main(void) {"
+ " gl_FragColor = texture2D(Texture, o_TexCoord);"
+ "}";
+
+ _vertexShader = [self compileShader:vertexPrg withType:GL_VERTEX_SHADER];
+ _fragmentShader = [self compileShader:fragmentPrg withType:GL_FRAGMENT_SHADER];
+
+ GLuint programHandle = glCreateProgram();
+ glAttachShader(programHandle, _vertexShader);
+ glAttachShader(programHandle, _fragmentShader);
+ glLinkProgram(programHandle);
+
+ GLint linkSuccess;
+ glGetProgramiv(programHandle, GL_LINK_STATUS, &linkSuccess);
+ if (linkSuccess == GL_FALSE) {
+ printOpenGLError();
+ exit(1);
+ }
+
+ glUseProgram(programHandle);
+
+ _screenSizeSlot = (GLuint) glGetUniformLocation(programHandle, "ScreenSize");
+ _textureSlot = (GLuint) glGetUniformLocation(programHandle, "Texture");
+ _shakeSlot = (GLuint) glGetUniformLocation(programHandle, "Shake");
+
+ _positionSlot = (GLuint) glGetAttribLocation(programHandle, "Position");
+ _textureCoordSlot = (GLuint) glGetAttribLocation(programHandle, "TexCoord");
+
+ glEnableVertexAttribArray(_positionSlot);
+ glEnableVertexAttribArray(_textureCoordSlot);
+
+ glUniform1i(_textureSlot, 0); printOpenGLError();
+}
+
+- (void)deleteShaders {
+ glDeleteShader(_vertexShader);
+ glDeleteShader(_fragmentShader);
+}
+
+- (void)setupTextures {
+ glGenTextures(1, &_screenTexture); printOpenGLError();
+ glGenTextures(1, &_overlayTexture); printOpenGLError();
+ glGenTextures(1, &_mouseCursorTexture); printOpenGLError();
+
+ [self setGraphicsMode];
+}
+
+- (void)deleteTextures {
+ if (_screenTexture) {
+ glDeleteTextures(1, &_screenTexture); printOpenGLError();
+ _screenTexture = 0;
+ }
+ if (_overlayTexture) {
+ glDeleteTextures(1, &_overlayTexture); printOpenGLError();
+ _overlayTexture = 0;
+ }
+ if (_mouseCursorTexture) {
+ glDeleteTextures(1, &_mouseCursorTexture); printOpenGLError();
+ _mouseCursorTexture = 0;
+ }
+}
+
+- (void)setupGestureRecognizers {
+ UISwipeGestureRecognizer *swipeRight = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(twoFingersSwipeRight:)];
+ swipeRight.direction = UISwipeGestureRecognizerDirectionRight;
+ swipeRight.numberOfTouchesRequired = 2;
+ swipeRight.delaysTouchesBegan = NO;
+ swipeRight.delaysTouchesEnded = NO;
+
+ UISwipeGestureRecognizer *swipeLeft = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(twoFingersSwipeLeft:)];
+ swipeLeft.direction = UISwipeGestureRecognizerDirectionLeft;
+ swipeLeft.numberOfTouchesRequired = 2;
+ swipeLeft.delaysTouchesBegan = NO;
+ swipeLeft.delaysTouchesEnded = NO;
+
+ UISwipeGestureRecognizer *swipeUp = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(twoFingersSwipeUp:)];
+ swipeUp.direction = UISwipeGestureRecognizerDirectionUp;
+ swipeUp.numberOfTouchesRequired = 2;
+ swipeUp.delaysTouchesBegan = NO;
+ swipeUp.delaysTouchesEnded = NO;
+
+ UISwipeGestureRecognizer *swipeDown = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(twoFingersSwipeDown:)];
+ swipeDown.direction = UISwipeGestureRecognizerDirectionDown;
+ swipeDown.numberOfTouchesRequired = 2;
+ swipeDown.delaysTouchesBegan = NO;
+ swipeDown.delaysTouchesEnded = NO;
+
+ UITapGestureRecognizer *doubleTapTwoFingers = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(twoFingersDoubleTap:)];
+ doubleTapTwoFingers.numberOfTapsRequired = 2;
+ doubleTapTwoFingers.numberOfTouchesRequired = 2;
+ doubleTapTwoFingers.delaysTouchesBegan = NO;
+ doubleTapTwoFingers.delaysTouchesEnded = NO;
+
+ [self addGestureRecognizer:swipeRight];
+ [self addGestureRecognizer:swipeLeft];
+ [self addGestureRecognizer:swipeUp];
+ [self addGestureRecognizer:swipeDown];
+ [self addGestureRecognizer:doubleTapTwoFingers];
+
+ [swipeRight release];
+ [swipeLeft release];
+ [swipeUp release];
+ [swipeDown release];
+ [doubleTapTwoFingers release];
+}
+
+- (id)initWithFrame:(struct CGRect)frame {
+ self = [super initWithFrame: frame];
+
+#if defined(USE_SCALERS) || defined(USE_HQ_SCALERS)
+ InitScalers(565);
+#endif
+
+ [self setupGestureRecognizers];
+
+ g_fullWidth = (int)MAX(frame.size.width, frame.size.height);
+ g_fullHeight = (int)MIN(frame.size.width, frame.size.height);
+
+ [self setContentScaleFactor:[[UIScreen mainScreen] scale]];
+
+ _scalerMemorySrc = NULL;
+ _scalerMemoryDst = NULL;
+ _scalerMemorySrcSize = 0;
+ _scalerMemoryDstSize = 0;
+ _scaler = NULL;
+ _scalerScale = 1;
+
+ _keyboardView = nil;
+ _screenTexture = 0;
+ _overlayTexture = 0;
+ _mouseCursorTexture = 0;
+
+ _scaledShakeOffsetY = 0;
+
+ _firstTouch = NULL;
+ _secondTouch = NULL;
+
+ _eventLock = [[NSLock alloc] init];
+
+ memset(_gameScreenCoords, 0, sizeof(GLVertex) * 4);
+ memset(_overlayCoords, 0, sizeof(GLVertex) * 4);
+ memset(_mouseCoords, 0, sizeof(GLVertex) * 4);
+
+ // Initialize the OpenGL ES context
+ [self createContext];
+
+ return self;
+}
+
+- (void)dealloc {
+ [_keyboardView release];
+
+ _videoContext.screenTexture.free();
+ _videoContext.overlayTexture.free();
+ _videoContext.mouseTexture.free();
+
+ free(_scalerMemorySrc);
+ free(_scalerMemoryDst);
+
+ [_eventLock release];
+ [super dealloc];
+}
+
+- (void)setFilterModeForTexture:(GLuint)tex {
+ if (!tex)
+ return;
+
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, tex); printOpenGLError();
+
+ GLint filter = GL_LINEAR;
+
+ switch (_videoContext.graphicsMode) {
+ case kGraphicsModeNone:
+ filter = GL_NEAREST;
+ break;
+
+ case kGraphicsModeLinear:
+ case kGraphicsMode2xSaI:
+ case kGraphicsModeSuper2xSaI:
+ case kGraphicsModeSuperEagle:
+ case kGraphicsModeAdvMame2x:
+ case kGraphicsModeAdvMame3x:
+ case kGraphicsModeHQ2x:
+ case kGraphicsModeHQ3x:
+ case kGraphicsModeTV2x:
+ case kGraphicsModeDotMatrix:
+ filter = GL_LINEAR;
+ break;
+ }
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter); printOpenGLError();
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter); printOpenGLError();
+ // We use GL_CLAMP_TO_EDGE here to avoid artifacts when linear filtering
+ // is used. If we would not use this for example the cursor in Loom would
+ // have a line/border artifact on the right side of the covered rect.
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); printOpenGLError();
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); printOpenGLError();
+}
+
+- (void)setScaler {
+ ScalerProc *scaler = NULL;
+ int scalerScale = 1;
+
+ switch (_videoContext.graphicsMode) {
+ case kGraphicsModeLinear:
+ break;
+
+ case kGraphicsModeNone:
+ break;
+#ifdef USE_SCALERS
+ case kGraphicsMode2xSaI:
+ scaler = _2xSaI;
+ scalerScale = 2;
+ break;
+
+ case kGraphicsModeSuper2xSaI:
+ scaler = Super2xSaI;
+ scalerScale = 2;
+ break;
+
+ case kGraphicsModeSuperEagle:
+ scaler = SuperEagle;
+ scalerScale = 2;
+ break;
+
+ case kGraphicsModeAdvMame2x:
+ scaler = AdvMame2x;
+ scalerScale = 2;
+ break;
+
+ case kGraphicsModeAdvMame3x:
+ scaler = AdvMame3x;
+ scalerScale = 3;
+ break;
+
+#ifdef USE_HQ_SCALERS
+ case kGraphicsModeHQ2x:
+ scaler = HQ2x;
+ scalerScale = 2;
+ break;
+
+ case kGraphicsModeHQ3x:
+ scaler = HQ3x;
+ scalerScale = 3;
+ break;
+#endif
+
+ case kGraphicsModeTV2x:
+ scaler = TV2x;
+ scalerScale = 2;
+ break;
+
+ case kGraphicsModeDotMatrix:
+ scaler = DotMatrix;
+ scalerScale = 2;
+ break;
+#endif
+
+ default:
+ break;
+ }
+
+ _scaler = scaler;
+ _scalerScale = scalerScale;
+}
+
+- (void)setGraphicsMode {
+ [self setFilterModeForTexture:_screenTexture];
+ [self setFilterModeForTexture:_overlayTexture];
+ [self setFilterModeForTexture:_mouseCursorTexture];
+ [self setScaler];
+}
+
+- (void)updateSurface {
+ if (!g_needsScreenUpdate) {
+ return;
+ }
+ g_needsScreenUpdate = 0;
+
+ glClear(GL_COLOR_BUFFER_BIT); printOpenGLError();
+
+ [self updateMainSurface];
+
+ if (_videoContext.overlayVisible)
+ [self updateOverlaySurface];
+
+ if (_videoContext.mouseIsVisible)
+ [self updateMouseSurface];
+
+ [_context presentRenderbuffer:GL_RENDERBUFFER];
+ glFinish();
+}
+
+- (void)notifyMouseMove {
+ const GLint mouseX = (GLint)(_videoContext.mouseX * _mouseScaleX) - _mouseHotspotX;
+ const GLint mouseY = (GLint)(_videoContext.mouseY * _mouseScaleY) - _mouseHotspotY;
+
+ _mouseCoords[0].x = _mouseCoords[2].x = mouseX;
+ _mouseCoords[0].y = _mouseCoords[1].y = mouseY;
+ _mouseCoords[1].x = _mouseCoords[3].x = mouseX + _mouseWidth;
+ _mouseCoords[2].y = _mouseCoords[3].y = mouseY + _mouseHeight;
+}
+
+- (void)updateMouseCursorScaling {
+ CGRect *rect;
+ int maxWidth, maxHeight;
+
+ if (!_videoContext.overlayVisible) {
+ rect = &_gameScreenRect;
+ maxWidth = _videoContext.screenWidth;
+ maxHeight = _videoContext.screenHeight;
+ } else {
+ rect = &_overlayRect;
+ maxWidth = _videoContext.overlayWidth;
+ maxHeight = _videoContext.overlayHeight;
+ }
+
+ if (!maxWidth || !maxHeight) {
+ printf("WARNING: updateMouseCursorScaling called when screen was not ready (%d)!\n", _videoContext.overlayVisible);
+ return;
+ }
+
+ _mouseScaleX = CGRectGetWidth(*rect) / (GLfloat)maxWidth;
+ _mouseScaleY = CGRectGetHeight(*rect) / (GLfloat)maxHeight;
+
+ _mouseWidth = (GLint)(_videoContext.mouseWidth * _mouseScaleX);
+ _mouseHeight = (GLint)(_videoContext.mouseHeight * _mouseScaleY);
+
+ _mouseHotspotX = (GLint)(_videoContext.mouseHotspotX * _mouseScaleX);
+ _mouseHotspotY = (GLint)(_videoContext.mouseHotspotY * _mouseScaleY);
+
+ // We subtract the screen offset to the hotspot here to simplify the
+ // screen offset handling in the mouse code. Note the subtraction here
+ // makes sure that the offset actually gets added to the mouse position,
+ // since the hotspot offset is substracted from the position.
+ _mouseHotspotX -= (GLint)CGRectGetMinX(*rect);
+ _mouseHotspotY -= (GLint)CGRectGetMinY(*rect);
+
+ // FIXME: For now we also adapt the mouse position here. In reality we
+ // would be better off to also adjust the event position when switching
+ // from overlay to game screen or vica versa.
+ [self notifyMouseMove];
+}
+
+- (void)updateMouseCursor {
+ [self updateMouseCursorScaling];
+
+ _mouseCoords[1].u = _mouseCoords[3].u = (_videoContext.mouseWidth - 1) / (GLfloat)_videoContext.mouseTexture.w;
+ _mouseCoords[2].v = _mouseCoords[3].v = (_videoContext.mouseHeight - 1) / (GLfloat)_videoContext.mouseTexture.h;
+
+ [self setFilterModeForTexture:_mouseCursorTexture];
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, _videoContext.mouseTexture.w, _videoContext.mouseTexture.h, 0, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, _videoContext.mouseTexture.getPixels()); printOpenGLError();
+}
+
+- (void)updateMainSurface {
+ glBufferData(GL_ARRAY_BUFFER, sizeof(GLVertex) * 4, _gameScreenCoords, GL_STATIC_DRAW);
+ glVertexAttribPointer(_positionSlot, 2, GL_FLOAT, GL_FALSE, sizeof(GLVertex), 0);
+ glVertexAttribPointer(_textureCoordSlot, 2, GL_FLOAT, GL_FALSE, sizeof(GLVertex), (GLvoid *) (sizeof(GLfloat) * 2));
+
+ [self setFilterModeForTexture:_screenTexture];
+
+ // Unfortunately we have to update the whole texture every frame, since glTexSubImage2D is actually slower in all cases
+ // due to the iPhone internals having to convert the whole texture back from its internal format when used.
+ // In the future we could use several tiled textures instead.
+ if (_scaler) {
+ size_t neededSrcMemorySize = (size_t) (_videoContext.screenTexture.pitch * (_videoContext.screenTexture.h + 4));
+ size_t neededDstMemorySize = (size_t) (_videoContext.screenTexture.pitch * (_videoContext.screenTexture.h + 4) * _scalerScale * _scalerScale);
+ if (neededSrcMemorySize != _scalerMemorySrcSize) {
+ _scalerMemorySrc = (uint8_t *) realloc(_scalerMemorySrc, neededSrcMemorySize);
+ _scalerMemorySrcSize = neededSrcMemorySize;
+ }
+ if (neededDstMemorySize != _scalerMemoryDstSize) {
+ _scalerMemoryDst = (uint8_t *) realloc(_scalerMemoryDst, neededDstMemorySize);
+ _scalerMemoryDstSize = neededDstMemorySize;
+ }
+
+ // Clear two lines before
+ memset(_scalerMemorySrc, 0, (size_t) (_videoContext.screenTexture.pitch * 2));
+ // Copy original buffer
+ memcpy(_scalerMemorySrc + _videoContext.screenTexture.pitch * 2, _videoContext.screenTexture.getPixels(), _videoContext.screenTexture.pitch * _videoContext.screenTexture.h);
+ // Clear two lines after
+ memset(_scalerMemorySrc + _videoContext.screenTexture.pitch * (2 + _videoContext.screenTexture.h), 0, (size_t) (_videoContext.screenTexture.pitch * 2));
+ // Apply scaler
+ _scaler(_scalerMemorySrc + _videoContext.screenTexture.pitch * 2,
+ _videoContext.screenTexture.pitch,
+ _scalerMemoryDst,
+ (uint32) (_videoContext.screenTexture.pitch * _scalerScale),
+ _videoContext.screenTexture.w,
+ _videoContext.screenTexture.h);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, _videoContext.screenTexture.w * _scalerScale, _videoContext.screenTexture.h * _scalerScale, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, _scalerMemoryDst); printOpenGLError();
+ }
+ else {
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, _videoContext.screenTexture.w, _videoContext.screenTexture.h, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, _videoContext.screenTexture.getPixels()); printOpenGLError();
+ }
+
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); printOpenGLError();
+}
+
+- (void)updateOverlaySurface {
+ glBufferData(GL_ARRAY_BUFFER, sizeof(GLVertex) * 4, _overlayCoords, GL_STATIC_DRAW);
+ glVertexAttribPointer(_positionSlot, 2, GL_FLOAT, GL_FALSE, sizeof(GLVertex), 0);
+ glVertexAttribPointer(_textureCoordSlot, 2, GL_FLOAT, GL_FALSE, sizeof(GLVertex), (GLvoid *) (sizeof(GLfloat) * 2));
+
+ [self setFilterModeForTexture:_overlayTexture];
+
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, _videoContext.overlayTexture.w, _videoContext.overlayTexture.h, 0, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, _videoContext.overlayTexture.getPixels()); printOpenGLError();
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); printOpenGLError();
+}
+
+- (void)updateMouseSurface {
+ glBufferData(GL_ARRAY_BUFFER, sizeof(GLVertex) * 4, _mouseCoords, GL_STATIC_DRAW);
+ glVertexAttribPointer(_positionSlot, 2, GL_FLOAT, GL_FALSE, sizeof(GLVertex), 0);
+ glVertexAttribPointer(_textureCoordSlot, 2, GL_FLOAT, GL_FALSE, sizeof(GLVertex), (GLvoid *) (sizeof(GLfloat) * 2));
+
+ glBindTexture(GL_TEXTURE_2D, _mouseCursorTexture); printOpenGLError();
+
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); printOpenGLError();
+}
+
+- (void)createScreenTexture {
+ const uint screenTexWidth = getSizeNextPOT(_videoContext.screenWidth);
+ const uint screenTexHeight = getSizeNextPOT(_videoContext.screenHeight);
+
+ _gameScreenCoords[1].u = _gameScreenCoords[3].u = _videoContext.screenWidth / (GLfloat)screenTexWidth;
+ _gameScreenCoords[2].v = _gameScreenCoords[3].v = _videoContext.screenHeight / (GLfloat)screenTexHeight;
+
+ _videoContext.screenTexture.create((uint16) screenTexWidth, (uint16) screenTexHeight, Graphics::createPixelFormat<565>());
+}
+
+- (void)initSurface {
+ if (_context) {
+ [self rebuildFrameBuffer];
+ }
+
+ BOOL isLandscape = (self.bounds.size.width > self.bounds.size.height); // UIDeviceOrientationIsLandscape([[UIDevice currentDevice] orientation]);
+
+ int screenWidth, screenHeight;
+ if (isLandscape) {
+ screenWidth = MAX(_renderBufferWidth, _renderBufferHeight);
+ screenHeight = MIN(_renderBufferWidth, _renderBufferHeight);
+ }
+ else {
+ screenWidth = MIN(_renderBufferWidth, _renderBufferHeight);
+ screenHeight = MAX(_renderBufferWidth, _renderBufferHeight);
+ }
+
+ glBindRenderbuffer(GL_RENDERBUFFER, _viewRenderbuffer); printOpenGLError();
+
+ [self clearColorBuffer];
+
+ GLfloat adjustedWidth = _videoContext.screenWidth;
+ GLfloat adjustedHeight = _videoContext.screenHeight;
+ if (_videoContext.asprectRatioCorrection) {
+ if (_videoContext.screenWidth == 320 && _videoContext.screenHeight == 200)
+ adjustedHeight = 240;
+ else if (_videoContext.screenWidth == 640 && _videoContext.screenHeight == 400)
+ adjustedHeight = 480;
+ }
+
+ float overlayPortraitRatio;
+
+ if (isLandscape) {
+ GLfloat gameScreenRatio = adjustedWidth / adjustedHeight;
+ GLfloat screenRatio = (GLfloat)screenWidth / (GLfloat)screenHeight;
+
+ // These are the width/height according to the portrait layout!
+ int rectWidth, rectHeight;
+ int xOffset, yOffset;
+
+ if (gameScreenRatio < screenRatio) {
+ // When the game screen ratio is less than the screen ratio
+ // we need to scale the width, since the game screen was higher
+ // compared to the width than our output screen is.
+ rectWidth = (int)(screenHeight * gameScreenRatio);
+ rectHeight = screenHeight;
+ xOffset = (screenWidth - rectWidth) / 2;
+ yOffset = 0;
+ } else {
+ // When the game screen ratio is bigger than the screen ratio
+ // we need to scale the height, since the game screen was wider
+ // compared to the height than our output screen is.
+ rectWidth = screenWidth;
+ rectHeight = (int)(screenWidth / gameScreenRatio);
+ xOffset = 0;
+ yOffset = (screenHeight - rectHeight) / 2;
+ }
+
+ [_keyboardView hideKeyboard];
+
+ //printf("Rect: %i, %i, %i, %i\n", xOffset, yOffset, rectWidth, rectHeight);
+ _gameScreenRect = CGRectMake(xOffset, yOffset, rectWidth, rectHeight);
+ overlayPortraitRatio = 1.0f;
+ } else {
+ GLfloat ratio = adjustedHeight / adjustedWidth;
+ int height = (int)(screenWidth * ratio);
+ //printf("Making rect (%u, %u)\n", screenWidth, height);
+ _gameScreenRect = CGRectMake(0, 0, screenWidth, height);
+
+ CGRect keyFrame = CGRectMake(0.0f, 0.0f, 0.0f, 0.0f);
+ if (_keyboardView == nil) {
+ _keyboardView = [[SoftKeyboard alloc] initWithFrame:keyFrame];
+ [_keyboardView setInputDelegate:self];
+ [self addSubview:[_keyboardView inputView]];
+ [self addSubview: _keyboardView];
+ }
+
+ [_keyboardView showKeyboard];
+ overlayPortraitRatio = (_videoContext.overlayHeight * ratio) / _videoContext.overlayWidth;
+ }
+ _overlayRect = CGRectMake(0, 0, screenWidth, screenHeight * overlayPortraitRatio);
+
+ _gameScreenCoords[0].x = _gameScreenCoords[2].x = CGRectGetMinX(_gameScreenRect);
+ _gameScreenCoords[0].y = _gameScreenCoords[1].y = CGRectGetMinY(_gameScreenRect);
+ _gameScreenCoords[1].x = _gameScreenCoords[3].x = CGRectGetMaxX(_gameScreenRect);
+ _gameScreenCoords[2].y = _gameScreenCoords[3].y = CGRectGetMaxY(_gameScreenRect);
+
+ _overlayCoords[1].x = _overlayCoords[3].x = CGRectGetMaxX(_overlayRect);
+ _overlayCoords[2].y = _overlayCoords[3].y = CGRectGetMaxY(_overlayRect);
+
+ [self setViewTransformation];
+ [self updateMouseCursorScaling];
+}
+
+- (void)setViewTransformation {
+ // Scale the shake offset according to the overlay size. We need this to
+ // adjust the overlay mouse click coordinates when an offset is set.
+ _scaledShakeOffsetY = (int)(_videoContext.shakeOffsetY / (GLfloat)_videoContext.screenHeight * CGRectGetHeight(_overlayRect));
+
+ glUniform1f(_shakeSlot, _scaledShakeOffsetY);
+}
+
+- (void)clearColorBuffer {
+ // The color buffer is triple-buffered, so we clear it multiple times right away to avid doing any glClears later.
+ int clearCount = 5;
+ while (clearCount-- > 0) {
+ glClear(GL_COLOR_BUFFER_BIT); printOpenGLError();
+ [_context presentRenderbuffer:GL_RENDERBUFFER];
+ glFinish();
+ }
+}
+
+- (void)addEvent:(InternalEvent)event {
+ [_eventLock lock];
+ _events.push_back(event);
+ [_eventLock unlock];
+}
+
+- (bool)fetchEvent:(InternalEvent *)event {
+ [_eventLock lock];
+ if (_events.empty()) {
+ [_eventLock unlock];
+ return false;
+ }
+
+ *event = *_events.begin();
+ _events.pop_front();
+ [_eventLock unlock];
+ return true;
+}
+
+- (bool)getMouseCoords:(CGPoint)point eventX:(int *)x eventY:(int *)y {
+ // We scale the input according to our scale factor to get actual screen
+ // coordinates.
+ point.x *= self.contentScaleFactor;
+ point.y *= self.contentScaleFactor;
+
+ CGRect *area;
+ int width, height, offsetY;
+ if (_videoContext.overlayVisible) {
+ area = &_overlayRect;
+ width = _videoContext.overlayWidth;
+ height = _videoContext.overlayHeight;
+ offsetY = _scaledShakeOffsetY;
+ } else {
+ area = &_gameScreenRect;
+ width = _videoContext.screenWidth;
+ height = _videoContext.screenHeight;
+ offsetY = _videoContext.shakeOffsetY;
+ }
+
+ point.x = (point.x - CGRectGetMinX(*area)) / CGRectGetWidth(*area);
+ point.y = (point.y - CGRectGetMinY(*area)) / CGRectGetHeight(*area);
+
+ *x = (int)(point.x * width);
+ // offsetY describes the translation of the screen in the upward direction,
+ // thus we need to add it here.
+ *y = (int)(point.y * height + offsetY);
+
+ if (!iOS7_touchpadModeEnabled()) {
+ // Clip coordinates
+ if (*x < 0 || *x > width || *y < 0 || *y > height)
+ return false;
+ }
+
+ return true;
+}
+
+- (void)deviceOrientationChanged:(UIDeviceOrientation)orientation {
+ [self addEvent:InternalEvent(kInputOrientationChanged, orientation, 0)];
+}
+
+- (UITouch *)secondTouchOtherTouchThan:(UITouch *)touch in:(NSSet *)set {
+ NSArray *all = [set allObjects];
+ for (UITouch *t in all) {
+ if (t != touch) {
+ return t;
+ }
+ }
+ return nil;
+}
+
+- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
+ int x, y;
+
+ NSSet *allTouches = [event allTouches];
+ if (allTouches.count == 1) {
+ _firstTouch = [allTouches anyObject];
+ CGPoint point = [_firstTouch locationInView:self];
+ if (![self getMouseCoords:point eventX:&x eventY:&y])
+ return;
+
+ [self addEvent:InternalEvent(kInputMouseDown, x, y)];
+ }
+ else if (allTouches.count == 2) {
+ _secondTouch = [self secondTouchOtherTouchThan:_firstTouch in:allTouches];
+ if (_secondTouch) {
+ CGPoint point = [_secondTouch locationInView:self];
+ if (![self getMouseCoords:point eventX:&x eventY:&y])
+ return;
+
+ [self addEvent:InternalEvent(kInputMouseSecondDown, x, y)];
+ }
+ }
+}
+
+- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
+ int x, y;
+
+ NSSet *allTouches = [event allTouches];
+ for (UITouch *touch in allTouches) {
+ if (touch == _firstTouch) {
+ CGPoint point = [touch locationInView:self];
+ if (![self getMouseCoords:point eventX:&x eventY:&y])
+ return;
+
+ [self addEvent:InternalEvent(kInputMouseDragged, x, y)];
+ } else if (touch == _secondTouch) {
+ CGPoint point = [touch locationInView:self];
+ if (![self getMouseCoords:point eventX:&x eventY:&y])
+ return;
+
+ [self addEvent:InternalEvent(kInputMouseSecondDragged, x, y)];
+ }
+ }
+}
+
+- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
+ int x, y;
+
+ NSSet *allTouches = [event allTouches];
+ if (allTouches.count == 1) {
+ UITouch *touch = [allTouches anyObject];
+ CGPoint point = [touch locationInView:self];
+ if (![self getMouseCoords:point eventX:&x eventY:&y]) {
+ return;
+ }
+
+ [self addEvent:InternalEvent(kInputMouseUp, x, y)];
+ }
+ else if (allTouches.count == 2) {
+ UITouch *touch = [[allTouches allObjects] objectAtIndex:1];
+ CGPoint point = [touch locationInView:self];
+ if (![self getMouseCoords:point eventX:&x eventY:&y])
+ return;
+
+ [self addEvent:InternalEvent(kInputMouseSecondUp, x, y)];
+ }
+ _firstTouch = nil;
+ _secondTouch = nil;
+}
+
+- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
+ _firstTouch = nil;
+ _secondTouch = nil;
+}
+
+- (void)twoFingersSwipeRight:(UISwipeGestureRecognizer *)recognizer {
+ [self addEvent:InternalEvent(kInputSwipe, kUIViewSwipeRight, 2)];
+}
+
+- (void)twoFingersSwipeLeft:(UISwipeGestureRecognizer *)recognizer {
+ [self addEvent:InternalEvent(kInputSwipe, kUIViewSwipeLeft, 2)];
+}
+
+- (void)twoFingersSwipeUp:(UISwipeGestureRecognizer *)recognizer {
+ [self addEvent:InternalEvent(kInputSwipe, kUIViewSwipeUp, 2)];
+}
+
+- (void)twoFingersSwipeDown:(UISwipeGestureRecognizer *)recognizer {
+ [self addEvent:InternalEvent(kInputSwipe, kUIViewSwipeDown, 2)];
+}
+
+- (void)twoFingersDoubleTap:(UITapGestureRecognizer *)recognizer {
+ [self addEvent:InternalEvent(kInputTap, kUIViewTapDouble, 2)];
+}
+
+- (void)handleKeyPress:(unichar)c {
+ [self addEvent:InternalEvent(kInputKeyPressed, c, 0)];
+}
+
+- (void)applicationSuspend {
+ [self addEvent:InternalEvent(kInputApplicationSuspended, 0, 0)];
+}
+
+- (void)applicationResume {
+ [self addEvent:InternalEvent(kInputApplicationResumed, 0, 0)];
+}
+
+@end
diff --git a/backends/platform/ios7/module.mk b/backends/platform/ios7/module.mk
new file mode 100644
index 0000000000..5ee4c7a601
--- /dev/null
+++ b/backends/platform/ios7/module.mk
@@ -0,0 +1,17 @@
+MODULE := backends/platform/ios7
+
+MODULE_OBJS := \
+ ios7_osys_main.o \
+ ios7_osys_events.o \
+ ios7_osys_sound.o \
+ ios7_osys_video.o \
+ ios7_main.o \
+ ios7_video.o \
+ ios7_keyboard.o \
+ iOS7ScummVMViewController.o \
+ iOS7AppDelegate.o
+
+# We don't use rules.mk but rather manually update OBJS and MODULE_DIRS.
+MODULE_OBJS := $(addprefix $(MODULE)/, $(MODULE_OBJS))
+OBJS := $(MODULE_OBJS) $(OBJS)
+MODULE_DIRS += $(sort $(dir $(MODULE_OBJS)))