Auto merge of #21234 - paulrouget:surface, r=MortimerGoro

Android: Introduce ServoSurface

(WIP as it depends on 2 other PRs)

Depends on #21199. Only last commit matters.

r? @MortimerGoro

Please look at ServoSurface.java. The rest is mostly some refactoring to share as much code as possible with ServoView.

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/21234)
<!-- Reviewable:end -->
This commit is contained in:
bors-servo 2018-08-01 12:04:32 -04:00 committed by GitHub
commit 733552d997
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 544 additions and 212 deletions

View file

@ -39,6 +39,9 @@ pub trait HostTrait {
/// Will be called from the thread used for the init call. /// Will be called from the thread used for the init call.
/// Will be called when the GL buffer has been updated. /// Will be called when the GL buffer has been updated.
fn flush(&self); fn flush(&self);
/// Will be called before drawing.
/// Time to make the targetted GL context current.
fn make_current(&self);
/// Page starts loading. /// Page starts loading.
/// "Reload button" should be disabled. /// "Reload button" should be disabled.
/// "Stop button" should be enabled. /// "Stop button" should be enabled.
@ -355,6 +358,7 @@ impl WindowMethods for ServoCallbacks {
_height: Length<u32, DevicePixel>, _height: Length<u32, DevicePixel>,
) -> bool { ) -> bool {
debug!("WindowMethods::prepare_for_composite"); debug!("WindowMethods::prepare_for_composite");
self.host_callbacks.make_current();
true true
} }

View file

@ -28,6 +28,7 @@ fn call<F>(f: F) where F: Fn(&mut ServoGlue) -> Result<(), &'static str> {
#[repr(C)] #[repr(C)]
pub struct CHostCallbacks { pub struct CHostCallbacks {
pub flush: extern fn(), pub flush: extern fn(),
pub make_current: extern fn(),
pub on_load_started: extern fn(), pub on_load_started: extern fn(),
pub on_load_ended: extern fn(), pub on_load_ended: extern fn(),
pub on_title_changed: extern fn(title: *const c_char), pub on_title_changed: extern fn(title: *const c_char),
@ -223,6 +224,11 @@ impl HostTrait for HostCallbacks {
(self.0.flush)(); (self.0.flush)();
} }
fn make_current(&self) {
debug!("make_current");
(self.0.make_current)();
}
fn on_load_started(&self) { fn on_load_started(&self) {
debug!("on_load_ended"); debug!("on_load_ended");
(self.0.on_load_started)(); (self.0.on_load_started)();

View file

@ -36,21 +36,19 @@ where
} }
#[no_mangle] #[no_mangle]
pub fn Java_com_mozilla_servoview_NativeServo_version(env: JNIEnv, _class: JClass) -> jstring { pub fn Java_com_mozilla_servoview_JNIServo_version(env: JNIEnv, _class: JClass) -> jstring {
let v = api::servo_version(); let v = api::servo_version();
let output = env.new_string(v).expect("Couldn't create java string"); let output = env.new_string(v).expect("Couldn't create java string");
output.into_inner() output.into_inner()
} }
#[no_mangle] #[no_mangle]
pub fn Java_com_mozilla_servoview_NativeServo_init( pub fn Java_com_mozilla_servoview_JNIServo_init(
env: JNIEnv, env: JNIEnv,
_: JClass, _: JClass,
activity: JObject, activity: JObject,
args: JString, args: JString,
url: JString, url: JString,
wakeup_obj: JObject,
readfile_obj: JObject,
callbacks_obj: JObject, callbacks_obj: JObject,
width: jint, width: jint,
height: jint, height: jint,
@ -80,9 +78,11 @@ pub fn Java_com_mozilla_servoview_NativeServo_init(
Some(env.get_string(url).expect("Couldn't get java string").into()) Some(env.get_string(url).expect("Couldn't get java string").into())
}; };
let wakeup = Box::new(WakeupCallback::new(wakeup_obj, &env)); let callbacks_ref = env.new_global_ref(callbacks_obj).unwrap();
let readfile = Box::new(ReadFileCallback::new(readfile_obj, &env));
let callbacks = Box::new(HostCallbacks::new(callbacks_obj, &env)); let wakeup = Box::new(WakeupCallback::new(callbacks_ref.clone(), &env));
let readfile = Box::new(ReadFileCallback::new(callbacks_ref.clone(), &env));
let callbacks = Box::new(HostCallbacks::new(callbacks_ref, &env));
gl_glue::egl::init().and_then(|gl| { gl_glue::egl::init().and_then(|gl| {
api::init( api::init(
@ -100,7 +100,7 @@ pub fn Java_com_mozilla_servoview_NativeServo_init(
} }
#[no_mangle] #[no_mangle]
pub fn Java_com_mozilla_servoview_NativeServo_setBatchMode( pub fn Java_com_mozilla_servoview_JNIServo_setBatchMode(
env: JNIEnv, env: JNIEnv,
_: JClass, _: JClass,
batch: jboolean, batch: jboolean,
@ -110,7 +110,7 @@ pub fn Java_com_mozilla_servoview_NativeServo_setBatchMode(
} }
#[no_mangle] #[no_mangle]
pub fn Java_com_mozilla_servoview_NativeServo_resize( pub fn Java_com_mozilla_servoview_JNIServo_resize(
env: JNIEnv, env: JNIEnv,
_: JClass, _: JClass,
width: jint, width: jint,
@ -121,38 +121,38 @@ pub fn Java_com_mozilla_servoview_NativeServo_resize(
} }
#[no_mangle] #[no_mangle]
pub fn Java_com_mozilla_servoview_NativeServo_performUpdates(env: JNIEnv, _class: JClass) { pub fn Java_com_mozilla_servoview_JNIServo_performUpdates(env: JNIEnv, _class: JClass) {
debug!("performUpdates"); debug!("performUpdates");
call(env, |s| s.perform_updates()); call(env, |s| s.perform_updates());
} }
#[no_mangle] #[no_mangle]
pub fn Java_com_mozilla_servoview_NativeServo_loadUri(env: JNIEnv, _class: JClass, url: JString) { pub fn Java_com_mozilla_servoview_JNIServo_loadUri(env: JNIEnv, _class: JClass, url: JString) {
debug!("loadUri"); debug!("loadUri");
let url: String = env.get_string(url).unwrap().into(); let url: String = env.get_string(url).unwrap().into();
call(env, |s| s.load_uri(&url)); call(env, |s| s.load_uri(&url));
} }
#[no_mangle] #[no_mangle]
pub fn Java_com_mozilla_servoview_NativeServo_reload(env: JNIEnv, _class: JClass) { pub fn Java_com_mozilla_servoview_JNIServo_reload(env: JNIEnv, _class: JClass) {
debug!("reload"); debug!("reload");
call(env, |s| s.reload()); call(env, |s| s.reload());
} }
#[no_mangle] #[no_mangle]
pub fn Java_com_mozilla_servoview_NativeServo_goBack(env: JNIEnv, _class: JClass) { pub fn Java_com_mozilla_servoview_JNIServo_goBack(env: JNIEnv, _class: JClass) {
debug!("goBack"); debug!("goBack");
call(env, |s| s.go_back()); call(env, |s| s.go_back());
} }
#[no_mangle] #[no_mangle]
pub fn Java_com_mozilla_servoview_NativeServo_goForward(env: JNIEnv, _class: JClass) { pub fn Java_com_mozilla_servoview_JNIServo_goForward(env: JNIEnv, _class: JClass) {
debug!("goForward"); debug!("goForward");
call(env, |s| s.go_forward()); call(env, |s| s.go_forward());
} }
#[no_mangle] #[no_mangle]
pub fn Java_com_mozilla_servoview_NativeServo_scrollStart( pub fn Java_com_mozilla_servoview_JNIServo_scrollStart(
env: JNIEnv, env: JNIEnv,
_: JClass, _: JClass,
dx: jint, dx: jint,
@ -165,7 +165,7 @@ pub fn Java_com_mozilla_servoview_NativeServo_scrollStart(
} }
#[no_mangle] #[no_mangle]
pub fn Java_com_mozilla_servoview_NativeServo_scrollEnd( pub fn Java_com_mozilla_servoview_JNIServo_scrollEnd(
env: JNIEnv, env: JNIEnv,
_: JClass, _: JClass,
dx: jint, dx: jint,
@ -179,7 +179,7 @@ pub fn Java_com_mozilla_servoview_NativeServo_scrollEnd(
#[no_mangle] #[no_mangle]
pub fn Java_com_mozilla_servoview_NativeServo_scroll( pub fn Java_com_mozilla_servoview_JNIServo_scroll(
env: JNIEnv, env: JNIEnv,
_: JClass, _: JClass,
dx: jint, dx: jint,
@ -192,7 +192,7 @@ pub fn Java_com_mozilla_servoview_NativeServo_scroll(
} }
#[no_mangle] #[no_mangle]
pub fn Java_com_mozilla_servoview_NativeServo_click(env: JNIEnv, _: JClass, x: jint, y: jint) { pub fn Java_com_mozilla_servoview_JNIServo_click(env: JNIEnv, _: JClass, x: jint, y: jint) {
debug!("click"); debug!("click");
call(env, |s| s.click(x as u32, y as u32)); call(env, |s| s.click(x as u32, y as u32));
} }
@ -203,12 +203,9 @@ pub struct WakeupCallback {
} }
impl WakeupCallback { impl WakeupCallback {
pub fn new(jobject: JObject, env: &JNIEnv) -> WakeupCallback { pub fn new(callback: GlobalRef, env: &JNIEnv) -> WakeupCallback {
let jvm = Arc::new(env.get_java_vm().unwrap()); let jvm = Arc::new(env.get_java_vm().unwrap());
WakeupCallback { WakeupCallback { callback, jvm }
callback: env.new_global_ref(jobject).unwrap(),
jvm,
}
} }
} }
@ -233,9 +230,9 @@ pub struct ReadFileCallback {
} }
impl ReadFileCallback { impl ReadFileCallback {
pub fn new(jobject: JObject, env: &JNIEnv) -> ReadFileCallback { pub fn new(callback: GlobalRef, env: &JNIEnv) -> ReadFileCallback {
let jvm = env.get_java_vm().unwrap(); let jvm = env.get_java_vm().unwrap();
let callback = Mutex::new(env.new_global_ref(jobject).unwrap()); let callback = Mutex::new(callback);
ReadFileCallback { callback, jvm } ReadFileCallback { callback, jvm }
} }
} }
@ -260,12 +257,9 @@ impl ReadFileTrait for ReadFileCallback {
} }
impl HostCallbacks { impl HostCallbacks {
pub fn new(jobject: JObject, env: &JNIEnv) -> HostCallbacks { pub fn new(callbacks: GlobalRef, env: &JNIEnv) -> HostCallbacks {
let jvm = env.get_java_vm().unwrap(); let jvm = env.get_java_vm().unwrap();
HostCallbacks { HostCallbacks { callbacks, jvm }
callbacks: env.new_global_ref(jobject).unwrap(),
jvm,
}
} }
} }
@ -277,6 +271,13 @@ impl HostTrait for HostCallbacks {
.unwrap(); .unwrap();
} }
fn make_current(&self) {
debug!("make_current");
let env = self.jvm.get_env().unwrap();
env.call_method(self.callbacks.as_obj(), "makeCurrent", "()V", &[])
.unwrap();
}
fn on_load_started(&self) { fn on_load_started(&self) {
debug!("on_load_started"); debug!("on_load_started");
let env = self.jvm.get_env().unwrap(); let env = self.jvm.get_env().unwrap();

View file

@ -20,10 +20,11 @@ import android.widget.EditText;
import android.widget.ProgressBar; import android.widget.ProgressBar;
import com.mozilla.servoview.ServoView; import com.mozilla.servoview.ServoView;
import com.mozilla.servoview.Servo;
import java.io.File; import java.io.File;
public class MainActivity extends Activity implements ServoView.Client { public class MainActivity extends Activity implements Servo.Client {
private static final String LOGTAG = "MainActivity"; private static final String LOGTAG = "MainActivity";
@ -49,10 +50,10 @@ public class MainActivity extends Activity implements ServoView.Client {
mUrlField = findViewById(R.id.urlfield); mUrlField = findViewById(R.id.urlfield);
mProgressBar = findViewById(R.id.progressbar); mProgressBar = findViewById(R.id.progressbar);
mServoView.setClient(this);
mBackButton.setEnabled(false); mBackButton.setEnabled(false);
mFwdButton.setEnabled(false); mFwdButton.setEnabled(false);
mServoView.setClient(this);
mServoView.requestFocus(); mServoView.requestFocus();
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
@ -66,9 +67,7 @@ public class MainActivity extends Activity implements ServoView.Client {
} }
String args = getIntent().getStringExtra("servoargs"); String args = getIntent().getStringExtra("servoargs");
if (args != null) { mServoView.setServoArgs(args);
mServoView.setServoArgs(args);
}
setupUrlField(); setupUrlField();
} }
@ -105,18 +104,16 @@ public class MainActivity extends Activity implements ServoView.Client {
mServoView.loadUri(Uri.parse(uri)); mServoView.loadUri(Uri.parse(uri));
} }
// From activity_main.xml:
public void onReloadClicked(View v) { public void onReloadClicked(View v) {
mServoView.reload(); mServoView.reload();
} }
public void onBackClicked(View v) { public void onBackClicked(View v) {
mServoView.goBack(); mServoView.goBack();
} }
public void onForwardClicked(View v) { public void onForwardClicked(View v) {
mServoView.goForward(); mServoView.goForward();
} }
public void onStopClicked(View v) { public void onStopClicked(View v) {
mServoView.stop(); mServoView.stop();
} }

View file

@ -10,48 +10,65 @@ import android.app.Activity;
/** /**
* Maps /ports/libsimpleservo API * Maps /ports/libsimpleservo API
*/ */
public class NativeServo { @SuppressWarnings("JniMissingFunction")
public native String version(); public class JNIServo {
public native void init(Activity activity, JNIServo() {
String args,
String url,
WakeupCallback wakeup,
ReadFileCallback readfile,
ServoCallbacks callbacks,
int width, int height, boolean log);
public native void setBatchMode(boolean mode);
public native void performUpdates();
public native void resize(int width, int height);
public native void reload();
public native void stop();
public native void goBack();
public native void goForward();
public native void loadUri(String uri);
public native void scrollStart(int dx, int dy, int x, int y);
public native void scroll(int dx, int dy, int x, int y);
public native void scrollEnd(int dx, int dy, int x, int y);
public native void click(int x, int y);
NativeServo() {
System.loadLibrary("c++_shared"); System.loadLibrary("c++_shared");
System.loadLibrary("simpleservo"); System.loadLibrary("simpleservo");
} }
public interface ReadFileCallback { public native String version();
public native void init(Activity activity,
String args,
String url,
Callbacks callbacks,
int width, int height, boolean log);
public native void setBatchMode(boolean mode);
public native void performUpdates();
public native void resize(int width, int height);
public native void reload();
public native void stop();
public native void goBack();
public native void goForward();
public native void loadUri(String uri);
public native void scrollStart(int dx, int dy, int x, int y);
public native void scroll(int dx, int dy, int x, int y);
public native void scrollEnd(int dx, int dy, int x, int y);
public native void click(int x, int y);
public interface Callbacks {
void wakeup();
void flush();
void makeCurrent();
void onAnimatingChanged(boolean animating);
void onLoadStarted();
void onLoadEnded();
void onTitleChanged(String title);
void onUrlChanged(String url);
void onHistoryChanged(boolean canGoBack, boolean canGoForward);
byte[] readfile(String file); byte[] readfile(String file);
} }
public interface WakeupCallback {
void wakeup();
}
public interface ServoCallbacks {
void flush();
void onLoadStarted();
void onLoadEnded();
void onTitleChanged(String title);
void onUrlChanged(String url);
void onHistoryChanged(boolean canGoBack, boolean canGoForward);
void onAnimatingChanged(boolean animating);
}
} }

View file

@ -0,0 +1,173 @@
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package com.mozilla.servoview;
import android.app.Activity;
import android.content.res.AssetManager;
import android.util.Log;
import java.io.IOException;
import java.io.InputStream;
public class Servo {
private static final String LOGTAG = "Servo";
private AssetManager mAssetMgr;
private JNIServo mJNI = new JNIServo();
private RunCallback mRunCallback;
public Servo(
RunCallback runCallback,
GfxCallbacks gfxcb,
Client client,
Activity activity,
String args, String url,
int width, int height, boolean log) {
mRunCallback = runCallback;
mAssetMgr = activity.getResources().getAssets();
Callbacks cbs = new Callbacks(client, gfxcb);
mRunCallback.inGLThread(() -> {
mJNI.init(activity, args, url, cbs, width, height, log);
});
}
public String version() {
return mJNI.version();
}
public void setBatchMode(boolean mode) {
mRunCallback.inGLThread(() -> mJNI.setBatchMode(mode));
}
public void resize(int width, int height) {
mRunCallback.inGLThread(() -> mJNI.resize(width, height));
}
public void reload() {
mRunCallback.inGLThread(() -> mJNI.reload());
}
public void stop() {
mRunCallback.inGLThread(() -> mJNI.stop());
}
public void goBack() {
mRunCallback.inGLThread(() -> mJNI.goBack());
}
public void goForward() {
mRunCallback.inGLThread(() -> mJNI.goForward());
}
public void loadUri(String uri) {
mRunCallback.inGLThread(() -> mJNI.loadUri(uri));
}
public void scrollStart(int dx, int dy, int x, int y) {
mRunCallback.inGLThread(() -> mJNI.scrollStart(dx, dy, x, y));
}
public void scroll(int dx, int dy, int x, int y) {
mRunCallback.inGLThread(() -> mJNI.scroll(dx, dy, x, y));
}
public void scrollEnd(int dx, int dy, int x, int y) {
mRunCallback.inGLThread(() -> mJNI.scrollEnd(dx, dy, x, y));
}
public void click(int x, int y) {
mRunCallback.inGLThread(() -> mJNI.click(x, y));
}
public interface Client {
void onLoadStarted();
void onLoadEnded();
void onTitleChanged(String title);
void onUrlChanged(String url);
void onHistoryChanged(boolean canGoBack, boolean canGoForward);
}
public interface RunCallback {
void inGLThread(Runnable f);
void inUIThread(Runnable f);
}
public interface GfxCallbacks {
void flushGLBuffers();
void animationStateChanged(boolean animating);
void makeCurrent();
}
private class Callbacks implements JNIServo.Callbacks, Client {
private final GfxCallbacks mGfxCb;
Client mClient;
Callbacks(Client client, GfxCallbacks gfxcb) {
mClient = client;
mGfxCb = gfxcb;
}
public void wakeup() {
mRunCallback.inGLThread(() -> mJNI.performUpdates());
}
public void flush() {
mRunCallback.inUIThread(() -> mGfxCb.flushGLBuffers());
}
public void makeCurrent() {
mRunCallback.inUIThread(() -> mGfxCb.makeCurrent());
}
public void onAnimatingChanged(boolean animating) {
mRunCallback.inUIThread(() -> mGfxCb.animationStateChanged(animating));
}
public void onLoadStarted() {
mRunCallback.inUIThread(() -> mClient.onLoadStarted());
}
public void onLoadEnded() {
mRunCallback.inUIThread(() -> mClient.onLoadEnded());
}
public void onTitleChanged(String title) {
mRunCallback.inUIThread(() -> mClient.onTitleChanged(title));
}
public void onUrlChanged(String url) {
mRunCallback.inUIThread(() -> mClient.onUrlChanged(url));
}
public void onHistoryChanged(boolean canGoBack, boolean canGoForward) {
mRunCallback.inUIThread(() -> mClient.onHistoryChanged(canGoBack, canGoForward));
}
public byte[] readfile(String file) {
try {
InputStream stream = mAssetMgr.open(file);
byte[] bytes = new byte[stream.available()];
stream.read(bytes);
stream.close();
return bytes;
} catch (IOException e) {
Log.e(LOGTAG, e.getMessage());
return null;
}
}
}
}

View file

@ -1,32 +0,0 @@
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package com.mozilla.servoview;
import android.opengl.GLES31;
import android.opengl.GLSurfaceView;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
public class ServoGLRenderer implements GLSurfaceView.Renderer {
private final ServoView mView;
ServoGLRenderer(ServoView view) {
mView = view;
}
public void onSurfaceCreated(GL10 unused, EGLConfig config) {
mView.onGLReady();
}
public void onDrawFrame(GL10 unused) {
}
public void onSurfaceChanged(GL10 unused, int width, int height) {
GLES31.glViewport(0, 0, width, height);
mView.onSurfaceResized(width, height);
}
}

View file

@ -0,0 +1,186 @@
package com.mozilla.servoview;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.net.Uri;
import android.opengl.EGL14;
import android.opengl.EGLConfig;
import android.opengl.EGLContext;
import android.opengl.EGLDisplay;
import android.opengl.EGLSurface;
import android.opengl.GLUtils;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import com.mozilla.servoview.Servo.Client;
import com.mozilla.servoview.Servo.GfxCallbacks;
import com.mozilla.servoview.Servo.RunCallback;
import static android.opengl.EGL14.EGL_CONTEXT_CLIENT_VERSION;
import static android.opengl.EGL14.EGL_OPENGL_ES2_BIT;
public class ServoSurface {
private final GLThread mGLThread;
private final Handler mMainLooperHandler;
private Handler mGLLooperHandler;
private Surface mASurface;
private int mWidth;
private int mHeight;
private Servo mServo;
private Client mClient = null;
private String mServoArgs = "";
private Uri mInitialUri = null;
private Activity mActivity;
public ServoSurface(Surface surface, int width, int height) {
mWidth = width;
mHeight = height;
mASurface = surface;
mMainLooperHandler = new Handler(Looper.getMainLooper());
mGLThread = new GLThread();
}
public void setClient(Client client) {
mClient = client;
}
public void setServoArgs(String args) {
mServoArgs = args != null ? args : "";
}
public void setActivity(Activity activity) {
mActivity = activity;
}
public void runLoop() {
mGLThread.start();
}
public void reload() {
mServo.reload();
}
public void goBack() {
mServo.goBack();
}
public void goForward() {
mServo.goForward();
}
public void stop() {
mServo.stop();
}
public void onSurfaceResized(int width, int height) {
mServo.resize(width, height);
}
public void loadUri(Uri uri) {
if (mServo != null) {
mServo.loadUri(uri.toString());
} else {
mInitialUri = uri;
}
}
static class Surface implements GfxCallbacks {
private static final String LOGTAG = "ServoSurface";
private EGLConfig[] mEGLConfigs;
private EGLDisplay mEglDisplay;
private EGLContext mEglContext;
private EGLSurface mEglSurface;
Surface(Surface surface) {
mEglDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
int[] version = new int[2];
if (!EGL14.eglInitialize(mEglDisplay, version, 0, version, 1)) {
throw new RuntimeException("Error: eglInitialize() Failed " + GLUtils.getEGLErrorString(EGL14.eglGetError()));
}
mEGLConfigs = new EGLConfig[1];
int[] configsCount = new int[1];
int[] configSpec = new int[]{
EGL14.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
EGL14.EGL_RED_SIZE, 8,
EGL14.EGL_GREEN_SIZE, 8,
EGL14.EGL_BLUE_SIZE, 8,
EGL14.EGL_ALPHA_SIZE, 8,
EGL14.EGL_DEPTH_SIZE, 0,
EGL14.EGL_STENCIL_SIZE, 0,
EGL14.EGL_NONE
};
if ((!EGL14.eglChooseConfig(mEglDisplay, configSpec, 0, mEGLConfigs, 0, 1, configsCount, 0)) || (configsCount[0] == 0)) {
throw new IllegalArgumentException("Error: eglChooseConfig() Failed " + GLUtils.getEGLErrorString(EGL14.eglGetError()));
}
if (mEGLConfigs[0] == null) {
throw new RuntimeException("Error: eglConfig() not Initialized");
}
int[] attrib_list = {EGL_CONTEXT_CLIENT_VERSION, 3, EGL14.EGL_NONE};
mEglContext = EGL14.eglCreateContext(mEglDisplay, mEGLConfigs[0], EGL14.EGL_NO_CONTEXT, attrib_list, 0);
int glError = EGL14.eglGetError();
if (glError != EGL14.EGL_SUCCESS) {
throw new RuntimeException("Error: eglCreateContext() Failed " + GLUtils.getEGLErrorString(glError));
}
mEglSurface = EGL14.eglCreateWindowSurface(mEglDisplay, mEGLConfigs[0], surface, new int[]{EGL14.EGL_NONE}, 0);
if (mEglSurface == null || mEglSurface == EGL14.EGL_NO_SURFACE) {
glError = EGL14.eglGetError();
if (glError == EGL14.EGL_BAD_NATIVE_WINDOW) {
Log.e(LOGTAG, "Error: createWindowSurface() Returned EGL_BAD_NATIVE_WINDOW.");
return;
}
throw new RuntimeException("Error: createWindowSurface() Failed " + GLUtils.getEGLErrorString(glError));
}
flushGLBuffers();
}
public void makeCurrent() {
if (!EGL14.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
throw new RuntimeException("Error: eglMakeCurrent() Failed " + GLUtils.getEGLErrorString(EGL14.eglGetError()));
}
}
public void flushGLBuffers() {
EGL14.eglSwapBuffers(mEglDisplay, mEglSurface);
}
public void animationStateChanged(boolean animating) {
// FIXME
}
}
class GLThread extends Thread implements RunCallback {
public void inGLThread(Runnable r) {
mGLLooperHandler.post(r);
}
public void inUIThread(Runnable r) {
mMainLooperHandler.post(r);
}
// FIXME: HandlerLeak
@SuppressLint("HandlerLeak")
public void run() {
Looper.prepare();
Surface surface = new Surface(mASurface);
final boolean showLogs = true;
String uri = mInitialUri == null ? null : mInitialUri.toString();
mServo = new Servo(this, surface, mClient, mActivity, mServoArgs, uri, mWidth, mHeight, showLogs);
mGLLooperHandler = new Handler() {
public void handleMessage(Message msg) {
}
};
Looper.loop();
}
}
}

View file

@ -7,28 +7,44 @@ package com.mozilla.servoview;
import android.app.Activity; import android.app.Activity;
import android.content.Context; import android.content.Context;
import android.content.res.AssetManager;
import android.net.Uri; import android.net.Uri;
import android.opengl.GLES31;
import android.opengl.GLSurfaceView; import android.opengl.GLSurfaceView;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.util.Log;
import android.view.Choreographer; import android.view.Choreographer;
import android.view.GestureDetector; import android.view.GestureDetector;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.widget.OverScroller; import android.widget.OverScroller;
import java.io.IOException;
import java.io.InputStream;
public class ServoView extends GLSurfaceView implements GestureDetector.OnGestureListener, Choreographer.FrameCallback { import com.mozilla.servoview.Servo.Client;
import com.mozilla.servoview.Servo.GfxCallbacks;
import com.mozilla.servoview.Servo.RunCallback;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
public class ServoView extends GLSurfaceView
implements
GestureDetector.OnGestureListener,
Choreographer.FrameCallback,
GfxCallbacks,
RunCallback {
private static final String LOGTAG = "ServoView"; private static final String LOGTAG = "ServoView";
private Activity mActivity; private Activity mActivity;
private NativeServo mServo; private Servo mServo;
private Client mClient = null; private Client mClient = null;
private Uri mInitialUri = null; private Uri mInitialUri = null;
private boolean mAnimating; private boolean mAnimating;
private String mServoArgs = ""; private String mServoArgs = "";
private GestureDetector mGestureDetector;
private OverScroller mScroller;
private int mLastX = 0;
private int mCurX = 0;
private int mLastY = 0;
private int mCurY = 0;
private boolean mFlinging;
public ServoView(Context context, AttributeSet attrs) { public ServoView(Context context, AttributeSet attrs) {
super(context, attrs); super(context, attrs);
@ -40,149 +56,87 @@ public class ServoView extends GLSurfaceView implements GestureDetector.OnGestur
setEGLConfigChooser(8, 8, 8, 8, 24, 0); setEGLConfigChooser(8, 8, 8, 8, 24, 0);
ServoGLRenderer mRenderer = new ServoGLRenderer(this); ServoGLRenderer mRenderer = new ServoGLRenderer(this);
setRenderer(mRenderer); setRenderer(mRenderer);
mServo = new NativeServo();
setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY); setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
initGestures(context); initGestures(context);
} }
public void setServoArgs(String args) { public void setServoArgs(String args) {
mServoArgs = args; mServoArgs = args != null ? args : "";
} }
public void reload() { public void reload() {
queueEvent(() -> mServo.reload()); mServo.reload();
} }
public void goBack() { public void goBack() {
queueEvent(() -> mServo.goBack()); mServo.goBack();
} }
public void goForward() { public void goForward() {
queueEvent(() -> mServo.goForward()); mServo.goForward();
} }
public void stop() { public void stop() {
queueEvent(() -> mServo.stop()); mServo.stop();
} }
public void onSurfaceResized(int width, int height) { public void onSurfaceResized(int width, int height) {
queueEvent(() -> mServo.resize(width, height)); if (mServo != null) {
mServo.resize(width, height);
}
} }
public void loadUri(Uri uri) { public void loadUri(Uri uri) {
if (mServo != null) { if (mServo != null) {
queueEvent(() -> mServo.loadUri(uri.toString())); mServo.loadUri(uri.toString());
} else { } else {
mInitialUri = uri; mInitialUri = uri;
} }
} }
class WakeupCallback implements NativeServo.WakeupCallback { public void flushGLBuffers() {
public void wakeup() { requestRender();
queueEvent(() -> mServo.performUpdates());
};
} }
class ReadFileCallback implements NativeServo.ReadFileCallback { // Scroll and click
public byte[] readfile(String file) {
try { public void animationStateChanged(boolean animating) {
AssetManager assetMgr = getContext().getResources().getAssets(); if (!mAnimating && animating) {
InputStream stream = assetMgr.open(file); post(() -> Choreographer.getInstance().postFrameCallback(ServoView.this));
byte[] bytes = new byte[stream.available()];
stream.read(bytes);
stream.close();
return bytes;
} catch (IOException e) {
Log.e(LOGTAG, e.getMessage());
return null;
}
} }
mAnimating = animating;
} }
class ServoCallbacks implements NativeServo.ServoCallbacks { public void makeCurrent() {
public void flush() { }
requestRender();
}
public void onLoadStarted() { public void inGLThread(Runnable f) {
if (mClient != null) { queueEvent(f);
post(() -> mClient.onLoadStarted()); }
}
}
public void onLoadEnded() { public void inUIThread(Runnable f) {
if (mClient != null) { post(f);
post(() -> mClient.onLoadEnded());
}
}
public void onTitleChanged(final String title) {
if (mClient != null) {
post(() -> mClient.onTitleChanged(title));
}
}
public void onUrlChanged(final String url) {
if (mClient != null) {
post(() -> mClient.onUrlChanged(url));
}
}
public void onHistoryChanged(final boolean canGoBack, final boolean canGoForward) {
if (mClient != null) {
post(() -> mClient.onHistoryChanged(canGoBack, canGoForward));
}
}
public void onAnimatingChanged(final boolean animating) {
if (!mAnimating && animating) {
post(() -> Choreographer.getInstance().postFrameCallback(ServoView.this));
}
mAnimating = animating;
}
} }
public void onGLReady() { public void onGLReady() {
final WakeupCallback c1 = new WakeupCallback();
final ReadFileCallback c2 = new ReadFileCallback();
final ServoCallbacks c3 = new ServoCallbacks();
final boolean showLogs = true; final boolean showLogs = true;
int width = getWidth(); int width = getWidth();
int height = getHeight(); int height = getHeight();
queueEvent(() -> { inGLThread(() -> {
String uri = mInitialUri == null ? null : mInitialUri.toString(); String uri = mInitialUri == null ? null : mInitialUri.toString();
mServo.init(mActivity, mServoArgs, uri, c1, c2, c3, width, height, showLogs); mServo = new Servo(this, this, mClient, mActivity, mServoArgs, uri, width, height, showLogs);
}); });
} }
public interface Client {
void onLoadStarted();
void onLoadEnded();
void onTitleChanged(String title);
void onUrlChanged(String url);
void onHistoryChanged(boolean canGoBack, boolean canGoForward);
}
public void setClient(Client client) { public void setClient(Client client) {
mClient = client; mClient = client;
} }
// Scroll and click
private GestureDetector mGestureDetector;
private OverScroller mScroller;
private int mLastX = 0;
private int mCurX = 0;
private int mLastY = 0;
private int mCurY = 0;
private boolean mFlinging;
private void initGestures(Context context) { private void initGestures(Context context) {
mGestureDetector = new GestureDetector(context, this); mGestureDetector = new GestureDetector(context, this);
mScroller = new OverScroller(context); mScroller = new OverScroller(context);
} }
@Override
public void doFrame(long frameTimeNanos) { public void doFrame(long frameTimeNanos) {
if (mScroller.isFinished() && mFlinging) { if (mScroller.isFinished() && mFlinging) {
@ -229,7 +183,7 @@ public class ServoView extends GLSurfaceView implements GestureDetector.OnGestur
mLastX = mCurX; mLastX = mCurX;
mCurY = velocityY < 0 ? mPageHeight : 0; mCurY = velocityY < 0 ? mPageHeight : 0;
mLastY = mCurY; mLastY = mCurY;
mScroller.fling(mCurX, mCurY, (int)velocityX, (int)velocityY, 0, mPageWidth, 0, mPageHeight); mScroller.fling(mCurX, mCurY, (int) velocityX, (int) velocityY, 0, mPageWidth, 0, mPageHeight);
return true; return true;
} }
@ -242,19 +196,19 @@ public class ServoView extends GLSurfaceView implements GestureDetector.OnGestur
mGestureDetector.onTouchEvent(e); mGestureDetector.onTouchEvent(e);
int action = e.getActionMasked(); int action = e.getActionMasked();
switch(action) { switch (action) {
case (MotionEvent.ACTION_DOWN): case (MotionEvent.ACTION_DOWN):
mCurX = (int)e.getX(); mCurX = (int) e.getX();
mLastX = mCurX; mLastX = mCurX;
mCurY = (int)e.getY(); mCurY = (int) e.getY();
mLastY = mCurY; mLastY = mCurY;
mScroller.forceFinished(true); mScroller.forceFinished(true);
queueEvent(() -> mServo.scrollStart(0, 0, mCurX, mCurY)); queueEvent(() -> mServo.scrollStart(0, 0, mCurX, mCurY));
Choreographer.getInstance().postFrameCallback(this); Choreographer.getInstance().postFrameCallback(this);
return true; return true;
case (MotionEvent.ACTION_MOVE): case (MotionEvent.ACTION_MOVE):
mCurX = (int)e.getX(); mCurX = (int) e.getX();
mCurY = (int)e.getY(); mCurY = (int) e.getY();
return true; return true;
case (MotionEvent.ACTION_UP): case (MotionEvent.ACTION_UP):
case (MotionEvent.ACTION_CANCEL): case (MotionEvent.ACTION_CANCEL):
@ -269,12 +223,38 @@ public class ServoView extends GLSurfaceView implements GestureDetector.OnGestur
} }
public boolean onSingleTapUp(MotionEvent e) { public boolean onSingleTapUp(MotionEvent e) {
queueEvent(() -> mServo.click((int)e.getX(), (int)e.getY())); queueEvent(() -> mServo.click((int) e.getX(), (int) e.getY()));
return false; return false;
} }
public void onLongPress(MotionEvent e) { } public void onLongPress(MotionEvent e) {
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { return true; } }
public void onShowPress(MotionEvent e) { }
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
return true;
}
public void onShowPress(MotionEvent e) {
}
static class ServoGLRenderer implements Renderer {
private final ServoView mView;
ServoGLRenderer(ServoView view) {
mView = view;
}
public void onSurfaceCreated(GL10 unused, EGLConfig config) {
mView.onGLReady();
}
public void onDrawFrame(GL10 unused) {
}
public void onSurfaceChanged(GL10 unused, int width, int height) {
GLES31.glViewport(0, 0, width, height);
mView.onSurfaceResized(width, height);
}
}
} }