1
0
Fork 0
mirror of https://github.com/luanti-org/luanti.git synced 2025-07-22 17:18:39 +00:00

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!
This commit is contained in:
sapier 2014-04-21 14:10:59 +02:00
parent ff36071d93
commit 1cc40c0a7c
66 changed files with 4425 additions and 162 deletions

View file

@ -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"

View file

@ -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

View file

@ -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 <map>
#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; i<DEBUGSTREAM_COUNT; i++)
{
if(g_debugstreams[i] == stderr && m_disable_stderr)

View file

@ -64,7 +64,7 @@ void set_default_settings(Settings *settings)
settings->setDefault("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)

View file

@ -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();

View file

@ -25,6 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <errno.h>
#include <fstream>
#include "log.h"
#include "config.h"
namespace fs
{
@ -34,8 +35,8 @@ namespace fs
#define _WIN32_WINNT 0x0501
#include <windows.h>
#include <malloc.h>
#include <tchar.h>
#include <wchar.h>
#include <tchar.h>
#include <wchar.h>
#define BUFSIZE MAX_PATH
@ -73,12 +74,12 @@ std::vector<DirListNode> 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<DirListNode> 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<DirListNode> 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 "
<<dwError<<std::endl;
@ -401,7 +402,11 @@ std::string TempPath()
compatible with lua's os.tmpname which under the default
configuration hardcodes mkstemp("/tmp/lua_XXXXXX").
*/
return std::string(DIR_DELIM) + "tmp";
#ifdef __ANDROID__
return DIR_DELIM "sdcard" DIR_DELIM PROJECT_NAME DIR_DELIM "tmp";
#else
return DIR_DELIM "tmp";
#endif
}
#endif

View file

@ -70,6 +70,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "drawscene.h"
#include "content_cao.h"
#ifdef HAVE_TOUCHSCREENGUI
#include "touchscreengui.h"
#endif
/*
Text input system
*/
@ -942,14 +946,20 @@ static inline void create_formspec_menu(GUIFormSpecMenu** cur_formspec,
}
}
#ifdef __ANDROID__
#define SIZE_TAG "size[11,5.5]"
#else
#define SIZE_TAG "size[11,5.5,true]"
#endif
static void show_chat_menu(GUIFormSpecMenu** cur_formspec,
InventoryManager *invmgr, IGameDef *gamedef,
IWritableTextureSource* tsrc, IrrlichtDevice * device,
Client* client, std::string text)
{
std::string formspec =
FORMSPEC_VERSION_STRING
"size[11,5.5,true]"
FORMSPEC_VERSION_STRING
SIZE_TAG
"field[3,2.35;6,0.5;f_text;;" + text + "]"
"button_exit[4,3;3,0.5;btn_send;" + wide_to_narrow(wstrgettext("Proceed")) + "]"
;
@ -969,7 +979,7 @@ static void show_deathscreen(GUIFormSpecMenu** cur_formspec,
{
std::string formspec =
std::string(FORMSPEC_VERSION_STRING) +
"size[11,5.5,true]"
SIZE_TAG
"bgcolor[#320000b4;true]"
"label[4.85,1.35;You died.]"
"button_exit[4,3;3,0.5;btn_respawn;" + gettext("Respawn") + "]"
@ -990,6 +1000,21 @@ static void show_pause_menu(GUIFormSpecMenu** cur_formspec,
IWritableTextureSource* tsrc, IrrlichtDevice * device,
bool singleplayermode)
{
#ifdef __ANDROID__
std::string control_text = wide_to_narrow(wstrgettext("Default Controls:\n"
"No menu visible:\n"
"- single tap: button activate\n"
"- double tap: place/use\n"
"- slide finger: look around\n"
"Menu/Inventory visible:\n"
"- double tap (outside):\n"
" -->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<<wide_to_narrow(error_message)<<std::endl;
break;
}
if(input->wasKeyDown(EscapeKey)){
if(input->wasKeyDown(EscapeKey) || input->wasKeyDown(CancelKey)) {
connect_aborted = true;
infostream<<"Connect aborted [Escape]"<<std::endl;
break;
@ -1310,8 +1335,8 @@ void the_game(bool &kill, bool random_input, InputHandler *input,
/*
Handle failure to connect
*/
if(!could_connect){
if(error_message == L"" && !connect_aborted){
if(!could_connect) {
if(error_message == L"" && !connect_aborted) {
error_message = L"Connection failed";
errorstream<<wide_to_narrow(error_message)<<std::endl;
}
@ -1330,8 +1355,7 @@ void the_game(bool &kill, bool random_input, InputHandler *input,
float fps_max = g_settings->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<<wide_to_narrow(error_message)<<std::endl;
break;
}
if(client.getState() < LC_Init){
if (client.getState() < LC_Init) {
error_message = L"Client disconnected";
errorstream<<wide_to_narrow(error_message)<<std::endl;
break;
}
if(input->wasKeyDown(EscapeKey)){
if (input->wasKeyDown(EscapeKey) || input->wasKeyDown(CancelKey)) {
content_aborted = true;
infostream<<"Connect aborted [Escape]"<<std::endl;
break;
@ -1548,6 +1572,11 @@ void the_game(bool &kill, bool random_input, InputHandler *input,
guitext_profiler->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(&current_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"<<std::endl;
first_loop_after_window_activation = false;
}
else{
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) {
} 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 "<<dx<<","<<dy<<std::endl;
@ -2247,18 +2294,23 @@ void the_game(bool &kill, bool random_input, InputHandler *input,
d = rangelim(d, 0.01, 100.0);
camera_yaw -= dx*d;
camera_pitch += dy*d;
turn_amount = v2f(dx, dy).getLength() * d;
#ifdef HAVE_TOUCHSCREENGUI
}
#endif
if(camera_pitch < -89.5) camera_pitch = -89.5;
if(camera_pitch > 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"<<std::endl;
first_loop_after_window_activation = true;
@ -2668,10 +2720,19 @@ void the_game(bool &kill, bool random_input, InputHandler *input,
core::line3d<f32> 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<f32>(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 "<<minetest_version_hash;
os<<"Minetest "<<minetest_version_hash <<" FPS = "<<fps;
guitext->setText(narrow_to_wide(os.str()).c_str());
guitext->setVisible(true);
}

View file

@ -32,6 +32,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "clouds.h"
#include "httpfetch.h"
#include "util/numeric.h"
#ifdef __ANDROID__
#include "tile.h"
#include <GLES/gl.h>
#endif
#include <IGUIStaticText.h>
#include <ICameraSceneNode.h>
@ -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
}
}

View file

@ -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<FieldSpec>::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<s32> 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; i<s.geom.X*s.geom.Y; i++)
{
for(s32 i=0; i<s.geom.X*s.geom.Y; i++) {
s32 item_i = i + s.start_item_i;
s32 x = (i%s.geom.X) * spacing.X;
s32 y = (i/s.geom.X) * spacing.Y;
@ -2051,8 +2099,6 @@ void GUIFormSpecMenu::drawMenu()
}
}
m_pointer = m_device->getCursorControl()->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<s32>(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<s32>(
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<FieldSpec>::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<FieldSpec>::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)
{

View file

@ -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

View file

@ -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<<request.url<<" not found ("
errorstream<<request.url<<" not found ("
<<curl_easy_strerror(res)<<")"
<<" (response code "<<result.response_code<<")"
<<std::endl;

View file

@ -33,6 +33,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "porting.h"
#include <IGUIStaticText.h>
#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<s32>& 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
}
}

View file

@ -38,6 +38,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <map>
#include <set>
#ifdef __ANDROID__
#include <GLES/gl.h>
#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);

View file

@ -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);
}

View file

@ -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);

View file

@ -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"),

View file

@ -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 <IEventReceiver.h>
#include <string>
@ -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

View file

@ -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<ILogOutput*> log_outputs[LMT_NUM_VALUES];
std::map<threadid_t, std::string> 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)

View file

@ -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 */

View file

@ -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)<<std::endl;
return true;
}
/* always return false in order to continue processing events */
@ -342,6 +360,9 @@ public:
MyEventReceiver()
{
clearInput();
#ifdef HAVE_TOUCHSCREENGUI
m_touchscreengui = NULL;
#endif
}
bool leftclicked;
@ -355,7 +376,12 @@ public:
s32 mouse_wheel;
#ifdef HAVE_TOUCHSCREENGUI
TouchScreenGUI* m_touchscreengui;
#endif
private:
IrrlichtDevice *m_device;
// The current state of keys
KeyList keyIsDown;
@ -372,7 +398,8 @@ class RealInputHandler : public InputHandler
public:
RealInputHandler(IrrlichtDevice *device, MyEventReceiver *receiver):
m_device(device),
m_receiver(receiver)
m_receiver(receiver),
m_mousepos(0,0)
{
}
virtual bool isKeyDown(const KeyPress &keyCode)
@ -385,11 +412,21 @@ public:
}
virtual v2s32 getMousePos()
{
return m_device->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<u32>(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

View file

@ -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;
};

View file

@ -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()

View file

@ -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<std::string>::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

View file

@ -53,12 +53,12 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#ifdef _WIN32
#include <windows.h>
#define sleep_ms(x) Sleep(x)
#else
#include <unistd.h>
#include <stdint.h> //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 <windows.h>
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 <sys/time.h>
#include <time.h>
@ -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 <sys/timeb.h>
inline u32 getTimeMs()
{
@ -373,5 +373,9 @@ v2u32 getWindowSize();
} // namespace porting
#ifdef __ANDROID__
#include "porting_android.h"
#endif
#endif // PORTING_HEADER

295
src/porting_android.cpp Normal file
View file

@ -0,0 +1,295 @@
/*
Minetest
Copyright (C) 2014 celeron55, Perttu Ahola <celeron55@gmail.com>
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 <sstream>
#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
}

81
src/porting_android.h Normal file
View file

@ -0,0 +1,81 @@
/*
Minetest
Copyright (C) 2014 celeron55, Perttu Ahola <celeron55@gmail.com>
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 <jni.h>
#include <android_native_app_glue.h>
#include <android/log.h>
#include <string>
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

View file

@ -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 <GLES/gl.h>
#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; i<m_textureinfo_cache.size(); i++){
TextureInfo *ti = &m_textureinfo_cache[i];
video::IImage *img = generateImageFromScratch(ti->name);
#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<u32> screen = driver->getScreenSize();
// Render scene
driver->beginScene(true, true, video::SColor(0,0,0,0));
driver->clearZBuffer();
smgr->drawAll();
core::dimension2d<u32> partsize(screen.Width * scaling,screen.Height * scaling);
irr::video::IImage* rawImage =
driver->createImage(irr::video::ECF_A8R8G8B8, partsize);
u8* pixels = static_cast<u8*>(rawImage->lock());
if (!pixels)
{
rawImage->drop();
return NULL;
}
core::rect<s32> 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"<<std::endl;
return NULL;
}
// Get a scene manager
scene::ISceneManager *smgr_main = m_device->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 <GLES/gl.h>
/**
* 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<u32> 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<u32>(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<u32> 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);

View file

@ -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,

690
src/touchscreengui.cpp Normal file
View file

@ -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 <iostream>
#include <algorithm>
#include <ISceneCollisionManager.h>
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<s32> 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<s32>(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<s32> 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<s32>(0, m_screensize.Y - (button_size/2),
(button_size/2), m_screensize.Y), L"inv", true);
/* init jump button */
initButton(jump_id,
rect<s32>(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<s32>(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<s32>(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<s32>(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<int>::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<int,rect<s32> >::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<int,irr::EKEY_CODE>::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<int>::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<id_status>::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<int>::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<s32> &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);
}

160
src/touchscreengui.h Normal file
View file

@ -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 <IGUIEnvironment.h>
#include <IGUIButton.h>
#include <IEventReceiver.h>
#include <vector>
#include <map>
#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<f32> getShootline() { return m_shootline; }
void step(float dtime);
void resetHud();
void registerHudItem(int index, const rect<s32> &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<int,rect<s32> > m_hud_rects;
std::map<int,irr::EKEY_CODE> m_hud_ids;
bool m_visible; // is the gui visible
/* value in degree */
double m_camera_yaw;
double m_camera_pitch;
line3d<f32> m_shootline;
rect<s32> 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<int> 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<s32> 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<id_status> 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

View file

@ -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

View file

@ -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