From 858dd2fdb237f3f2a09297462e0f99a9e951bc54 Mon Sep 17 00:00:00 2001 From: pelya Date: Tue, 28 Dec 2010 13:46:13 +0000 Subject: [PATCH] Option for calibrating touchscreen --- project/java/Globals.java | 3 +- project/java/MainActivity.java | 28 +- project/java/Settings.java | 109 ++- project/java/translations/values/strings.xml | 3 + .../fheroes2/AndroidAppSettings.cfg | 4 +- .../fheroes2/fheroes2-no-cout.diff | 664 ------------------ project/jni/application/src | 2 +- .../src/video/android/SDL_androidinput.c | 24 +- .../src/video/android/SDL_androidvideo.c | 2 + .../src/video/android/SDL_androidvideo.h | 4 + project/res/drawable/calibrate.png | Bin 0 -> 8086 bytes 11 files changed, 149 insertions(+), 694 deletions(-) delete mode 100644 project/jni/application/fheroes2/fheroes2-no-cout.diff create mode 100644 project/res/drawable/calibrate.png diff --git a/project/java/Globals.java b/project/java/Globals.java index 9d00b8416..3a0c46d74 100644 --- a/project/java/Globals.java +++ b/project/java/Globals.java @@ -47,7 +47,7 @@ class Globals { public static int AppTouchscreenKeyboardKeysAmountAutoFire = 1; - // Phone-specific config + // Phone-specific config, TODO: move this to settings public static boolean DownloadToSdcard = true; public static boolean PhoneHasTrackball = false; public static boolean PhoneHasArrowKeys = false; @@ -86,6 +86,7 @@ class Globals { public static int RemapMultitouchGestureKeycode[] = new int[4]; public static boolean MultitouchGesturesUsed[] = new boolean[4]; public static int MultitouchGestureSensitivity = 1; + public static int TouchscreenCalibration[] = new int[4]; } class LoadLibrary { diff --git a/project/java/MainActivity.java b/project/java/MainActivity.java index 0e4dbe26b..48487ca04 100644 --- a/project/java/MainActivity.java +++ b/project/java/MainActivity.java @@ -73,7 +73,10 @@ public class MainActivity extends Activity { img.setLayoutParams(new ViewGroup.LayoutParams( ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT)); _layout.addView(img); - setContentView(_layout); + _videoLayout = new FrameLayout(this); + _videoLayout.addView(_layout); + + setContentView(_videoLayout); if(mAudioThread == null) // Starting from background (should not happen) { @@ -153,10 +156,15 @@ public class MainActivity extends Activity { if(Globals.UseAccelerometerAsArrowKeys) getWindow().setFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON, WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); - mGLView = new DemoGLSurfaceView(this); + _videoLayout.removeView(_layout); + _layout = null; + _layout2 = null; + _btn = null; + _tv = null; _videoLayout = new FrameLayout(this); - _videoLayout.addView(mGLView); setContentView(_videoLayout); + mGLView = new DemoGLSurfaceView(this); + _videoLayout.addView(mGLView); // Receive keyboard events mGLView.setFocusableInTouchMode(true); mGLView.setFocusable(true); @@ -289,9 +297,9 @@ public class MainActivity extends Activity { onStop(); } else - if( keyRemapTool != null ) + if( keyListener != null ) { - keyRemapTool.onKeyEvent(keyCode); + keyListener.onKeyEvent(keyCode); } return true; } @@ -317,8 +325,8 @@ public class MainActivity extends Activity { if( _btn != null ) return _btn.dispatchTouchEvent(ev); else - if( touchMeasurementTool != null ) - touchMeasurementTool.onTouchEvent(ev); + if( touchListener != null ) + touchListener.onTouchEvent(ev); return true; } @@ -369,6 +377,8 @@ public class MainActivity extends Activity { NotificationManager.cancel(NOTIFY_ID); } + public FrameLayout getVideoLayout() { return _videoLayout; } + static int NOTIFY_ID = 12367098; // Random ID private static DemoGLSurfaceView mGLView = null; @@ -384,8 +394,8 @@ public class MainActivity extends Activity { private FrameLayout _videoLayout = null; private EditText _screenKeyboard = null; private boolean sdlInited = false; - public Settings.TouchEventsListener touchMeasurementTool = null; - public Settings.KeyEventsListener keyRemapTool = null; + public Settings.TouchEventsListener touchListener = null; + public Settings.KeyEventsListener keyListener = null; boolean _isPaused = false; public LinkedList textInput = new LinkedList (); diff --git a/project/java/Settings.java b/project/java/Settings.java index 5d4a9e161..6e870c791 100644 --- a/project/java/Settings.java +++ b/project/java/Settings.java @@ -23,6 +23,15 @@ import android.content.Context; import android.content.res.Configuration; import android.content.res.Resources; import java.lang.String; +import android.graphics.Matrix; +import android.graphics.RectF; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.FrameLayout; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.BitmapFactory; +import android.graphics.Bitmap; + // TODO: too much code here, split into multiple files @@ -84,6 +93,8 @@ class Settings out.writeBoolean(Globals.MultitouchGesturesUsed[i]); } out.writeInt(Globals.MultitouchGestureSensitivity); + for( int i = 0; i < Globals.TouchscreenCalibration.length; i++ ) + out.writeInt(Globals.TouchscreenCalibration[i]); out.close(); settingsLoaded = true; @@ -187,6 +198,8 @@ class Settings Globals.MultitouchGesturesUsed[i] = settingsFile.readBoolean(); } Globals.MultitouchGestureSensitivity = settingsFile.readInt(); + for( int i = 0; i < Globals.TouchscreenCalibration.length; i++ ) + Globals.TouchscreenCalibration[i] = settingsFile.readInt(); settingsLoaded = true; @@ -363,6 +376,8 @@ class Settings if( Globals.RightClickMethod == Globals.RIGHT_CLICK_WITH_PRESSURE || Globals.LeftClickMethod == Globals.LEFT_CLICK_WITH_PRESSURE ) items.add(p.getResources().getString(R.string.measurepressure)); + items.add(p.getResources().getString(R.string.calibrate_touchscreen)); + items.add(p.getResources().getString(R.string.ok)); AlertDialog.Builder builder = new AlertDialog.Builder(p); @@ -404,6 +419,10 @@ class Settings item++; selected++; + if( item == selected ) + showCalibrateTouchscreenMenu(p); + selected++; + if( item == selected ) showConfigMainMenu(p); } @@ -988,15 +1007,8 @@ class Settings static void showTouchPressureMeasurementTool(final MainActivity p) { - if( Globals.RightClickMethod == Globals.RIGHT_CLICK_WITH_PRESSURE || Globals.LeftClickMethod == Globals.LEFT_CLICK_WITH_PRESSURE ) - { - p.setText(p.getResources().getString(R.string.measurepressure_touchplease)); - p.touchMeasurementTool = new TouchMeasurementTool(p); - } - else - { - showMouseConfigMainMenu(p); - } + p.setText(p.getResources().getString(R.string.measurepressure_touchplease)); + p.touchListener = new TouchMeasurementTool(p); } public static class TouchMeasurementTool implements TouchEventsListener @@ -1022,7 +1034,7 @@ class Settings if( force.size() >= maxEventAmount ) { - p.touchMeasurementTool = null; + p.touchListener = null; Globals.ClickScreenPressure = getAverageForce(); Globals.ClickScreenTouchspotSize = getAverageRadius(); System.out.println("SDL: measured average force " + Globals.ClickScreenPressure + " radius " + Globals.ClickScreenTouchspotSize); @@ -1053,7 +1065,7 @@ class Settings static void showRemapHwKeysConfig(final MainActivity p) { p.setText(p.getResources().getString(R.string.remap_hwkeys_press)); - p.keyRemapTool = new KeyRemapTool(p); + p.keyListener = new KeyRemapTool(p); } public static class KeyRemapTool implements KeyEventsListener @@ -1066,7 +1078,7 @@ class Settings public void onKeyEvent(final int keyCode) { - p.keyRemapTool = null; + p.touchListener = null; int keyIndex = keyCode; if( keyIndex < 0 ) keyIndex = 0; @@ -1281,6 +1293,77 @@ class Settings alert.setOwnerActivity(p); alert.show(); } + + static void showCalibrateTouchscreenMenu(final MainActivity p) + { + p.setText(p.getResources().getString(R.string.calibrate_touchscreen_touch)); + Globals.TouchscreenCalibration[0] = 0; + Globals.TouchscreenCalibration[1] = 0; + Globals.TouchscreenCalibration[2] = 0; + Globals.TouchscreenCalibration[3] = 0; + ScreenEdgesCalibrationTool tool = new ScreenEdgesCalibrationTool(p); + p.touchListener = tool; + p.keyListener = tool; + } + + public static class ScreenEdgesCalibrationTool implements TouchEventsListener, KeyEventsListener + { + MainActivity p; + ImageView img; + Bitmap bmp; + + public ScreenEdgesCalibrationTool(MainActivity _p) + { + p = _p; + img = new ImageView(p); + img.setLayoutParams(new ViewGroup.LayoutParams( ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT)); + img.setScaleType(ImageView.ScaleType.MATRIX); + bmp = BitmapFactory.decodeResource( p.getResources(), R.drawable.calibrate ); + img.setImageBitmap(bmp); + Matrix m = new Matrix(); + RectF src = new RectF(0, 0, bmp.getWidth(), bmp.getHeight()); + RectF dst = new RectF(Globals.TouchscreenCalibration[0], Globals.TouchscreenCalibration[1], + Globals.TouchscreenCalibration[2], Globals.TouchscreenCalibration[3]); + m.setRectToRect(src, dst, Matrix.ScaleToFit.FILL); + img.setImageMatrix(m); + p.getVideoLayout().addView(img); + } + + public void onTouchEvent(final MotionEvent ev) + { + if( Globals.TouchscreenCalibration[0] == Globals.TouchscreenCalibration[1] && + Globals.TouchscreenCalibration[1] == Globals.TouchscreenCalibration[2] && + Globals.TouchscreenCalibration[2] == Globals.TouchscreenCalibration[3] ) + { + Globals.TouchscreenCalibration[0] = (int)ev.getX(); + Globals.TouchscreenCalibration[1] = (int)ev.getY(); + Globals.TouchscreenCalibration[2] = (int)ev.getX(); + Globals.TouchscreenCalibration[3] = (int)ev.getY(); + } + if( ev.getX() < Globals.TouchscreenCalibration[0] ) + Globals.TouchscreenCalibration[0] = (int)ev.getX(); + if( ev.getY() < Globals.TouchscreenCalibration[1] ) + Globals.TouchscreenCalibration[1] = (int)ev.getY(); + if( ev.getX() > Globals.TouchscreenCalibration[2] ) + Globals.TouchscreenCalibration[2] = (int)ev.getX(); + if( ev.getY() > Globals.TouchscreenCalibration[3] ) + Globals.TouchscreenCalibration[3] = (int)ev.getY(); + Matrix m = new Matrix(); + RectF src = new RectF(0, 0, bmp.getWidth(), bmp.getHeight()); + RectF dst = new RectF(Globals.TouchscreenCalibration[0], Globals.TouchscreenCalibration[1], + Globals.TouchscreenCalibration[2], Globals.TouchscreenCalibration[3]); + m.setRectToRect(src, dst, Matrix.ScaleToFit.FILL); + img.setImageMatrix(m); + } + + public void onKeyEvent(final int keyCode) + { + p.touchListener = null; + p.keyListener = null; + p.getVideoLayout().removeView(img); + showMouseConfigMainMenu(p); + } + } // =============================================================================================== @@ -1325,6 +1408,7 @@ class Settings for( int i = 0; i < Globals.RemapMultitouchGestureKeycode.length; i++ ) nativeSetKeymapKeyMultitouchGesture(i, Globals.MultitouchGesturesUsed[i] ? SDL_Keys.values[Globals.RemapMultitouchGestureKeycode[i]] : 0); nativeSetMultitouchGestureSensitivity(Globals.MultitouchGestureSensitivity); + nativeSetTouchscreenCalibration(Globals.TouchscreenCalibration[0], Globals.TouchscreenCalibration[1], Globals.TouchscreenCalibration[2], Globals.TouchscreenCalibration[3]); String lang = new String(Locale.getDefault().getLanguage()); if( Locale.getDefault().getCountry().length() > 0 ) @@ -1389,6 +1473,7 @@ class Settings private static native int nativeGetKeymapKeyMultitouchGesture(int keynum); private static native void nativeSetKeymapKeyMultitouchGesture(int keynum, int key); private static native void nativeSetMultitouchGestureSensitivity(int sensitivity); + private static native void nativeSetTouchscreenCalibration(int x1, int y1, int x2, int y2); public static native void nativeSetEnv(final String name, final String value); } diff --git a/project/java/translations/values/strings.xml b/project/java/translations/values/strings.xml index 980f2a708..e3cc5b03f 100644 --- a/project/java/translations/values/strings.xml +++ b/project/java/translations/values/strings.xml @@ -114,4 +114,7 @@ Rotate left two-finger gesture Rotate right two-finger gesture + Calibrate touchscreen + Touch all four edges of the screen, press Back when done + diff --git a/project/jni/application/fheroes2/AndroidAppSettings.cfg b/project/jni/application/fheroes2/AndroidAppSettings.cfg index 6d589b0a8..ce0c84363 100644 --- a/project/jni/application/fheroes2/AndroidAppSettings.cfg +++ b/project/jni/application/fheroes2/AndroidAppSettings.cfg @@ -22,8 +22,8 @@ AppTouchscreenKeyboardKeysAmount=0 AppTouchscreenKeyboardKeysAmountAutoFire=0 RedefinedKeysScreenKb="LCTRL M T H E C SPACE C S L" MultiABI=n -AppVersionCode=214708 -AppVersionName="2147.08" +AppVersionCode=214908 +AppVersionName="2149.08" CompiledLibraries="sdl_net sdl_mixer sdl_image sdl_ttf png intl" CustomBuildScript=n AppCflags='-finline-functions -O2 -DWITH_ZLIB -DWITH_MIXER -DWITH_XML -DWITH_IMAGE -DWITH_TTF' diff --git a/project/jni/application/fheroes2/fheroes2-no-cout.diff b/project/jni/application/fheroes2/fheroes2-no-cout.diff deleted file mode 100644 index 7fadce281..000000000 --- a/project/jni/application/fheroes2/fheroes2-no-cout.diff +++ /dev/null @@ -1,664 +0,0 @@ -Index: src/engine/midi_mthd.cpp -=================================================================== ---- src/engine/midi_mthd.cpp (revision 2146) -+++ src/engine/midi_mthd.cpp (working copy) -@@ -27,7 +27,9 @@ - - void MThd::Dump(void) const - { -+#ifndef ANDROID - std::cerr << "[MThd] format: " << Format() << ", tracks: " << Tracks() << ", ppqn: " << PPQN() << std::endl; -+#endif - } - - void MThd::SetFormat(const u16 f) -Index: src/engine/audio.cpp -=================================================================== ---- src/engine/audio.cpp (revision 2146) -+++ src/engine/audio.cpp (working copy) -@@ -68,15 +68,19 @@ - { - if(1 == SDL_BuildAudioCVT(this, src.format, src.channels, src.freq, dst.format, dst.channels, dst.freq)) return true; - -+#ifndef ANDROID - std::cerr << "Audio::CVT::Build: " << SDL_GetError() << std::endl; -+#endif - return false; - } - - bool Audio::CVT::Convert(void) - { - if(0 == SDL_ConvertAudio(this)) return true; -- -+ -+#ifndef ANDROID - std::cerr << "Audio::CVT::Convert: " << SDL_GetError() << std::endl; -+#endif - return false; - } - -Index: src/engine/thread.cpp -=================================================================== ---- src/engine/thread.cpp (revision 2146) -+++ src/engine/thread.cpp (working copy) -@@ -128,5 +128,7 @@ - - void Time::Print(const char* header) const - { -+#ifndef ANDROID - std::cerr << (header ? header : "time: ") << Get() << " ms" << std::endl; -+#endif - } -Index: src/engine/midi_chunk.cpp -=================================================================== ---- src/engine/midi_chunk.cpp (revision 2146) -+++ src/engine/midi_chunk.cpp (working copy) -@@ -183,6 +183,7 @@ - - void Chunk::Dump(void) const - { -+#ifndef ANDROID - std::cerr << "id: "; - std::cerr.write(id, 4); - std::cerr << std::endl << "size: " << std::dec << size << std::endl << "data: " << std::endl; -@@ -200,4 +201,5 @@ - } - } - std::cerr << std::endl; -+#endif - } -Index: src/engine/midi_mid.cpp -=================================================================== ---- src/engine/midi_mid.cpp (revision 2146) -+++ src/engine/midi_mid.cpp (working copy) -@@ -77,8 +77,9 @@ - - if(! mthd.isValid()) - { -+#ifndef ANDROID - std::cerr << "Mid::Read: " << "error format" << std::endl; -- -+#endif - return false; - } - -@@ -89,8 +90,9 @@ - { - if(ptr >= &body[0] + body.size()) - { -+#ifndef ANDROID - std::cerr << "Mid::Read: " << "error read chunk, total: " << count << ", current: " << ii << std::endl; -- -+#endif - return false; - } - -@@ -111,8 +113,9 @@ - - if(!fd.is_open()) - { -+#ifndef ANDROID - std::cerr << "Mid::Read: " << "error read: " << filename << std::endl; -- -+#endif - return false; - } - -@@ -120,8 +123,9 @@ - - if(! mthd.isValid()) - { -+#ifndef ANDROID - std::cerr << "Mid::Read: " << "error format: " << filename << std::endl; -- -+#endif - return false; - } - -@@ -131,8 +135,9 @@ - { - if(fd.fail()) - { -+#ifndef ANDROID - std::cerr << "Mid::Read: " << "error read chunk, total: " << count << ", current: " << ii << std::endl; -- -+#endif - return false; - } - -@@ -187,8 +192,9 @@ - - if(!fd.is_open()) - { -+#ifndef ANDROID - std::cerr << "Mid::Write: " << "error write: " << filename << std::endl; -- -+#endif - return false; - } - -Index: src/engine/midi_mtrk.cpp -=================================================================== ---- src/engine/midi_mtrk.cpp (revision 2146) -+++ src/engine/midi_mtrk.cpp (working copy) -@@ -102,7 +102,9 @@ - default: - end = true; - CloseEvents(); -+#ifndef ANDROID - std::cerr << "unknown st: 0x" << std::setw(2) << std::setfill('0') << std::hex << static_cast(status) << ", ln: " << static_cast(p + s - ptr) << std::endl; -+#endif - break; - } - } -@@ -202,8 +204,8 @@ - - void MTrk::Dump(void) const - { -+#ifndef ANDROID - std::cerr << "[MTrk]\n"; -- - if(events.size()) - { - std::list::const_iterator it1 = events.begin(); -@@ -211,6 +213,7 @@ - for(; it1 != it2; ++it1) if(*it1) (*it1)->Dump(); - } - std::cerr << std::endl; -+#endif - } - - void MTrk::ImportXmiEVNT(const Chunk & evnt) -@@ -345,7 +348,9 @@ - // unused command - default: - CloseEvents(); -+#ifndef ANDROID - std::cerr << "unknown st: 0x" << std::setw(2) << std::setfill('0') << std::hex << static_cast(*ptr) << ", ln: " << static_cast(evnt.data + evnt.size - ptr) << std::endl; -+#endif - break; - } - } -Index: src/engine/midi_event.cpp -=================================================================== ---- src/engine/midi_event.cpp (revision 2146) -+++ src/engine/midi_event.cpp (working copy) -@@ -125,6 +125,7 @@ - - void Event::Dump(void) const - { -+#ifndef ANDROID - std::cerr << std::hex << std::setfill('0') \ - << "[dl:0x" << std::setw(4) << delta \ - << ":st:0x" << std::setw(2) << static_cast(static_cast(status)) << ":dt"; -@@ -143,4 +144,5 @@ - } - - std::cerr << "]" << std::endl; -+#endif - } -Index: src/engine/rand.cpp -=================================================================== ---- src/engine/rand.cpp (revision 2146) -+++ src/engine/rand.cpp (working copy) -@@ -80,6 +80,8 @@ - if(rand <= amount) return (*it).first; - } - -+#ifndef ANDROID - std::cerr << "Rand::Queue::Get:" << " weight not found, return 0" << std::endl; -+#endif - return 0; - } -Index: src/engine/surface.cpp -=================================================================== ---- src/engine/surface.cpp (revision 2146) -+++ src/engine/surface.cpp (working copy) -@@ -135,7 +135,9 @@ - if(bs.surface) - { - surface = SDL_ConvertSurface(bs.surface, bs.surface->format, bs.surface->flags); -+#ifndef ANDROID - if(!surface) std::cerr << "Surface: copy constructor, error: " << SDL_GetError() << std::endl; -+#endif - } - } - -@@ -303,7 +305,9 @@ - - if(!surface) - { -+#ifndef ANDROID - std::cerr << "w: " << sw << ", h: " << sh << std::endl; -+#endif - Error::Except("Surface::CreateSurface: empty surface, error:", SDL_GetError()); - } - } -@@ -797,13 +801,17 @@ - // valid sf_src - if(!sf_src.surface || sf_src.w() != sf_src.h()) - { -+#ifndef ANDROID - std::cerr << "Surface::TILReflect: " << "incorrect size" << std::endl; -+#endif - return; - } - - if(sf_src.depth() != 8) - { -+#ifndef ANDROID - std::cerr << "Surface::TILReflect: " << "incorrect depth, use only 8 bpp" << std::endl; -+#endif - return; - } - -@@ -905,14 +913,26 @@ - /* scale surface */ - void Surface::ScaleMinifyByTwo(Surface & sf_dst, const Surface & sf_src, bool event) - { -- if(!sf_src.isValid()) { std::cerr << "Surface::ScaleMinifyByTwo: " << "invalid surface" << std::endl; return; }; -+ if(!sf_src.isValid()) -+ { -+#ifndef ANDROID -+ std::cerr << "Surface::ScaleMinifyByTwo: " << "invalid surface" << std::endl; -+#endif -+ return; -+ }; - u16 x, y, x2, y2; - - u8 mul = 2; - u16 w = sf_src.w() / mul; - u16 h = sf_src.h() / mul; - -- if(2 > w || 2 > h){ std::cerr << "Surface::ScaleMinifyByTwo: " << "small size" << std::endl; return; }; -+ if(2 > w || 2 > h) -+ { -+#ifndef ANDROID -+ std::cerr << "Surface::ScaleMinifyByTwo: " << "small size" << std::endl; -+#endif -+ return; -+ }; - - sf_dst.Set(w, h, sf_src.depth(), SWSURFACE); - sf_dst.SetColorKey(); -Index: src/engine/midi_xmi.cpp -=================================================================== ---- src/engine/midi_xmi.cpp (revision 2146) -+++ src/engine/midi_xmi.cpp (working copy) -@@ -42,7 +42,9 @@ - { - if(0 == body.size()) - { -+#ifndef ANDROID - std::cerr << "Xmi: " << "incorrect size" << std::endl; -+#endif - return false; - } - -@@ -50,7 +52,9 @@ - - if(memcmp(ID_FORM, ptr, 4)) - { -+#ifndef ANDROID - std::cerr << "Xmi: " << "incorrect id: " << ID_FORM << std::endl; -+#endif - return false; - } - -@@ -59,7 +63,9 @@ - - if(memcmp(ID_CAT, ptr, 4)) - { -+#ifndef ANDROID - std::cerr << "Xmi: " << "incorrect id: " << ID_CAT<< std::endl; -+#endif - return false; - } - -@@ -67,7 +73,9 @@ - - if(memcmp(ID_XMID, ptr, 4)) - { -+#ifndef ANDROID - std::cerr << "Xmi: " << "incorrect cat id: " << ID_XMID << std::endl; -+#endif - return false; - } - -@@ -75,7 +83,9 @@ - - if(memcmp(ID_FORM, ptr, 4)) - { -+#ifndef ANDROID - std::cerr << "Xmi: " << "incorrect xmid id: " << ID_FORM << std::endl; -+#endif - return false; - } - else -@@ -83,7 +93,9 @@ - - if(memcmp(ID_XMID, ptr, 4)) - { -+#ifndef ANDROID - std::cerr << "Xmi: " << "incorrect form id: " << ID_XMID << std::endl; -+#endif - return false; - } - -@@ -91,7 +103,9 @@ - - if(memcmp(ID_TIMB, ptr, 4)) - { -+#ifndef ANDROID - std::cerr << "Xmi: " << "incorrect id: " << ID_TIMB << std::endl; -+#endif - return false; - } - -@@ -100,7 +114,9 @@ - - if(memcmp(ID_EVNT, ptr, 4)) - { -+#ifndef ANDROID - std::cerr << "Xmi: " << "incorrect id: " << ID_EVNT << std::endl; -+#endif - return false; - } - -@@ -115,7 +131,9 @@ - - if(!fd.is_open()) - { -+#ifndef ANDROID - std::cerr << "Xmi: " << "error read: " << filename.c_str() << std::endl; -+#endif - return false; - } - -Index: src/engine/audio_music.cpp -=================================================================== ---- src/engine/audio_music.cpp (revision 2146) -+++ src/engine/audio_music.cpp (working copy) -@@ -41,12 +41,20 @@ - if(fadein) - { - if(music && Mix_FadeInMusic(music, loop ? -1 : 0, fadein) == -1) -+ { -+#ifndef ANDROID - std::cerr << "Music::Play: " << Mix_GetError() << std::endl; -+#endif -+ } - } - else - { - if(music && Mix_PlayMusic(music, loop ? -1 : 0) == -1) -+ { -+#ifndef ANDROID - std::cerr << "Music::Play: " << Mix_GetError() << std::endl; -+#endif -+ } - } - } - -@@ -73,7 +81,11 @@ - music = Mix_LoadMUS(file); - - if(! music) -+ { -+#ifndef ANDROID - std::cerr << "Music::Play: " << Mix_GetError() << std::endl; -+#endif -+ } - else - Music::Play(loop); - } -Index: src/engine/audio_mixer.cpp -=================================================================== ---- src/engine/audio_mixer.cpp (revision 2146) -+++ src/engine/audio_mixer.cpp (working copy) -@@ -61,7 +61,9 @@ - - if(0 != Mix_OpenAudio(hardware.freq, hardware.format, hardware.channels, hardware.samples)) - { -+#ifndef ANDROID - std::cerr << "Mixer: " << SDL_GetError() << std::endl; -+#endif - valid = false; - } - else -@@ -75,7 +77,9 @@ - } - else - { -+#ifndef ANDROID - std::cerr << "Mixer: audio subsystem not initialize" << std::endl; -+#endif - valid = false; - } - } -@@ -104,21 +108,27 @@ - Mixer::chunk_t* Mixer::LoadWAV(const char* file) - { - Mix_Chunk *sample = Mix_LoadWAV(file); -+#ifndef ANDROID - if(!sample) std::cerr << "Mixer::LoadWAV: " << Mix_GetError() << std::endl; -+#endif - return sample; - } - - Mixer::chunk_t* Mixer::LoadWAV(const u8* ptr, u32 size) - { - Mix_Chunk *sample = Mix_LoadWAV_RW(SDL_RWFromConstMem(ptr, size), 1); -+#ifndef ANDROID - if(!sample) std::cerr << "Mixer::LoadWAV: "<< Mix_GetError() << std::endl; -+#endif - return sample; - } - - int Mixer::Play(chunk_t* sample, int channel, bool loop) - { - int res = Mix_PlayChannel(channel, sample, loop ? -1 : 0); -+#ifndef ANDROID - if(res == -1) std::cerr << "Mixer::Play: " << Mix_GetError() << std::endl;; -+#endif - return res; - } - -@@ -302,7 +312,9 @@ - - if(0 > SDL_OpenAudio(&spec, &Audio::GetHardwareSpec())) - { -+#ifndef ANDROID - std::cerr << "Mixer::Init: " << SDL_GetError() << std::endl; -+#endif - valid = false; - } - else -@@ -314,7 +326,9 @@ - } - else - { -+#ifndef ANDROID - std::cerr << "Mixer::Init: audio subsystem not initialize" << std::endl; -+#endif - valid = false; - } - } -@@ -388,7 +402,9 @@ - it = std::find_if(chunks.begin() + reserved_channels, chunks.end(), PredicateIsFreeSound); - if(it == chunks.end()) - { -+#ifndef ANDROID - std::cerr << "Mixer::PlayRAW: mixer is full" << std::endl; -+#endif - return -1; - } - } -Index: src/engine/display.cpp -=================================================================== ---- src/engine/display.cpp (revision 2146) -+++ src/engine/display.cpp (working copy) -@@ -346,7 +346,9 @@ - - if(modes == (SDL_Rect **) 0) - { -+#ifndef ANDROID - std::cerr << "Display::GetMaxMode: " << "no modes available" << std::endl; -+#endif - return 0; - } - else -Index: src/engine/engine.cpp -=================================================================== ---- src/engine/engine.cpp (revision 2146) -+++ src/engine/engine.cpp (working copy) -@@ -59,7 +59,9 @@ - - if(0 > SDL_Init(system)) - { -+#ifndef ANDROID - std::cerr << "SDL::Init: error: " << SDL_GetError() << std::endl; -+#endif - return false; - } - -Index: src/engine/font.cpp -=================================================================== ---- src/engine/font.cpp (revision 2146) -+++ src/engine/font.cpp (working copy) -@@ -41,7 +41,12 @@ - - void SDL::Font::Init(void) - { -- if(0 != TTF_Init()) std::cerr << "Font::Init: error" << std::endl; -+ if(0 != TTF_Init()) -+ { -+#ifndef ANDROID -+ std::cerr << "Font::Init: error" << std::endl; -+#endif -+ } - else init = true; - } - -@@ -64,7 +69,9 @@ - - fnt = TTF_OpenFont(filename.c_str(), size); - -+#ifndef ANDROID - if(!fnt) std::cerr << "Font::Open: error open: " << filename << std::endl; -+#endif - } - return fnt; - } -Index: src/engine/midi.cpp -=================================================================== ---- src/engine/midi.cpp (revision 2146) -+++ src/engine/midi.cpp (working copy) -@@ -32,7 +32,9 @@ - { - if(4 <= p2 - p) - { -+#ifndef ANDROID - std::cerr << "Event: unpack delta mistake" << std::endl; -+#endif - break; - } - -Index: src/engine/zzlib.cpp -=================================================================== ---- src/engine/zzlib.cpp (revision 2146) -+++ src/engine/zzlib.cpp (working copy) -@@ -40,9 +40,21 @@ - switch(res) - { - case Z_OK: return true; -- case Z_MEM_ERROR: if(debug) std::cerr << "ZLib::UnCompress: " << "Z_MEM_ERROR" << std::endl; return false; -- case Z_BUF_ERROR: if(debug) std::cerr << "ZLib::UnCompress: " << "Z_BUF_ERROR" << std::endl; return false; -- case Z_DATA_ERROR:if(debug) std::cerr << "ZLib::UnCompress: " << "Z_DATA_ERROR"<< std::endl; return false; -+ case Z_MEM_ERROR: -+#ifndef ANDROID -+ if(debug) std::cerr << "ZLib::UnCompress: " << "Z_MEM_ERROR" << std::endl; -+#endif -+ return false; -+ case Z_BUF_ERROR: -+#ifndef ANDROID -+ if(debug) std::cerr << "ZLib::UnCompress: " << "Z_BUF_ERROR" << std::endl; -+#endif -+ return false; -+ case Z_DATA_ERROR: -+#ifndef ANDROID -+ if(debug) std::cerr << "ZLib::UnCompress: " << "Z_DATA_ERROR"<< std::endl; -+#endif -+ return false; - default: break; - } - -Index: src/fheroes2/system/settings.cpp -=================================================================== ---- src/fheroes2/system/settings.cpp (revision 2146) -+++ src/fheroes2/system/settings.cpp (working copy) -@@ -499,6 +499,13 @@ - return true; - } - -+void Settings::Dump() const -+{ -+ std::ostringstream dumped; -+ Dump(dumped); -+ VERBOSE(dumped); -+}; -+ - void Settings::Dump(std::ostream & stream) const - { - std::string str; -Index: src/fheroes2/system/settings.h -=================================================================== ---- src/fheroes2/system/settings.h (revision 2146) -+++ src/fheroes2/system/settings.h (working copy) -@@ -78,7 +78,7 @@ - #define VERBOSE(x) - #define VERBOSN(x) - #define DEBUG(x, y, z) --#elif defined(ANDROID) -+#elif defined(ANDROID) || defined(__ANDROID__) - #define VERBOSE(x) { std::ostringstream osss; osss << x; __android_log_print(ANDROID_LOG_INFO, "FHeroes", "%s", osss.str().c_str()); } - #define VERBOSN(x) { std::ostringstream osss; osss << x; __android_log_print(ANDROID_LOG_INFO, "FHeroes", "%s", osss.str().c_str()); } - #define DEBUG(x, y, z) if(IS_DEBUG((x), (y))) VERBOSE(z) -@@ -156,7 +156,8 @@ - bool Read(const std::string & filename); - bool Save(const std::string & filename) const; - -- void Dump(std::ostream & stream = std::cout) const; -+ void Dump(std::ostream & stream) const; -+ void Dump() const; - bool LoadFileMapsMP2(const std::string & file); - Maps::FileInfo & CurrentFileInfo(void); - -Index: src/fheroes2/maps/pairs.h -=================================================================== ---- src/fheroes2/maps/pairs.h (revision 2146) -+++ src/fheroes2/maps/pairs.h (working copy) -@@ -23,8 +23,8 @@ - #ifndef H2PAIRS_H - #define H2PAIRS_H - -+#include // Broken STLPort implementaion on Android requires this file to be included first - #include --#include - #include "maps_tiles.h" - - class IndexDistance : public std::pair -Index: src/fheroes2/army/army.cpp -=================================================================== ---- src/fheroes2/army/army.cpp (revision 2146) -+++ src/fheroes2/army/army.cpp (working copy) -@@ -967,7 +967,9 @@ - void Army::army_t::Dump(const char* prefix) const - { - if(prefix) -+ { - VERBOSN(prefix); -+ } - else - { - VERBOSN("Army::Dump: " << diff --git a/project/jni/application/src b/project/jni/application/src index c2cbfe2dc..59d41f41e 120000 --- a/project/jni/application/src +++ b/project/jni/application/src @@ -1 +1 @@ -ltris-1.0.15 \ No newline at end of file +fheroes2 \ No newline at end of file diff --git a/project/jni/sdl-1.3/src/video/android/SDL_androidinput.c b/project/jni/sdl-1.3/src/video/android/SDL_androidinput.c index dd009147f..d046f125c 100644 --- a/project/jni/sdl-1.3/src/video/android/SDL_androidinput.c +++ b/project/jni/sdl-1.3/src/video/android/SDL_androidinput.c @@ -94,6 +94,10 @@ int multitouchGestureDist = -1; int multitouchGestureAngle = 0; int multitouchGestureX = -1; int multitouchGestureY = -1; +int SDL_ANDROID_TouchscreenCalibrationWidth = 480; +int SDL_ANDROID_TouchscreenCalibrationHeight = 320; +int SDL_ANDROID_TouchscreenCalibrationX = 0; +int SDL_ANDROID_TouchscreenCalibrationY = 0; static inline int InsideRect(const SDL_Rect * r, int x, int y) { @@ -203,11 +207,13 @@ JAVA_EXPORT_NAME(DemoGLSurfaceView_nativeMouse) ( JNIEnv* env, jobject thiz, j } } + x -= SDL_ANDROID_TouchscreenCalibrationX; + y -= SDL_ANDROID_TouchscreenCalibrationY; #if SDL_VIDEO_RENDER_RESIZE // Translate mouse coordinates - x = x * SDL_ANDROID_sFakeWindowWidth / SDL_ANDROID_sWindowWidth; - y = y * SDL_ANDROID_sFakeWindowHeight / SDL_ANDROID_sWindowHeight; + x = x * SDL_ANDROID_sFakeWindowWidth / SDL_ANDROID_TouchscreenCalibrationWidth; + y = y * SDL_ANDROID_sFakeWindowHeight / SDL_ANDROID_TouchscreenCalibrationHeight; if( x < 0 ) x = 0; if( x > SDL_ANDROID_sFakeWindowWidth ) @@ -216,7 +222,9 @@ JAVA_EXPORT_NAME(DemoGLSurfaceView_nativeMouse) ( JNIEnv* env, jobject thiz, j y = 0; if( y > SDL_ANDROID_sFakeWindowHeight ) y = SDL_ANDROID_sFakeWindowHeight; - +#else + x = x * SDL_ANDROID_sRealWindowWidth / SDL_ANDROID_TouchscreenCalibrationWidth; + y = y * SDL_ANDROID_sRealWindowHeight / SDL_ANDROID_TouchscreenCalibrationHeight; #endif if( action == MOUSE_UP ) @@ -305,8 +313,6 @@ JAVA_EXPORT_NAME(DemoGLSurfaceView_nativeMouse) ( JNIEnv* env, jobject thiz, j multitouchGestureKeyPressed[3] = 0; SDL_ANDROID_MainThreadPushKeyboardKey( SDL_RELEASED, multitouchGestureKeycode[3] ); } - __android_log_print(ANDROID_LOG_INFO, "libSDL", "x %d y %d multitouchGestureX %d multitouchGestureY %d dist %d multitouchGestureDist %d angle %08X multitouchGestureAngle %08X angleDiff %09d", - x, y, multitouchGestureX, multitouchGestureY, dist, multitouchGestureDist, angle, multitouchGestureAngle, angleDiff ); } } } @@ -1485,6 +1491,14 @@ JAVA_EXPORT_NAME(Settings_nativeSetMultitouchGestureSensitivity) ( JNIEnv* env, multitouchGestureSensitivity = sensitivity; } +JNIEXPORT void JNICALL +JAVA_EXPORT_NAME(Settings_nativeSetTouchscreenCalibration) (JNIEnv* env, jobject thiz, jint x1, jint y1, jint x2, jint y2) +{ + SDL_ANDROID_TouchscreenCalibrationX = x1; + SDL_ANDROID_TouchscreenCalibrationY = y1; + SDL_ANDROID_TouchscreenCalibrationWidth = x2 - x1; + SDL_ANDROID_TouchscreenCalibrationHeight = y2 - y1; +} JNIEXPORT void JNICALL JAVA_EXPORT_NAME(Settings_nativeInitKeymap) ( JNIEnv* env, jobject thiz ) diff --git a/project/jni/sdl-1.3/src/video/android/SDL_androidvideo.c b/project/jni/sdl-1.3/src/video/android/SDL_androidvideo.c index 80fd450de..2f4ac8aec 100644 --- a/project/jni/sdl-1.3/src/video/android/SDL_androidvideo.c +++ b/project/jni/sdl-1.3/src/video/android/SDL_androidvideo.c @@ -163,6 +163,8 @@ JAVA_EXPORT_NAME(DemoRenderer_nativeResize) ( JNIEnv* env, jobject thiz, jint SDL_ANDROID_sWindowHeight = h; } __android_log_print(ANDROID_LOG_INFO, "libSDL", "Physical screen resolution is %dx%d, virtual screen %dx%d", w, h, SDL_ANDROID_sWindowWidth, SDL_ANDROID_sWindowHeight ); + SDL_ANDROID_TouchscreenCalibrationWidth = SDL_ANDROID_sWindowWidth; + SDL_ANDROID_TouchscreenCalibrationHeight = SDL_ANDROID_sWindowHeight; } } diff --git a/project/jni/sdl-1.3/src/video/android/SDL_androidvideo.h b/project/jni/sdl-1.3/src/video/android/SDL_androidvideo.h index da5e7f049..9cc9ec37d 100644 --- a/project/jni/sdl-1.3/src/video/android/SDL_androidvideo.h +++ b/project/jni/sdl-1.3/src/video/android/SDL_androidvideo.h @@ -34,6 +34,10 @@ extern int SDL_ANDROID_sRealWindowWidth; extern int SDL_ANDROID_sRealWindowHeight; extern int SDL_ANDROID_sFakeWindowWidth; // SDL 1.2 only extern int SDL_ANDROID_sFakeWindowHeight; // SDL 1.2 only +extern int SDL_ANDROID_TouchscreenCalibrationWidth; +extern int SDL_ANDROID_TouchscreenCalibrationHeight; +extern int SDL_ANDROID_TouchscreenCalibrationX; +extern int SDL_ANDROID_TouchscreenCalibrationY; extern SDL_Surface *SDL_CurrentVideoSurface; extern SDL_Rect SDL_ANDROID_ForceClearScreenRect; extern int SDL_ANDROID_ShowScreenUnderFinger; diff --git a/project/res/drawable/calibrate.png b/project/res/drawable/calibrate.png new file mode 100644 index 0000000000000000000000000000000000000000..ddec032746488c636775eab25280e519ddb5fc46 GIT binary patch literal 8086 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 Hl+XkK