1 package org.hedgewars.hedgeroid; |
1 package org.hedgewars.hedgeroid; |
2 |
2 /* |
|
3 * Copyright (c) 2004-2012 Andrey Korotaev <unC0Rr@gmail.com> |
|
4 */ |
|
5 import java.io.IOException; |
3 import java.io.UnsupportedEncodingException; |
6 import java.io.UnsupportedEncodingException; |
4 import java.util.concurrent.atomic.AtomicBoolean; |
7 import java.net.ConnectException; |
5 |
8 |
6 import javax.microedition.khronos.egl.EGL10; |
9 import javax.microedition.khronos.egl.EGL10; |
7 import javax.microedition.khronos.egl.EGLConfig; |
10 import javax.microedition.khronos.egl.EGLConfig; |
8 import javax.microedition.khronos.egl.EGLContext; |
11 import javax.microedition.khronos.egl.EGLContext; |
9 import javax.microedition.khronos.egl.EGLDisplay; |
12 import javax.microedition.khronos.egl.EGLDisplay; |
10 import javax.microedition.khronos.egl.EGLSurface; |
13 import javax.microedition.khronos.egl.EGLSurface; |
11 |
14 |
12 import org.hedgewars.hedgeroid.Datastructures.GameConfig; |
15 import org.hedgewars.hedgeroid.Datastructures.GameConfig; |
13 import org.hedgewars.hedgeroid.EngineProtocol.PascalExports; |
16 import org.hedgewars.hedgeroid.EngineProtocol.PascalExports; |
14 import org.hedgewars.hedgeroid.frontlib.Flib; |
|
15 import org.hedgewars.hedgeroid.frontlib.Frontlib.GameSetupPtr; |
|
16 import org.hedgewars.hedgeroid.frontlib.Frontlib.GameconnPtr; |
|
17 import org.hedgewars.hedgeroid.frontlib.Frontlib.IntCallback; |
|
18 import org.hedgewars.hedgeroid.netplay.Netplay; |
17 import org.hedgewars.hedgeroid.netplay.Netplay; |
19 import org.hedgewars.hedgeroid.util.FileUtils; |
18 import org.hedgewars.hedgeroid.util.FileUtils; |
20 import org.hedgewars.hedgeroid.util.TickHandler; |
|
21 |
|
22 import com.sun.jna.Pointer; |
|
23 |
19 |
24 import android.app.Activity; |
20 import android.app.Activity; |
25 import android.content.Context; |
21 import android.content.Context; |
26 import android.graphics.Canvas; |
22 import android.graphics.Canvas; |
27 import android.graphics.PixelFormat; |
23 import android.graphics.PixelFormat; |
31 import android.hardware.SensorManager; |
27 import android.hardware.SensorManager; |
32 import android.media.AudioFormat; |
28 import android.media.AudioFormat; |
33 import android.media.AudioManager; |
29 import android.media.AudioManager; |
34 import android.media.AudioTrack; |
30 import android.media.AudioTrack; |
35 import android.os.Bundle; |
31 import android.os.Bundle; |
36 import android.os.Handler; |
|
37 import android.os.HandlerThread; |
|
38 import android.os.Message; |
|
39 import android.util.Base64; |
32 import android.util.Base64; |
40 import android.util.DisplayMetrics; |
33 import android.util.DisplayMetrics; |
41 import android.util.Log; |
34 import android.util.Log; |
42 import android.view.KeyEvent; |
35 import android.view.KeyEvent; |
43 import android.view.MotionEvent; |
36 import android.view.MotionEvent; |
77 private static int mGLMajor, mGLMinor; |
68 private static int mGLMajor, mGLMinor; |
78 |
69 |
79 // Load the .so |
70 // Load the .so |
80 static { |
71 static { |
81 System.loadLibrary("SDL"); |
72 System.loadLibrary("SDL"); |
82 //System.loadLibrary("SDL_image"); |
|
83 //System.loadLibrary("SDL_mixer"); |
|
84 //System.loadLibrary("SDL_ttf"); |
|
85 System.loadLibrary("main"); |
73 System.loadLibrary("main"); |
86 } |
74 } |
87 |
75 |
88 // Setup |
76 // Setup |
89 protected void onCreate(Bundle savedInstanceState) { |
77 protected void onCreate(Bundle savedInstanceState) { |
90 //Log.v("SDL", "onCreate()"); |
|
91 super.onCreate(savedInstanceState); |
78 super.onCreate(savedInstanceState); |
92 |
79 |
93 // So we can call stuff from static callbacks |
80 // So we can call stuff from static callbacks |
94 mSingleton = this; |
81 mSingleton = this; |
95 |
82 |
96 // Set up the surface |
83 // Set up the surface |
97 mSurface = new SDLSurface(getApplication(), startConfig, startNetgame); |
84 mSurface = new SDLSurface(getApplication(), startConfig, startNetgame); |
|
85 startConfig = null; |
98 setContentView(mSurface); |
86 setContentView(mSurface); |
99 } |
87 } |
100 |
88 |
101 // Events |
89 // Events |
102 protected void onPause() { |
90 protected void onPause() { |
121 protected void onDestroy() { |
109 protected void onDestroy() { |
122 super.onDestroy(); |
110 super.onDestroy(); |
123 Log.v("SDL", "onDestroy()"); |
111 Log.v("SDL", "onDestroy()"); |
124 // Send a quit message to the application |
112 // Send a quit message to the application |
125 SDLActivity.nativeQuit(); |
113 SDLActivity.nativeQuit(); |
126 |
|
127 // Now wait for the SDL thread to quit |
114 // Now wait for the SDL thread to quit |
128 synchronized(SDLActivity.class) { |
115 if (mSDLThread != null) { |
129 if (mSDLThread != null) { |
116 try { |
130 try { |
117 mSDLThread.join(); |
131 mSDLThread.join(); |
118 } catch(Exception e) { |
132 } catch(Exception e) { |
119 Log.w("SDL", "Problem stopping thread: " + e); |
133 Log.w("SDL", "Problem stopping thread: " + e); |
120 } |
134 } |
121 mSDLThread = null; |
135 mSDLThread = null; |
122 } |
136 } |
123 mSingleton = null; |
137 } |
124 } |
138 } |
125 |
139 |
|
140 // Messages from the SDLMain thread |
|
141 static int COMMAND_CHANGE_TITLE = 1; |
|
142 |
|
143 // Handler for the messages |
|
144 Handler commandHandler = new Handler() { |
|
145 public void handleMessage(Message msg) { |
|
146 if (msg.arg1 == COMMAND_CHANGE_TITLE) { |
|
147 setTitle((String)msg.obj); |
|
148 } |
|
149 } |
|
150 }; |
|
151 |
|
152 // Send a message from the SDLMain thread |
|
153 void sendCommand(int command, Object data) { |
|
154 Message msg = commandHandler.obtainMessage(); |
|
155 msg.arg1 = command; |
|
156 msg.obj = data; |
|
157 commandHandler.sendMessage(msg); |
|
158 } |
|
159 |
|
160 public static void synchronizedNativeInit(String...args) { |
126 public static void synchronizedNativeInit(String...args) { |
161 synchronized(PascalExports.engineMutex) { |
127 synchronized(PascalExports.engineMutex) { |
162 nativeInit(args); |
128 nativeInit(args); |
163 } |
129 } |
164 } |
130 } |
186 |
152 |
187 public static void flipBuffers() { |
153 public static void flipBuffers() { |
188 flipEGL(); |
154 flipEGL(); |
189 } |
155 } |
190 |
156 |
191 public static void setActivityTitle(String title) { |
157 public static void setActivityTitle(final String title) { |
192 // Called from SDLMain() thread and can't directly affect the view |
158 // Called from SDLMain() thread and can't directly affect the view |
193 mSingleton.sendCommand(COMMAND_CHANGE_TITLE, title); |
159 mSingleton.runOnUiThread(new Runnable() { |
|
160 public void run() { |
|
161 mSingleton.setTitle(title); |
|
162 } |
|
163 }); |
194 } |
164 } |
195 |
165 |
196 public static Context getContext() { |
166 public static Context getContext() { |
197 return mSingleton; |
167 return mSingleton; |
198 } |
168 } |
199 |
169 |
200 public static void startApp(final int width, final int height, GameConfig config, boolean netgame) { |
170 public static void startApp(final int width, final int height, GameConfig config, boolean netgame) { |
201 synchronized(SDLActivity.class) { |
171 // Start up the C app thread |
202 // Start up the C app thread TODO this is silly code |
172 if (mSDLThread == null) { |
203 if (mSDLThread == null) { |
173 mSDLThread = new Thread(new SDLMain(width, height, config, netgame)); |
204 final AtomicBoolean gameconnStartDone = new AtomicBoolean(false); |
174 mSDLThread.start(); |
205 GameConnection.Listener listener = new GameConnection.Listener() { |
175 } else { |
206 public void gameConnectionReady(int port) { |
176 SDLActivity.nativeResume(); |
207 mSDLThread = new Thread(new SDLMain(width, height, port, "Medo")); |
|
208 mSDLThread.start(); |
|
209 gameconnStartDone.set(true); |
|
210 } |
|
211 |
|
212 public void gameConnectionDisconnected(int reason) { |
|
213 Log.e("startApp", "disconnected: "+reason); |
|
214 gameconnStartDone.set(true); |
|
215 } |
|
216 }; |
|
217 if(netgame) { |
|
218 Netplay netplay = Netplay.getAppInstance(mSingleton.getApplicationContext()); |
|
219 GameConnection.forNetgame(config, netplay, listener); |
|
220 } else { |
|
221 GameConnection.forLocalGame(config, listener); |
|
222 } |
|
223 } else { |
|
224 SDLActivity.nativeResume(); |
|
225 } |
|
226 } |
177 } |
227 } |
178 } |
228 |
179 |
229 // EGL functions |
180 // EGL functions |
230 public static boolean initEGL(int majorVersion, int minorVersion) { |
181 public static boolean initEGL(int majorVersion, int minorVersion) { |
231 if (SDLActivity.mEGLDisplay == null) { |
182 if (SDLActivity.mEGLDisplay == null) { |
232 //Log.v("SDL", "Starting up OpenGL ES " + majorVersion + "." + minorVersion); |
|
233 |
|
234 try { |
183 try { |
235 EGL10 egl = (EGL10)EGLContext.getEGL(); |
184 EGL10 egl = (EGL10)EGLContext.getEGL(); |
236 |
185 |
237 EGLDisplay dpy = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY); |
186 EGLDisplay dpy = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY); |
238 |
187 |
258 Log.e("SDL", "No EGL config available"); |
206 Log.e("SDL", "No EGL config available"); |
259 return false; |
207 return false; |
260 } |
208 } |
261 EGLConfig config = configs[0]; |
209 EGLConfig config = configs[0]; |
262 |
210 |
263 /*int EGL_CONTEXT_CLIENT_VERSION=0x3098; |
|
264 int contextAttrs[] = new int[] { EGL_CONTEXT_CLIENT_VERSION, majorVersion, EGL10.EGL_NONE }; |
|
265 EGLContext ctx = egl.eglCreateContext(dpy, config, EGL10.EGL_NO_CONTEXT, contextAttrs); |
|
266 |
|
267 if (ctx == EGL10.EGL_NO_CONTEXT) { |
|
268 Log.e("SDL", "Couldn't create context"); |
|
269 return false; |
|
270 } |
|
271 SDLActivity.mEGLContext = ctx;*/ |
|
272 SDLActivity.mEGLDisplay = dpy; |
211 SDLActivity.mEGLDisplay = dpy; |
273 SDLActivity.mEGLConfig = config; |
212 SDLActivity.mEGLConfig = config; |
274 SDLActivity.mGLMajor = majorVersion; |
213 SDLActivity.mGLMajor = majorVersion; |
275 SDLActivity.mGLMinor = minorVersion; |
214 SDLActivity.mGLMinor = minorVersion; |
276 |
215 |
452 |
391 |
453 /** |
392 /** |
454 Simple nativeInit() runnable |
393 Simple nativeInit() runnable |
455 */ |
394 */ |
456 class SDLMain implements Runnable { |
395 class SDLMain implements Runnable { |
457 |
396 public static final String TAG = "SDLMain"; |
|
397 |
|
398 public static final int RQ_LOWRES = 0x00000001; // use half land array |
|
399 public static final int RQ_BLURRY_LAND = 0x00000002; // downscaled terrain |
|
400 public static final int RQ_NO_BACKGROUND = 0x00000004; // don't draw background |
|
401 public static final int RQ_SIMPLE_ROPE = 0x00000008; // avoid drawing rope |
|
402 public static final int RQ_2D_WATER = 0x00000010; // disabe 3D water effect |
|
403 public static final int RQ_SIMPLE_EXPLOSIONS = 0x00000020; // no fancy explosion effects |
|
404 public static final int RQ_NO_FLAKES = 0x00000040; // no flakes |
|
405 public static final int RQ_NO_MENU_ANIM = 0x00000080; // ammomenu appears with no animation |
|
406 public static final int RQ_NO_DROPLETS = 0x00000100; // no droplets |
|
407 public static final int RQ_NO_CLAMPING = 0x00000200; // don't clamp textures |
|
408 public static final int RQ_NO_TOOLTIPS = 0x00000400; // tooltips are not drawn |
|
409 public static final int RQ_NO_VSYNC = 0x00000800; // don't sync on vblank |
|
410 |
458 private final int surfaceWidth, surfaceHeight; |
411 private final int surfaceWidth, surfaceHeight; |
459 private final int port; |
|
460 private final String playerName; |
412 private final String playerName; |
461 HandlerThread thread = new HandlerThread("IPC thread"); |
413 private final GameConfig config; |
462 |
414 private final boolean netgame; |
463 public SDLMain(int width, int height, int port, String playerName) { |
415 |
|
416 public SDLMain(int width, int height, GameConfig config, boolean netgame) { |
464 surfaceWidth = width; |
417 surfaceWidth = width; |
465 surfaceHeight = height; |
418 surfaceHeight = height; |
466 this.port = port; |
419 if(netgame) { |
467 this.playerName = playerName; |
420 playerName = Netplay.getAppInstance(SDLActivity.getContext().getApplicationContext()).getPlayerName(); |
|
421 } else { |
|
422 playerName = "Player"; |
|
423 } |
|
424 this.config = config; |
|
425 this.netgame = netgame; |
468 } |
426 } |
469 |
427 |
470 public void run() { |
428 public void run() { |
471 //Set up the IPC socket server to communicate with the engine |
429 //Set up the IPC socket server to communicate with the engine |
472 String path = FileUtils.getDataPath(SDLActivity.mSingleton);//This represents the data directory |
430 GameConnection gameConn; |
473 path = path.substring(0, path.length()-1);//remove the trailing '/' |
431 String path; |
474 |
|
475 Log.d("SDLMain", "Starting engine"); |
|
476 // Runs SDL_main() with added parameters |
|
477 try { |
432 try { |
478 SDLActivity.synchronizedNativeInit(new String[] { String.valueOf(port), |
433 if(netgame) { |
479 String.valueOf(surfaceWidth), String.valueOf(surfaceHeight), |
434 Netplay netplay = Netplay.getAppInstance(SDLActivity.mSingleton.getApplicationContext()); |
480 Integer.toString(0x40+0x10+0x100+0x2), "en.txt", Base64.encodeToString(playerName.getBytes("UTF-8"), 0), "1", "1", "1", path, "" }); |
435 gameConn = GameConnection.forNetgame(config, netplay); |
481 } catch (UnsupportedEncodingException e) { |
436 } else { |
482 throw new AssertionError(e); // never happens |
437 gameConn = GameConnection.forLocalGame(config); |
483 } |
438 } |
484 Log.d("SDLMain", "Engine stopped"); |
439 |
|
440 path = FileUtils.getDataPathFile(SDLActivity.mSingleton).getAbsolutePath(); |
|
441 Log.d(TAG, "Starting engine"); |
|
442 // Runs SDL_main() with added parameters |
|
443 try { |
|
444 String pPort = String.valueOf(gameConn.port); |
|
445 String pWidth = String.valueOf(surfaceWidth); |
|
446 String pHeight = String.valueOf(surfaceHeight); |
|
447 String pQuality = Integer.toString(RQ_NO_FLAKES|RQ_NO_DROPLETS|RQ_SIMPLE_EXPLOSIONS); |
|
448 String pPlayerName = Base64.encodeToString(playerName.getBytes("UTF-8"), 0); |
|
449 SDLActivity.synchronizedNativeInit(new String[] { pPort, pWidth, pHeight, pQuality, "en.txt", pPlayerName, "1", "1", "1", path, "" }); |
|
450 } catch (UnsupportedEncodingException e) { |
|
451 throw new AssertionError(e); // never happens |
|
452 } |
|
453 Log.d(TAG, "Engine stopped"); |
|
454 } catch(ConnectException e) { |
|
455 Log.e(TAG, "Error starting IPC connection"); |
|
456 } catch (IOException e) { |
|
457 Log.e(TAG, "Missing SDCard"); |
|
458 } |
|
459 SDLActivity.mSingleton.runOnUiThread(new Runnable() { public void run() { |
|
460 if(SDLActivity.mSingleton != null) { |
|
461 SDLActivity.mSingleton.finish(); |
|
462 } |
|
463 }}); |
485 } |
464 } |
486 } |
465 } |
487 |
466 |
488 |
467 |
489 /** |
468 /** |