diff --git a/.gitignore b/.gitignore
index 777f0de0c75a8..cc06ef55f0ad4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -216,6 +216,7 @@ stage
# Apple Xcode IDE
Xcode/
+*.xcodeproj
# gdb
.gdb_history
@@ -245,5 +246,8 @@ weather.output
.jekyll-metadata
Gemfile.lock
+# xcodegen developer id file
+xcode_dev_id.yml
+
# VERSION.txt generated by our CMakeLists.txt
VERSION.txt
diff --git a/build-data/ios/resources/AppIcon.icns b/build-data/ios/resources/AppIcon.icns
new file mode 100644
index 0000000000000..d5bf44f64ab97
Binary files /dev/null and b/build-data/ios/resources/AppIcon.icns differ
diff --git a/build-data/ios/resources/LaunchScreen.png b/build-data/ios/resources/LaunchScreen.png
new file mode 100644
index 0000000000000..260cfcc104ea4
Binary files /dev/null and b/build-data/ios/resources/LaunchScreen.png differ
diff --git a/build-data/ios/resources/LaunchScreen.storyboard b/build-data/ios/resources/LaunchScreen.storyboard
new file mode 100644
index 0000000000000..dd2564863a477
--- /dev/null
+++ b/build-data/ios/resources/LaunchScreen.storyboard
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/build-scripts/ios-xcode.sh b/build-scripts/ios-xcode.sh
new file mode 100644
index 0000000000000..ced330ae73d28
--- /dev/null
+++ b/build-scripts/ios-xcode.sh
@@ -0,0 +1,51 @@
+#!/bin/bash
+# adapted from https://marcelbraghetto.github.io/a-simple-triangle/
+
+# Given the name of a Homebrew formula, check if its installed and if not, install it.
+fetch_brew_dependency() {
+ FORMULA_NAME=$1
+
+ echo "Checking for Brew dependency: '$FORMULA_NAME'."
+
+ if brew ls --versions $FORMULA_NAME > /dev/null; then
+ echo "Dependency '$FORMULA_NAME' is already installed, continuing ..."
+ else
+ echo "Dependency '$FORMULA_NAME' is not installed, installing via Homebrew ..."
+ brew install $FORMULA_NAME
+ fi
+}
+
+fetch_brew_dependency "xcodegen"
+
+echo "Checking for build folder ..."
+if [ ! -d "../build" ]; then
+ mkdir ../build
+fi
+
+echo "Checking for libs folder ..."
+if [ ! -d "../build/libs" ]; then
+ echo "ERROR: Static SDL libraries not found at /build/libs ..."
+ echo "Reffer to /doc/COMPILING/COMPILING-XCODE.md for instructions on how to build the static libraries."
+ exit
+fi
+
+
+if [ ! -f "xcode_dev_id.yml" ]; then
+ echo "xcode_dev_id.yml not found -"
+ echo " You will need to manually specify your Development Team for signing in Xcode."
+ echo " See the following link for details:"
+ echo " https://help.apple.com/xcode/mac/current/#/dev23aab79b4"
+ echo ""
+ echo "To avoid this message in the future see the xcode_dev_id_example.yml file."
+fi
+# Invoke the xcodegen tool to create our project file.
+echo "Generating Xcode project"
+xcodegen -s xcodegen-cataclysm.yml -p ../build
+
+if [ ! -f "../build/CataclysmExperimental.xcodeproj" ]; then
+ echo "Project built successfully. Launching Xcode..."
+ open ../build/CataclysmExperimental.xcodeproj
+ exit
+else
+ echo "Error in project creation process. Review output above for errors."
+fi
\ No newline at end of file
diff --git a/build-scripts/xcode_dev_id_example.yml b/build-scripts/xcode_dev_id_example.yml
new file mode 100644
index 0000000000000..898f9ad733578
--- /dev/null
+++ b/build-scripts/xcode_dev_id_example.yml
@@ -0,0 +1,5 @@
+# For more information on setting up a development team see:
+# https://help.apple.com/developer-account/#/devf2eb157f8
+
+settings:
+ DEVELOPMENT_TEAM: XXXXXXXXXX
\ No newline at end of file
diff --git a/build-scripts/xcodegen-cataclysm.yml b/build-scripts/xcodegen-cataclysm.yml
new file mode 100644
index 0000000000000..f3316b0160ea2
--- /dev/null
+++ b/build-scripts/xcodegen-cataclysm.yml
@@ -0,0 +1,149 @@
+name: CataclysmExperimental
+
+include:
+ # see xcode_dev_id_example.yml
+ - xcode_dev_id.yml
+
+options:
+ bundleIdPrefix: com.cataclysmdda.en.cataclysm
+ groupOrdering:
+ - order: [Sources, Resources, Frameworks, Products]
+ usesTabs: false
+ indentWidth: 4
+ tabWidth: 4
+ deploymentTarget:
+ iOS: "15.0"
+
+schemes:
+ CataclysmExperimental:
+ build:
+ targets:
+ CataclysmExperimental: all
+
+targets:
+ CataclysmExperimental:
+ type: application
+ platform: iOS
+ info:
+ path: ../build/Info.plist
+ properties:
+ LSRequiresIPhoneOS: true
+ UIRequiredDeviceCapabilities: [arm64]
+ UIRequiresFullScreen: true
+ UIStatusBarHidden: true
+ UISupportedInterfaceOrientations:
+ - UIInterfaceOrientationLandscapeLeft
+ - UIInterfaceOrientationLandscapeRight
+ UILaunchStoryboardName: LaunchScreen
+ CFBundleIconFile: AppIcon.icns
+ UIApplicationSupportsIndirectInputEvents: true
+ UIFileSharingEnabled: true
+ LSSupportsOpeningDocumentsInPlace: true
+ sources:
+ - path: ../src
+ name: Sources
+ buildPhase: sources
+ includes:
+ - "**/swift/*.swift"
+ - "**/third-party/imgui/*.cpp"
+ - "**/third-party/imgui/*.h"
+ - "*.cpp"
+ - "*.h"
+ excludes:
+ - "*.txt"
+ - "*.in"
+ - "*.rc"
+ - "*.cmake"
+ - "**/third-party/flatbuffers/**"
+ - "**/third-party/ghc/**"
+ - "**/third-party/imtui/**"
+ - "**/third-party/pinyin/**"
+ - "**/chkjson/**"
+ - path: ../build-data/ios/resources/LaunchScreen.storyboard
+ group: Resources
+ - path: ../build-data/ios/resources/AppIcon.icns
+ group: Resources
+ buildPhase: resources
+ - path: ../build-data/ios/resources/LaunchScreen.png
+ group: Resources
+ buildPhase: resources
+ # TODO: Imitate cmake selection for data and gfx
+ - path: ../data
+ type: folder
+ group: Resources
+ buildPhase: resources
+ - path: ../gfx
+ type: folder
+ group: Resources
+ buildPhase: resources
+ - path: ../ios
+ type: folder
+ group: Resources
+ buildPhase: resources
+ settings:
+ base:
+ CLANG_CXX_LANGUAGE_STANDARD: c++17
+ CMAKE_CXX_STANDARD: 17
+ CMAKE_CXX_STANDARD_REQUIRED: ON
+ CMAKE_CXX_EXTENSIONS: OFF
+ CLANG_WARN_DOCUMENTATION_COMMENTS: false
+ CLANG_ENABLE_MODULES: YES
+ GCC_WARN_64_TO_32_BIT_CONVERSION: No
+ SWIFT_OBJC_INTEROP_MODE: objcxx
+ HEADER_SEARCH_PATHS:
+ - ../src/third-party/**
+ - ../build/libs/headers
+ LIBRARY_SEARCH_PATHS:
+ - ../build/libs/
+ - $(inherited)
+ GCC_PREPROCESSOR_DEFINITIONS:
+ - GIT_VERSION
+ - TILES
+ - BACKTRACE
+ - SDL_SOUND
+ - $(inherited)
+ OTHER_CPLUSPLUSFLAGS:
+ #- "-Werror"
+ - "-Wall -Wextra -Wformat-signedness -Wlogical-op -Wmissing-declarations -Wmissing-noreturn"
+ - "-Wnon-virtual-dtor -Wold-style-cast -Woverloaded-virtual -Wpedantic -Wsuggest-override"
+ - "-Wunused-macros -Wzero-as-null-pointer-constant -Wno-unknown-warning-option -Wno-nullability-extension -Wredundant-decls"
+ - "-fsigned-char"
+ OTHER_LDFLAGS:
+ - "-Wl,-ld_classic"
+ Debug:
+ OTHER_CPLUSPLUSFLAGS:
+ - "-Og -pthread '-std=c++17'"
+ Release:
+ GCC_PREPROCESSOR_DEFINITIONS:
+ - RELEASE
+ OTHER_CPLUSPLUSFLAGS:
+ - "-Os -DNDEBUG -pthread '-std=c++17'"
+ dependencies:
+ # TODO: build libcataclysm-tiles-common.a as it's own step again. Currently rolled in to single build
+ # - framework: libs/libcataclysm-tiles-common.a
+ - framework: ../build/libs/libSDL2.a
+ - framework: ../build/libs/libSDL2_image.a
+ - framework: ../build/libs/libSDL2_mixer.a
+ - framework: ../build/libs/libSDL2_ttf.a
+ - sdk: libllvm-flatbuffers.tbd
+ - sdk: libz.tbd
+ - sdk: libncurses.tbd
+ - sdk: MobileCoreServices.framework
+ - sdk: CoreMotion.framework
+ - sdk: CoreGraphics.framework
+ - sdk: CoreHaptics.framework
+ - sdk: AudioToolbox.framework
+ - sdk: CoreAudio.framework
+ - sdk: QuartzCore.framework
+ - sdk: GameController.framework
+ - sdk: Foundation.framework
+ - sdk: OpenGLES.framework
+ - sdk: UIKit.framework
+ - sdk: AVFoundation.framework
+ - sdk: ImageIO.framework
+ - sdk: Metal.framework
+ configs:
+ Debug:
+ DEBUG_MODE: YES
+ Release:
+ DEBUG_MODE: NO
diff --git a/data/json/construction/walls.json b/data/json/construction/walls.json
index a11c10c688abe..207c040aeb1e9 100644
--- a/data/json/construction/walls.json
+++ b/data/json/construction/walls.json
@@ -1408,4 +1408,4 @@
"pre_terrain": "t_wall_wood",
"post_terrain": "t_wood_wall_y"
}
-]
+]
\ No newline at end of file
diff --git a/doc/COMPILING_XCODE.md b/doc/COMPILING_XCODE.md
new file mode 100644
index 0000000000000..ee05dc512ae33
--- /dev/null
+++ b/doc/COMPILING_XCODE.md
@@ -0,0 +1,56 @@
+# Disclaimer
+
+**WARNING**: Xcode build should be used for _iOS builds only_. This can only be done on OS X.
+
+To build CataclysmDDA for other OS, see:
+
+- [COMPILING.md](COMPILING.md)
+
+# Prerequisites
+
+The project will build for tiles and sound by default.
+
+_Note: At this time there is no specific guidance on minimum versioning, all testing has been done on iOS 15.0+_
+
+### Get the tools
+
+- Xcode on the AppStore - https://apps.apple.com/us/app/xcode/id497799835.
+- Install Brew - https://brew.sh/
+ Or in a terminal window run:
+ `/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"`
+
+### Build SDL libs for iOS
+
+You will need to download the SDL source code and build static libraries for iOS (ARM64). The source code is available [here](https://github.com/libsdl-org/SDL). Directions on building the SDL for iOS in Xcode is available [here](https://github.com/libsdl-org/SDL/blob/main/docs/README-ios.md). The project setup will expect to find the static libraries in `/build/libs` and related header files in `/build/libs/headers`.
+
+### (Optional) Apple Developer ID pre-configuration
+
+If you already know you developer ID, you can add it to the file `xcode_dev_id_example.yml`, and rename the file to `xcode_dev_id.yml`.
+
+Additional details are in the example file.
+
+### Run the script
+
+Open terminal window to the build-scripts directory and run:
+`./ios-xcode.sh`
+
+If the Xcode project builds successfully Xcode will try to open automatically.
+
+### Xcode Steps
+
+If you have not already set the developer ID you will need to set it in the Project settings in the section "Signing & Capabilities"
+
+Product->Run will build for debugging
+Product->Archive will build a release version.
+
+### Play!
+
+Configuration and save files are accessible in your documents folder via the Files app. This should allow you to import and export data from other places. For now there is no auto-synchronization with any cloud based data services.
+
+### Notes
+
+I appreciate the many folks in the community who have contributed to making such a wonderful game, and my intent was to make sure that everyone had access to an iPad version that was freely available and built to integrate with the existing codebase, making it inclusive of experimental builds.
+
+If there are other setup related steps needed, please update accordingly or find me on Discord with questions or issues (smileynet on both Official and Community servers) understanding that my support of this build is at-will and may be intermittent. (Written on 10/26/2022, if you are unable to @ me on either server, I have likely wandered on down the road or been ill prepared for an encounter with a Kevlar Hulk o.O)
+
+My use case for this build is playing on an iPad with an attached magic keyboard, so while I've tested the soft keyboard's basic functionality, at this point I have not done any of the UI enhancements found in the other ports found on the AppStore (or the Android build, but with any luck that will be reasonable to reuse in this case).
\ No newline at end of file
diff --git a/ios/joystick.png b/ios/joystick.png
new file mode 100644
index 0000000000000..eb0c0ac854f77
Binary files /dev/null and b/ios/joystick.png differ
diff --git a/src/activity_item_handling.cpp b/src/activity_item_handling.cpp
index 2b1ca60ed3665..d2c1d2857fa3c 100644
--- a/src/activity_item_handling.cpp
+++ b/src/activity_item_handling.cpp
@@ -3872,4 +3872,4 @@ bool try_fuel_fire( player_activity &act, Character &you, const bool starting_fi
}
}
return true;
-}
+}
\ No newline at end of file
diff --git a/src/advanced_inv.cpp b/src/advanced_inv.cpp
index d7c5dfbfff6ad..0e34461c7f673 100644
--- a/src/advanced_inv.cpp
+++ b/src/advanced_inv.cpp
@@ -68,7 +68,7 @@
#include "units_utility.h"
#include "vehicle.h"
-#if defined(__ANDROID__)
+#if defined(__ANDROID__) || defined(__IPHONEOS__)
# include
#endif
diff --git a/src/advanced_inv_pane.cpp b/src/advanced_inv_pane.cpp
index a2300a371efb1..24250e2532059 100644
--- a/src/advanced_inv_pane.cpp
+++ b/src/advanced_inv_pane.cpp
@@ -29,7 +29,7 @@
class item_category;
-#if defined(__ANDROID__)
+#if defined(__ANDROID__) || defined(__IPHONEOS__)
# include
#endif
diff --git a/src/bionics_ui.cpp b/src/bionics_ui.cpp
index 5dced78d52090..0de187004c3d8 100644
--- a/src/bionics_ui.cpp
+++ b/src/bionics_ui.cpp
@@ -757,7 +757,7 @@ void avatar::power_bionics()
scroll_position = clamp( scroll_position, 0, max_scroll_position );
cursor = clamp( cursor, 0, current_bionic_list->size() );
-#if defined(__ANDROID__)
+#if defined(__ANDROID__) || defined(__IPHONEOS__)
ctxt.get_registered_manual_keys().clear();
for( size_t i = 0; i < current_bionic_list->size(); i++ ) {
ctxt.register_manual_key( ( *current_bionic_list )[i]->invlet,
diff --git a/src/cata_tiles.cpp b/src/cata_tiles.cpp
index 6c97af7c941e1..bbdf6ff96662b 100644
--- a/src/cata_tiles.cpp
+++ b/src/cata_tiles.cpp
@@ -1313,7 +1313,7 @@ void cata_tiles::draw( const point &dest, const tripoint_bub_ms ¢er, int wid
return;
}
-#if defined(__ANDROID__)
+#if defined(__ANDROID__) || defined(__IPHONEOS__)
// Attempted bugfix for Google Play crash - prevent divide-by-zero if no tile
// width/height specified
if( tile_width == 0 || tile_height == 0 ) {
diff --git a/src/debug.cpp b/src/debug.cpp
index 94b7f38c3e7b6..e8c35a6407ac0 100644
--- a/src/debug.cpp
+++ b/src/debug.cpp
@@ -1587,7 +1587,7 @@ std::string game_info::operating_system()
#if TARGET_IPHONE_SIMULATOR == 1
/* iOS in Xcode simulator */
return "iOS Simulator";
-#elif TARGET_OS_IPHONE == 1
+#elif TARGET_OS_IPHONE == 1 || defined(__IPHONEOS__)
/* iOS on iPhone, iPad, etc. */
return "iOS";
#elif TARGET_OS_MAC == 1
diff --git a/src/do_turn.cpp b/src/do_turn.cpp
index c8b1249ecdf22..46a091e3b03a4 100644
--- a/src/do_turn.cpp
+++ b/src/do_turn.cpp
@@ -90,7 +90,7 @@ static const event_statistic_id event_statistic_last_words( "last_words" );
static const trait_id trait_HAS_NEMESIS( "HAS_NEMESIS" );
-#if defined(__ANDROID__)
+#if defined(__ANDROID__) || defined(__IPHONEOS__)
extern std::map> quick_shortcuts_map;
extern bool add_best_key_for_action_to_quick_shortcuts( action_id action,
const std::string &category, bool back );
@@ -214,7 +214,7 @@ bool cleanup_at_end()
MAPBUFFER.clear();
overmap_buffer.clear();
-#if defined(__ANDROID__)
+#if defined(__ANDROID__) || defined(__IPHONEOS__)
quick_shortcuts_map.clear();
#endif
return true;
diff --git a/src/game.h b/src/game.h
index 4e2c7fcec34b5..148811976630b 100644
--- a/src/game.h
+++ b/src/game.h
@@ -853,7 +853,7 @@ class game
// Game-start procedures
bool load( const save_t &name ); // Load a player-specific save file
void load_master(); // Load the master data file, with factions &c
-#if defined(__ANDROID__)
+#if defined(__ANDROID__) || defined(__IPHONEOS__)
void load_shortcuts( const cata_path &path );
#endif
bool start_game(); // Starts a new game in the active world
@@ -865,7 +865,7 @@ class game
void serialize_master( std::ostream &fout );
// returns false if saving failed for whatever reason
bool save_maps();
-#if defined(__ANDROID__)
+#if defined(__ANDROID__) || defined(__IPHONEOS__)
void save_shortcuts( std::ostream &fout );
#endif
// Data Initialization
diff --git a/src/handle_action.cpp b/src/handle_action.cpp
index fe2aa95c4f4c9..ffc36ba6888ef 100644
--- a/src/handle_action.cpp
+++ b/src/handle_action.cpp
@@ -174,7 +174,7 @@ static const zone_type_id zone_type_VEHICLE_REPAIR( "VEHICLE_REPAIR" );
#define dbg(x) DebugLog((x),D_GAME) << __FILE__ << ":" << __LINE__ << ": "
-#if defined(__ANDROID__)
+#if defined(__ANDROID__) || defined(__IPHONEOS__)
extern std::map> quick_shortcuts_map;
extern bool add_best_key_for_action_to_quick_shortcuts( action_id action,
const std::string &category, bool back );
@@ -3181,7 +3181,7 @@ bool game::handle_action()
if( act == ACTION_NULL ) {
return false;
}
-#if defined(__ANDROID__)
+#if defined(__ANDROID__) || defined(__IPHONEOS__)
if( get_option( "ANDROID_ACTIONMENU_AUTOADD" ) && ctxt.get_category() == "DEFAULTMODE" ) {
add_best_key_for_action_to_quick_shortcuts( act, ctxt.get_category(), false );
}
diff --git a/src/input.cpp b/src/input.cpp
index 8362bc90d5721..9e7026123ff9f 100644
--- a/src/input.cpp
+++ b/src/input.cpp
@@ -62,7 +62,7 @@ bool is_mouse_enabled()
bool is_keycode_mode_supported()
{
-#if defined(TILES) && !defined(__ANDROID__) && !(defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE == 1)
+#if defined(TILES) && !defined(__ANDROID__) && !defined(__IPHONEOS__)
return keycode_mode;
#else
return false;
@@ -90,9 +90,7 @@ input_event::input_event( const std::set &mod, const int s, const inpu
: type( t ), modifiers( mod ), edit_refresh( false )
{
sequence.emplace_back( s );
-#if defined(__ANDROID__)
shortcut_last_used_action_counter = 0;
-#endif
}
int input_event::get_first_input() const
diff --git a/src/input_context.cpp b/src/input_context.cpp
index cb5cdd0160d0c..fc4e54b3d89b6 100644
--- a/src/input_context.cpp
+++ b/src/input_context.cpp
@@ -151,7 +151,6 @@ const std::string &input_context::input_to_action( const input_event &inp ) cons
return CATA_ERROR;
}
-#if defined(__ANDROID__)
std::list input_context::input_context_stack;
void input_context::register_manual_key( manual_key mk )
@@ -175,7 +174,6 @@ void input_context::register_manual_key( int key, const std::string text )
registered_manual_keys.push_back( manual_key( key, text ) );
}
-#endif
void input_context::register_action( const std::string &action_descriptor )
{
@@ -1483,4 +1481,4 @@ const hotkey_queue &hotkey_queue::alpha_digits()
queue->modifiers_keycode.emplace_back( std::set( { keymod_t::shift } ) );
}
return *queue;
-}
+}
\ No newline at end of file
diff --git a/src/input_context.h b/src/input_context.h
index 380f12f5f0d6d..4f30e8bbf7181 100644
--- a/src/input_context.h
+++ b/src/input_context.h
@@ -8,9 +8,7 @@
#include
#include
-#if defined(__ANDROID__)
#include
-#endif
#include "action.h"
#include "input_enums.h"
@@ -40,17 +38,13 @@ class input_context
{
friend class keybindings_ui;
public:
-#if defined(__ANDROID__)
// Whatever's on top is our current input context.
static std::list input_context_stack;
-#endif
input_context() : registered_any_input( false ), category( "default" ),
coordinate_input_received( false ), handling_coordinate_input( false ) {
-#if defined(__ANDROID__)
input_context_stack.push_back( this );
allow_text_entry = false;
-#endif
register_action( "toggle_language_to_en" );
}
// TODO: consider making the curses WINDOW an argument to the constructor, so that mouse input
@@ -60,14 +54,11 @@ class input_context
: registered_any_input( false ), category( category ),
coordinate_input_received( false ), handling_coordinate_input( false ),
preferred_keyboard_mode( preferred_keyboard_mode ) {
-#if defined(__ANDROID__)
input_context_stack.push_back( this );
allow_text_entry = false;
-#endif
register_action( "toggle_language_to_en" );
}
-#if defined(__ANDROID__)
virtual ~input_context() {
input_context_stack.remove( this );
}
@@ -145,7 +136,6 @@ class input_context
bool operator!=( const input_context &other ) const {
return !( *this == other );
}
-#endif
/**
* Register an action with this input context.
@@ -415,9 +405,7 @@ class input_context
input_event first_unassigned_hotkey( const hotkey_queue &queue ) const;
input_event next_unassigned_hotkey( const hotkey_queue &queue, const input_event &prev ) const;
private:
-#if defined(__ANDROID__)
std::vector registered_manual_keys;
-#endif
std::vector registered_actions;
std::string edittext;
public:
diff --git a/src/input_enums.h b/src/input_enums.h
index 314a0cd676456..26c570bb2470c 100644
--- a/src/input_enums.h
+++ b/src/input_enums.h
@@ -78,10 +78,8 @@ struct input_event {
std::string edit;
bool edit_refresh;
-#if defined(__ANDROID__)
// Used exclusively by the quick shortcuts to determine how stale a shortcut is
int shortcut_last_used_action_counter = 0;
-#endif
input_event() : edit_refresh( false ) {
type = input_event_t::error;
@@ -164,4 +162,4 @@ enum class keyboard_mode {
// some platforms, such as non-android SDL. On other platforms
// this falls back to `keychar` automatically.
keycode,
-};
+};
\ No newline at end of file
diff --git a/src/inventory.cpp b/src/inventory.cpp
index 8b30405c33ed6..62719717442a2 100644
--- a/src/inventory.cpp
+++ b/src/inventory.cpp
@@ -351,7 +351,7 @@ void inventory::push_back( const item &newit )
add_item( newit );
}
-#if defined(__ANDROID__)
+#if defined(__ANDROID__) || defined(__IPHONEOS__)
extern void remove_stale_inventory_quick_shortcuts();
#endif
@@ -446,7 +446,7 @@ void inventory::restack( Character &p )
}
items.sort( stack_compare );
-#if defined(__ANDROID__)
+#if defined(__ANDROID__) || defined(__IPHONEOS__)
remove_stale_inventory_quick_shortcuts();
#endif
}
diff --git a/src/inventory_ui.cpp b/src/inventory_ui.cpp
index 23ea3432f0715..88948edf647ea 100644
--- a/src/inventory_ui.cpp
+++ b/src/inventory_ui.cpp
@@ -1,4 +1,6 @@
+#if !defined(__IPHONEOS__)
#include "inventory_ui.h"
+#endif
#include
#include
@@ -50,7 +52,12 @@
#include "vehicle_selector.h"
#include "vpart_position.h"
-#if defined(__ANDROID__)
+#if defined(__IPHONEOS__)
+#include "inventory_ui.h"
+#include "input_context.h"
+#endif
+
+#if defined(__ANDROID__) || defined(__IPHONEOS__)
#include
#endif
@@ -3854,7 +3861,8 @@ inventory_drop_selector::inventory_drop_selector( Character &p,
inventory_multiselector( p, preset, selection_column_title ),
warn_liquid( warn_liquid )
{
-#if defined(__ANDROID__)
+ //FIXME: allow_text_entry is inaccessible??????
+#if defined(__ANDROID__) || defined(__IPHONEOS__)
// allow user to type a drop number without dismissing virtual keyboard after each keypress
ctxt.allow_text_entry = true;
#endif
@@ -3866,7 +3874,8 @@ inventory_insert_selector::inventory_insert_selector( Character &p,
const bool warn_liquid ) :
inventory_drop_selector( p, preset, selection_column_title, warn_liquid )
{
-#if defined(__ANDROID__)
+ //FIXME: allow_text_entry is inaccessible??????
+#if defined(__ANDROID__) || defined(__IPHONEOS__)
// allow user to type a drop number without dismissing virtual keyboard after each keypress
ctxt.allow_text_entry = true;
#endif
@@ -4187,7 +4196,8 @@ pickup_selector::pickup_selector( Character &p, const inventory_selector_preset
{
ctxt.register_action( "WEAR" );
ctxt.register_action( "WIELD" );
-#if defined(__ANDROID__)
+ //FIXME: allow_text_entry is inaccessible??????
+#if defined(__ANDROID__) || defined(__IPHONEOS__)
// allow user to type a drop number without dismissing virtual keyboard after each keypress
ctxt.allow_text_entry = true;
#endif
diff --git a/src/main.cpp b/src/main.cpp
index cd7b94e642690..a502ea2119552 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -80,12 +80,15 @@ class ui_adaptor;
# endif
#endif
-#if defined(__ANDROID__)
+#if defined(__ANDROID__) || defined(__IPHONEOS__)
+#include
#include
#include
-#include
-#include
#include
+#endif
+
+#if defined(__ANDROID__)
+#include
// Taken from: https://codelab.wordpress.com/2014/11/03/how-to-use-standard-output-streams-for-logging-in-android-apps/
// Force Android standard output to adb logcat output
@@ -619,7 +622,7 @@ int APIENTRY WinMain( _In_ HINSTANCE /* hInstance */, _In_opt_ HINSTANCE /* hPre
{
int argc = __argc;
char **argv = __argv;
-#elif defined(__ANDROID__)
+#elif defined(__ANDROID__) || defined(__IPHONEOS__)
extern "C" int SDL_main( int argc, char **argv ) {
#else
int main( int argc, const char *argv[] )
@@ -663,23 +666,20 @@ int main( int argc, const char *argv[] )
std::string external_storage_path( SDL_AndroidGetExternalStoragePath() );
PATH_INFO::init_base_path( external_storage_path );
-#else
- // Set default file paths
-#if defined(PREFIX)
+#elif defined(PREFIX)
PATH_INFO::init_base_path( std::string( PREFIX ) );
#else
PATH_INFO::init_base_path( "" );
#endif
-#endif
#if defined(__ANDROID__)
PATH_INFO::init_user_dir( external_storage_path );
-#else
-# if defined(USE_HOME_DIR) || defined(USE_XDG_DIR) || defined(EMSCRIPTEN)
+#elif defined(__IPHONEOS__)
+ PATH_INFO::init_user_dir( std::string( getenv( "HOME" ) ) + "/Documents/" );
+#elif defined(USE_HOME_DIR) || defined(USE_XDG_DIR) || defined(EMSCRIPTEN)
PATH_INFO::init_user_dir( "" );
-# else
+#else
PATH_INFO::init_user_dir( "." );
-# endif
#endif
PATH_INFO::set_standard_filenames();
@@ -867,3 +867,17 @@ int main( int argc, const char *argv[] )
exit_handler( -999 );
return 0;
}
+
+#if defined(__IPHONEOS__)
+#ifndef SDL_MAIN_HANDLED
+#include
+#ifdef main
+#undef main
+#endif
+
+int main( int argc, char *argv[] )
+{
+ return SDL_UIKitRunApp( argc, argv, SDL_main );
+}
+#endif /* !SDL_MAIN_HANDLED */
+#endif
diff --git a/src/messages.cpp b/src/messages.cpp
index 3df98f1bd0981..e196fe98cffa9 100644
--- a/src/messages.cpp
+++ b/src/messages.cpp
@@ -19,7 +19,7 @@
#include "ui_manager.h"
#include "viewer.h"
-#if defined(__ANDROID__)
+#if defined(__ANDROID__) || defined(__IPHONEOS__)
#include
#endif
#include
diff --git a/src/mutation_ui.cpp b/src/mutation_ui.cpp
index 5d00cefdeb0c6..5c6b6c361abf6 100644
--- a/src/mutation_ui.cpp
+++ b/src/mutation_ui.cpp
@@ -227,7 +227,7 @@ void avatar::power_mutations()
ctxt.register_action( "CONFIRM" );
ctxt.register_action( "HELP_KEYBINDINGS" );
ctxt.register_action( "QUIT" );
-#if defined(__ANDROID__)
+#if defined(__ANDROID__) || defined(__IPHONEOS__)
for( const auto &p : passive ) {
ctxt.register_manual_key( cached_mutations[p].key, p.obj().name() );
}
diff --git a/src/npctalk.cpp b/src/npctalk.cpp
index c39476c327ea0..9aa46f4a892f0 100644
--- a/src/npctalk.cpp
+++ b/src/npctalk.cpp
@@ -2867,7 +2867,7 @@ talk_topic dialogue::opt( dialogue_window &d_win, const talk_topic &topic )
std::vector response_lines;
std::vector response_hotkeys;
const auto generate_response_lines = [&]() {
-#if defined(__ANDROID__)
+#if defined(__ANDROID__) || defined(__IPHONEOS__)
ctxt.get_registered_manual_keys().clear();
#endif
const hotkey_queue &queue = hotkey_queue::alphabets();
@@ -2878,7 +2878,7 @@ talk_topic dialogue::opt( dialogue_window &d_win, const talk_topic &topic )
const talk_data &td = response.create_option_line( *this, evt, d_win.is_computer );
response_lines.emplace_back( td );
response_hotkeys.emplace_back( evt );
-#if defined(__ANDROID__)
+#if defined(__ANDROID__) || defined(__IPHONEOS__)
ctxt.register_manual_key( evt.get_first_input(), td.text );
#endif
evt = ctxt.next_unassigned_hotkey( queue, evt );
diff --git a/src/options.cpp b/src/options.cpp
index d35a468a3e766..608d0d4d75224 100644
--- a/src/options.cpp
+++ b/src/options.cpp
@@ -217,8 +217,8 @@ options_manager::options_manager()
pages_.emplace_back( "debug", to_translation( "Debug" ) );
}
-#if defined(__ANDROID__)
- pages_.emplace_back( "android", to_translation( "Android" ) );
+#if defined(__ANDROID__) || defined(__IPHONEOS__)
+ pages_.emplace_back( "android", to_translation( "Mobile" ) );
#endif
enable_json( "DEFAULT_REGION" );
@@ -2626,7 +2626,7 @@ void options_manager::add_options_graphics()
display_list.front().first, COPT_CURSES_HIDE );
#endif
-#if !defined(__ANDROID__) || !defined(__EMSCRIPTEN__) // Android and Emscripten are always fullscreen
+#if !defined(__ANDROID__) || !defined(__IPHONEOS__) || !defined(__EMSCRIPTEN__) // Android, iOS and Emscripten are always fullscreen
add( "FULLSCREEN", page_id, to_translation( "Fullscreen" ),
to_translation( "Starts Cataclysm in one of the fullscreen modes. Requires restart." ),
{ { "no", to_translation( "No" ) }, { "maximized", to_translation( "Maximized" ) }, { "fullscreen", to_translation( "Fullscreen" ) }, { "windowedbl", to_translation( "Windowed borderless" ) } },
@@ -2655,6 +2655,14 @@ void options_manager::add_options_graphics()
break;
}
}
+# endif
+# if defined(__IPHONEOS__)
+ for( const id_and_option &renderer : renderer_list ) {
+ if( renderer.first == "meta" ) {
+ default_renderer = renderer.first;
+ break;
+ }
+ }
# endif
add( "RENDERER", page_id, to_translation( "Renderer" ),
to_translation( "Set which renderer to use. Requires restart." ), renderer_list,
@@ -2703,7 +2711,7 @@ void options_manager::add_options_graphics()
},
"none", COPT_CURSES_HIDE );
-#if !defined(__ANDROID__)
+#if !defined(__ANDROID__) || !defined(__IPHONEOS__)
add( "SCALING_FACTOR", page_id, to_translation( "Scaling factor" ),
to_translation( "Factor by which to scale the display. Requires restart." ), {
{ "1", to_translation( "1x" )},
@@ -2982,7 +2990,7 @@ void options_manager::add_options_debug()
void options_manager::add_options_android()
{
-#if defined(__ANDROID__)
+#if defined(__ANDROID__) || defined(__IPHONEOS__)
const auto add_empty_line = [&]() {
this->add_empty_line( "android" );
};
@@ -2995,9 +3003,10 @@ void options_manager::add_options_android()
add_empty_line();
add_option_group( "android", Group( "android_keyboard_opts",
- to_translation( "Android keyboard options" ),
- to_translation( "Options regarding Android keyboard." ) ),
+ to_translation( "Keyboard options" ),
+ to_translation( "Options regarding keyboard." ) ),
[&]( const std::string & page_id ) {
+#if defined(__ANDROID__)
add( "ANDROID_TRAP_BACK_BUTTON", page_id, to_translation( "Trap Back button" ),
to_translation( "If true, the back button will NOT back out of the app and will be passed to the application as SDL_SCANCODE_AC_BACK. Requires restart." ),
// take default setting from pre-game settings screen - important as there are issues with Back button on Android 9 with specific devices
@@ -3009,6 +3018,7 @@ void options_manager::add_options_android()
"such as popup messages and yes/no dialogs." ),
android_get_default_setting( "Native Android UI", true )
);
+#endif
add( "ANDROID_AUTO_KEYBOARD", page_id, to_translation( "Auto-manage virtual keyboard" ),
to_translation( "If true, automatically show/hide the virtual keyboard when necessary based on context. If false, virtual keyboard must be toggled manually." ),
@@ -3267,7 +3277,6 @@ void options_manager::add_options_android()
50, 1000, 130
);
} );
-
#endif
}
@@ -3999,7 +4008,7 @@ std::string options_manager::show( bool ingame, const bool world_options_only, b
calendar::set_eternal_night( ::get_option( "ETERNAL_TIME_OF_DAY" ) == "night" );
calendar::set_eternal_day( ::get_option( "ETERNAL_TIME_OF_DAY" ) == "day" );
-#if !defined(EMSCRIPTEN) && !defined(__ANDROID__) && !defined(TUI)
+#if !defined(EMSCRIPTEN) && !defined(__ANDROID__) && !defined(__IPHONEOS__) && !defined(TUI)
if( terminal_size_changed ) {
int scaling_factor = get_scaling_factor();
point TERM( ::get_option( "TERMINAL_X" ), ::get_option( "TERMINAL_Y" ) );
diff --git a/src/overmap_ui.cpp b/src/overmap_ui.cpp
index 29dec893f939d..244618943d4ec 100644
--- a/src/overmap_ui.cpp
+++ b/src/overmap_ui.cpp
@@ -87,7 +87,7 @@ static const oter_type_str_id oter_type_forest_trail( "forest_trail" );
static const trait_id trait_DEBUG_CLAIRVOYANCE( "DEBUG_CLAIRVOYANCE" );
static const trait_id trait_DEBUG_NIGHTVISION( "DEBUG_NIGHTVISION" );
-#if defined(__ANDROID__)
+#if defined(__ANDROID__) || defined(__IPHONEOS__)
#include
#endif
diff --git a/src/path_info.cpp b/src/path_info.cpp
index db8c64d220ee9..29417cea2c734 100644
--- a/src/path_info.cpp
+++ b/src/path_info.cpp
@@ -229,7 +229,11 @@ cata_path PATH_INFO::base_path()
}
std::string PATH_INFO::cache_dir()
{
+#if defined(__IPHONEOS__)
+ return std::string( getenv( "HOME" ) ) + "/Library/Cache/"
+#else
return datadir_value + "cache/";
+#endif
}
cata_path PATH_INFO::colors()
{
diff --git a/src/savegame.cpp b/src/savegame.cpp
index aa6df0c106e3e..dfe3afa60d965 100644
--- a/src/savegame.cpp
+++ b/src/savegame.cpp
@@ -59,7 +59,7 @@ static const oter_str_id oter_omt_obsolete( "omt_obsolete" );
static const string_id overmap_connection_local_road( "local_road" );
-#if defined(__ANDROID__)
+#if defined(__ANDROID__) || defined(__IPHONEOS__)
#include "input.h"
extern std::map> quick_shortcuts_map;
@@ -318,7 +318,7 @@ void scent_map::deserialize( const std::string &data, bool is_type )
}
}
-#if defined(__ANDROID__)
+#if defined(__ANDROID__) || defined(__IPHONEOS__)
///// quick shortcuts
void game::load_shortcuts( const cata_path &path )
{
diff --git a/src/sdltiles.cpp b/src/sdltiles.cpp
index 95d17ee7cfc49..51689b0ff2843 100644
--- a/src/sdltiles.cpp
+++ b/src/sdltiles.cpp
@@ -90,7 +90,9 @@ std::unique_ptr imclient;
#if defined(__ANDROID__)
#include
+#endif
+#if defined(__IPHONEOS__) || defined(__ANDROID__)
#include "action.h"
#include "inventory.h"
#include "map.h"
@@ -99,6 +101,10 @@ std::unique_ptr imclient;
#include "worldfactory.h"
#endif
+#if defined(__IPHONEOS__)
+#include
+#endif
+
#if defined(EMSCRIPTEN)
#include
#endif
@@ -135,7 +141,7 @@ static SDL_Renderer_Ptr renderer;
static SDL_PixelFormat_Ptr format;
static SDL_Texture_Ptr display_buffer;
static GeometryRenderer_Ptr geometry;
-#if defined(__ANDROID__)
+#if defined(__ANDROID__) || defined(__IPHONEOS__)
static SDL_Texture_Ptr touch_joystick;
#endif
static int WindowWidth; //Width of the actual window, not the curses window
@@ -257,7 +263,7 @@ static void WinCreate()
SDL_SetHint( SDL_HINT_RENDER_SCALE_QUALITY, get_option( "SCALING_MODE" ).c_str() );
}
-#if !defined(__ANDROID__) && !defined(EMSCRIPTEN)
+#if !defined(__ANDROID__) && !defined(__IPHONEOS__) && !defined(EMSCRIPTEN)
if( get_option( "FULLSCREEN" ) == "fullscreen" ) {
window_flags |= SDL_WINDOW_FULLSCREEN;
fullscreen = true;
@@ -277,7 +283,8 @@ static void WinCreate()
// Without this, the game only displays in the top-left 1/4 of the window.
window_flags &= ~SDL_WINDOW_ALLOW_HIGHDPI;
#endif
-#if defined(__ANDROID__)
+
+#if defined(__ANDROID__) || defined(__IPHONEOS__)
// Without this, the game only displays in the top-left 1/4 of the window.
window_flags = SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_MAXIMIZED;
#endif
@@ -287,7 +294,7 @@ static void WinCreate()
display = 0;
}
-#if defined(__ANDROID__)
+#if defined(__ANDROID__) || defined(__IPHONEOS__)
// Bugfix for red screen on Samsung S3/Mali
// https://forums.libsdl.org/viewtopic.php?t=11445
SDL_GL_SetAttribute( SDL_GL_RED_SIZE, 5 );
@@ -295,18 +302,22 @@ static void WinCreate()
SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, 5 );
// Fix Back button crash on Android 9
-#if defined(SDL_HINT_ANDROID_TRAP_BACK_BUTTON )
+#if defined(SDL_HINT_ANDROID_TRAP_BACK_BUTTON ) && !defined(__IPHONEOS__)
const bool trap_back_button = get_option( "ANDROID_TRAP_BACK_BUTTON" );
SDL_SetHint( SDL_HINT_ANDROID_TRAP_BACK_BUTTON, trap_back_button ? "1" : "0" );
#endif
// Prevent mouse|touch input confusion
-#if defined(SDL_HINT_ANDROID_SEPARATE_MOUSE_AND_TOUCH)
+#if defined(SDL_HINT_ANDROID_SEPARATE_MOUSE_AND_TOUCH) && !defined(__IPHONEOS__)
SDL_SetHint( SDL_HINT_ANDROID_SEPARATE_MOUSE_AND_TOUCH, "1" );
#else
SDL_SetHint( SDL_HINT_MOUSE_TOUCH_EVENTS, "0" );
SDL_SetHint( SDL_HINT_TOUCH_MOUSE_EVENTS, "0" );
#endif
+
+#if defined(SDL_HINT_IOS_HIDE_HOME_INDICATOR)
+ SDL_SetHint( SDL_HINT_IOS_HIDE_HOME_INDICATOR, "1" );
+#endif
#endif
::window.reset( SDL_CreateWindow( "",
@@ -318,7 +329,7 @@ static void WinCreate()
) );
throwErrorIf( !::window, "SDL_CreateWindow failed" );
-#if !defined(__ANDROID__) && !defined(EMSCRIPTEN)
+#if !defined(__ANDROID__) || !defined(__IPHONEOS__) || !defined(EMSCRIPTEN)
// On Android SDL seems janky in windowed mode so we're fullscreen all the time.
// Fullscreen mode is now modified so it obeys terminal width/height, rather than
// overwriting it with this calculation.
@@ -396,17 +407,30 @@ static void WinCreate()
SDL_SetWindowMinimumSize( ::window.get(), fontwidth * EVEN_MINIMUM_TERM_WIDTH * scaling_factor,
fontheight * EVEN_MINIMUM_TERM_HEIGHT * scaling_factor );
-#if defined(__ANDROID__)
+#if defined(__ANDROID__) || defined(__IPHONEOS__)
// TODO: Not too sure why this works to make fullscreen on Android behave. :/
if( window_flags & SDL_WINDOW_FULLSCREEN || window_flags & SDL_WINDOW_FULLSCREEN_DESKTOP
|| window_flags & SDL_WINDOW_MAXIMIZED ) {
SDL_GetWindowSize( ::window.get(), &WindowWidth, &WindowHeight );
+#if defined(__IPHONEOS__)
+ WindowWidth = static_cast( WindowWidth * CataclysmExperimental::iosGetDisplayDensity() );
+ WindowHeight = static_cast( WindowHeight * CataclysmExperimental::iosGetDisplayDensity() );
+#endif
+ DebugLog( D_INFO, DC_ALL ) << "WindowWidth: " << WindowWidth;
+ DebugLog( D_INFO, DC_ALL ) << "WindowHeight: " << WindowHeight;
}
+#endif
+#if defined(__ANDROID__)
// Load virtual joystick texture
touch_joystick = CreateTextureFromSurface( renderer, load_image( "android/joystick.png" ) );
#endif
+#if defined(__IPHONEOS__)
+ // Load virtual joystick texture
+ touch_joystick = CreateTextureFromSurface( renderer, load_image( "ios/joystick.png" ) );
+#endif
+
ClearScreen();
// Errors here are ignored, worst case: the option does not work as expected,
@@ -438,7 +462,7 @@ static void WinCreate()
static void WinDestroy()
{
-#if defined(__ANDROID__)
+#if defined(__ANDROID__) || defined(__IPHONEOS__)
touch_joystick.reset();
#endif
imclient.reset();
@@ -458,7 +482,7 @@ static const SDL_Color &color_as_sdl( const unsigned char color )
return windowsPalette[color];
}
-#if defined(__ANDROID__)
+#if defined(__ANDROID__) || defined(__IPHONEOS__)
void draw_terminal_size_preview();
void draw_quick_shortcuts();
void draw_virtual_joystick();
@@ -476,6 +500,8 @@ extern "C" {
static bool has_visible_display_frame = false;
static SDL_Rect visible_display_frame;
+ // TODO: get this thing to work on iOS
+#if defined(__ANDROID__)
JNIEXPORT void JNICALL Java_org_libsdl_app_SDLActivity_onNativeVisibleDisplayFrameChanged(
JNIEnv *env, jclass jcls, jint left, jint top, jint right, jint bottom )
{
@@ -488,7 +514,7 @@ extern "C" {
visible_display_frame.w = right - left;
visible_display_frame.h = bottom - top;
}
-
+#endif
} // "C"
SDL_Rect get_android_render_rect( float DisplayBufferWidth, float DisplayBufferHeight )
@@ -514,9 +540,8 @@ SDL_Rect get_android_render_rect( float DisplayBufferWidth, float DisplayBufferH
dstrect.w = WindowHeightLessShortcuts * DisplayBufferAspect;
dstrect.h = WindowHeightLessShortcuts;
}
-
// Make sure the destination rectangle fits within the visible area
- if( get_option( "ANDROID_KEYBOARD_SCREEN_SCALE" ) && has_visible_display_frame ) {
+/* if( get_option( "ANDROID_KEYBOARD_SCREEN_SCALE" ) && has_visible_display_frame ) {
int vdf_right = visible_display_frame.x + visible_display_frame.w;
int vdf_bottom = visible_display_frame.y + visible_display_frame.h;
if( vdf_right < dstrect.x + dstrect.w ) {
@@ -525,10 +550,14 @@ SDL_Rect get_android_render_rect( float DisplayBufferWidth, float DisplayBufferH
if( vdf_bottom < dstrect.y + dstrect.h ) {
dstrect.h = vdf_bottom - dstrect.y;
}
- }
+ } */
+ DebugLog( D_INFO, DC_ALL ) << "dstrect.x: " << dstrect.x;
+ DebugLog( D_INFO, DC_ALL ) << "dstrect.y: " << dstrect.y;
+ DebugLog( D_INFO, DC_ALL ) << "dstrect.w: " << dstrect.w;
+ DebugLog( D_INFO, DC_ALL ) << "dstrect.h: " << dstrect.h;
+
return dstrect;
}
-
#endif
void refresh_display()
@@ -544,7 +573,7 @@ void refresh_display()
// there, present it, select the buffer as target again.
SetRenderTarget( renderer, nullptr );
ClearScreen();
-#if defined(__ANDROID__)
+#if defined(__ANDROID__) || defined(__IPHONEOS__)
SDL_Rect dstrect = get_android_render_rect( TERMINAL_WIDTH * fontwidth,
TERMINAL_HEIGHT * fontheight );
RenderCopy( renderer, display_buffer, NULL, &dstrect );
@@ -552,7 +581,7 @@ void refresh_display()
RenderCopy( renderer, display_buffer, nullptr, nullptr );
#endif
-#if defined(__ANDROID__)
+#if defined(__ANDROID__) || defined(__IPHONEOS__)
draw_terminal_size_preview();
if( g ) {
draw_quick_shortcuts();
@@ -758,7 +787,7 @@ void cata_tiles::draw_om( const point &dest, const tripoint_abs_omt ¢er_abs_
return;
}
-#if defined(__ANDROID__)
+#if defined(__ANDROID__) || defined(__IPHONEOS__)
// Attempted bugfix for Google Play crash - prevent divide-by-zero if no tile
// width/height specified
if( tile_width == 0 || tile_height == 0 ) {
@@ -1863,7 +1892,7 @@ void toggle_fullscreen_window()
fullscreen = !fullscreen;
}
-#if defined(__ANDROID__)
+#if defined(__ANDROID__) || defined(__IPHONEOS__)
static float finger_down_x = -1.0f; // in pixels
static float finger_down_y = -1.0f; // in pixels
static float finger_curr_x = -1.0f; // in pixels
@@ -1917,6 +1946,7 @@ std::string get_quick_shortcut_name( const std::string &category )
float android_get_display_density()
{
+#if defined(__ANDROID__)
JNIEnv *env = ( JNIEnv * )SDL_AndroidGetJNIEnv();
jobject activity = ( jobject )SDL_AndroidGetActivity();
jclass clazz( env->GetObjectClass( activity ) );
@@ -1924,6 +1954,10 @@ float android_get_display_density()
jfloat ans = env->CallFloatMethod( activity, method_id );
env->DeleteLocalRef( activity );
env->DeleteLocalRef( clazz );
+#endif
+#if defined(__IPHONEOS__)
+ float ans = CataclysmExperimental::iosGetDisplayDensity();
+#endif
return ans;
}
@@ -2409,7 +2443,6 @@ void draw_quick_shortcuts()
void draw_virtual_joystick()
{
-
// Bail out if we don't need to draw the joystick
if( !get_option( "ANDROID_SHOW_VIRTUAL_JOYSTICK" ) ||
finger_down_time <= 0 ||
@@ -2421,6 +2454,8 @@ void draw_virtual_joystick()
return;
}
+ dbg( D_INFO ) << "Drawing virtual joystick";
+
SDL_SetTextureAlphaMod( touch_joystick.get(),
get_option( "ANDROID_VIRTUAL_JOYSTICK_OPACITY" ) * 0.01f * 255.0f );
@@ -2579,6 +2614,7 @@ void handle_finger_input( uint32_t ticks )
bool android_is_hardware_keyboard_available()
{
+#if defined(__ANDROID__)
JNIEnv *env = ( JNIEnv * )SDL_AndroidGetJNIEnv();
jobject activity = ( jobject )SDL_AndroidGetActivity();
jclass clazz( env->GetObjectClass( activity ) );
@@ -2586,6 +2622,10 @@ bool android_is_hardware_keyboard_available()
jboolean ans = env->CallBooleanMethod( activity, method_id );
env->DeleteLocalRef( activity );
env->DeleteLocalRef( clazz );
+#endif
+#if defined(__IPHONEOS__)
+ bool ans = CataclysmExperimental::isIOSKeyBoardAvailable();
+#endif
return ans;
}
@@ -2593,6 +2633,7 @@ void android_vibrate()
{
int vibration_ms = get_option( "ANDROID_VIBRATION" );
if( vibration_ms > 0 && !android_is_hardware_keyboard_available() ) {
+#if defined(__ANDROID__)
JNIEnv *env = ( JNIEnv * )SDL_AndroidGetJNIEnv();
jobject activity = ( jobject )SDL_AndroidGetActivity();
jclass clazz( env->GetObjectClass( activity ) );
@@ -2600,11 +2641,15 @@ void android_vibrate()
env->CallVoidMethod( activity, method_id, vibration_ms );
env->DeleteLocalRef( activity );
env->DeleteLocalRef( clazz );
+#endif
+#if defined(__IPHONEOS__)
+ CataclysmExperimental::vibrateDevice();
+#endif
}
}
#endif
-#if !defined(__ANDROID__)
+#if !defined(__ANDROID__) || !defined(__IPHONEOS__)
static bool window_focus = false;
static bool text_input_active_when_regaining_focus = false;
#endif
@@ -2615,7 +2660,7 @@ void StartTextInput()
if( SDL_IsTextInputActive() == SDL_TRUE ) {
return;
}
-#if defined(__ANDROID__)
+#if defined(__ANDROID__) || defined(__IPHONEOS__)
SDL_StartTextInput();
#else
if( window_focus ) {
@@ -2628,7 +2673,7 @@ void StartTextInput()
void StopTextInput()
{
-#if defined(__ANDROID__)
+#if defined(__ANDROID__) || defined(__IPHONEOS__)
SDL_StopTextInput();
#else
if( window_focus ) {
@@ -2647,7 +2692,7 @@ static void CheckMessages()
bool text_refresh = false;
bool is_repeat = false;
-#if defined(__ANDROID__)
+#if defined(__ANDROID__) || defined(__IPHONEOS__)
if( visible_display_frame_dirty ) {
needupdate = true;
ui_manager::redraw_invalidated();
@@ -2660,7 +2705,7 @@ static void CheckMessages()
if( android_is_hardware_keyboard_available() && !SDL_IsTextInputActive() ) {
StartTextInput();
}
-
+#if defined(__ANDROID__)
// Make sure the SDL surface view is visible, otherwise the "Z" loading screen is visible.
if( needs_sdl_surface_visibility_refresh ) {
needs_sdl_surface_visibility_refresh = false;
@@ -2674,6 +2719,7 @@ static void CheckMessages()
env->DeleteLocalRef( activity );
env->DeleteLocalRef( clazz );
}
+#endif
// Copy the current input context
if( !input_context::input_context_stack.empty() ) {
@@ -2892,15 +2938,17 @@ static void CheckMessages()
// Display an Android toast message
{
- JNIEnv *env = ( JNIEnv * )SDL_AndroidGetJNIEnv();
- jobject activity = ( jobject )SDL_AndroidGetActivity();
- jclass clazz( env->GetObjectClass( activity ) );
- jstring toast_message = env->NewStringUTF( quick_shortcuts_enabled ? "Shortcuts visible" :
- "Shortcuts hidden" );
- jmethodID method_id = env->GetMethodID( clazz, "toast", "(Ljava/lang/String;)V" );
- env->CallVoidMethod( activity, method_id, toast_message );
- env->DeleteLocalRef( activity );
- env->DeleteLocalRef( clazz );
+#if defined(__ANDROID__)
+ JNIEnv *env = ( JNIEnv * )SDL_AndroidGetJNIEnv();
+ jobject activity = ( jobject )SDL_AndroidGetActivity();
+ jclass clazz( env->GetObjectClass( activity ) );
+ jstring toast_message = env->NewStringUTF( quick_shortcuts_enabled ? "Shortcuts visible" :
+ "Shortcuts hidden" );
+ jmethodID method_id = env->GetMethodID( clazz, "toast", "(Ljava/lang/String;)V" );
+ env->CallVoidMethod( activity, method_id, toast_message );
+ env->DeleteLocalRef( activity );
+ env->DeleteLocalRef( clazz );
+#endif
}
}
}
@@ -2953,7 +3001,7 @@ static void CheckMessages()
}
}
#endif
-
+
last_input = input_event();
std::optional resize_dims;
@@ -2964,7 +3012,7 @@ static void CheckMessages()
switch( ev.type ) {
case SDL_WINDOWEVENT:
switch( ev.window.event ) {
-#if defined(__ANDROID__)
+#if defined(__ANDROID__) || defined(__IPHONEOS__)
// SDL will send a focus lost event whenever the app loses focus (eg. lock screen, switch app focus etc.)
// If we detect it and the game seems in a saveable state, try and do a quicksave. This is a bit dodgy
// as the player could be ANYWHERE doing ANYTHING (a sub-menu, interacting with an NPC/computer etc.)
@@ -2995,7 +3043,7 @@ static void CheckMessages()
if( SDL_IsTextInputActive() ) {
text_input_active_when_regaining_focus = true;
// Stop text input to not intefere with other programs
- SDL_StopTextInput();
+ StopTextInput();
// Clear uncommited IME text. TODO: commit IME text instead.
last_input = input_event();
last_input.type = input_event_t::keyboard_char;
@@ -3021,7 +3069,7 @@ static void CheckMessages()
needupdate = true;
break;
case SDL_WINDOWEVENT_RESTORED:
-#if defined(__ANDROID__)
+#if defined(__ANDROID__) || defined(__IPHONEOS__)
needs_sdl_surface_visibility_refresh = true;
if( android_is_hardware_keyboard_available() ) {
StopTextInput();
@@ -3036,11 +3084,45 @@ static void CheckMessages()
break;
}
break;
+#if defined(__IPHONEOS__)
+ case SDL_APP_WILLENTERBACKGROUND:
+ if( world_generator &&
+ world_generator->active_world &&
+ g && g->uquit == QUIT_NO &&
+ !std::uncaught_exception() ) {
+ g->quicksave();
+ }
+ if( SDL_IsTextInputActive() ) {
+ // TODO: Abstract common method with above usage
+ text_input_active_when_regaining_focus = true;
+ // Stop text input to not intefere with other programs
+ StopTextInput();
+ // Clear uncommited IME text. TODO: commit IME text instead.
+ last_input = input_event();
+ last_input.type = input_event_t::keyboard_char;
+ last_input.edit.clear();
+ last_input.edit_refresh = true;
+ text_refresh = true;
+ } else {
+ text_input_active_when_regaining_focus = false;
+ }
+ break;
+ case SDL_APP_DIDENTERBACKGROUND:
+ window_focus = false;
+ break;
+ case SDL_APP_DIDENTERFOREGROUND:
+ window_focus = true;
+ // Restore text input status
+ if( !SDL_IsTextInputActive() || text_input_active_when_regaining_focus ) {
+ SDL_StartTextInput();
+ }
+ break;
+#endif
case SDL_RENDER_TARGETS_RESET:
render_target_reset = true;
break;
case SDL_KEYDOWN: {
-#if defined(__ANDROID__)
+#if defined(__ANDROID__) || defined(__IPHONEOS__)
// Toggle virtual keyboard with Android back button. For some reason I get double inputs, so ignore everything once it's already down.
if( ev.key.keysym.sym == SDLK_AC_BACK && ac_back_down_time == 0 ) {
ac_back_down_time = ticks;
@@ -3053,7 +3135,7 @@ static void CheckMessages()
SDL_ShowCursor( SDL_DISABLE );
}
keyboard_mode mode = keyboard_mode::keychar;
-#if !defined(__ANDROID__) && !(defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE == 1)
+#if !defined(__ANDROID__) && !defined(__IPHONEOS__)
if( !SDL_IsTextInputActive() ) {
mode = keyboard_mode::keycode;
}
@@ -3067,7 +3149,7 @@ static void CheckMessages()
// key was handled
} else {
last_input = input_event( lc, input_event_t::keyboard_char );
-#if defined(__ANDROID__)
+#if defined(__ANDROID__) || defined(__IPHONEOS__)
if( !android_is_hardware_keyboard_available() ) {
if( !is_string_input( touch_input_context ) && !touch_input_context.allow_text_entry ) {
if( get_option( "ANDROID_AUTO_KEYBOARD" ) ) {
@@ -3111,7 +3193,7 @@ static void CheckMessages()
}
#endif
keyboard_mode mode = keyboard_mode::keychar;
-#if !defined(__ANDROID__) && !(defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE == 1)
+#if !defined(__ANDROID__) && !defined(__IPHONEOS__)
if( !SDL_IsTextInputActive() ) {
mode = keyboard_mode::keycode;
}
@@ -3135,7 +3217,7 @@ static void CheckMessages()
if( strlen( ev.text.text ) > 0 ) {
const unsigned lc = UTF8_getch( ev.text.text );
last_input = input_event( lc, input_event_t::keyboard_char );
-#if defined(__ANDROID__)
+#if defined(__ANDROID__) || defined(__IPHONEOS__)
if( !android_is_hardware_keyboard_available() ) {
if( !is_string_input( touch_input_context ) && !touch_input_context.allow_text_entry ) {
if( get_option( "ANDROID_AUTO_KEYBOARD" ) ) {
@@ -3266,10 +3348,14 @@ static void CheckMessages()
}
break;
-#if defined(__ANDROID__)
+#if defined(__ANDROID__) || defined(__IPHONEOS__)
case SDL_FINGERMOTION:
+ dbg( D_INFO ) << "Fingermotion triggered.";
+ dbg( D_INFO ) << "ev.tfinger.fingerId: " << ev.tfinger.fingerId;
+ dbg( D_INFO ) << "ev.tfinger.touchId: " << ev.tfinger.touchId;
if( SDL_GetNumTouchFingers( ev.tfinger.touchId ) == 1 ) {
if( !is_quick_shortcut_touch ) {
+ dbg( D_INFO ) << "Not quick shortcut touch";
update_finger_repeat_delay();
}
needupdate = true; // ensure virtual joystick and quick shortcuts redraw as we interact
@@ -3291,7 +3377,6 @@ static void CheckMessages()
finger_down_y += delta_y * delta_ratio;
}
}
-
} else if( SDL_GetNumTouchFingers( ev.tfinger.touchId ) == 2 ) {
second_finger_curr_x = ev.tfinger.x * WindowWidth;
second_finger_curr_y = ev.tfinger.y * WindowHeight;
@@ -3301,13 +3386,19 @@ static void CheckMessages()
}
break;
case SDL_FINGERDOWN:
+ dbg( D_INFO ) << "Fingerdown triggered.";
+ dbg( D_INFO ) << "ev.tfinger.fingerId: " << ev.tfinger.fingerId;
+ dbg( D_INFO ) << "ev.tfinger.touchId: " << ev.tfinger.touchId;
if( SDL_GetNumTouchFingers( ev.tfinger.touchId ) == 1 ) {
finger_down_x = finger_curr_x = ev.tfinger.x * WindowWidth;
finger_down_y = finger_curr_y = ev.tfinger.y * WindowHeight;
finger_down_time = ticks;
finger_repeat_time = 0;
is_quick_shortcut_touch = get_quick_shortcut_under_finger() != NULL;
+ DebugLog( D_INFO, DC_ALL ) << "finger_down_x: " << finger_down_x;
+ DebugLog( D_INFO, DC_ALL ) << "finger_down_y: " << finger_down_y;
if( !is_quick_shortcut_touch ) {
+ dbg( D_INFO ) << "Not quick shortcut touch";
update_finger_repeat_delay();
}
ui_manager::redraw_invalidated();
@@ -3315,6 +3406,8 @@ static void CheckMessages()
} else if( SDL_GetNumTouchFingers( ev.tfinger.touchId ) == 2 && !is_quick_shortcut_touch ) {
second_finger_down_x = second_finger_curr_x = ev.tfinger.x * WindowWidth;
second_finger_down_y = second_finger_curr_y = ev.tfinger.y * WindowHeight;
+ DebugLog( D_INFO, DC_ALL ) << "second_finger_curr_x: " << finger_down_x;
+ DebugLog( D_INFO, DC_ALL ) << "second_finger_curr_y: " << second_finger_curr_y;
is_two_finger_touch = true;
} else if( SDL_GetNumTouchFingers( ev.tfinger.touchId ) == 3 && !is_quick_shortcut_touch ) {
third_finger_down_x = third_finger_curr_x = ev.tfinger.x * WindowWidth;
@@ -3324,9 +3417,14 @@ static void CheckMessages()
}
break;
case SDL_FINGERUP:
+ dbg( D_INFO ) << "Fingerup triggered.";
+ dbg( D_INFO ) << "ev.tfinger.fingerId: " << ev.tfinger.fingerId;
+ dbg( D_INFO ) << "ev.tfinger.touchId: " << ev.tfinger.touchId;
if( SDL_GetNumTouchFingers( ev.tfinger.touchId ) == 1 ) {
finger_curr_x = ev.tfinger.x * WindowWidth;
finger_curr_y = ev.tfinger.y * WindowHeight;
+ DebugLog( D_INFO, DC_ALL ) << "finger_curr_x: " << finger_curr_x;
+ DebugLog( D_INFO, DC_ALL ) << "finger_curr_y: " << finger_curr_y;
if( is_quick_shortcut_touch ) {
input_event *quick_shortcut = get_quick_shortcut_under_finger();
if( quick_shortcut ) {
@@ -3436,9 +3534,10 @@ static void CheckMessages()
quick_shortcuts_enabled = !quick_shortcuts_enabled;
quick_shortcuts_toggle_handled = true;
-
+ //TODO: get some equivalent to iOS
// Display an Android toast message
{
+#if defined(__ANDROID__)
JNIEnv *env = ( JNIEnv * )SDL_AndroidGetJNIEnv();
jobject activity = ( jobject )SDL_AndroidGetActivity();
jclass clazz( env->GetObjectClass( activity ) );
@@ -3448,6 +3547,7 @@ static void CheckMessages()
env->CallVoidMethod( activity, method_id, toast_message );
env->DeleteLocalRef( activity );
env->DeleteLocalRef( clazz );
+#endif
}
} else {
last_input = input_event( three_tap_key, input_event_t::keyboard_char );
@@ -3565,7 +3665,7 @@ static void init_term_size_and_scaling_factor()
scaling_factor = 1;
point terminal( get_option( "TERMINAL_X" ), get_option( "TERMINAL_Y" ) );
-#if !defined(__ANDROID__)
+#if !defined(__ANDROID__) || !defined(__IPHONEOS__)
if( get_option( "SCALING_FACTOR" ) == "2" ) {
scaling_factor = 2;
@@ -3759,7 +3859,7 @@ void catacurses::init_interface()
stdscr = newwin( get_terminal_height(), get_terminal_width(), point::zero );
//newwin calls `new WINDOW`, and that will throw, but not return nullptr.
imclient->load_fonts( gui_font, font, windowsPalette, fl.gui_typeface, fl.typeface );
-#if defined(__ANDROID__)
+#if defined(__ANDROID__) || defined(__IPHONEOS__)
// Make sure we initialize preview_terminal_width/height to sensible values
preview_terminal_width = TERMINAL_WIDTH * fontwidth;
preview_terminal_height = TERMINAL_HEIGHT * fontheight;
@@ -3845,7 +3945,7 @@ input_event input_manager::get_input_event( const keyboard_mode preferred_keyboa
throw std::runtime_error( "input_manager::get_input_event called in test mode" );
}
-#if !defined(__ANDROID__) && !(defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE == 1)
+#if !defined(__ANDROID__) && !defined(__IPHONEOS__)
if( actual_keyboard_mode( preferred_keyboard_mode ) == keyboard_mode::keychar ) {
StartTextInput();
} else {
@@ -3903,11 +4003,11 @@ input_event input_manager::get_input_event( const keyboard_mode preferred_keyboa
SDL_GetMouseState( &last_input.mouse_pos.x, &last_input.mouse_pos.y );
if( last_input.type == input_event_t::keyboard_char ) {
previously_pressed_key = last_input.get_first_input();
-#if defined(__ANDROID__)
+#if defined(__ANDROID__) || defined(__IPHONEOS__)
android_vibrate();
#endif
}
-#if defined(__ANDROID__)
+#if defined(__ANDROID__) || defined(__IPHONEOS__)
else if( last_input.type == input_event_t::gamepad ) {
android_vibrate();
}
diff --git a/src/sdltiles.h b/src/sdltiles.h
index 2617b6716a08e..60177bc597ffb 100644
--- a/src/sdltiles.h
+++ b/src/sdltiles.h
@@ -19,11 +19,6 @@ class window;
#include "sdl_wrappers.h"
#include "string_id.h"
-#if defined(__APPLE__)
-// For TARGET_OS_IPHONE macro to test if is on iOS
-#include
-#endif
-
class cata_tiles;
struct weather_type;
diff --git a/src/string_editor_window.cpp b/src/string_editor_window.cpp
index 0b89d488f0f91..3a77706ec79ed 100644
--- a/src/string_editor_window.cpp
+++ b/src/string_editor_window.cpp
@@ -4,7 +4,7 @@
#include "sdl_wrappers.h"
#endif
-#if defined(__ANDROID__)
+#if defined(__ANDROID__) || defined(__IPHONEOS__)
#include
#include "cata_utility.h"
#include "options.h"
@@ -403,7 +403,7 @@ std::pair string_editor_window::query_string()
wnoutrefresh( _win );
} );
-#if defined(__ANDROID__)
+#if defined(__ANDROID__) || defined(__IPHONEOS__)
on_out_of_scope stop_text_input( []() {
if( get_option( "ANDROID_AUTO_KEYBOARD" ) ) {
StopTextInput();
diff --git a/src/string_input_popup.cpp b/src/string_input_popup.cpp
index d8143b8db5340..d74c82fa851b9 100644
--- a/src/string_input_popup.cpp
+++ b/src/string_input_popup.cpp
@@ -19,7 +19,7 @@
#include "sdl_wrappers.h"
#endif
-#if defined(__ANDROID__)
+#if defined(__ANDROID__) || defined(__IPHONEOS__)
#include
#include "options.h"
@@ -462,7 +462,7 @@ const std::string &string_input_popup::query_string( const bool loop, const bool
if( desc_view_ptr && desc_view_ptr->handle_navigation( action, *ctxt ) ) {
// NO FURTHER ACTION REQUIRED
} else if( action == "TEXT.QUIT" ) {
-#if defined(__ANDROID__)
+#if defined(__ANDROID__) || defined(__IPHONEOS__)
if( get_option( "ANDROID_AUTO_KEYBOARD" ) ) {
StopTextInput();
}
diff --git a/src/swift/ios_stuff.swift b/src/swift/ios_stuff.swift
new file mode 100644
index 0000000000000..df40d9dbccbe5
--- /dev/null
+++ b/src/swift/ios_stuff.swift
@@ -0,0 +1,36 @@
+import Foundation
+import GameController
+import UIKit
+import AVFoundation
+
+//https://stackoverflow.com/a/76728094
+extension UIWindow {
+ static var current: UIWindow? {
+ for scene in UIApplication.shared.connectedScenes {
+ guard let windowScene = scene as? UIWindowScene else { continue }
+ for window in windowScene.windows {
+ if window.isKeyWindow { return window }
+ }
+ }
+ return nil
+ }
+}
+
+extension UIScreen {
+ static var current: UIScreen? {
+ UIWindow.current?.screen
+ }
+}
+
+public func iosGetDisplayDensity() -> Float {
+ let scale = UIScreen.current?.nativeScale ?? 1.0
+ return Float(scale)
+}
+
+public func isIOSKeyBoardAvailable() -> Bool {
+ return GCKeyboard.coalesced != nil
+}
+
+public func vibrateDevice() {
+ AudioServicesPlaySystemSound(kSystemSoundID_Vibrate)
+}
diff --git a/src/ui.cpp b/src/ui.cpp
index 612342c27b50e..1136e163451ea 100644
--- a/src/ui.cpp
+++ b/src/ui.cpp
@@ -27,6 +27,9 @@
#if defined(__ANDROID__)
#include
+#endif
+
+#if defined(__ANDROID__) || defined(__IPHONEOS__)
#include
#include "options.h"
@@ -934,7 +937,7 @@ void uilist::query( bool loop, int timeout, bool allow_unfiltered_hotkeys )
shared_ptr_fast ui = create_or_get_ui();
-#if defined(__ANDROID__)
+#if defined(__ANDROID__) || defined(__IPHONEOS__)
for( const auto &entry : entries ) {
if( entry.enabled && entry.hotkey.has_value()
&& entry.hotkey.value() != input_event() ) {
diff --git a/tests/act_build_test.cpp b/tests/act_build_test.cpp
index 45b4e25e53ea9..aaecea5228687 100644
--- a/tests/act_build_test.cpp
+++ b/tests/act_build_test.cpp
@@ -302,4 +302,4 @@ TEST_CASE( "npc_act_multiple_construction", "[npc][zones][activities][constructi
u.set_body();
u.set_fac( faction_free_merchants );
run_test_case( u );
-}
+}
\ No newline at end of file