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