summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Martitz <kugel@rockbox.org>2011-02-19 01:13:38 +0000
committerThomas Martitz <kugel@rockbox.org>2011-02-19 01:13:38 +0000
commit144df92bfb3bde9d843363ac5ab537c4d5ee24e3 (patch)
tree237abe424decc33b7c1e9c8caa89b5a18cf0dd88
parent6cbb0e4229225adc1a8222acd21f6ce01b88e472 (diff)
downloadrockbox-144df92bfb3bde9d843363ac5ab537c4d5ee24e3.tar.gz
rockbox-144df92bfb3bde9d843363ac5ab537c4d5ee24e3.zip
Android: Rewrite lcd subsystem to use the SurfaceView API.
That enables drawing from outside the apps UI thread, i.e. from within the Rockbox native thread, without needing synchronization means, and adds determinism as to when the draw happens. It simplifies the drawing routines and adds a convinient way of detecting whether drawing should happen or not (surfaceCreated/Destroyed). It also restores max. fps on my phone which went down drastically with the gingerbread(CM7) update. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@29333 a1c6a512-1295-4272-9138-f99709370657
-rw-r--r--android/src/org/rockbox/RockboxActivity.java45
-rw-r--r--android/src/org/rockbox/RockboxFramebuffer.java101
-rw-r--r--android/src/org/rockbox/RockboxService.java45
-rw-r--r--apps/keymaps/keymap-android.c3
-rw-r--r--firmware/target/hosted/android/app/button-target.h2
-rw-r--r--firmware/target/hosted/android/lcd-android.c140
6 files changed, 158 insertions, 178 deletions
diff --git a/android/src/org/rockbox/RockboxActivity.java b/android/src/org/rockbox/RockboxActivity.java
index 76535687f9..f3f0d2151c 100644
--- a/android/src/org/rockbox/RockboxActivity.java
+++ b/android/src/org/rockbox/RockboxActivity.java
@@ -66,10 +66,9 @@ public class RockboxActivity extends Activity
66 protected void onReceiveResult(final int resultCode, final Bundle resultData) 66 protected void onReceiveResult(final int resultCode, final Bundle resultData)
67 { 67 {
68 switch (resultCode) { 68 switch (resultCode) {
69 case RockboxService.RESULT_LIB_LOADED: 69 case RockboxService.RESULT_INVOKING_MAIN:
70 rbservice = RockboxService.get_instance();
71 if (loadingdialog != null) 70 if (loadingdialog != null)
72 loadingdialog.setIndeterminate(true); 71 loadingdialog.dismiss();
73 break; 72 break;
74 case RockboxService.RESULT_LIB_LOAD_PROGRESS: 73 case RockboxService.RESULT_LIB_LOAD_PROGRESS:
75 if (loadingdialog == null) 74 if (loadingdialog == null)
@@ -79,10 +78,10 @@ public class RockboxActivity extends Activity
79 loadingdialog.setMax(resultData.getInt("max", 100)); 78 loadingdialog.setMax(resultData.getInt("max", 100));
80 loadingdialog.setProgress(resultData.getInt("value", 0)); 79 loadingdialog.setProgress(resultData.getInt("value", 0));
81 break; 80 break;
82 case RockboxService.RESULT_FB_INITIALIZED: 81 case RockboxService.RESULT_SERVICE_RUNNING:
82 rbservice = RockboxService.get_instance();
83 setServiceActivity(true);
83 attachFramebuffer(); 84 attachFramebuffer();
84 if (loadingdialog != null)
85 loadingdialog.dismiss();
86 break; 85 break;
87 case RockboxService.RESULT_ERROR_OCCURED: 86 case RockboxService.RESULT_ERROR_OCCURED:
88 Toast.makeText(RockboxActivity.this, resultData.getString("error"), Toast.LENGTH_LONG); 87 Toast.makeText(RockboxActivity.this, resultData.getString("error"), Toast.LENGTH_LONG);
@@ -93,17 +92,17 @@ public class RockboxActivity extends Activity
93 startService(intent); 92 startService(intent);
94 } 93 }
95 94
96 private boolean isRockboxRunning() 95 private void setServiceActivity(boolean set)
97 { 96 {
98 if (rbservice == null) 97 if (rbservice != null)
99 rbservice = RockboxService.get_instance(); 98 rbservice.set_activity(this);
100 return (rbservice!= null && rbservice.isRockboxRunning() == true);
101 } 99 }
102 100
103 private void attachFramebuffer() 101 private void attachFramebuffer()
104 { 102 {
105 View rbFramebuffer = rbservice.get_fb(); 103 View rbFramebuffer = null;
106 try { 104 try {
105 rbFramebuffer = rbservice.get_fb();
107 setContentView(rbFramebuffer); 106 setContentView(rbFramebuffer);
108 } catch (IllegalStateException e) { 107 } catch (IllegalStateException e) {
109 /* we are already using the View, 108 /* we are already using the View,
@@ -111,17 +110,17 @@ public class RockboxActivity extends Activity
111 ViewGroup g = (ViewGroup) rbFramebuffer.getParent(); 110 ViewGroup g = (ViewGroup) rbFramebuffer.getParent();
112 g.removeView(rbFramebuffer); 111 g.removeView(rbFramebuffer);
113 setContentView(rbFramebuffer); 112 setContentView(rbFramebuffer);
114 } finally { 113 } catch (NullPointerException e) {
115 rbFramebuffer.requestFocus(); 114 return;
116 rbservice.set_activity(this);
117 } 115 }
116 rbFramebuffer.requestFocus();
118 } 117 }
119 118
120 public void onResume() 119 public void onResume()
121 { 120 {
122 super.onResume(); 121 super.onResume();
123 if (isRockboxRunning()) 122 setVisible(true);
124 attachFramebuffer(); 123 attachFramebuffer();
125 } 124 }
126 125
127 /* this is also called when the backlight goes off, 126 /* this is also called when the backlight goes off,
@@ -131,27 +130,23 @@ public class RockboxActivity extends Activity
131 protected void onPause() 130 protected void onPause()
132 { 131 {
133 super.onPause(); 132 super.onPause();
134 if (rbservice != null) 133 /* this will cause the framebuffer's Surface to be destroyed, enabling
135 { 134 * us to disable drawing */
136 rbservice.set_activity(null); 135 setVisible(false);
137 rbservice.get_fb().dispatchWindowVisibilityChanged(View.INVISIBLE);
138 }
139 } 136 }
140 137
141 @Override 138 @Override
142 protected void onStop() 139 protected void onStop()
143 { 140 {
144 super.onStop(); 141 super.onStop();
145 if (rbservice != null) 142 setServiceActivity(false);
146 rbservice.set_activity(null);
147 } 143 }
148 144
149 @Override 145 @Override
150 protected void onDestroy() 146 protected void onDestroy()
151 { 147 {
152 super.onDestroy(); 148 super.onDestroy();
153 if (rbservice != null) 149 setServiceActivity(false);
154 rbservice.set_activity(null);
155 } 150 }
156 151
157 private void LOG(CharSequence text) 152 private void LOG(CharSequence text)
diff --git a/android/src/org/rockbox/RockboxFramebuffer.java b/android/src/org/rockbox/RockboxFramebuffer.java
index 05d2b11184..037fd2db85 100644
--- a/android/src/org/rockbox/RockboxFramebuffer.java
+++ b/android/src/org/rockbox/RockboxFramebuffer.java
@@ -23,8 +23,6 @@ package org.rockbox;
23 23
24import java.nio.ByteBuffer; 24import java.nio.ByteBuffer;
25 25
26import org.rockbox.Helper.MediaButtonReceiver;
27
28import android.content.Context; 26import android.content.Context;
29import android.graphics.Bitmap; 27import android.graphics.Bitmap;
30import android.graphics.Canvas; 28import android.graphics.Canvas;
@@ -33,45 +31,72 @@ import android.util.DisplayMetrics;
33import android.util.Log; 31import android.util.Log;
34import android.view.KeyEvent; 32import android.view.KeyEvent;
35import android.view.MotionEvent; 33import android.view.MotionEvent;
36import android.view.View; 34import android.view.SurfaceHolder;
35import android.view.SurfaceView;
37import android.view.ViewConfiguration; 36import android.view.ViewConfiguration;
38 37
39public class RockboxFramebuffer extends View 38public class RockboxFramebuffer extends SurfaceView
39 implements SurfaceHolder.Callback
40{ 40{
41 private Bitmap btm;
42 private Rect rect;
43 private ByteBuffer native_buf;
44 private MediaButtonReceiver media_monitor;
45 private final DisplayMetrics metrics; 41 private final DisplayMetrics metrics;
46 private final ViewConfiguration view_config; 42 private final ViewConfiguration view_config;
43 private ByteBuffer native_buf;
44 private Bitmap btm;
47 45
48 public RockboxFramebuffer(Context c, int lcd_width, 46 /* first stage init; needs to run from a thread that has a Looper
49 int lcd_height, ByteBuffer native_fb) 47 * setup stuff that needs a Context */
48 public RockboxFramebuffer(Context c)
50 { 49 {
51 super(c); 50 super(c);
51
52 metrics = c.getResources().getDisplayMetrics();
53 view_config = ViewConfiguration.get(c);
54 getHolder().addCallback(this);
52 /* Needed so we can catch KeyEvents */ 55 /* Needed so we can catch KeyEvents */
53 setFocusable(true); 56 setFocusable(true);
54 setFocusableInTouchMode(true); 57 setFocusableInTouchMode(true);
55 setClickable(true); 58 setClickable(true);
59 /* don't draw until native is ready (2nd stage) */
60 setEnabled(false);
61 }
62
63 /* second stage init; called from Rockbox with information about the
64 * display framebuffer */
65 @SuppressWarnings("unused")
66 private void java_lcd_init(int lcd_width, int lcd_height, ByteBuffer native_fb)
67 {
56 btm = Bitmap.createBitmap(lcd_width, lcd_height, Bitmap.Config.RGB_565); 68 btm = Bitmap.createBitmap(lcd_width, lcd_height, Bitmap.Config.RGB_565);
57 rect = new Rect();
58 native_buf = native_fb; 69 native_buf = native_fb;
59 media_monitor = new MediaButtonReceiver(c); 70 setEnabled(true);
60 media_monitor.register();
61 /* the service needs to know the about us */
62 ((RockboxService)c).set_fb(this);
63
64 metrics = c.getResources().getDisplayMetrics();
65 view_config = ViewConfiguration.get(c);
66 } 71 }
67 72
68 public void onDraw(Canvas c) 73 @SuppressWarnings("unused")
74 private void java_lcd_update()
75 {
76 SurfaceHolder holder = getHolder();
77 Canvas c = holder.lockCanvas(null);
78 btm.copyPixelsFromBuffer(native_buf);
79 synchronized (holder)
80 { /* draw */
81 c.drawBitmap(btm, 0.0f, 0.0f, null);
82 }
83 holder.unlockCanvasAndPost(c);
84 }
85
86 @SuppressWarnings("unused")
87 private void java_lcd_update_rect(int x, int y, int width, int height)
69 { 88 {
70 /* can't copy a partial buffer :( */ 89 SurfaceHolder holder = getHolder();
90 Rect dirty = new Rect(x, y, x+width, y+height);
91 Canvas c = holder.lockCanvas(dirty);
92 /* can't copy a partial buffer,
93 * but it doesn't make a noticeable difference anyway */
71 btm.copyPixelsFromBuffer(native_buf); 94 btm.copyPixelsFromBuffer(native_buf);
72 c.getClipBounds(rect); 95 synchronized (holder)
73 c.drawBitmap(btm, rect, rect, null); 96 { /* draw */
74 post_update_done(); 97 c.drawBitmap(btm, dirty, dirty, null);
98 }
99 holder.unlockCanvasAndPost(c);
75 } 100 }
76 101
77 @SuppressWarnings("unused") 102 @SuppressWarnings("unused")
@@ -109,28 +134,6 @@ public class RockboxFramebuffer extends View
109 { 134 {
110 return buttonHandler(keyCode, false); 135 return buttonHandler(keyCode, false);
111 } 136 }
112
113 public void destroy()
114 {
115 set_lcd_active(0);
116 media_monitor.unregister();
117 }
118
119 @Override
120 protected void onWindowVisibilityChanged(int visibility)
121 {
122 super.onWindowVisibilityChanged(visibility);
123
124 switch (visibility) {
125 case VISIBLE:
126 set_lcd_active(1);
127 break;
128 case GONE:
129 case INVISIBLE:
130 set_lcd_active(0);
131 break;
132 }
133 }
134 137
135 @SuppressWarnings("unused") 138 @SuppressWarnings("unused")
136 private int getDpi() 139 private int getDpi()
@@ -144,8 +147,12 @@ public class RockboxFramebuffer extends View
144 return view_config.getScaledTouchSlop(); 147 return view_config.getScaledTouchSlop();
145 } 148 }
146 149
147 private native void post_update_done();
148 private native void set_lcd_active(int active);
149 private native void touchHandler(boolean down, int x, int y); 150 private native void touchHandler(boolean down, int x, int y);
150 private native static boolean buttonHandler(int keycode, boolean state); 151 private native static boolean buttonHandler(int keycode, boolean state);
152
153 public native void surfaceCreated(SurfaceHolder holder);
154 public native void surfaceDestroyed(SurfaceHolder holder);
155 public void surfaceChanged(SurfaceHolder holder, int format, int width, int height)
156 {
157 }
151} 158}
diff --git a/android/src/org/rockbox/RockboxService.java b/android/src/org/rockbox/RockboxService.java
index de90999783..4f5df62280 100644
--- a/android/src/org/rockbox/RockboxService.java
+++ b/android/src/org/rockbox/RockboxService.java
@@ -31,6 +31,7 @@ import java.util.TimerTask;
31import java.util.zip.ZipEntry; 31import java.util.zip.ZipEntry;
32import java.util.zip.ZipFile; 32import java.util.zip.ZipFile;
33 33
34import org.rockbox.Helper.MediaButtonReceiver;
34import org.rockbox.Helper.RunForegroundManager; 35import org.rockbox.Helper.RunForegroundManager;
35 36
36import android.app.Activity; 37import android.app.Activity;
@@ -59,20 +60,21 @@ public class RockboxService extends Service
59 60
60 /* locals needed for the c code and rockbox state */ 61 /* locals needed for the c code and rockbox state */
61 private RockboxFramebuffer fb = null; 62 private RockboxFramebuffer fb = null;
62 private boolean mRockboxRunning = false; 63 private volatile boolean rockbox_running;
63 private volatile boolean rbLibLoaded;
64 private Activity current_activity = null; 64 private Activity current_activity = null;
65 private IntentFilter itf; 65 private IntentFilter itf;
66 private BroadcastReceiver batt_monitor; 66 private BroadcastReceiver batt_monitor;
67 private RunForegroundManager fg_runner; 67 private RunForegroundManager fg_runner;
68 private MediaButtonReceiver mMediaButtonReceiver;
68 @SuppressWarnings("unused") 69 @SuppressWarnings("unused")
69 private int battery_level; 70 private int battery_level;
70 private ResultReceiver resultReceiver; 71 private ResultReceiver resultReceiver;
71 72
72 public static final int RESULT_LIB_LOADED = 0; 73 public static final int RESULT_INVOKING_MAIN = 0;
73 public static final int RESULT_LIB_LOAD_PROGRESS = 1; 74 public static final int RESULT_LIB_LOAD_PROGRESS = 1;
74 public static final int RESULT_FB_INITIALIZED = 2; 75 public static final int RESULT_FB_INITIALIZED = 2;
75 public static final int RESULT_ERROR_OCCURED = 3; 76 public static final int RESULT_SERVICE_RUNNING = 3;
77 public static final int RESULT_ERROR_OCCURED = 4;
76 78
77 @Override 79 @Override
78 public void onCreate() 80 public void onCreate()
@@ -89,14 +91,6 @@ public class RockboxService extends Service
89 { 91 {
90 return fb; 92 return fb;
91 } 93 }
92 /* framebuffer is initialised by the native code(!) so this is needed */
93 public void set_fb(RockboxFramebuffer newfb)
94 {
95 fb = newfb;
96 mRockboxRunning = true;
97 if (resultReceiver != null)
98 resultReceiver.send(RESULT_FB_INITIALIZED, null);
99 }
100 94
101 public Activity get_activity() 95 public Activity get_activity()
102 { 96 {
@@ -113,7 +107,7 @@ public class RockboxService extends Service
113 107
114 if (intent != null && intent.hasExtra("callback")) 108 if (intent != null && intent.hasExtra("callback"))
115 resultReceiver = (ResultReceiver) intent.getParcelableExtra("callback"); 109 resultReceiver = (ResultReceiver) intent.getParcelableExtra("callback");
116 if (!rbLibLoaded) 110 if (!rockbox_running)
117 startservice(); 111 startservice();
118 112
119 if (intent != null && intent.getAction() != null) 113 if (intent != null && intent.getAction() != null)
@@ -151,6 +145,8 @@ public class RockboxService extends Service
151 e.printStackTrace(); 145 e.printStackTrace();
152 } 146 }
153 } 147 }
148 if (resultReceiver != null)
149 resultReceiver.send(RESULT_SERVICE_RUNNING, null);
154 } 150 }
155 151
156 private void LOG(CharSequence text) 152 private void LOG(CharSequence text)
@@ -176,6 +172,11 @@ public class RockboxService extends Service
176 private void startservice() 172 private void startservice()
177 { 173 {
178 final int BUFFER = 8*1024; 174 final int BUFFER = 8*1024;
175 fb = new RockboxFramebuffer(this);
176 if (resultReceiver != null)
177 resultReceiver.send(RESULT_FB_INITIALIZED, null);
178 mMediaButtonReceiver = new MediaButtonReceiver(this);
179 mMediaButtonReceiver.register();
179 Thread rb = new Thread(new Runnable() 180 Thread rb = new Thread(new Runnable()
180 { 181 {
181 public void run() 182 public void run()
@@ -247,10 +248,11 @@ public class RockboxService extends Service
247 } 248 }
248 } 249 }
249 250
250 System.loadLibrary("rockbox"); 251 System.loadLibrary("rockbox");
251 rbLibLoaded = true; 252
253 rockbox_running = true;
252 if (resultReceiver != null) 254 if (resultReceiver != null)
253 resultReceiver.send(RESULT_LIB_LOADED, null); 255 resultReceiver.send(RESULT_INVOKING_MAIN, null);
254 256
255 main(); 257 main();
256 throw new IllegalStateException("native main() returned!"); 258 throw new IllegalStateException("native main() returned!");
@@ -259,15 +261,8 @@ public class RockboxService extends Service
259 rb.setDaemon(false); 261 rb.setDaemon(false);
260 rb.start(); 262 rb.start();
261 } 263 }
264
262 private native void main(); 265 private native void main();
263
264 /* returns true once rockbox is up and running.
265 * This is considered done once the framebuffer is initialised
266 */
267 public boolean isRockboxRunning()
268 {
269 return mRockboxRunning;
270 }
271 266
272 @Override 267 @Override
273 public IBinder onBind(Intent intent) 268 public IBinder onBind(Intent intent)
@@ -329,7 +324,7 @@ public class RockboxService extends Service
329 public void onDestroy() 324 public void onDestroy()
330 { 325 {
331 super.onDestroy(); 326 super.onDestroy();
332 fb.destroy(); 327 mMediaButtonReceiver.unregister();
333 /* Make sure our notification is gone. */ 328 /* Make sure our notification is gone. */
334 stopForeground(); 329 stopForeground();
335 } 330 }
diff --git a/apps/keymaps/keymap-android.c b/apps/keymaps/keymap-android.c
index df187e7ac8..c822a64fc2 100644
--- a/apps/keymaps/keymap-android.c
+++ b/apps/keymaps/keymap-android.c
@@ -50,6 +50,9 @@ static const struct button_mapping button_context_standard[] = {
50 { ACTION_STD_MENU, BUTTON_MENU|BUTTON_REL, BUTTON_MENU }, 50 { ACTION_STD_MENU, BUTTON_MENU|BUTTON_REL, BUTTON_MENU },
51 { ACTION_STD_CONTEXT, BUTTON_MENU|BUTTON_REPEAT, BUTTON_MENU }, 51 { ACTION_STD_CONTEXT, BUTTON_MENU|BUTTON_REPEAT, BUTTON_MENU },
52 52
53 /* special hack to get a redraw on activity resume, see lcd-android.c */
54 { ACTION_REDRAW, BUTTON_FORCE_REDRAW, BUTTON_NONE },
55
53 LAST_ITEM_IN_LIST 56 LAST_ITEM_IN_LIST
54}; /* button_context_standard */ 57}; /* button_context_standard */
55 58
diff --git a/firmware/target/hosted/android/app/button-target.h b/firmware/target/hosted/android/app/button-target.h
index 6106b612f9..3b6028739c 100644
--- a/firmware/target/hosted/android/app/button-target.h
+++ b/firmware/target/hosted/android/app/button-target.h
@@ -58,6 +58,8 @@ void android_ignore_back_button(bool yes);
58#define BUTTON_BOTTOMMIDDLE 0x00080000 58#define BUTTON_BOTTOMMIDDLE 0x00080000
59#define BUTTON_BOTTOMRIGHT 0x00100000 59#define BUTTON_BOTTOMRIGHT 0x00100000
60 60
61#define BUTTON_FORCE_REDRAW 0x00200000
62
61/* No remote */ 63/* No remote */
62#define BUTTON_REMOTE 0 64#define BUTTON_REMOTE 0
63 65
diff --git a/firmware/target/hosted/android/lcd-android.c b/firmware/target/hosted/android/lcd-android.c
index 92e8f5b96f..f719329819 100644
--- a/firmware/target/hosted/android/lcd-android.c
+++ b/firmware/target/hosted/android/lcd-android.c
@@ -21,10 +21,12 @@
21 21
22 22
23#include <jni.h> 23#include <jni.h>
24#include <string.h>
24#include "config.h" 25#include "config.h"
25#include "system.h" 26#include "system.h"
26#include "kernel.h" 27#include "kernel.h"
27#include "lcd.h" 28#include "lcd.h"
29#include "button.h"
28 30
29extern JNIEnv *env_ptr; 31extern JNIEnv *env_ptr;
30extern jclass RockboxService_class; 32extern jclass RockboxService_class;
@@ -32,57 +34,48 @@ extern jobject RockboxService_instance;
32 34
33static jclass RockboxFramebuffer_class; 35static jclass RockboxFramebuffer_class;
34static jobject RockboxFramebuffer_instance; 36static jobject RockboxFramebuffer_instance;
35static jmethodID postInvalidate1; 37static jmethodID java_lcd_update;
36static jmethodID postInvalidate2; 38static jmethodID java_lcd_update_rect;
37 39
38static bool display_on;
39static int dpi; 40static int dpi;
40static int scroll_threshold; 41static int scroll_threshold;
41static struct wakeup lcd_wakeup; 42static bool display_on;
42static struct mutex lcd_mtx;
43 43
44void lcd_init_device(void) 44void lcd_init_device(void)
45{ 45{
46 JNIEnv e = *env_ptr; 46 JNIEnv e = *env_ptr;
47 wakeup_init(&lcd_wakeup); 47 /* get existing instance from the Service */
48 mutex_init(&lcd_mtx); 48 jmethodID get_fb = e->GetMethodID(env_ptr, RockboxService_class, "get_fb",
49 RockboxFramebuffer_class = e->FindClass(env_ptr, 49 "()Lorg/rockbox/RockboxFramebuffer;");
50 "org/rockbox/RockboxFramebuffer"); 50 RockboxFramebuffer_instance = e->CallObjectMethod(env_ptr,
51 /* instantiate a RockboxFramebuffer instance 51 RockboxService_instance,
52 * 52 get_fb);
53 * Pass lcd width and height and our framebuffer so the java layer 53 RockboxFramebuffer_class = (*env_ptr)->GetObjectClass(env_ptr,
54 * can create a Bitmap which directly maps to it 54 RockboxFramebuffer_instance);
55 **/ 55
56 56 /* Get init function and set up what's left from the constructor */
57 /* map the framebuffer to a ByteBuffer, this way lcd updates will 57 jmethodID java_lcd_init = (*env_ptr)->GetMethodID(env_ptr,
58 * be directly feched from the framebuffer */ 58 RockboxFramebuffer_class,
59 "java_lcd_init",
60 "(IILjava/nio/ByteBuffer;)V");
61
59 jobject buf = e->NewDirectByteBuffer(env_ptr, 62 jobject buf = e->NewDirectByteBuffer(env_ptr,
60 lcd_framebuffer, 63 lcd_framebuffer,
61 (jlong)sizeof(lcd_framebuffer)); 64 (jlong)sizeof(lcd_framebuffer));
62 65
63 jmethodID constructor = e->GetMethodID(env_ptr, 66 e->CallVoidMethod(env_ptr, RockboxFramebuffer_instance, java_lcd_init,
64 RockboxFramebuffer_class,
65 "<init>",
66 "(Landroid/content/Context;" /* Service */
67 "II" /* lcd width/height */
68 "Ljava/nio/ByteBuffer;)V"); /* ByteBuffer */
69
70 RockboxFramebuffer_instance = e->NewObject(env_ptr,
71 RockboxFramebuffer_class,
72 constructor,
73 RockboxService_instance,
74 (jint)LCD_WIDTH, 67 (jint)LCD_WIDTH,
75 (jint)LCD_HEIGHT, 68 (jint)LCD_HEIGHT,
76 buf); 69 buf);
77 70
78 /* cache update functions */ 71 /* cache update functions */
79 postInvalidate1 = (*env_ptr)->GetMethodID(env_ptr, 72 java_lcd_update = (*env_ptr)->GetMethodID(env_ptr,
80 RockboxFramebuffer_class, 73 RockboxFramebuffer_class,
81 "postInvalidate", 74 "java_lcd_update",
82 "()V"); 75 "()V");
83 postInvalidate2 = (*env_ptr)->GetMethodID(env_ptr, 76 java_lcd_update_rect = (*env_ptr)->GetMethodID(env_ptr,
84 RockboxFramebuffer_class, 77 RockboxFramebuffer_class,
85 "postInvalidate", 78 "java_lcd_update_rect",
86 "(IIII)V"); 79 "(IIII)V");
87 80
88 jmethodID get_dpi = e->GetMethodID(env_ptr, 81 jmethodID get_dpi = e->GetMethodID(env_ptr,
@@ -98,49 +91,54 @@ void lcd_init_device(void)
98 get_dpi); 91 get_dpi);
99 scroll_threshold = e->CallIntMethod(env_ptr, RockboxFramebuffer_instance, 92 scroll_threshold = e->CallIntMethod(env_ptr, RockboxFramebuffer_instance,
100 get_scroll_threshold); 93 get_scroll_threshold);
101 display_on = true; 94 /* must not draw until surface is created */
95 display_on = false;
102} 96}
103 97
104/* the update mechanism is asynchronous since
105 * onDraw() must be called from the UI thread
106 *
107 * The Rockbox thread calling lcd_update() has to wait
108 * for the update to complete, so that it's synchronous,
109 * and we need to notify it (we could wait in the java layer, but
110 * that'd block the other Rockbox threads too)
111 *
112 * That should give more smoonth animations
113 */
114void lcd_update(void) 98void lcd_update(void)
115{ 99{
116 /* tell the system we're ready for drawing */
117 if (display_on) 100 if (display_on)
118 { 101 (*env_ptr)->CallVoidMethod(env_ptr, RockboxFramebuffer_instance,
119 mutex_lock(&lcd_mtx); 102 java_lcd_update);
120 (*env_ptr)->CallVoidMethod(env_ptr, RockboxFramebuffer_instance, postInvalidate1);
121 wakeup_wait(&lcd_wakeup, TIMEOUT_BLOCK);
122 mutex_unlock(&lcd_mtx);
123 }
124} 103}
125 104
126void lcd_update_rect(int x, int y, int width, int height) 105void lcd_update_rect(int x, int y, int width, int height)
127{ 106{
128 if (display_on) 107 if (display_on)
129 { 108 (*env_ptr)->CallVoidMethod(env_ptr, RockboxFramebuffer_instance,
130 mutex_lock(&lcd_mtx); 109 java_lcd_update_rect, x, y, width, height);
131 (*env_ptr)->CallVoidMethod(env_ptr, RockboxFramebuffer_instance, postInvalidate2, 110}
132 (jint)x, (jint)y, (jint)x+width, (jint)y+height); 111
133 wakeup_wait(&lcd_wakeup, TIMEOUT_BLOCK); 112/*
134 mutex_unlock(&lcd_mtx); 113 * this is called when the surface is created, which called is everytime
135 } 114 * the activity is brought in front and the RockboxFramebuffer gains focus
115 *
116 * Note this is considered interrupt context
117 */
118JNIEXPORT void JNICALL
119Java_org_rockbox_RockboxFramebuffer_surfaceCreated(JNIEnv *e, jobject this,
120 jobject surfaceholder)
121{
122 (void)e; (void)this; (void)surfaceholder;
123
124 display_on = true;
125 send_event(LCD_EVENT_ACTIVATION, NULL);
126 /* Force an update, since the newly created surface is initially black
127 * waiting for the next normal update results in a longish black screen */
128 queue_post(&button_queue, BUTTON_FORCE_REDRAW, 0);
136} 129}
137 130
131/*
132 * the surface is destroyed everytime the RockboxFramebuffer loses focus and
133 * goes invisible
134 */
138JNIEXPORT void JNICALL 135JNIEXPORT void JNICALL
139Java_org_rockbox_RockboxFramebuffer_post_1update_1done(JNIEnv *e, jobject this) 136Java_org_rockbox_RockboxFramebuffer_surfaceDestroyed(JNIEnv *e, jobject this,
137 jobject surfaceholder)
140{ 138{
141 (void)e; 139 (void)e; (void)this; (void)surfaceholder;
142 (void)this; 140
143 wakeup_signal(&lcd_wakeup); 141 display_on = false;
144} 142}
145 143
146bool lcd_active(void) 144bool lcd_active(void)
@@ -158,26 +156,6 @@ int touchscreen_get_scroll_threshold(void)
158 return scroll_threshold; 156 return scroll_threshold;
159} 157}
160 158
161/*
162 * (un)block lcd updates.
163 *
164 * Notice: This is called from the activity thread, so take it
165 * as interrupt context and take care what the event callback does
166 * (it shouldn't block in particular
167 *
168 * the 1s are needed due to strange naming conventions...
169 **/
170JNIEXPORT void JNICALL
171Java_org_rockbox_RockboxFramebuffer_set_1lcd_1active(JNIEnv *e,
172 jobject this,
173 jint active)
174{
175 (void)e;
176 (void)this;
177 display_on = active != 0;
178 if (active)
179 send_event(LCD_EVENT_ACTIVATION, NULL);
180}
181/* below is a plain copy from lcd-sdl.c */ 159/* below is a plain copy from lcd-sdl.c */
182 160
183/** 161/**