From 1cc40c0a7c260f0562572bc99f39a666a12f1b09 Mon Sep 17 00:00:00 2001 From: sapier Date: Mon, 21 Apr 2014 14:10:59 +0200 Subject: Add support for Android 2.3+ There have been plenty of ppl involved in creating this version. I don't wanna mention names as I'm sure I'd forget someone so I just tell where help has been done: - The partial android versions done by various ppl - Testing on different android devices - reviewing code (especially the in core changes) - testing controls - reviewing texts A big thank you to everyone helping this to be completed! --- src/client.cpp | 1 - src/config.h | 14 +- src/debug.cpp | 6 +- src/defaultsettings.cpp | 36 +- src/drawscene.cpp | 7 + src/filesys.cpp | 21 +- src/game.cpp | 120 +++++-- src/guiEngine.cpp | 18 + src/guiFormSpecMenu.cpp | 248 ++++++++++++- src/guiFormSpecMenu.h | 14 +- src/httpfetch.cpp | 8 +- src/hud.cpp | 13 + src/itemdef.cpp | 9 + src/jthread/pthread/jsemaphore.cpp | 8 + src/jthread/pthread/jthread.cpp | 4 + src/keycode.cpp | 1 + src/keycode.h | 4 + src/log.cpp | 5 + src/lua/src/llex.c | 6 + src/main.cpp | 107 ++++-- src/mainmenumanager.h | 5 + src/modalMenu.h | 7 + src/porting.cpp | 39 ++- src/porting.h | 28 +- src/porting_android.cpp | 295 ++++++++++++++++ src/porting_android.h | 81 +++++ src/tile.cpp | 227 ++++++++++-- src/tile.h | 19 + src/touchscreengui.cpp | 690 +++++++++++++++++++++++++++++++++++++ src/touchscreengui.h | 160 +++++++++ src/util/string.cpp | 83 +++++ src/version.cpp | 12 +- 32 files changed, 2169 insertions(+), 127 deletions(-) create mode 100644 src/porting_android.cpp create mode 100644 src/porting_android.h create mode 100644 src/touchscreengui.cpp create mode 100644 src/touchscreengui.h (limited to 'src') diff --git a/src/client.cpp b/src/client.cpp index 8b89dd63c..601561f7d 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -47,7 +47,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "serialization.h" #include "util/serialize.h" #include "config.h" -#include "cmake_config_githash.h" #include "util/directiontables.h" #include "util/pointedthing.h" #include "version.h" diff --git a/src/config.h b/src/config.h index 8a9d7d63d..54c13d440 100644 --- a/src/config.h +++ b/src/config.h @@ -8,7 +8,10 @@ #define PROJECT_NAME "Minetest" #define RUN_IN_PLACE 0 +#define STATIC_SHAREDIR "" + #define USE_GETTEXT 0 + #ifndef USE_SOUND #define USE_SOUND 0 #endif @@ -17,8 +20,9 @@ #define USE_CURL 0 #endif -#define USE_FREETYPE 0 -#define STATIC_SHAREDIR "" +#ifndef USE_FREETYPE + #define USE_FREETYPE 0 +#endif #ifndef USE_LEVELDB #define USE_LEVELDB 0 @@ -70,5 +74,11 @@ #define VERSION_EXTRA_STRING CMAKE_VERSION_EXTRA_STRING #endif +#ifdef __ANDROID__ + #include "android_version.h" +#else + #include "cmake_config_githash.h" +#endif + #endif diff --git a/src/debug.cpp b/src/debug.cpp index 278902a08..8c02f1d6b 100644 --- a/src/debug.cpp +++ b/src/debug.cpp @@ -18,6 +18,7 @@ with this program; if not, write to the Free Software Foundation, Inc., */ +#include "porting.h" #include "debug.h" #include "exceptions.h" #include "threads.h" @@ -27,7 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include "jthread/jmutex.h" #include "jthread/jmutexautolock.h" - +#include "config.h" /* Debug output */ @@ -95,6 +96,9 @@ public: } std::streamsize xsputn(const char *s, std::streamsize n) { +#ifdef __ANDROID__ + __android_log_print(ANDROID_LOG_VERBOSE, PROJECT_NAME, "%s", s); +#endif for(int i=0; isetDefault("doubletap_jump", "false"); settings->setDefault("always_fly_fast", "true"); settings->setDefault("directional_colored_fog", "true"); - settings->setDefault("tooltip_show_delay", "400"); + settings->setDefault("tooltip_show_delay", "400"); // Some (temporary) keys for debugging settings->setDefault("keymap_print_debug_stacks", "KEY_KEY_P"); @@ -154,6 +154,7 @@ void set_default_settings(Settings *settings) settings->setDefault("curl_timeout", "5000"); settings->setDefault("curl_parallel_limit", "8"); settings->setDefault("curl_file_download_timeout", "300000"); + settings->setDefault("curl_verify_cert", "true"); settings->setDefault("enable_remote_media_server", "true"); @@ -278,6 +279,39 @@ void set_default_settings(Settings *settings) settings->setDefault("high_precision_fpu", "true"); settings->setDefault("language", ""); + +#ifdef __ANDROID__ + settings->setDefault("screenW", "0"); + settings->setDefault("screenH", "0"); + settings->setDefault("enable_shaders", "false"); + settings->setDefault("fullscreen", "true"); + settings->setDefault("enable_particles", "false"); + settings->setDefault("video_driver", "ogles1"); + settings->setDefault("touchtarget", "true"); + settings->setDefault("main_menu_script","/sdcard/Minetest/builtin/mainmenu/init_android.lua"); + settings->setDefault("TMPFolder","/sdcard/Minetest/tmp/"); + settings->setDefault("touchscreen_threshold","20"); + settings->setDefault("smooth_lighting", "false"); + settings->setDefault("max_simultaneous_block_sends_per_client", "3"); + settings->setDefault("emergequeue_limit_diskonly", "8"); + settings->setDefault("emergequeue_limit_generate", "8"); + settings->setDefault("preload_item_visuals", "false"); + + settings->setDefault("viewing_range_nodes_max", "50"); + settings->setDefault("viewing_range_nodes_min", "20"); + settings->setDefault("inventory_image_hack", "false"); + + //check for device with small screen + float x_inches = ((double) porting::getDisplaySize().X / + (160 * porting::getDisplayDensity())); + if (x_inches < 3.5) { + settings->setDefault("gui_scaling", "0.6"); + } + else if (x_inches < 4.5) { + settings->setDefault("gui_scaling", "0.7"); + } + settings->setDefault("curl_verify_cert","false"); +#endif } void late_init_default_settings(Settings* settings) diff --git a/src/drawscene.cpp b/src/drawscene.cpp index a69cf4403..9672affea 100644 --- a/src/drawscene.cpp +++ b/src/drawscene.cpp @@ -427,6 +427,13 @@ void draw_scene(video::IVideoDriver* driver, scene::ISceneManager* smgr, bool draw_crosshair = ((player->hud_flags & HUD_FLAG_CROSSHAIR_VISIBLE) && (camera.getCameraMode() != CAMERA_MODE_THIRD_FRONT)); +#ifdef HAVE_TOUCHSCREENGUI + try { + draw_crosshair = !g_settings->getBool("touchtarget"); + } + catch(SettingNotFoundException) {} +#endif + std::string draw_mode = g_settings->get("3d_mode"); smgr->drawAll(); diff --git a/src/filesys.cpp b/src/filesys.cpp index eda36c833..7c72a4b27 100644 --- a/src/filesys.cpp +++ b/src/filesys.cpp @@ -25,6 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include #include "log.h" +#include "config.h" namespace fs { @@ -34,8 +35,8 @@ namespace fs #define _WIN32_WINNT 0x0501 #include #include -#include -#include +#include +#include #define BUFSIZE MAX_PATH @@ -73,12 +74,12 @@ std::vector GetDirListing(std::string pathstring) // Find the first file in the directory. hFind = FindFirstFile(DirSpec, &FindFileData); - if (hFind == INVALID_HANDLE_VALUE) + if (hFind == INVALID_HANDLE_VALUE) { retval = (-1); goto Cleanup; - } - else + } + else { // NOTE: // Be very sure to not include '..' in the results, it will @@ -91,7 +92,7 @@ std::vector GetDirListing(std::string pathstring) listing.push_back(node); // List all the other files in the directory. - while (FindNextFile(hFind, &FindFileData) != 0) + while (FindNextFile(hFind, &FindFileData) != 0) { DirListNode node; node.name = FindFileData.cFileName; @@ -102,7 +103,7 @@ std::vector GetDirListing(std::string pathstring) dwError = GetLastError(); FindClose(hFind); - if (dwError != ERROR_NO_MORE_FILES) + if (dwError != ERROR_NO_MORE_FILES) { errorstream<<"GetDirListing: FindNextFile error. Error is " <close\n" + "- touch stack, touch slot:\n" + " --> move stack\n" + "- touch&drag, tap 2nd finger\n" + " --> place single item to slot\n" + )); +#else std::string control_text = wide_to_narrow(wstrgettext("Default Controls:\n" "- WASD: move\n" "- Space: jump/climb\n" @@ -1002,11 +1027,11 @@ static void show_pause_menu(GUIFormSpecMenu** cur_formspec, "- Mouse wheel: select item\n" "- T: chat\n" )); - +#endif float ypos = singleplayermode ? 1.0 : 0.5; std::ostringstream os; - os << FORMSPEC_VERSION_STRING << "size[11,5.5,true]" + os << FORMSPEC_VERSION_STRING << SIZE_TAG << "button_exit[4," << (ypos++) << ";3,0.5;btn_continue;" << wide_to_narrow(wstrgettext("Continue")) << "]"; @@ -1021,7 +1046,7 @@ static void show_pause_menu(GUIFormSpecMenu** cur_formspec, << wide_to_narrow(wstrgettext("Exit to Menu")) << "]"; os << "button_exit[4," << (ypos++) << ";3,0.5;btn_exit_os;" << wide_to_narrow(wstrgettext("Exit to OS")) << "]" - << "textarea[7.5,0.25;3.75,6;;" << control_text << ";]" + << "textarea[7.5,0.25;3.9,6.25;;" << control_text << ";]" << "textarea[0.4,0.25;3.5,6;;" << "Minetest\n" << minetest_build_info << "\n" << "path_user = " << wrap_rows(porting::path_user, 20) @@ -1253,18 +1278,18 @@ void the_game(bool &kill, bool random_input, InputHandler *input, server->step(dtime); // End condition - if(client.getState() == LC_Init){ + if(client.getState() == LC_Init) { could_connect = true; break; } // Break conditions - if(client.accessDenied()){ + if(client.accessDenied()) { error_message = L"Access denied. Reason: " +client.accessDeniedReason(); errorstream<wasKeyDown(EscapeKey)){ + if(input->wasKeyDown(EscapeKey) || input->wasKeyDown(CancelKey)) { connect_aborted = true; infostream<<"Connect aborted [Escape]"<getFloat("fps_max"); bool cloud_menu_background = g_settings->getBool("menu_clouds"); u32 lasttime = device->getTimer()->getTime(); - while(device->run()) - { + while (device->run()) { f32 dtime = 0.033; // in seconds if (cloud_menu_background) { u32 time = device->getTimer()->getTime(); @@ -1343,29 +1367,29 @@ void the_game(bool &kill, bool random_input, InputHandler *input, } // Update client and server client.step(dtime); - if(server != NULL) + if (server != NULL) server->step(dtime); // End condition - if(client.mediaReceived() && + if (client.mediaReceived() && client.itemdefReceived() && - client.nodedefReceived()){ + client.nodedefReceived()) { got_content = true; break; } // Break conditions - if(client.accessDenied()){ + if (client.accessDenied()) { error_message = L"Access denied. Reason: " +client.accessDeniedReason(); errorstream<wasKeyDown(EscapeKey)){ + if (input->wasKeyDown(EscapeKey) || input->wasKeyDown(CancelKey)) { content_aborted = true; infostream<<"Connect aborted [Escape]"<setVisible(false); guitext_profiler->setWordWrap(true); +#ifdef HAVE_TOUCHSCREENGUI + if (g_touchscreengui) + g_touchscreengui->init(tsrc,porting::getDisplayDensity()); +#endif + /* Some statistics are collected in these */ @@ -1641,7 +1670,8 @@ void the_game(bool &kill, bool random_input, InputHandler *input, for(;;) { - if(device->run() == false || kill == true) + if(device->run() == false || kill == true || + g_gamecallback->shutdown_requested) break; v2u32 screensize = driver->getScreenSize(); @@ -1858,6 +1888,15 @@ void the_game(bool &kill, bool random_input, InputHandler *input, // Input handler step() (used by the random input generator) input->step(dtime); +#ifdef HAVE_TOUCHSCREENGUI + if (g_touchscreengui) { + g_touchscreengui->step(dtime); + } +#endif +#ifdef __ANDROID__ + if (current_formspec != 0) + current_formspec->getAndroidUIInput(); +#endif // Increase timer for doubleclick of "jump" if(g_settings->getBool("doubletap_jump") && jump_timer <= 0.2) @@ -1890,7 +1929,7 @@ void the_game(bool &kill, bool random_input, InputHandler *input, inventoryloc.setCurrentPlayer(); current_formspec->setFormSpec(fs_src->getForm(), inventoryloc); } - else if(input->wasKeyDown(EscapeKey)) + else if(input->wasKeyDown(EscapeKey) || input->wasKeyDown(CancelKey)) { show_pause_menu(¤t_formspec, &client, gamedef, tsrc, device, simple_singleplayer_mode); @@ -2214,21 +2253,29 @@ void the_game(bool &kill, bool random_input, InputHandler *input, float turn_amount = 0; if((device->isWindowActive() && noMenuActive()) || random_input) { +#ifndef __ANDROID__ if(!random_input) { // Mac OSX gets upset if this is set every frame if(device->getCursorControl()->isVisible()) device->getCursorControl()->setVisible(false); } +#endif if(first_loop_after_window_activation){ //infostream<<"window active, first loop"<getMousePos().X - (driver->getScreenSize().Width/2); - s32 dy = input->getMousePos().Y - (driver->getScreenSize().Height/2); - if(invert_mouse || camera.getCameraMode() == CAMERA_MODE_THIRD_FRONT) { + } else { +#ifdef HAVE_TOUCHSCREENGUI + if (g_touchscreengui) { + camera_yaw = g_touchscreengui->getYaw(); + camera_pitch = g_touchscreengui->getPitch(); + } else { +#endif + s32 dx = input->getMousePos().X - (driver->getScreenSize().Width/2); + s32 dy = input->getMousePos().Y - (driver->getScreenSize().Height/2); + if ((invert_mouse) + || (camera.getCameraMode() == CAMERA_MODE_THIRD_FRONT)) { dy = -dy; } //infostream<<"window active, pos difference "< 89.5) camera_pitch = 89.5; - - turn_amount = v2f(dx, dy).getLength() * d; } input->setMousePos((driver->getScreenSize().Width/2), (driver->getScreenSize().Height/2)); } else{ +#ifndef ANDROID // Mac OSX gets upset if this is set every frame if(device->getCursorControl()->isVisible() == false) device->getCursorControl()->setVisible(true); +#endif //infostream<<"window inactive"< shootline(camera_position, camera_position + camera_direction * BS * (d+1)); + // prevent player pointing anything in front-view if (camera.getCameraMode() == CAMERA_MODE_THIRD_FRONT) shootline = core::line3d(0,0,0,0,0,0); +#ifdef HAVE_TOUCHSCREENGUI + if ((g_settings->getBool("touchtarget")) && (g_touchscreengui)) { + shootline = g_touchscreengui->getShootline(); + shootline.start += intToFloat(camera_offset,BS); + shootline.end += intToFloat(camera_offset,BS); + } +#endif + ClientActiveObject *selected_object = NULL; PointedThing pointed = getPointedThing( @@ -3156,8 +3217,9 @@ void the_game(bool &kill, bool random_input, InputHandler *input, } else if(show_hud || show_chat) { + u16 fps = (1.0/dtime_avg1); std::ostringstream os(std::ios_base::binary); - os<<"Minetest "< +#endif #include #include @@ -83,6 +87,16 @@ video::ITexture* MenuTextureSource::getTexture(const std::string &name, u32 *id) if(name.empty()) return NULL; m_to_delete.insert(name); + +#ifdef __ANDROID__ + video::IImage *image = m_driver->createImageFromFile(name.c_str()); + if (image) { + image = Align2Npot2(image, m_driver); + video::ITexture* retval = m_driver->addTexture(name.c_str(), image); + image->drop(); + return retval; + } +#endif return m_driver->getTexture(name.c_str()); } @@ -266,6 +280,10 @@ void GUIEngine::run() sleep_ms(25); m_script->step(); + +#ifdef __ANDROID__ + m_menu->getAndroidUIInput(); +#endif } } diff --git a/src/guiFormSpecMenu.cpp b/src/guiFormSpecMenu.cpp index fd12c4d4d..54414d7e7 100644 --- a/src/guiFormSpecMenu.cpp +++ b/src/guiFormSpecMenu.cpp @@ -88,6 +88,9 @@ GUIFormSpecMenu::GUIFormSpecMenu(irr::IrrlichtDevice* dev, m_ext_ptr(ext_ptr), m_font(dev->getGUIEnvironment()->getSkin()->getFont()), m_formspec_version(0) +#ifdef __ANDROID__ + ,m_JavaDialogFieldName(L"") +#endif { current_keys_pending.key_down = false; current_keys_pending.key_up = false; @@ -1878,6 +1881,52 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize) setInitialFocus(); } +#ifdef __ANDROID__ +bool GUIFormSpecMenu::getAndroidUIInput() +{ + /* no dialog shown */ + if (m_JavaDialogFieldName == L"") { + return false; + } + + /* still waiting */ + if (porting::getInputDialogState() == -1) { + return true; + } + + std::wstring fieldname = m_JavaDialogFieldName; + m_JavaDialogFieldName = L""; + + /* no value abort dialog processing */ + if (porting::getInputDialogState() != 0) { + return false; + } + + for(std::vector::iterator iter = m_fields.begin(); + iter != m_fields.end(); iter++) { + + if (iter->fname != fieldname) { + continue; + } + IGUIElement* tochange = getElementFromId(iter->fid); + + if (tochange == 0) { + return false; + } + + if (tochange->getType() != irr::gui::EGUIET_EDIT_BOX) { + return false; + } + + std::string text = porting::getInputDialogValue(); + + ((gui::IGUIEditBox*) tochange)-> + setText(narrow_to_wide(text).c_str()); + } + return false; +} +#endif + GUIFormSpecMenu::ItemSpec GUIFormSpecMenu::getItemAtPos(v2s32 p) const { core::rect imgrect(0,0,imgsize.X,imgsize.Y); @@ -1886,8 +1935,7 @@ GUIFormSpecMenu::ItemSpec GUIFormSpecMenu::getItemAtPos(v2s32 p) const { const ListDrawSpec &s = m_inventorylists[i]; - for(s32 i=0; igetCursorControl()->getPosition(); - updateSelectedItem(); gui::IGUISkin* skin = Environment->getSkin(); @@ -2195,6 +2241,11 @@ void GUIFormSpecMenu::drawMenu() */ gui::IGUIElement::draw(); +/* TODO find way to show tooltips on touchscreen */ +#ifndef HAVE_TOUCHSCREENGUI + m_pointer = m_device->getCursorControl()->getPosition(); +#endif + /* Draw fields/buttons tooltips */ @@ -2491,7 +2542,8 @@ bool GUIFormSpecMenu::preprocessEvent(const SEvent& event) // Fix Esc/Return key being eaten by checkboxen and tables if(event.EventType==EET_KEY_INPUT_EVENT) { KeyPress kp(event.KeyInput); - if (kp == EscapeKey || kp == getKeySetting("keymap_inventory") + if (kp == EscapeKey || kp == CancelKey + || kp == getKeySetting("keymap_inventory") || event.KeyInput.Key==KEY_RETURN) { gui::IGUIElement *focused = Environment->getFocus(); if (focused && isMyChild(focused) && @@ -2533,6 +2585,156 @@ bool GUIFormSpecMenu::preprocessEvent(const SEvent& event) } } + #ifdef __ANDROID__ + // display software keyboard when clicking edit boxes + if (event.EventType == EET_MOUSE_INPUT_EVENT + && event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN) { + gui::IGUIElement *hovered = + Environment->getRootGUIElement()->getElementFromPoint( + core::position2d(event.MouseInput.X, event.MouseInput.Y)); + if ((hovered) && (hovered->getType() == irr::gui::EGUIET_EDIT_BOX)) { + bool retval = hovered->OnEvent(event); + if (retval) { + Environment->setFocus(hovered); + } + m_JavaDialogFieldName = getNameByID(hovered->getID()); + std::string message = gettext("Enter "); + std::string label = wide_to_narrow(getLabelByID(hovered->getID())); + if (label == "") { + label = "text"; + } + message += gettext(label) + ":"; + + /* single line text input */ + int type = 2; + + /* multi line text input */ + if (((gui::IGUIEditBox*) hovered)->isMultiLineEnabled()) { + type = 1; + } + + /* passwords are always single line */ + if (((gui::IGUIEditBox*) hovered)->isPasswordBox()) { + type = 3; + } + + porting::showInputDialog(gettext("ok"), "", + wide_to_narrow(((gui::IGUIEditBox*) hovered)->getText()), + type); + return retval; + } + } + + if (event.EventType == EET_TOUCH_INPUT_EVENT) + { + SEvent translated; + memset(&translated, 0, sizeof(SEvent)); + translated.EventType = EET_MOUSE_INPUT_EVENT; + gui::IGUIElement* root = Environment->getRootGUIElement(); + + if (!root) { + errorstream + << "GUIFormSpecMenu::preprocessEvent unable to get root element" + << std::endl; + return false; + } + gui::IGUIElement* hovered = root->getElementFromPoint( + core::position2d( + event.TouchInput.X, + event.TouchInput.Y)); + + translated.MouseInput.X = event.TouchInput.X; + translated.MouseInput.Y = event.TouchInput.Y; + translated.MouseInput.Control = false; + + bool dont_send_event = false; + + if (event.TouchInput.touchedCount == 1) { + switch (event.TouchInput.Event) { + case ETIE_PRESSED_DOWN: + m_pointer = v2s32(event.TouchInput.X,event.TouchInput.Y); + translated.MouseInput.Event = EMIE_LMOUSE_PRESSED_DOWN; + translated.MouseInput.ButtonStates = EMBSM_LEFT; + m_down_pos = m_pointer; + break; + case ETIE_MOVED: + m_pointer = v2s32(event.TouchInput.X,event.TouchInput.Y); + translated.MouseInput.Event = EMIE_MOUSE_MOVED; + translated.MouseInput.ButtonStates = EMBSM_LEFT; + break; + case ETIE_LEFT_UP: + translated.MouseInput.Event = EMIE_LMOUSE_LEFT_UP; + translated.MouseInput.ButtonStates = 0; + hovered = root->getElementFromPoint(m_down_pos); + /* we don't have a valid pointer element use last + * known pointer pos */ + translated.MouseInput.X = m_pointer.X; + translated.MouseInput.Y = m_pointer.Y; + + /* reset down pos */ + m_down_pos = v2s32(0,0); + break; + default: + dont_send_event = true; + //this is not supposed to happen + errorstream + << "GUIFormSpecMenu::preprocessEvent unexpected usecase Event=" + << event.TouchInput.Event << std::endl; + } + } else if ( (event.TouchInput.touchedCount == 2) && + (event.TouchInput.Event == ETIE_PRESSED_DOWN) ) { + hovered = root->getElementFromPoint(m_down_pos); + + translated.MouseInput.Event = EMIE_RMOUSE_PRESSED_DOWN; + translated.MouseInput.ButtonStates = EMBSM_LEFT | EMBSM_RIGHT; + translated.MouseInput.X = m_pointer.X; + translated.MouseInput.Y = m_pointer.Y; + + if (hovered) { + hovered->OnEvent(translated); + } + + translated.MouseInput.Event = EMIE_RMOUSE_LEFT_UP; + translated.MouseInput.ButtonStates = EMBSM_LEFT; + + + if (hovered) { + hovered->OnEvent(translated); + } + dont_send_event = true; + } + /* ignore unhandled 2 touch events ... accidental moving for example */ + else if (event.TouchInput.touchedCount == 2) { + dont_send_event = true; + } + else if (event.TouchInput.touchedCount > 2) { + errorstream + << "GUIFormSpecMenu::preprocessEvent to many multitouch events " + << event.TouchInput.touchedCount << " ignoring them" << std::endl; + } + + if (dont_send_event) { + return true; + } + + /* check if translated event needs to be preprocessed again */ + if (preprocessEvent(translated)) { + return true; + } + if (hovered) { + grab(); + bool retval = hovered->OnEvent(translated); + + if (event.TouchInput.Event == ETIE_LEFT_UP) { + /* reset pointer */ + m_pointer = v2s32(0,0); + } + drop(); + return retval; + } + } + #endif + return false; } @@ -2584,8 +2786,8 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event) { if(event.EventType==EET_KEY_INPUT_EVENT) { KeyPress kp(event.KeyInput); - if (event.KeyInput.PressedDown && (kp == EscapeKey || - kp == getKeySetting("keymap_inventory"))) { + if (event.KeyInput.PressedDown && ( (kp == EscapeKey) || + (kp == getKeySetting("keymap_inventory")) || (kp == CancelKey))) { if (m_allowclose) { doPause = false; acceptInput(quit_mode_cancel); @@ -3015,6 +3217,38 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event) return Parent ? Parent->OnEvent(event) : false; } +/** + * get name of element by element id + * @param id of element + * @return name string or empty string + */ +std::wstring GUIFormSpecMenu::getNameByID(s32 id) +{ + for(std::vector::iterator iter = m_fields.begin(); + iter != m_fields.end(); iter++) { + if (iter->fid == id) { + return iter->fname; + } + } + return L""; +} + +/** + * get label of element by id + * @param id of element + * @return label string or empty string + */ +std::wstring GUIFormSpecMenu::getLabelByID(s32 id) +{ + for(std::vector::iterator iter = m_fields.begin(); + iter != m_fields.end(); iter++) { + if (iter->fid == id) { + return iter->flabel; + } + } + return L""; +} + bool GUIFormSpecMenu::parseColor(const std::string &value, video::SColor &color, bool quiet) { diff --git a/src/guiFormSpecMenu.h b/src/guiFormSpecMenu.h index 5d74978a9..72a188bc5 100644 --- a/src/guiFormSpecMenu.h +++ b/src/guiFormSpecMenu.h @@ -151,7 +151,7 @@ class GUIFormSpecMenu : public GUIModalMenu { } FieldSpec(const std::wstring &name, const std::wstring &label, - const std::wstring &fdeflt, int id) : + const std::wstring &fdeflt, int id) : fname(name), flabel(label), fdefault(fdeflt), @@ -274,6 +274,10 @@ public: static bool parseColor(const std::string &value, video::SColor &color, bool quiet); +#ifdef __ANDROID__ + bool getAndroidUIInput(); +#endif + protected: v2s32 getBasePos() const { @@ -409,6 +413,14 @@ private: clickpos m_doubleclickdetect[2]; int m_btn_height; + + std::wstring getLabelByID(s32 id); + std::wstring getNameByID(s32 id); +#ifdef __ANDROID__ + v2s32 m_down_pos; + std::wstring m_JavaDialogFieldName; +#endif + }; class FormspecFormSource: public IFormSource diff --git a/src/httpfetch.cpp b/src/httpfetch.cpp index 313988fd8..69c366ee0 100644 --- a/src/httpfetch.cpp +++ b/src/httpfetch.cpp @@ -46,7 +46,7 @@ HTTPFetchRequest::HTTPFetchRequest() request_id = 0; timeout = g_settings->getS32("curl_timeout"); connect_timeout = timeout; - + useragent = std::string("Minetest/") + minetest_version_hash + " (" + porting::get_sysinfo() + ")"; } @@ -259,6 +259,10 @@ struct HTTPFetchOngoing request.extra_headers[i].c_str()); } curl_easy_setopt(curl, CURLOPT_HTTPHEADER, httpheader); + + if (!g_settings->getBool("curl_verify_cert")) { + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, false); + } } } @@ -302,7 +306,7 @@ struct HTTPFetchOngoing } if (res != CURLE_OK) { - infostream< +#ifdef HAVE_TOUCHSCREENGUI +#include "touchscreengui.h" +#endif Hud::Hud(video::IVideoDriver *driver, scene::ISceneManager* smgr, gui::IGUIEnvironment* guienv, gui::IGUIFont *font, @@ -160,6 +163,11 @@ void Hud::drawItem(const ItemStack &item, const core::rect& rect, bool sele void Hud::drawItems(v2s32 upperleftpos, s32 itemcount, s32 offset, InventoryList *mainlist, u16 selectitem, u16 direction) { +#ifdef HAVE_TOUCHSCREENGUI + if ( (g_touchscreengui) && (offset == 0)) + g_touchscreengui->resetHud(); +#endif + s32 height = m_hotbar_imagesize + m_padding * 2; s32 width = (itemcount - offset) * (m_hotbar_imagesize + m_padding * 2); @@ -222,6 +230,11 @@ void Hud::drawItems(v2s32 upperleftpos, s32 itemcount, s32 offset, } drawItem(mainlist->getItem(i), (imgrect + pos + steppos), (i +1) == selectitem ); + +#ifdef HAVE_TOUCHSCREENGUI + if (g_touchscreengui) + g_touchscreengui->registerHudItem(i, (imgrect + pos + steppos)); +#endif } } diff --git a/src/itemdef.cpp b/src/itemdef.cpp index 0187c7387..10e1afe2d 100644 --- a/src/itemdef.cpp +++ b/src/itemdef.cpp @@ -38,6 +38,10 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include +#ifdef __ANDROID__ +#include +#endif + /* ItemDefinition */ @@ -433,6 +437,11 @@ public: params.light_color.set(1.0, 0.5, 0.5, 0.5); params.light_radius = 1000; +#ifdef __ANDROID__ + params.camera_position.set(0, -1.0, -1.5); + params.camera_position.rotateXZBy(45); + params.light_position.set(10, -100, -50); +#endif cc->inventory_texture = tsrc->generateTextureFromMesh(params); diff --git a/src/jthread/pthread/jsemaphore.cpp b/src/jthread/pthread/jsemaphore.cpp index f6d7f022f..609e2f518 100644 --- a/src/jthread/pthread/jsemaphore.cpp +++ b/src/jthread/pthread/jsemaphore.cpp @@ -51,7 +51,15 @@ JSemaphore::JSemaphore() { JSemaphore::~JSemaphore() { int sem_destroy_retval = sem_destroy(&m_semaphore); +#ifdef __ANDROID__ +// WORKAROUND for broken bionic semaphore implementation! + assert( + (sem_destroy_retval == 0) || + (errno == EBUSY) + ); +#else assert(sem_destroy_retval == 0); +#endif UNUSED(sem_destroy_retval); } diff --git a/src/jthread/pthread/jthread.cpp b/src/jthread/pthread/jthread.cpp index a8e54e315..e90c03456 100644 --- a/src/jthread/pthread/jthread.cpp +++ b/src/jthread/pthread/jthread.cpp @@ -111,7 +111,11 @@ int JThread::Kill() } return ERR_JTHREAD_NOTRUNNING; } +#ifdef __ANDROID__ + pthread_kill(threadid, SIGKILL); +#else pthread_cancel(threadid); +#endif if (started) { int pthread_join_retval = pthread_join(threadid,&status); assert(pthread_join_retval == 0); diff --git a/src/keycode.cpp b/src/keycode.cpp index 96631b4ea..890c97cc2 100644 --- a/src/keycode.cpp +++ b/src/keycode.cpp @@ -334,6 +334,7 @@ const char *KeyPress::name() const } const KeyPress EscapeKey("KEY_ESCAPE"); +const KeyPress CancelKey("KEY_CANCEL"); const KeyPress NumberKey[] = { KeyPress("KEY_KEY_0"), KeyPress("KEY_KEY_1"), KeyPress("KEY_KEY_2"), KeyPress("KEY_KEY_3"), KeyPress("KEY_KEY_4"), KeyPress("KEY_KEY_5"), diff --git a/src/keycode.h b/src/keycode.h index 65f04d8d7..459a85a46 100644 --- a/src/keycode.h +++ b/src/keycode.h @@ -21,6 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #define KEYCODE_HEADER #include "irrlichttypes.h" +#include "Keycodes.h" #include #include @@ -57,6 +58,7 @@ protected: }; extern const KeyPress EscapeKey; +extern const KeyPress CancelKey; extern const KeyPress NumberKey[10]; // Key configuration getter @@ -65,5 +67,7 @@ KeyPress getKeySetting(const char *settingname); // Clear fast lookup cache void clearKeyCache(); +irr::EKEY_CODE keyname_to_keycode(const char *name); + #endif diff --git a/src/log.cpp b/src/log.cpp index 97f25cc77..ff2e16333 100644 --- a/src/log.cpp +++ b/src/log.cpp @@ -26,6 +26,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "threads.h" #include "debug.h" #include "gettime.h" +#include "porting.h" +#include "config.h" std::list log_outputs[LMT_NUM_VALUES]; std::map log_threadnames; @@ -139,6 +141,9 @@ public: void printbuf() { log_printline(m_lev, m_buf); +#ifdef __ANDROID__ + __android_log_print(ANDROID_LOG_ERROR, PROJECT_NAME, "%s", m_buf.c_str()); +#endif } void bufchar(char c) diff --git a/src/lua/src/llex.c b/src/lua/src/llex.c index 6dc319358..98068c1aa 100644 --- a/src/lua/src/llex.c +++ b/src/lua/src/llex.c @@ -176,9 +176,15 @@ static void buffreplace (LexState *ls, char from, char to) { static void trydecpoint (LexState *ls, SemInfo *seminfo) { /* format error: try to update decimal point separator */ +#ifndef __ANDROID__ struct lconv *cv = localeconv(); +#endif char old = ls->decpoint; +#ifndef __ANDROID__ ls->decpoint = (cv ? cv->decimal_point[0] : '.'); +#else + ls->decpoint = '.'; +#endif buffreplace(ls, old, ls->decpoint); /* try updated decimal separator */ if (!luaO_str2d(luaZ_buffer(ls->buff), &seminfo->r)) { /* format error with correct decimal point: no more options */ diff --git a/src/main.cpp b/src/main.cpp index 5a0be2f75..1caa918b8 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -84,10 +84,14 @@ with this program; if not, write to the Free Software Foundation, Inc., #ifdef USE_LEVELDB #include "database-leveldb.h" #endif + #if USE_REDIS #include "database-redis.h" #endif +#ifdef HAVE_TOUCHSCREENGUI +#include "touchscreengui.h" +#endif /* Settings. These are loaded from the config file. @@ -253,6 +257,11 @@ public: React to nothing here if a menu is active */ if (noMenuActive() == false) { +#ifdef HAVE_TOUCHSCREENGUI + if (m_touchscreengui != 0) { + m_touchscreengui->Toggle(false); + } +#endif return g_menumgr.preprocessEvent(event); } @@ -266,7 +275,16 @@ public: } } - if (event.EventType == irr::EET_MOUSE_INPUT_EVENT) { +#ifdef HAVE_TOUCHSCREENGUI + // case of touchscreengui we have to handle different events + if ((m_touchscreengui != 0) && + (event.EventType == irr::EET_TOUCH_INPUT_EVENT)) { + m_touchscreengui->translateEvent(event); + return true; + } +#endif + // handle mouse events + if(event.EventType == irr::EET_MOUSE_INPUT_EVENT) { if (noMenuActive() == false) { left_active = false; middle_active = false; @@ -293,8 +311,8 @@ public: } } } - if (event.EventType == irr::EET_LOG_TEXT_EVENT) { - dstream << "Irrlicht log: " << event.LogEvent.Text << std::endl; + if(event.EventType == irr::EET_LOG_TEXT_EVENT) { + dstream<< std::string("Irrlicht log: ") + std::string(event.LogEvent.Text)<getCursorControl()->getPosition(); + if (m_device->getCursorControl()) { + return m_device->getCursorControl()->getPosition(); + } + else { + return m_mousepos; + } } virtual void setMousePos(s32 x, s32 y) { - m_device->getCursorControl()->setPosition(x, y); + if (m_device->getCursorControl()) { + m_device->getCursorControl()->setPosition(x, y); + } + else { + m_mousepos = v2s32(x,y); + } } virtual bool getLeftState() @@ -445,8 +482,9 @@ public: m_receiver->clearInput(); } private: - IrrlichtDevice *m_device; + IrrlichtDevice *m_device; MyEventReceiver *m_receiver; + v2s32 m_mousepos; }; class RandomInputHandler : public InputHandler @@ -855,8 +893,18 @@ int main(int argc, char *argv[]) porting::initializePaths(); +#ifdef __ANDROID__ + porting::initAndroid(); + + porting::setExternalStorageDir(porting::jnienv); + if (!fs::PathExists(porting::path_user)) { + fs::CreateDir(porting::path_user); + } + porting::copyAssets(); +#else // Create user data directory fs::CreateDir(porting::path_user); +#endif infostream << "path_share = " << porting::path_share << std::endl; infostream << "path_user = " << porting::path_user << std::endl; @@ -975,14 +1023,15 @@ int main(int argc, char *argv[]) // Initialize HTTP fetcher httpfetch_init(g_settings->getS32("curl_parallel_limit")); +#ifndef __ANDROID__ /* Run unit tests */ - if ((ENABLE_TESTS && cmd_args.getFlag("disable-unittests") == false) || cmd_args.getFlag("enable-unittests") == true) { run_tests(); } +#endif #ifdef _MSC_VER init_gettext((porting::path_share + DIR_DELIM + "locale").c_str(), g_settings->get("language"), argc, argv); @@ -1348,7 +1397,7 @@ int main(int argc, char *argv[]) List video modes if requested */ - MyEventReceiver receiver; + MyEventReceiver* receiver = new MyEventReceiver(); if (cmd_args.getFlag("videomodes")) { IrrlichtDevice *nulldevice; @@ -1361,7 +1410,7 @@ int main(int argc, char *argv[]) params.Fullscreen = false; params.Stencilbuffer = false; params.Vsync = vsync; - params.EventReceiver = &receiver; + params.EventReceiver = receiver; params.HighPrecisionFPU = g_settings->getBool("high_precision_fpu"); nulldevice = createDeviceEx(params); @@ -1397,15 +1446,13 @@ int main(int argc, char *argv[]) nulldevice->drop(); + delete receiver; return 0; } /* Create device and exit if creation failed */ - - IrrlichtDevice *device; - SIrrlichtCreationParameters params = SIrrlichtCreationParameters(); params.DriverType = driverType; params.WindowSize = core::dimension2d(screenW, screenH); @@ -1414,12 +1461,18 @@ int main(int argc, char *argv[]) params.Fullscreen = fullscreen; params.Stencilbuffer = false; params.Vsync = vsync; - params.EventReceiver = &receiver; + params.EventReceiver = receiver; params.HighPrecisionFPU = g_settings->getBool("high_precision_fpu"); +#ifdef __ANDROID__ + params.PrivateData = porting::app_global; + params.OGLES2ShaderPath = std::string(porting::path_user + DIR_DELIM + + "media" + DIR_DELIM + "Shaders" + DIR_DELIM).c_str(); +#endif - device = createDeviceEx(params); + IrrlichtDevice * device = createDeviceEx(params); if (device == 0) { + delete receiver; return 1; // could not create selected driver. } @@ -1476,10 +1529,11 @@ int main(int argc, char *argv[]) bool random_input = g_settings->getBool("random_input") || cmd_args.getFlag("random-input"); InputHandler *input = NULL; + if (random_input) { input = new RandomInputHandler(); } else { - input = new RealInputHandler(device, &receiver); + input = new RealInputHandler(device,receiver); } scene::ISceneManager* smgr = device->getSceneManager(); @@ -1564,7 +1618,8 @@ int main(int argc, char *argv[]) /* Menu-game loop */ - while (device->run() && kill == false) + while (device->run() && (kill == false) && + (g_gamecallback->shutdown_requested == false)) { // Set the window caption wchar_t* text = wgettext("Main Menu"); @@ -1612,7 +1667,9 @@ int main(int argc, char *argv[]) first_loop = false; // Cursor can be non-visible when coming from the game + #ifndef ANDROID device->getCursorControl()->setVisible(true); + #endif // Some stuff are left to scene manager when coming from the game // (map at least?) smgr->clear(); @@ -1661,10 +1718,9 @@ int main(int argc, char *argv[]) } infostream << "Waited for other menus" << std::endl; - GUIEngine* temp = new GUIEngine(device, guiroot, - &g_menumgr, smgr, &menudata, kill); + /* show main menu */ + GUIEngine mymenu(device, guiroot, &g_menumgr,smgr,&menudata,kill); - delete temp; //once finished you'll never end up here smgr->clear(); } @@ -1788,6 +1844,10 @@ int main(int argc, char *argv[]) /* Run game */ +#ifdef HAVE_TOUCHSCREENGUI + receiver->m_touchscreengui = new TouchScreenGUI(device, receiver); + g_touchscreengui = receiver->m_touchscreengui; +#endif the_game( kill, random_input, @@ -1805,6 +1865,11 @@ int main(int argc, char *argv[]) simple_singleplayer_mode ); smgr->clear(); +#ifdef HAVE_TOUCHSCREENGUI + delete g_touchscreengui; + g_touchscreengui = NULL; + receiver->m_touchscreengui = NULL; +#endif } //try catch(con::PeerNotFoundException &e) @@ -1849,7 +1914,7 @@ int main(int argc, char *argv[]) if (use_freetype) font->drop(); #endif - + delete receiver; #endif // !SERVER // Update configuration file diff --git a/src/mainmenumanager.h b/src/mainmenumanager.h index 78ae1fcfa..28fe1ac11 100644 --- a/src/mainmenumanager.h +++ b/src/mainmenumanager.h @@ -124,13 +124,17 @@ public: disconnect_requested(false), changepassword_requested(false), changevolume_requested(false), + shutdown_requested(false), device(a_device) { } virtual void exitToOS() { + shutdown_requested = true; +#ifndef __ANDROID__ device->closeDevice(); +#endif } virtual void disconnect() @@ -151,6 +155,7 @@ public: bool disconnect_requested; bool changepassword_requested; bool changevolume_requested; + bool shutdown_requested; IrrlichtDevice *device; }; diff --git a/src/modalMenu.h b/src/modalMenu.h index 251b7aa3b..2c512d3ca 100644 --- a/src/modalMenu.h +++ b/src/modalMenu.h @@ -21,6 +21,9 @@ with this program; if not, write to the Free Software Foundation, Inc., #define MODALMENU_HEADER #include "irrlichttypes_extrabloated.h" +#ifdef HAVE_TOUCHSCREENGUI +#include "touchscreengui.h" +#endif class GUIModalMenu; @@ -101,6 +104,10 @@ public: Environment->removeFocus(this); m_menumgr->deletingMenu(this); this->remove(); +#ifdef HAVE_TOUCHSCREENGUI + if (g_touchscreengui) + g_touchscreengui->Show(); +#endif } void removeChildren() diff --git a/src/porting.cpp b/src/porting.cpp index 3c2a5c824..ad942b0bc 100644 --- a/src/porting.cpp +++ b/src/porting.cpp @@ -167,6 +167,7 @@ int getNumberOfProcessors() { } +#ifndef __ANDROID__ bool threadBindToProcessor(threadid_t tid, int pnumber) { #if defined(_WIN32) @@ -194,7 +195,7 @@ bool threadBindToProcessor(threadid_t tid, int pnumber) { pnumber, NULL) == 0; #elif defined(_AIX) - + return bindprocessor(BINDTHREAD, (tid_t)tid, pnumber) == 0; #elif defined(__hpux) || defined(hpux) @@ -203,11 +204,11 @@ bool threadBindToProcessor(threadid_t tid, int pnumber) { return pthread_processor_bind_np(PTHREAD_BIND_ADVISORY_NP, &answer, pnumber, tid) == 0; - + #elif defined(__APPLE__) struct thread_affinity_policy tapol; - + thread_port_t threadport = pthread_mach_thread_np(tid); tapol.affinity_tag = pnumber + 1; return thread_policy_set(threadport, THREAD_AFFINITY_POLICY, @@ -219,7 +220,7 @@ bool threadBindToProcessor(threadid_t tid, int pnumber) { #endif } - +#endif bool threadSetPriority(threadid_t tid, int prio) { #if defined(_WIN32) @@ -232,21 +233,21 @@ bool threadSetPriority(threadid_t tid, int prio) { CloseHandle(hThread); return success; - + #else struct sched_param sparam; int policy; - + if (pthread_getschedparam(tid, &policy, &sparam) != 0) return false; - + int min = sched_get_priority_min(policy); int max = sched_get_priority_max(policy); sparam.sched_priority = min + prio * (max - min) / THREAD_PRIORITY_HIGHEST; return pthread_setschedparam(tid, policy, &sparam) == 0; - + #endif } @@ -458,9 +459,15 @@ void initializePaths() { char buf[BUFSIZ]; memset(buf, 0, BUFSIZ); - assert(readlink("/proc/self/exe", buf, BUFSIZ-1) != -1); - pathRemoveFile(buf, '/'); - bindir = buf; + if (readlink("/proc/self/exe", buf, BUFSIZ-1) == -1) { + errorstream << "Unable to read bindir "<< std::endl; +#ifndef __ANDROID__ + assert("Unable to read bindir" == 0); +#endif + } else { + pathRemoveFile(buf, '/'); + bindir = buf; + } } // Find share directory from these. @@ -472,6 +479,9 @@ void initializePaths() trylist.push_back( bindir + DIR_DELIM + ".." + DIR_DELIM + "share" + DIR_DELIM + PROJECT_NAME); trylist.push_back(bindir + DIR_DELIM + ".."); +#ifdef __ANDROID__ + trylist.push_back(DIR_DELIM "sdcard" DIR_DELIM PROJECT_NAME); +#endif for(std::list::const_iterator i = trylist.begin(); i != trylist.end(); i++) @@ -490,8 +500,11 @@ void initializePaths() path_share = trypath; break; } - +#ifndef __ANDROID__ path_user = std::string(getenv("HOME")) + DIR_DELIM + "." + PROJECT_NAME; +#else + path_user = std::string(DIR_DELIM "sdcard" DIR_DELIM PROJECT_NAME DIR_DELIM); +#endif /* OS X @@ -539,6 +552,7 @@ v2u32 getWindowSize() { return device->getVideoDriver()->getScreenSize(); } +#ifndef __ANDROID__ float getDisplayDensity() { float gui_scaling = g_settings->getFloat("gui_scaling"); @@ -562,6 +576,7 @@ v2u32 getDisplaySize() { return deskres; } #endif +#endif } //namespace porting diff --git a/src/porting.h b/src/porting.h index b5a5d00f2..9be09da75 100644 --- a/src/porting.h +++ b/src/porting.h @@ -53,12 +53,12 @@ with this program; if not, write to the Free Software Foundation, Inc., #ifdef _WIN32 #include - + #define sleep_ms(x) Sleep(x) #else #include #include //for uintptr_t - + #if (defined(linux) || defined(__linux)) && !defined(_GNU_SOURCE) #define _GNU_SOURCE #endif @@ -79,7 +79,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #endif #define sleep_ms(x) usleep(x*1000) - + #define THREAD_PRIORITY_LOWEST 0 #define THREAD_PRIORITY_BELOW_NORMAL 1 #define THREAD_PRIORITY_NORMAL 2 @@ -197,17 +197,17 @@ void initIrrlicht(irr::IrrlichtDevice * ); #define _WIN32_WINNT 0x0501 #endif #include - + inline u32 getTimeS() { return GetTickCount() / 1000; } - + inline u32 getTimeMs() { return GetTickCount(); } - + inline u32 getTimeUs() { LARGE_INTEGER freq, t; @@ -215,7 +215,7 @@ void initIrrlicht(irr::IrrlichtDevice * ); QueryPerformanceCounter(&t); return (double)(t.QuadPart) / ((double)(freq.QuadPart) / 1000000.0); } - + inline u32 getTimeNs() { LARGE_INTEGER freq, t; @@ -223,7 +223,7 @@ void initIrrlicht(irr::IrrlichtDevice * ); QueryPerformanceCounter(&t); return (double)(t.QuadPart) / ((double)(freq.QuadPart) / 1000000000.0); } - + #else // Posix #include #include @@ -238,21 +238,21 @@ void initIrrlicht(irr::IrrlichtDevice * ); gettimeofday(&tv, NULL); return tv.tv_sec; } - + inline u32 getTimeMs() { struct timeval tv; gettimeofday(&tv, NULL); return tv.tv_sec * 1000 + tv.tv_usec / 1000; } - + inline u32 getTimeUs() { struct timeval tv; gettimeofday(&tv, NULL); return tv.tv_sec * 1000000 + tv.tv_usec; } - + inline u32 getTimeNs() { struct timespec ts; @@ -270,7 +270,7 @@ void initIrrlicht(irr::IrrlichtDevice * ); #endif return ts.tv_sec * 1000000000 + ts.tv_nsec; } - + /*#include inline u32 getTimeMs() { @@ -373,5 +373,9 @@ v2u32 getWindowSize(); } // namespace porting +#ifdef __ANDROID__ +#include "porting_android.h" +#endif + #endif // PORTING_HEADER diff --git a/src/porting_android.cpp b/src/porting_android.cpp new file mode 100644 index 000000000..96c9385a6 --- /dev/null +++ b/src/porting_android.cpp @@ -0,0 +1,295 @@ +/* +Minetest +Copyright (C) 2014 celeron55, Perttu Ahola + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#ifndef __ANDROID__ +#error This file may only be compiled for android! +#endif + +#include "porting.h" +#include "porting_android.h" +#include "config.h" +#include "filesys.h" +#include "log.h" +#include + +#ifdef GPROF +#include "prof.h" +#endif + +extern int main(int argc, char *argv[]); + +void android_main(android_app *app) +{ + int retval = 0; + porting::app_global = app; + + porting::setThreadName("MainThread"); + + try { + app_dummy(); + char *argv[] = { (char*) "minetest" }; + main(sizeof(argv) / sizeof(argv[0]), argv); + } + catch(BaseException e) { + std::stringstream msg; + msg << "Exception handled by main: " << e.what(); + const char* message = msg.str().c_str(); + __android_log_print(ANDROID_LOG_ERROR, PROJECT_NAME, "%s", message); + errorstream << msg << std::endl; + retval = -1; + } + catch(...) { + __android_log_print(ANDROID_LOG_ERROR, PROJECT_NAME, + "Some exception occured"); + errorstream << "Uncaught exception in main thread!" << std::endl; + retval = -1; + } + + porting::cleanupAndroid(); + errorstream << "Shutting down minetest." << std::endl; + exit(retval); +} + +/* handler for finished message box input */ +/* Intentionally NOT in namespace porting */ +/* TODO this doesn't work as expected, no idea why but there's a workaround */ +/* for it right now */ +extern "C" { + JNIEXPORT void JNICALL Java_org_minetest_MtNativeActivity_putMessageBoxResult( + JNIEnv * env, jclass thiz, jstring text) + { + errorstream << "Java_org_minetest_MtNativeActivity_putMessageBoxResult got: " + << std::string((const char*)env->GetStringChars(text,0)) + << std::endl; + } +} + +namespace porting { + +std::string path_storage = DIR_DELIM "sdcard" DIR_DELIM; + +android_app* app_global; +JNIEnv* jnienv; +jclass nativeActivity; + +jclass findClass(std::string classname) +{ + if (jnienv == 0) { + return 0; + } + + jclass nativeactivity = jnienv->FindClass("android/app/NativeActivity"); + jmethodID getClassLoader = + jnienv->GetMethodID(nativeactivity,"getClassLoader", + "()Ljava/lang/ClassLoader;"); + jobject cls = + jnienv->CallObjectMethod(app_global->activity->clazz, getClassLoader); + jclass classLoader = jnienv->FindClass("java/lang/ClassLoader"); + jmethodID findClass = + jnienv->GetMethodID(classLoader, "loadClass", + "(Ljava/lang/String;)Ljava/lang/Class;"); + jstring strClassName = + jnienv->NewStringUTF(classname.c_str()); + return (jclass) jnienv->CallObjectMethod(cls, findClass, strClassName); +} + +void copyAssets() +{ + jmethodID assetcopy = jnienv->GetMethodID(nativeActivity,"copyAssets","()V"); + + if (assetcopy == 0) { + assert("porting::copyAssets unable to find copy assets method" == 0); + } + + jnienv->CallVoidMethod(app_global->activity->clazz, assetcopy); +} + +void initAndroid() +{ + porting::jnienv = NULL; + JavaVM *jvm = app_global->activity->vm; + JavaVMAttachArgs lJavaVMAttachArgs; + lJavaVMAttachArgs.version = JNI_VERSION_1_6; + lJavaVMAttachArgs.name = "MinetestNativeThread"; + lJavaVMAttachArgs.group = NULL; +#ifdef NDEBUG + // This is a ugly hack as arm v7a non debuggable builds crash without this + // printf ... if someone finds out why please fix it! + infostream << "Attaching native thread. " << std::endl; +#endif + if ( jvm->AttachCurrentThread(&porting::jnienv, &lJavaVMAttachArgs) == JNI_ERR) { + errorstream << "Failed to attach native thread to jvm" << std::endl; + exit(-1); + } + + nativeActivity = findClass("org/minetest/minetest/MtNativeActivity"); + if (nativeActivity == 0) { + errorstream << + "porting::initAndroid unable to find java native activity class" << + std::endl; + } + +#ifdef GPROF + /* in the start-up code */ + __android_log_print(ANDROID_LOG_ERROR, PROJECT_NAME, + "Initializing GPROF profiler"); + monstartup("libminetest.so"); +#endif +} + +void cleanupAndroid() +{ + +#ifdef GPROF + errorstream << "Shutting down GPROF profiler" << std::endl; + setenv("CPUPROFILE", (path_user + DIR_DELIM + "gmon.out").c_str(), 1); + moncleanup(); +#endif + + JavaVM *jvm = app_global->activity->vm; + jvm->DetachCurrentThread(); +} + +void setExternalStorageDir(JNIEnv* lJNIEnv) +{ + // Android: Retrieve ablsolute path to external storage device (sdcard) + jclass ClassEnv = lJNIEnv->FindClass("android/os/Environment"); + jmethodID MethodDir = + lJNIEnv->GetStaticMethodID(ClassEnv, + "getExternalStorageDirectory","()Ljava/io/File;"); + jobject ObjectFile = lJNIEnv->CallStaticObjectMethod(ClassEnv, MethodDir); + jclass ClassFile = lJNIEnv->FindClass("java/io/File"); + + jmethodID MethodPath = + lJNIEnv->GetMethodID(ClassFile, "getAbsolutePath", + "()Ljava/lang/String;"); + jstring StringPath = + (jstring) lJNIEnv->CallObjectMethod(ObjectFile, MethodPath); + + const char *externalPath = lJNIEnv->GetStringUTFChars(StringPath, NULL); + std::string userPath(externalPath); + lJNIEnv->ReleaseStringUTFChars(StringPath, externalPath); + + path_storage = userPath; + path_user = userPath + DIR_DELIM + PROJECT_NAME; + path_share = userPath + DIR_DELIM + PROJECT_NAME; +} + +void showInputDialog(const std::string& acceptButton, const std::string& hint, + const std::string& current, int editType) +{ + jmethodID showdialog = jnienv->GetMethodID(nativeActivity,"showDialog", + "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;I)V"); + + if (showdialog == 0) { + assert("porting::showInputDialog unable to find java show dialog method" == 0); + } + + jstring jacceptButton = jnienv->NewStringUTF(acceptButton.c_str()); + jstring jhint = jnienv->NewStringUTF(hint.c_str()); + jstring jcurrent = jnienv->NewStringUTF(current.c_str()); + jint jeditType = editType; + + jnienv->CallVoidMethod(app_global->activity->clazz, showdialog, + jacceptButton, jhint, jcurrent, jeditType); +} + +int getInputDialogState() +{ + jmethodID dialogstate = jnienv->GetMethodID(nativeActivity, + "getDialogState", "()I"); + + if (dialogstate == 0) { + assert("porting::getInputDialogState unable to find java dialog state method" == 0); + } + + return jnienv->CallIntMethod(app_global->activity->clazz, dialogstate); +} + +std::string getInputDialogValue() +{ + jmethodID dialogvalue = jnienv->GetMethodID(nativeActivity, + "getDialogValue", "()Ljava/lang/String;"); + + if (dialogvalue == 0) { + assert("porting::getInputDialogValue unable to find java dialog value method" == 0); + } + + jobject result = jnienv->CallObjectMethod(app_global->activity->clazz, + dialogvalue); + + const char* javachars = jnienv->GetStringUTFChars((jstring) result,0); + std::string text(javachars); + jnienv->ReleaseStringUTFChars((jstring) result, javachars); + + return text; +} + +#if not defined(SERVER) +float getDisplayDensity() +{ + static bool firstrun = true; + static float value = 0; + + if (firstrun) { + jmethodID getDensity = jnienv->GetMethodID(nativeActivity, "getDensity", + "()F"); + + if (getDensity == 0) { + assert("porting::getDisplayDensity unable to find java getDensity method" == 0); + } + + value = jnienv->CallFloatMethod(app_global->activity->clazz, getDensity); + firstrun = false; + } + return value; +} + +v2u32 getDisplaySize() +{ + static bool firstrun = true; + static v2u32 retval; + + if (firstrun) { + jmethodID getDisplayWidth = jnienv->GetMethodID(nativeActivity, + "getDisplayWidth", "()I"); + + if (getDisplayWidth == 0) { + assert("porting::getDisplayWidth unable to find java getDisplayWidth method" == 0); + } + + retval.X = jnienv->CallIntMethod(app_global->activity->clazz, + getDisplayWidth); + + jmethodID getDisplayHeight = jnienv->GetMethodID(nativeActivity, + "getDisplayHeight", "()I"); + + if (getDisplayHeight == 0) { + assert("porting::getDisplayHeight unable to find java getDisplayHeight method" == 0); + } + + retval.Y = jnienv->CallIntMethod(app_global->activity->clazz, + getDisplayHeight); + + firstrun = false; + } + return retval; +} +#endif //SERVER +} diff --git a/src/porting_android.h b/src/porting_android.h new file mode 100644 index 000000000..bfdadfbff --- /dev/null +++ b/src/porting_android.h @@ -0,0 +1,81 @@ +/* +Minetest +Copyright (C) 2014 celeron55, Perttu Ahola + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ +#ifndef __PORTING_ANDROID_H__ +#define __PORTING_ANDROID_H__ + +#ifndef __ANDROID__ +#error this include has to be included on android port only! +#endif + +#include +#include +#include + +#include + +namespace porting { +/** java app **/ +extern android_app *app_global; + +/** java <-> c++ interaction interface **/ +extern JNIEnv *jnienv; + +/** + * do initialization required on android only + */ +void initAndroid(); +void cleanupAndroid(); + +/** + * set storage dir on external sdcard# + * @param lJNIEnv environment from android + */ +void setExternalStorageDir(JNIEnv* lJNIEnv); + +/** + * use java function to copy media from assets to external storage + */ +void copyAssets(); + +/** + * show text input dialog in java + * @param acceptButton text to display on accept button + * @param hint hint to show + * @param current initial value to display + * @param editType type of texfield + * (1==multiline text input; 2==single line text input; 3=password field) + */ +void showInputDialog(const std::string& acceptButton, + const std::string& hint, const std::string& current, int editType); + +/** + * WORKAROUND for not working callbacks from java -> c++ + * get current state of input dialog + */ +int getInputDialogState(); + +/** + * WORKAROUND for not working callbacks from java -> c++ + * get text in current input dialog + */ +std::string getInputDialogValue(); + +} + +#endif diff --git a/src/tile.cpp b/src/tile.cpp index c9a36918a..17ec51614 100644 --- a/src/tile.cpp +++ b/src/tile.cpp @@ -32,6 +32,10 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "util/thread.h" #include "util/numeric.h" +#ifdef __ANDROID__ +#include +#endif + /* A cache from texture name to texture path */ @@ -702,6 +706,9 @@ u32 TextureSource::getTextureIdDirect(const std::string &name) if(baseimg != NULL) { +#ifdef __ANDROID__ + baseimg = Align2Npot2(baseimg, driver); +#endif // Create texture from resulting image t = driver->addTexture(name.c_str(), baseimg); baseimg->drop(); @@ -790,11 +797,17 @@ void TextureSource::rebuildImagesAndTextures() JMutexAutoLock lock(m_textureinfo_cache_mutex); video::IVideoDriver* driver = m_device->getVideoDriver(); + assert(driver != 0); // Recreate textures for(u32 i=0; iname); +#ifdef __ANDROID__ + img = Align2Npot2(img,driver); + assert(img->getDimension().Height == npot2(img->getDimension().Height)); + assert(img->getDimension().Width == npot2(img->getDimension().Width)); +#endif // Create texture from resulting image video::ITexture *t = NULL; if(img) { @@ -816,6 +829,126 @@ video::ITexture* TextureSource::generateTextureFromMesh( video::IVideoDriver *driver = m_device->getVideoDriver(); assert(driver); +#ifdef __ANDROID__ + const GLubyte* renderstr = glGetString(GL_RENDERER); + std::string renderer((char*) renderstr); + + // use no render to texture hack + if ( + (renderer.find("Adreno") != std::string::npos) || + (renderer.find("Mali") != std::string::npos) || + (renderer.find("Immersion") != std::string::npos) || + (renderer.find("Tegra") != std::string::npos) || + g_settings->getBool("inventory_image_hack") + ) { + // Get a scene manager + scene::ISceneManager *smgr_main = m_device->getSceneManager(); + assert(smgr_main); + scene::ISceneManager *smgr = smgr_main->createNewSceneManager(); + assert(smgr); + + const float scaling = 0.2; + + scene::IMeshSceneNode* meshnode = + smgr->addMeshSceneNode(params.mesh, NULL, + -1, v3f(0,0,0), v3f(0,0,0), + v3f(1.0 * scaling,1.0 * scaling,1.0 * scaling), true); + meshnode->setMaterialFlag(video::EMF_LIGHTING, true); + meshnode->setMaterialFlag(video::EMF_ANTI_ALIASING, true); + meshnode->setMaterialFlag(video::EMF_TRILINEAR_FILTER, m_setting_trilinear_filter); + meshnode->setMaterialFlag(video::EMF_BILINEAR_FILTER, m_setting_bilinear_filter); + meshnode->setMaterialFlag(video::EMF_ANISOTROPIC_FILTER, m_setting_anisotropic_filter); + + scene::ICameraSceneNode* camera = smgr->addCameraSceneNode(0, + params.camera_position, params.camera_lookat); + // second parameter of setProjectionMatrix (isOrthogonal) is ignored + camera->setProjectionMatrix(params.camera_projection_matrix, false); + + smgr->setAmbientLight(params.ambient_light); + smgr->addLightSceneNode(0, + params.light_position, + params.light_color, + params.light_radius*scaling); + + core::dimension2d screen = driver->getScreenSize(); + + // Render scene + driver->beginScene(true, true, video::SColor(0,0,0,0)); + driver->clearZBuffer(); + smgr->drawAll(); + + core::dimension2d partsize(screen.Width * scaling,screen.Height * scaling); + + irr::video::IImage* rawImage = + driver->createImage(irr::video::ECF_A8R8G8B8, partsize); + + u8* pixels = static_cast(rawImage->lock()); + if (!pixels) + { + rawImage->drop(); + return NULL; + } + + core::rect source( + screen.Width /2 - (screen.Width * (scaling / 2)), + screen.Height/2 - (screen.Height * (scaling / 2)), + screen.Width /2 + (screen.Width * (scaling / 2)), + screen.Height/2 + (screen.Height * (scaling / 2)) + ); + + glReadPixels(source.UpperLeftCorner.X, source.UpperLeftCorner.Y, + partsize.Width, partsize.Height, GL_RGBA, + GL_UNSIGNED_BYTE, pixels); + + driver->endScene(); + + // Drop scene manager + smgr->drop(); + + unsigned int pixelcount = partsize.Width*partsize.Height; + + u8* runptr = pixels; + for (unsigned int i=0; i < pixelcount; i++) { + + u8 B = *runptr; + u8 G = *(runptr+1); + u8 R = *(runptr+2); + u8 A = *(runptr+3); + + //BGRA -> RGBA + *runptr = R; + runptr ++; + *runptr = G; + runptr ++; + *runptr = B; + runptr ++; + *runptr = A; + runptr ++; + } + + video::IImage* inventory_image = + driver->createImage(irr::video::ECF_A8R8G8B8, params.dim); + + rawImage->copyToScaling(inventory_image); + rawImage->drop(); + + video::ITexture *rtt = driver->addTexture(params.rtt_texture_name.c_str(), inventory_image); + inventory_image->drop(); + + if (rtt == NULL) { + errorstream << "TextureSource::generateTextureFromMesh(): failed to recreate texture from image: " << params.rtt_texture_name << std::endl; + return NULL; + } + + driver->makeColorKeyTexture(rtt, v2s32(0,0)); + + if(params.delete_texture_on_shutdown) + m_texture_trash.push_back(rtt); + + return rtt; + } +#endif + if(driver->queryFeature(video::EVDF_RENDER_TO_TARGET) == false) { static bool warned = false; @@ -840,7 +973,12 @@ video::ITexture* TextureSource::generateTextureFromMesh( } // Set render target - driver->setRenderTarget(rtt, false, true, video::SColor(0,0,0,0)); + if (!driver->setRenderTarget(rtt, false, true, video::SColor(0,0,0,0))) { + driver->removeTexture(rtt); + errorstream<<"TextureSource::generateTextureFromMesh(): " + <<"failed to set render target"<getSceneManager(); @@ -848,7 +986,9 @@ video::ITexture* TextureSource::generateTextureFromMesh( scene::ISceneManager *smgr = smgr_main->createNewSceneManager(); assert(smgr); - scene::IMeshSceneNode* meshnode = smgr->addMeshSceneNode(params.mesh, NULL, -1, v3f(0,0,0), v3f(0,0,0), v3f(1,1,1), true); + scene::IMeshSceneNode* meshnode = + smgr->addMeshSceneNode(params.mesh, NULL, + -1, v3f(0,0,0), v3f(0,0,0), v3f(1,1,1), true); meshnode->setMaterialFlag(video::EMF_LIGHTING, true); meshnode->setMaterialFlag(video::EMF_ANTI_ALIASING, true); meshnode->setMaterialFlag(video::EMF_TRILINEAR_FILTER, m_setting_trilinear_filter); @@ -871,11 +1011,6 @@ video::ITexture* TextureSource::generateTextureFromMesh( smgr->drawAll(); driver->endScene(); - // NOTE: The scene nodes should not be dropped, otherwise - // smgr->drop() segfaults - /*cube->drop(); - camera->drop(); - light->drop();*/ // Drop scene manager smgr->drop(); @@ -938,6 +1073,57 @@ video::IImage* TextureSource::generateImageFromScratch(std::string name) return baseimg; } +#ifdef __ANDROID__ +#include +/** + * Check and align image to npot2 if required by hardware + * @param image image to check for npot2 alignment + * @param driver driver to use for image operations + * @return image or copy of image aligned to npot2 + */ +video::IImage * Align2Npot2(video::IImage * image, + video::IVideoDriver* driver) +{ + if(image == NULL) { + return image; + } + + core::dimension2d dim = image->getDimension(); + + std::string extensions = (char*) glGetString(GL_EXTENSIONS); + if (extensions.find("GL_OES_texture_npot") != std::string::npos) { + return image; + } + + unsigned int height = npot2(dim.Height); + unsigned int width = npot2(dim.Width); + + if ((dim.Height == height) && + (dim.Width == width)) { + return image; + } + + if (dim.Height > height) { + height *= 2; + } + + if (dim.Width > width) { + width *= 2; + } + + video::IImage *targetimage = + driver->createImage(video::ECF_A8R8G8B8, + core::dimension2d(width, height)); + + if (targetimage != NULL) { + image->copyToScaling(targetimage); + } + image->drop(); + return targetimage; +} + +#endif + bool TextureSource::generateImage(std::string part_of_name, video::IImage *& baseimg) { video::IVideoDriver* driver = m_device->getVideoDriver(); @@ -947,21 +1133,9 @@ bool TextureSource::generateImage(std::string part_of_name, video::IImage *& bas if(part_of_name.size() == 0 || part_of_name[0] != '[') { video::IImage *image = m_sourcecache.getOrLoad(part_of_name, m_device); - - if (image != NULL) { - if (!driver->queryFeature(irr::video::EVDF_TEXTURE_NPOT)) { - core::dimension2d dim = image->getDimension(); - - - if ((dim.Height %2 != 0) || - (dim.Width %2 != 0)) { - infostream << "TextureSource::generateImage " - << part_of_name << " size npot2 x=" << dim.Width - << " y=" << dim.Height << std::endl; - } - } - } - +#ifdef __ANDROID__ + image = Align2Npot2(image,driver); +#endif if (image == NULL) { if (part_of_name != "") { if (part_of_name.find("_normal.png") == std::string::npos){ @@ -1284,7 +1458,16 @@ bool TextureSource::generateImage(std::string part_of_name, video::IImage *& bas video::IImage *img_right = generateImageFromScratch(imagename_right); assert(img_top && img_left && img_right); +#ifdef __ANDROID__ + assert(img_top->getDimension().Height == npot2(img_top->getDimension().Height)); + assert(img_top->getDimension().Width == npot2(img_top->getDimension().Width)); + + assert(img_left->getDimension().Height == npot2(img_left->getDimension().Height)); + assert(img_left->getDimension().Width == npot2(img_left->getDimension().Width)); + assert(img_right->getDimension().Height == npot2(img_right->getDimension().Height)); + assert(img_right->getDimension().Width == npot2(img_right->getDimension().Width)); +#endif // Create textures from images video::ITexture *texture_top = driver->addTexture( (imagename_top + "__temp__").c_str(), img_top); diff --git a/src/tile.h b/src/tile.h index 486d193fd..29c6b69f2 100644 --- a/src/tile.h +++ b/src/tile.h @@ -131,6 +131,25 @@ public: IWritableTextureSource* createTextureSource(IrrlichtDevice *device); +#ifdef __ANDROID__ +/** + * @param size get next npot2 value + * @return npot2 value + */ +inline unsigned int npot2(unsigned int size) +{ + if (size == 0) return 0; + unsigned int npot = 1; + + while ((size >>= 1) > 0) { + npot <<= 1; + } + return npot; +} + +video::IImage * Align2Npot2(video::IImage * image, video::IVideoDriver* driver); +#endif + enum MaterialType{ TILE_MATERIAL_BASIC, TILE_MATERIAL_ALPHA, diff --git a/src/touchscreengui.cpp b/src/touchscreengui.cpp new file mode 100644 index 000000000..a04b7fe5e --- /dev/null +++ b/src/touchscreengui.cpp @@ -0,0 +1,690 @@ +/* +Copyright (C) 2014 sapier + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "touchscreengui.h" +#include "irrlichttypes.h" +#include "irr_v2d.h" +#include "log.h" +#include "keycode.h" +#include "settings.h" +#include "gettime.h" +#include "util/numeric.h" +#include "porting.h" + +#include +#include + +#include + +using namespace irr::core; + +extern Settings *g_settings; + +const char** touchgui_button_imagenames = (const char*[]) { + "up_arrow.png", + "down_arrow.png", + "left_arrow.png", + "right_arrow.png", + "jump_btn.png", + "down.png", + "inventory_btn.png", + "chat_btn.png" +}; + +static irr::EKEY_CODE id2keycode(touch_gui_button_id id) +{ + std::string key = ""; + switch (id) { + case forward_id: + key = "forward"; + break; + case left_id: + key = "left"; + break; + case right_id: + key = "right"; + break; + case backward_id: + key = "backward"; + break; + case jump_id: + key = "jump"; + break; + case inventory_id: + key = "inventory"; + break; + case chat_id: + key = "chat"; + break; + case crunch_id: + key = "sneak"; + break; + } + assert(key != ""); + return keyname_to_keycode(g_settings->get("keymap_" + key).c_str()); +} + +TouchScreenGUI *g_touchscreengui; + +TouchScreenGUI::TouchScreenGUI(IrrlichtDevice *device, IEventReceiver* receiver): + m_device(device), + m_guienv(device->getGUIEnvironment()), + m_camera_yaw(0.0), + m_camera_pitch(0.0), + m_visible(false), + m_move_id(-1), + m_receiver(receiver) +{ + for (unsigned int i=0; i < after_last_element_id; i++) { + m_buttons[i].guibutton = 0; + m_buttons[i].repeatcounter = -1; + } + + m_screensize = m_device->getVideoDriver()->getScreenSize(); +} + +void TouchScreenGUI::loadButtonTexture(button_info* btn, const char* path) +{ + unsigned int tid; + video::ITexture *texture = m_texturesource->getTexture(path,&tid); + if (texture) { + btn->guibutton->setUseAlphaChannel(true); + btn->guibutton->setImage(texture); + btn->guibutton->setPressedImage(texture); + btn->guibutton->setScaleImage(true); + btn->guibutton->setDrawBorder(false); + btn->guibutton->setText(L""); + } +} + +void TouchScreenGUI::initButton(touch_gui_button_id id, rect button_rect, + std::wstring caption, bool immediate_release ) +{ + + button_info* btn = &m_buttons[id]; + btn->guibutton = m_guienv->addButton(button_rect, 0, id, caption.c_str()); + btn->guibutton->grab(); + btn->repeatcounter = -1; + btn->keycode = id2keycode(id); + btn->immediate_release = immediate_release; + btn->ids.clear(); + + loadButtonTexture(btn,touchgui_button_imagenames[id]); +} + +static int getMaxControlPadSize(float density) { + return 200 * density * g_settings->getFloat("gui_scaling"); +} + +void TouchScreenGUI::init(ISimpleTextureSource* tsrc, float density) +{ + assert(tsrc != 0); + + u32 control_pad_size = + MYMIN((2 * m_screensize.Y) / 3,getMaxControlPadSize(density)); + + u32 button_size = control_pad_size / 3; + m_visible = true; + m_texturesource = tsrc; + m_control_pad_rect = rect(0, m_screensize.Y - 3 * button_size, + 3 * button_size, m_screensize.Y); + /* + draw control pad + 0 1 2 + 3 4 5 + for now only 0, 1, 2, and 4 are used + */ + int number = 0; + for (int y = 0; y < 2; ++y) + for (int x = 0; x < 3; ++x, ++number) { + rect button_rect( + x * button_size, m_screensize.Y - button_size * (2 - y), + (x + 1) * button_size, m_screensize.Y - button_size * (1 - y) + ); + touch_gui_button_id id = after_last_element_id; + std::wstring caption; + switch (number) { + case 0: + id = left_id; + caption = L"<"; + break; + case 1: + id = forward_id; + caption = L"^"; + break; + case 2: + id = right_id; + caption = L">"; + break; + case 4: + id = backward_id; + caption = L"v"; + break; + } + if (id != after_last_element_id) { + initButton(id, button_rect, caption, false); + } + } + + /* init inventory button */ + initButton(inventory_id, + rect(0, m_screensize.Y - (button_size/2), + (button_size/2), m_screensize.Y), L"inv", true); + + /* init jump button */ + initButton(jump_id, + rect(m_screensize.X-(1.75*button_size), + m_screensize.Y - (0.5*button_size), + m_screensize.X-(0.25*button_size), + m_screensize.Y), + L"x",false); + + /* init crunch button */ + initButton(crunch_id, + rect(m_screensize.X-(3.25*button_size), + m_screensize.Y - (0.5*button_size), + m_screensize.X-(1.75*button_size), + m_screensize.Y), + L"H",false); + + /* init chat button */ + initButton(chat_id, + rect(m_screensize.X-(1.5*button_size), 0, + m_screensize.X, button_size), + L"Chat", true); +} + +touch_gui_button_id TouchScreenGUI::getButtonID(s32 x, s32 y) +{ + IGUIElement* rootguielement = m_guienv->getRootGUIElement(); + + if (rootguielement != NULL) { + gui::IGUIElement *element = + rootguielement->getElementFromPoint(core::position2d(x,y)); + + if (element) { + for (unsigned int i=0; i < after_last_element_id; i++) { + if (element == m_buttons[i].guibutton) { + return (touch_gui_button_id) i; + } + } + } + } + return after_last_element_id; +} + +touch_gui_button_id TouchScreenGUI::getButtonID(int eventID) +{ + for (unsigned int i=0; i < after_last_element_id; i++) { + button_info* btn = &m_buttons[i]; + + std::vector::iterator id = + std::find(btn->ids.begin(),btn->ids.end(), eventID); + + if (id != btn->ids.end()) + return (touch_gui_button_id) i; + } + + return after_last_element_id; +} + +bool TouchScreenGUI::isHUDButton(const SEvent &event) +{ + // check if hud item is pressed + for (std::map >::iterator iter = m_hud_rects.begin(); + iter != m_hud_rects.end(); iter++) { + if (iter->second.isPointInside( + v2s32(event.TouchInput.X, + event.TouchInput.Y) + )) { + if ( iter->first < 8) { + SEvent* translated = new SEvent(); + memset(translated,0,sizeof(SEvent)); + translated->EventType = irr::EET_KEY_INPUT_EVENT; + translated->KeyInput.Key = (irr::EKEY_CODE) (KEY_KEY_1 + iter->first); + translated->KeyInput.Control = false; + translated->KeyInput.Shift = false; + translated->KeyInput.PressedDown = true; + m_receiver->OnEvent(*translated); + m_hud_ids[event.TouchInput.ID] = translated->KeyInput.Key; + delete translated; + return true; + } + } + } + return false; +} + +bool TouchScreenGUI::isReleaseHUDButton(int eventID) +{ + std::map::iterator iter = m_hud_ids.find(eventID); + + if (iter != m_hud_ids.end()) { + SEvent* translated = new SEvent(); + memset(translated,0,sizeof(SEvent)); + translated->EventType = irr::EET_KEY_INPUT_EVENT; + translated->KeyInput.Key = iter->second; + translated->KeyInput.PressedDown = false; + translated->KeyInput.Control = false; + translated->KeyInput.Shift = false; + m_receiver->OnEvent(*translated); + m_hud_ids.erase(iter); + delete translated; + return true; + } + return false; +} + +void TouchScreenGUI::ButtonEvent(touch_gui_button_id button, + int eventID, bool action) +{ + button_info* btn = &m_buttons[button]; + SEvent* translated = new SEvent(); + memset(translated,0,sizeof(SEvent)); + translated->EventType = irr::EET_KEY_INPUT_EVENT; + translated->KeyInput.Key = btn->keycode; + translated->KeyInput.Control = false; + translated->KeyInput.Shift = false; + translated->KeyInput.Char = 0; + + /* add this event */ + if (action) { + assert(std::find(btn->ids.begin(),btn->ids.end(), eventID) == btn->ids.end()); + + btn->ids.push_back(eventID); + + if (btn->ids.size() > 1) return; + + btn->repeatcounter = 0; + translated->KeyInput.PressedDown = true; + translated->KeyInput.Key = btn->keycode; + m_receiver->OnEvent(*translated); + } + /* remove event */ + if ((!action) || (btn->immediate_release)) { + + std::vector::iterator pos = + std::find(btn->ids.begin(),btn->ids.end(), eventID); + /* has to be in touch list */ + assert(pos != btn->ids.end()); + btn->ids.erase(pos); + + if (btn->ids.size() > 0) { return; } + + translated->KeyInput.PressedDown = false; + btn->repeatcounter = -1; + m_receiver->OnEvent(*translated); + } + delete translated; +} + +void TouchScreenGUI::translateEvent(const SEvent &event) +{ + if (!m_visible) { + infostream << "TouchScreenGUI::translateEvent got event but not visible?!" << std::endl; + return; + } + + if (event.EventType != EET_TOUCH_INPUT_EVENT) { + return; + } + + if (event.TouchInput.Event == ETIE_PRESSED_DOWN) { + + /* add to own copy of eventlist ... + * android would provide this information but irrlicht guys don't + * wanna design a efficient interface + */ + id_status toadd; + toadd.id = event.TouchInput.ID; + toadd.X = event.TouchInput.X; + toadd.Y = event.TouchInput.Y; + m_known_ids.push_back(toadd); + + int eventID = event.TouchInput.ID; + + touch_gui_button_id button = + getButtonID(event.TouchInput.X, event.TouchInput.Y); + + /* handle button events */ + if (button != after_last_element_id) { + ButtonEvent(button,eventID,true); + } + else if (isHUDButton(event)) + { + /* already handled in isHUDButton() */ + } + /* handle non button events */ + else { + /* if we don't already have a moving point make this the moving one */ + if (m_move_id == -1) { + m_move_id = event.TouchInput.ID; + m_move_has_really_moved = false; + m_move_downtime = getTimeMs(); + m_move_downlocation = v2s32(event.TouchInput.X, event.TouchInput.Y); + m_move_sent_as_mouse_event = false; + } + } + + m_pointerpos[event.TouchInput.ID] = v2s32(event.TouchInput.X, event.TouchInput.Y); + } + else if (event.TouchInput.Event == ETIE_LEFT_UP) { + verbosestream << "Up event for pointerid: " << event.TouchInput.ID << std::endl; + + touch_gui_button_id button = getButtonID(event.TouchInput.ID); + + /* handle button events */ + if (button != after_last_element_id) { + ButtonEvent(button,event.TouchInput.ID,false); + } + /* handle hud button events */ + else if (isReleaseHUDButton(event.TouchInput.ID)) { + /* nothing to do here */ + } + /* handle the point used for moving view */ + else if (event.TouchInput.ID == m_move_id) { + m_move_id = -1; + + /* if this pointer issued a mouse event issue symmetric release here */ + if (m_move_sent_as_mouse_event) { + SEvent* translated = new SEvent; + memset(translated,0,sizeof(SEvent)); + translated->EventType = EET_MOUSE_INPUT_EVENT; + translated->MouseInput.X = m_move_downlocation.X; + translated->MouseInput.Y = m_move_downlocation.Y; + translated->MouseInput.Shift = false; + translated->MouseInput.Control = false; + translated->MouseInput.ButtonStates = 0; + translated->MouseInput.Event = EMIE_LMOUSE_LEFT_UP; + m_receiver->OnEvent(*translated); + delete translated; + } + else { + /* do double tap detection */ + doubleTapDetection(); + } + } + else { + infostream + << "TouchScreenGUI::translateEvent released unknown button: " + << event.TouchInput.ID << std::endl; + } + + for (std::vector::iterator iter = m_known_ids.begin(); + iter != m_known_ids.end(); iter++) { + if (iter->id == event.TouchInput.ID) { + m_known_ids.erase(iter); + break; + } + } + } + else { + assert(event.TouchInput.Event == ETIE_MOVED); + int move_idx = event.TouchInput.ID; + + if (m_pointerpos[event.TouchInput.ID] == + v2s32(event.TouchInput.X, event.TouchInput.Y)) { + return; + } + + if (m_move_id != -1) { + if ((event.TouchInput.ID == m_move_id) && + (!m_move_sent_as_mouse_event)) { + + double distance = sqrt( + (m_pointerpos[event.TouchInput.ID].X - event.TouchInput.X) * + (m_pointerpos[event.TouchInput.ID].X - event.TouchInput.X) + + (m_pointerpos[event.TouchInput.ID].Y - event.TouchInput.Y) * + (m_pointerpos[event.TouchInput.ID].Y - event.TouchInput.Y)); + + if ((distance > g_settings->getU16("touchscreen_threshold")) || + (m_move_has_really_moved)) { + m_move_has_really_moved = true; + s32 X = event.TouchInput.X; + s32 Y = event.TouchInput.Y; + + // update camera_yaw and camera_pitch + s32 dx = X - m_pointerpos[event.TouchInput.ID].X; + s32 dy = Y - m_pointerpos[event.TouchInput.ID].Y; + + /* adapt to similar behaviour as pc screen */ + double d = g_settings->getFloat("mouse_sensitivity") *4; + double old_yaw = m_camera_yaw; + double old_pitch = m_camera_pitch; + + m_camera_yaw -= dx * d; + m_camera_pitch = MYMIN(MYMAX( m_camera_pitch + (dy * d),-180),180); + + while (m_camera_yaw < 0) + m_camera_yaw += 360; + + while (m_camera_yaw > 360) + m_camera_yaw -= 360; + + // update shootline + m_shootline = m_device + ->getSceneManager() + ->getSceneCollisionManager() + ->getRayFromScreenCoordinates(v2s32(X, Y)); + m_pointerpos[event.TouchInput.ID] = v2s32(X, Y); + } + } + else if ((event.TouchInput.ID == m_move_id) && + (m_move_sent_as_mouse_event)) { + m_shootline = m_device + ->getSceneManager() + ->getSceneCollisionManager() + ->getRayFromScreenCoordinates( + v2s32(event.TouchInput.X,event.TouchInput.Y)); + } + } + else { + handleChangedButton(event); + } + } +} + +void TouchScreenGUI::handleChangedButton(const SEvent &event) +{ + for (unsigned int i = 0; i < after_last_element_id; i++) { + + if (m_buttons[i].ids.empty()) { + continue; + } + for(std::vector::iterator iter = m_buttons[i].ids.begin(); + iter != m_buttons[i].ids.end(); iter++) { + + if (event.TouchInput.ID == *iter) { + + int current_button_id = + getButtonID(event.TouchInput.X, event.TouchInput.Y); + + if (current_button_id == i) { + continue; + } + + /* remove old button */ + ButtonEvent((touch_gui_button_id) i,*iter,false); + + if (current_button_id == after_last_element_id) { + return; + } + ButtonEvent((touch_gui_button_id) current_button_id,*iter,true); + return; + + } + } + } + + int current_button_id = getButtonID(event.TouchInput.X, event.TouchInput.Y); + + if (current_button_id == after_last_element_id) { + return; + } + + button_info* btn = &m_buttons[current_button_id]; + if (std::find(btn->ids.begin(),btn->ids.end(), event.TouchInput.ID) == btn->ids.end()) { + ButtonEvent((touch_gui_button_id) current_button_id,event.TouchInput.ID,true); + } + +} + +bool TouchScreenGUI::doubleTapDetection() +{ + m_key_events[0].down_time = m_key_events[1].down_time; + m_key_events[0].x = m_key_events[1].x; + m_key_events[0].y = m_key_events[1].y; + m_key_events[1].down_time = m_move_downtime; + m_key_events[1].x = m_move_downlocation.X; + m_key_events[1].y = m_move_downlocation.Y; + + u32 delta = porting::getDeltaMs(m_key_events[0].down_time,getTimeMs()); + if (delta > 400) + return false; + + double distance = sqrt( + (m_key_events[0].x - m_key_events[1].x) * (m_key_events[0].x - m_key_events[1].x) + + (m_key_events[0].y - m_key_events[1].y) * (m_key_events[0].y - m_key_events[1].y)); + + + if (distance >(20 + g_settings->getU16("touchscreen_threshold"))) + return false; + + SEvent* translated = new SEvent(); + memset(translated,0,sizeof(SEvent)); + translated->EventType = EET_MOUSE_INPUT_EVENT; + translated->MouseInput.X = m_key_events[0].x; + translated->MouseInput.Y = m_key_events[0].y; + translated->MouseInput.Shift = false; + translated->MouseInput.Control = false; + translated->MouseInput.ButtonStates = EMBSM_RIGHT; + + // update shootline + m_shootline = m_device + ->getSceneManager() + ->getSceneCollisionManager() + ->getRayFromScreenCoordinates(v2s32(m_key_events[0].x, m_key_events[0].y)); + + translated->MouseInput.Event = EMIE_RMOUSE_PRESSED_DOWN; + verbosestream << "TouchScreenGUI::translateEvent right click press" << std::endl; + m_receiver->OnEvent(*translated); + + translated->MouseInput.ButtonStates = 0; + translated->MouseInput.Event = EMIE_RMOUSE_LEFT_UP; + verbosestream << "TouchScreenGUI::translateEvent right click release" << std::endl; + m_receiver->OnEvent(*translated); + delete translated; + return true; + +} + +TouchScreenGUI::~TouchScreenGUI() +{ + for (unsigned int i=0; i < after_last_element_id; i++) { + button_info* btn = &m_buttons[i]; + if (btn->guibutton != 0) { + btn->guibutton->drop(); + btn->guibutton = NULL; + } + } +} + +void TouchScreenGUI::step(float dtime) +{ + /* simulate keyboard repeats */ + for (unsigned int i=0; i < after_last_element_id; i++) { + button_info* btn = &m_buttons[i]; + + if (btn->ids.size() > 0) { + btn->repeatcounter += dtime; + + if (btn->repeatcounter < 0.2) continue; + + btn->repeatcounter = 0; + SEvent translated; + memset(&translated,0,sizeof(SEvent)); + translated.EventType = irr::EET_KEY_INPUT_EVENT; + translated.KeyInput.Key = btn->keycode; + translated.KeyInput.PressedDown = false; + m_receiver->OnEvent(translated); + + translated.KeyInput.PressedDown = true; + m_receiver->OnEvent(translated); + } + } + + /* if a new placed pointer isn't moved for some time start digging */ + if ((m_move_id != -1) && + (!m_move_has_really_moved) && + (!m_move_sent_as_mouse_event)) { + + u32 delta = porting::getDeltaMs(m_move_downtime,getTimeMs()); + + if (delta > MIN_DIG_TIME_MS) { + m_shootline = m_device + ->getSceneManager() + ->getSceneCollisionManager() + ->getRayFromScreenCoordinates( + v2s32(m_move_downlocation.X,m_move_downlocation.Y)); + + SEvent translated; + memset(&translated,0,sizeof(SEvent)); + translated.EventType = EET_MOUSE_INPUT_EVENT; + translated.MouseInput.X = m_move_downlocation.X; + translated.MouseInput.Y = m_move_downlocation.Y; + translated.MouseInput.Shift = false; + translated.MouseInput.Control = false; + translated.MouseInput.ButtonStates = EMBSM_LEFT; + translated.MouseInput.Event = EMIE_LMOUSE_PRESSED_DOWN; + verbosestream << "TouchScreenGUI::step left click press" << std::endl; + m_receiver->OnEvent(translated); + m_move_sent_as_mouse_event = true; + } + } +} + +void TouchScreenGUI::resetHud() +{ + m_hud_rects.clear(); +} + +void TouchScreenGUI::registerHudItem(int index, const rect &rect) +{ + m_hud_rects[index] = rect; +} + +void TouchScreenGUI::Toggle(bool visible) +{ + m_visible = visible; + for (unsigned int i=0; i < after_last_element_id; i++) { + button_info* btn = &m_buttons[i]; + if (btn->guibutton != 0) { + btn->guibutton->setVisible(visible); + } + } +} + +void TouchScreenGUI::Hide() +{ + Toggle(false); +} + +void TouchScreenGUI::Show() +{ + Toggle(true); +} diff --git a/src/touchscreengui.h b/src/touchscreengui.h new file mode 100644 index 000000000..8dc482034 --- /dev/null +++ b/src/touchscreengui.h @@ -0,0 +1,160 @@ +/* +Copyright (C) 2014 sapier + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#ifndef TOUCHSCREENGUI_HEADER +#define TOUCHSCREENGUI_HEADER + +#include +#include +#include + +#include +#include + +#include "game.h" +#include "tile.h" + +using namespace irr; +using namespace irr::core; +using namespace irr::gui; + +typedef enum { + forward_id = 0, + backward_id, + left_id, + right_id, + jump_id, + crunch_id, + inventory_id, + chat_id, + after_last_element_id +} touch_gui_button_id; + +#define MIN_DIG_TIME_MS 500 +#define MAX_TOUCH_COUNT 64 + +extern const char** touchgui_button_imagenames; + +class TouchScreenGUI +{ +public: + TouchScreenGUI(IrrlichtDevice *device, IEventReceiver* receiver); + ~TouchScreenGUI(); + + void translateEvent(const SEvent &event); + + void init(ISimpleTextureSource* tsrc,float density); + + double getYaw() { return m_camera_yaw; } + double getPitch() { return m_camera_pitch; } + line3d getShootline() { return m_shootline; } + + void step(float dtime); + void resetHud(); + void registerHudItem(int index, const rect &rect); + void Toggle(bool visible); + + void Hide(); + void Show(); + +private: + IrrlichtDevice* m_device; + IGUIEnvironment* m_guienv; + IEventReceiver* m_receiver; + ISimpleTextureSource* m_texturesource; + v2u32 m_screensize; + std::map > m_hud_rects; + std::map m_hud_ids; + bool m_visible; // is the gui visible + + /* value in degree */ + double m_camera_yaw; + double m_camera_pitch; + + line3d m_shootline; + + rect m_control_pad_rect; + + int m_move_id; + bool m_move_has_really_moved; + s32 m_move_downtime; + bool m_move_sent_as_mouse_event; + v2s32 m_move_downlocation; + + struct button_info { + float repeatcounter; + irr::EKEY_CODE keycode; + std::vector ids; + IGUIButton* guibutton; + bool immediate_release; + }; + + button_info m_buttons[after_last_element_id]; + + /* gui button detection */ + touch_gui_button_id getButtonID(s32 x, s32 y); + + /* gui button by eventID */ + touch_gui_button_id getButtonID(int eventID); + + /* check if a button has changed */ + void handleChangedButton(const SEvent &event); + + /* initialize a button */ + void initButton(touch_gui_button_id id, rect button_rect, + std::wstring caption, bool immediate_release ); + + /* load texture */ + void loadButtonTexture(button_info* btn, const char* path); + + struct id_status{ + int id; + int X; + int Y; + }; + + /* vector to store known ids and their initial touch positions*/ + std::vector m_known_ids; + + /* handle a button event */ + void ButtonEvent(touch_gui_button_id bID, int eventID, bool action); + + /* handle pressed hud buttons */ + bool isHUDButton(const SEvent &event); + + /* handle released hud buttons */ + bool isReleaseHUDButton(int eventID); + + /* handle double taps */ + bool doubleTapDetection(); + + /* doubleclick detection variables */ + struct key_event { + unsigned int down_time; + s32 x; + s32 y; + }; + + /* array for saving last known position of a pointer */ + v2s32 m_pointerpos[MAX_TOUCH_COUNT]; + + /* array for doubletap detection */ + key_event m_key_events[2]; +}; +extern TouchScreenGUI *g_touchscreengui; +#endif diff --git a/src/util/string.cpp b/src/util/string.cpp index a3888c9ce..363a15e65 100644 --- a/src/util/string.cpp +++ b/src/util/string.cpp @@ -29,6 +29,58 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "../hex.h" #include "../porting.h" +#ifdef __ANDROID__ +const wchar_t* wide_chars = L" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"; + +int wctomb(char *s, wchar_t wc) +{ + for (unsigned int j = 0; j < (sizeof(wide_chars)/sizeof(wchar_t));j++) { + if (wc == wide_chars[j]) { + *s = (char) (j+32); + return 1; + } + else if (wc == L'\n') { + *s = '\n'; + return 1; + } + } + return -1; +} + +int mbtowc(wchar_t *pwc, const char *s, size_t n) +{ + std::wstring intermediate = narrow_to_wide(s); + + if (intermediate.length() > 0) { + *pwc = intermediate[0]; + return 1; + } + else { + return -1; + } +} + +std::wstring narrow_to_wide(const std::string& mbs) { + size_t wcl = mbs.size(); + + std::wstring retval = L""; + + for (unsigned int i = 0; i < wcl; i++) { + if (((unsigned char) mbs[i] >31) && + ((unsigned char) mbs[i] < 127)) { + + retval += wide_chars[(unsigned char) mbs[i] -32]; + } + //handle newline + else if (mbs[i] == '\n') { + retval += L'\n'; + } + } + + return retval; +} +#else + std::wstring narrow_to_wide(const std::string& mbs) { size_t wcl = mbs.size(); @@ -40,6 +92,35 @@ std::wstring narrow_to_wide(const std::string& mbs) return *wcs; } +#endif + +#ifdef __ANDROID__ +std::string wide_to_narrow(const std::wstring& wcs) { + size_t mbl = wcs.size()*4; + + std::string retval = ""; + for (unsigned int i = 0; i < wcs.size(); i++) { + wchar_t char1 = (wchar_t) wcs[i]; + + if (char1 == L'\n') { + retval += '\n'; + continue; + } + + for (unsigned int j = 0; j < wcslen(wide_chars);j++) { + wchar_t char2 = (wchar_t) wide_chars[j]; + + if (char1 == char2) { + char toadd = (j+32); + retval += toadd; + break; + } + } + } + + return retval; +} +#else std::string wide_to_narrow(const std::wstring& wcs) { size_t mbl = wcs.size()*4; @@ -53,6 +134,8 @@ std::string wide_to_narrow(const std::wstring& wcs) return *mbs; } +#endif + // Get an sha-1 hash of the player's name combined with // the password entered. That's what the server uses as // their password. (Exception : if the password field is diff --git a/src/version.cpp b/src/version.cpp index e5de8a61e..83b0a4c40 100644 --- a/src/version.cpp +++ b/src/version.cpp @@ -20,19 +20,15 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "version.h" #include "config.h" -#ifdef USE_CMAKE_CONFIG_H - -#include "cmake_config_githash.h" - const char *minetest_version_simple = CMAKE_VERSION_STRING; const char *minetest_version_hash = CMAKE_VERSION_GITHASH; + +#ifdef USE_CMAKE_CONFIG_H const char *minetest_build_info = "VER=" CMAKE_VERSION_GITHASH " " CMAKE_BUILD_INFO; - +#elif defined(ANDROID) +const char *minetest_build_info = "android jni"; #else - -const char *minetest_version_simple = "unknown"; -const char *minetest_version_hash = "unknown"; const char *minetest_build_info = "non-cmake"; #endif -- cgit v1.2.3