mirror of
https://github.com/servo/servo.git
synced 2025-08-03 12:40:06 +01:00
Android: proper shutdown mechanism
This commit is contained in:
parent
b19f9d9c5b
commit
549c8c565a
7 changed files with 145 additions and 17 deletions
|
@ -77,6 +77,8 @@ pub trait HostTrait {
|
||||||
/// has events for Servo, or Servo has woken up the embedder event loop via
|
/// has events for Servo, or Servo has woken up the embedder event loop via
|
||||||
/// EventLoopWaker).
|
/// EventLoopWaker).
|
||||||
fn on_animating_changed(&self, animating: bool);
|
fn on_animating_changed(&self, animating: bool);
|
||||||
|
/// Servo finished shutting down.
|
||||||
|
fn on_shutdown_complete(&self);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ServoGlue {
|
pub struct ServoGlue {
|
||||||
|
@ -169,6 +171,12 @@ pub fn init(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn deinit() {
|
||||||
|
SERVO.with(|s| {
|
||||||
|
s.replace(None).unwrap().deinit()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
impl ServoGlue {
|
impl ServoGlue {
|
||||||
fn get_browser_id(&self) -> Result<BrowserId, &'static str> {
|
fn get_browser_id(&self) -> Result<BrowserId, &'static str> {
|
||||||
let browser_id = match self.browser_id {
|
let browser_id = match self.browser_id {
|
||||||
|
@ -177,6 +185,17 @@ impl ServoGlue {
|
||||||
};
|
};
|
||||||
Ok(browser_id)
|
Ok(browser_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Request shutdown. Will call on_shutdown_complete.
|
||||||
|
pub fn request_shutdown(&mut self) -> Result<(), &'static str> {
|
||||||
|
self.process_event(WindowEvent::Quit)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Call after on_shutdown_complete
|
||||||
|
pub fn deinit(self) {
|
||||||
|
self.servo.deinit();
|
||||||
|
}
|
||||||
|
|
||||||
/// This is the Servo heartbeat. This needs to be called
|
/// This is the Servo heartbeat. This needs to be called
|
||||||
/// everytime wakeup is called or when embedder wants Servo
|
/// everytime wakeup is called or when embedder wants Servo
|
||||||
/// to act on its pending events.
|
/// to act on its pending events.
|
||||||
|
@ -404,6 +423,9 @@ impl ServoGlue {
|
||||||
self.events.push(WindowEvent::Quit);
|
self.events.push(WindowEvent::Quit);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
EmbedderMsg::Shutdown => {
|
||||||
|
self.callbacks.host_callbacks.on_shutdown_complete();
|
||||||
|
},
|
||||||
EmbedderMsg::Status(..) |
|
EmbedderMsg::Status(..) |
|
||||||
EmbedderMsg::SelectFiles(..) |
|
EmbedderMsg::SelectFiles(..) |
|
||||||
EmbedderMsg::MoveTo(..) |
|
EmbedderMsg::MoveTo(..) |
|
||||||
|
@ -415,7 +437,6 @@ impl ServoGlue {
|
||||||
EmbedderMsg::SetFullscreenState(..) |
|
EmbedderMsg::SetFullscreenState(..) |
|
||||||
EmbedderMsg::ShowIME(..) |
|
EmbedderMsg::ShowIME(..) |
|
||||||
EmbedderMsg::HideIME |
|
EmbedderMsg::HideIME |
|
||||||
EmbedderMsg::Shutdown |
|
|
||||||
EmbedderMsg::Panic(..) => {},
|
EmbedderMsg::Panic(..) => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,6 +35,7 @@ pub struct CHostCallbacks {
|
||||||
pub on_url_changed: extern fn(url: *const c_char),
|
pub on_url_changed: extern fn(url: *const c_char),
|
||||||
pub on_history_changed: extern fn(can_go_back: bool, can_go_forward: bool),
|
pub on_history_changed: extern fn(can_go_back: bool, can_go_forward: bool),
|
||||||
pub on_animating_changed: extern fn(animating: bool),
|
pub on_animating_changed: extern fn(animating: bool),
|
||||||
|
pub on_shutdown_complete: extern fn(),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Servo options
|
/// Servo options
|
||||||
|
@ -108,6 +109,18 @@ pub extern "C" fn init_with_gl(
|
||||||
init(opts, gl, wakeup, readfile, callbacks)
|
init(opts, gl, wakeup, readfile, callbacks)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn deinit() {
|
||||||
|
debug!("deinit");
|
||||||
|
api::deinit();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn request_shutdown() {
|
||||||
|
debug!("request_shutdown");
|
||||||
|
call(|s| s.request_shutdown());
|
||||||
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn set_batch_mode(batch: bool) {
|
pub extern "C" fn set_batch_mode(batch: bool) {
|
||||||
debug!("set_batch_mode");
|
debug!("set_batch_mode");
|
||||||
|
@ -296,4 +309,9 @@ impl HostTrait for HostCallbacks {
|
||||||
debug!("on_animating_changed");
|
debug!("on_animating_changed");
|
||||||
(self.0.on_animating_changed)(animating);
|
(self.0.on_animating_changed)(animating);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn on_shutdown_complete(&self) {
|
||||||
|
debug!("on_shutdown_complete");
|
||||||
|
(self.0.on_shutdown_complete)();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,6 +70,8 @@ pub fn Java_org_mozilla_servoview_JNIServo_init(
|
||||||
"script::dom::bindings::error",
|
"script::dom::bindings::error",
|
||||||
// Show GL errors by default.
|
// Show GL errors by default.
|
||||||
"canvas::webgl_thread",
|
"canvas::webgl_thread",
|
||||||
|
"compositing::compositor",
|
||||||
|
"constellation::constellation",
|
||||||
];
|
];
|
||||||
let mut filter = Filter::default().with_min_level(Level::Debug);
|
let mut filter = Filter::default().with_min_level(Level::Debug);
|
||||||
for &module in &filters {
|
for &module in &filters {
|
||||||
|
@ -117,6 +119,18 @@ pub fn Java_org_mozilla_servoview_JNIServo_setBatchMode(
|
||||||
call(&env, |s| s.set_batch_mode(batch == JNI_TRUE));
|
call(&env, |s| s.set_batch_mode(batch == JNI_TRUE));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub fn Java_org_mozilla_servoview_JNIServo_requestShutdown(env: JNIEnv, _class: JClass) {
|
||||||
|
debug!("requestShutdown");
|
||||||
|
call(&env, |s| s.request_shutdown());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub fn Java_org_mozilla_servoview_JNIServo_deinit(_env: JNIEnv, _class: JClass) {
|
||||||
|
debug!("deinit");
|
||||||
|
api::deinit();
|
||||||
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub fn Java_org_mozilla_servoview_JNIServo_resize(
|
pub fn Java_org_mozilla_servoview_JNIServo_resize(
|
||||||
env: JNIEnv,
|
env: JNIEnv,
|
||||||
|
@ -357,6 +371,13 @@ impl HostTrait for HostCallbacks {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn on_shutdown_complete(&self) {
|
||||||
|
debug!("on_shutdown_complete");
|
||||||
|
let env = self.jvm.get_env().unwrap();
|
||||||
|
env.call_method(self.callbacks.as_obj(), "onShutdownComplete", "()V", &[])
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
fn on_title_changed(&self, title: String) {
|
fn on_title_changed(&self, title: String) {
|
||||||
debug!("on_title_changed");
|
debug!("on_title_changed");
|
||||||
let env = self.jvm.get_env().unwrap();
|
let env = self.jvm.get_env().unwrap();
|
||||||
|
|
|
@ -22,6 +22,10 @@ public class JNIServo {
|
||||||
|
|
||||||
public native void init(Activity activity, ServoOptions options, Callbacks callbacks);
|
public native void init(Activity activity, ServoOptions options, Callbacks callbacks);
|
||||||
|
|
||||||
|
public native void deinit();
|
||||||
|
|
||||||
|
public native void requestShutdown();
|
||||||
|
|
||||||
public native void setBatchMode(boolean mode);
|
public native void setBatchMode(boolean mode);
|
||||||
|
|
||||||
public native void performUpdates();
|
public native void performUpdates();
|
||||||
|
@ -84,6 +88,8 @@ public class JNIServo {
|
||||||
|
|
||||||
void onHistoryChanged(boolean canGoBack, boolean canGoForward);
|
void onHistoryChanged(boolean canGoBack, boolean canGoForward);
|
||||||
|
|
||||||
|
void onShutdownComplete();
|
||||||
|
|
||||||
byte[] readfile(String file);
|
byte[] readfile(String file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,6 +47,14 @@ public class Servo {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void requestShutdown() {
|
||||||
|
mRunCallback.inGLThread(() -> mJNI.requestShutdown());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void deinit() {
|
||||||
|
mRunCallback.inGLThread(() -> mJNI.deinit());
|
||||||
|
}
|
||||||
|
|
||||||
public String version() {
|
public String version() {
|
||||||
return mJNI.version();
|
return mJNI.version();
|
||||||
}
|
}
|
||||||
|
@ -137,6 +145,8 @@ public class Servo {
|
||||||
void inGLThread(Runnable f);
|
void inGLThread(Runnable f);
|
||||||
|
|
||||||
void inUIThread(Runnable f);
|
void inUIThread(Runnable f);
|
||||||
|
|
||||||
|
void finalizeShutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface GfxCallbacks {
|
public interface GfxCallbacks {
|
||||||
|
@ -173,6 +183,10 @@ public class Servo {
|
||||||
mGfxCb.makeCurrent();
|
mGfxCb.makeCurrent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void onShutdownComplete() {
|
||||||
|
mRunCallback.finalizeShutdown();
|
||||||
|
}
|
||||||
|
|
||||||
public void onAnimatingChanged(boolean animating) {
|
public void onAnimatingChanged(boolean animating) {
|
||||||
mRunCallback.inGLThread(() -> mGfxCb.animationStateChanged(animating));
|
mRunCallback.inGLThread(() -> mGfxCb.animationStateChanged(animating));
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,6 @@
|
||||||
|
|
||||||
package org.mozilla.servoview;
|
package org.mozilla.servoview;
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.opengl.EGL14;
|
import android.opengl.EGL14;
|
||||||
|
@ -16,7 +15,6 @@ import android.opengl.EGLSurface;
|
||||||
import android.opengl.GLUtils;
|
import android.opengl.GLUtils;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
import android.os.Message;
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.Surface;
|
import android.view.Surface;
|
||||||
|
|
||||||
|
@ -26,6 +24,8 @@ import org.mozilla.servoview.Servo.GfxCallbacks;
|
||||||
import org.mozilla.servoview.Servo.RunCallback;
|
import org.mozilla.servoview.Servo.RunCallback;
|
||||||
|
|
||||||
import static android.opengl.EGL14.EGL_CONTEXT_CLIENT_VERSION;
|
import static android.opengl.EGL14.EGL_CONTEXT_CLIENT_VERSION;
|
||||||
|
import static android.opengl.EGL14.EGL_NO_CONTEXT;
|
||||||
|
import static android.opengl.EGL14.EGL_NO_SURFACE;
|
||||||
import static android.opengl.EGL14.EGL_OPENGL_ES2_BIT;
|
import static android.opengl.EGL14.EGL_OPENGL_ES2_BIT;
|
||||||
|
|
||||||
public class ServoSurface {
|
public class ServoSurface {
|
||||||
|
@ -68,6 +68,17 @@ public class ServoSurface {
|
||||||
mGLThread.start();
|
mGLThread.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void shutdown() {
|
||||||
|
Log.d(LOGTAG, "shutdown");
|
||||||
|
mServo.requestShutdown();
|
||||||
|
try {
|
||||||
|
Log.d(LOGTAG, "Waiting for GL thread to shutdown");
|
||||||
|
mGLThread.join();
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void reload() {
|
public void reload() {
|
||||||
mServo.reload();
|
mServo.reload();
|
||||||
}
|
}
|
||||||
|
@ -121,12 +132,20 @@ public class ServoSurface {
|
||||||
private EGLDisplay mEglDisplay;
|
private EGLDisplay mEglDisplay;
|
||||||
private EGLContext mEglContext;
|
private EGLContext mEglContext;
|
||||||
private EGLSurface mEglSurface;
|
private EGLSurface mEglSurface;
|
||||||
|
|
||||||
|
void throwGLError(String function) {
|
||||||
|
throwGLError(function, EGL14.eglGetError());
|
||||||
|
}
|
||||||
|
|
||||||
|
void throwGLError(String function, int error) {
|
||||||
|
throw new RuntimeException("Error: " + function + "() Failed " + GLUtils.getEGLErrorString(error));
|
||||||
|
}
|
||||||
|
|
||||||
GLSurface(Surface surface) {
|
GLSurface(Surface surface) {
|
||||||
mEglDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
|
mEglDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
|
||||||
int[] version = new int[2];
|
int[] version = new int[2];
|
||||||
if (!EGL14.eglInitialize(mEglDisplay, version, 0, version, 1)) {
|
if (!EGL14.eglInitialize(mEglDisplay, version, 0, version, 1)) {
|
||||||
throw new RuntimeException("Error: eglInitialize() Failed " + GLUtils.getEGLErrorString(EGL14.eglGetError()));
|
throwGLError("eglInitialize");
|
||||||
}
|
}
|
||||||
mEGLConfigs = new EGLConfig[1];
|
mEGLConfigs = new EGLConfig[1];
|
||||||
int[] configsCount = new int[1];
|
int[] configsCount = new int[1];
|
||||||
|
@ -141,7 +160,7 @@ public class ServoSurface {
|
||||||
EGL14.EGL_NONE
|
EGL14.EGL_NONE
|
||||||
};
|
};
|
||||||
if ((!EGL14.eglChooseConfig(mEglDisplay, configSpec, 0, mEGLConfigs, 0, 1, configsCount, 0)) || (configsCount[0] == 0)) {
|
if ((!EGL14.eglChooseConfig(mEglDisplay, configSpec, 0, mEGLConfigs, 0, 1, configsCount, 0)) || (configsCount[0] == 0)) {
|
||||||
throw new IllegalArgumentException("Error: eglChooseConfig() Failed " + GLUtils.getEGLErrorString(EGL14.eglGetError()));
|
throwGLError("eglChooseConfig");
|
||||||
}
|
}
|
||||||
if (mEGLConfigs[0] == null) {
|
if (mEGLConfigs[0] == null) {
|
||||||
throw new RuntimeException("Error: eglConfig() not Initialized");
|
throw new RuntimeException("Error: eglConfig() not Initialized");
|
||||||
|
@ -150,7 +169,7 @@ public class ServoSurface {
|
||||||
mEglContext = EGL14.eglCreateContext(mEglDisplay, mEGLConfigs[0], EGL14.EGL_NO_CONTEXT, attrib_list, 0);
|
mEglContext = EGL14.eglCreateContext(mEglDisplay, mEGLConfigs[0], EGL14.EGL_NO_CONTEXT, attrib_list, 0);
|
||||||
int glError = EGL14.eglGetError();
|
int glError = EGL14.eglGetError();
|
||||||
if (glError != EGL14.EGL_SUCCESS) {
|
if (glError != EGL14.EGL_SUCCESS) {
|
||||||
throw new RuntimeException("Error: eglCreateContext() Failed " + GLUtils.getEGLErrorString(glError));
|
throwGLError("eglCreateContext", glError);
|
||||||
}
|
}
|
||||||
mEglSurface = EGL14.eglCreateWindowSurface(mEglDisplay, mEGLConfigs[0], surface, new int[]{EGL14.EGL_NONE}, 0);
|
mEglSurface = EGL14.eglCreateWindowSurface(mEglDisplay, mEGLConfigs[0], surface, new int[]{EGL14.EGL_NONE}, 0);
|
||||||
if (mEglSurface == null || mEglSurface == EGL14.EGL_NO_SURFACE) {
|
if (mEglSurface == null || mEglSurface == EGL14.EGL_NO_SURFACE) {
|
||||||
|
@ -159,7 +178,7 @@ public class ServoSurface {
|
||||||
Log.e(LOGTAG, "Error: createWindowSurface() Returned EGL_BAD_NATIVE_WINDOW.");
|
Log.e(LOGTAG, "Error: createWindowSurface() Returned EGL_BAD_NATIVE_WINDOW.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
throw new RuntimeException("Error: createWindowSurface() Failed " + GLUtils.getEGLErrorString(glError));
|
throwGLError("createWindowSurface", glError);
|
||||||
}
|
}
|
||||||
|
|
||||||
makeCurrent();
|
makeCurrent();
|
||||||
|
@ -168,7 +187,7 @@ public class ServoSurface {
|
||||||
|
|
||||||
public void makeCurrent() {
|
public void makeCurrent() {
|
||||||
if (!EGL14.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
|
if (!EGL14.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
|
||||||
throw new RuntimeException("Error: eglMakeCurrent() Failed " + GLUtils.getEGLErrorString(EGL14.eglGetError()));
|
throwGLError("eglMakeCurrent");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -180,9 +199,26 @@ public class ServoSurface {
|
||||||
// FIXME
|
// FIXME
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void destroy() {
|
||||||
|
Log.d(LOGTAG, "Destroying surface");
|
||||||
|
if (!EGL14.eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)) {
|
||||||
|
throwGLError("eglMakeCurrent");
|
||||||
|
}
|
||||||
|
if (!EGL14.eglDestroyContext(mEglDisplay, mEglContext)) {
|
||||||
|
throwGLError("eglDestroyContext");
|
||||||
|
}
|
||||||
|
if (!EGL14.eglDestroySurface(mEglDisplay, mEglSurface)) {
|
||||||
|
throwGLError("eglDestroySurface");
|
||||||
|
}
|
||||||
|
if (!EGL14.eglTerminate(mEglDisplay)) {
|
||||||
|
throwGLError("eglTerminate");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class GLThread extends Thread implements RunCallback {
|
class GLThread extends Thread implements RunCallback {
|
||||||
|
private GLSurface mSurface;
|
||||||
|
|
||||||
public void inGLThread(Runnable r) {
|
public void inGLThread(Runnable r) {
|
||||||
mGLLooperHandler.post(r);
|
mGLLooperHandler.post(r);
|
||||||
|
@ -192,17 +228,19 @@ public class ServoSurface {
|
||||||
mMainLooperHandler.post(r);
|
mMainLooperHandler.post(r);
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: HandlerLeak
|
public void finalizeShutdown() {
|
||||||
@SuppressLint("HandlerLeak")
|
Log.d(LOGTAG, "finalizeShutdown");
|
||||||
|
mServo.deinit();
|
||||||
|
mSurface.destroy();
|
||||||
|
mGLLooperHandler.getLooper().quitSafely();
|
||||||
|
}
|
||||||
|
|
||||||
public void run() {
|
public void run() {
|
||||||
Looper.prepare();
|
Looper.prepare();
|
||||||
|
|
||||||
GLSurface surface = new GLSurface(mASurface);
|
mSurface = new GLSurface(mASurface);
|
||||||
|
|
||||||
mGLLooperHandler = new Handler() {
|
mGLLooperHandler = new Handler();
|
||||||
public void handleMessage(Message msg) {
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
inUIThread(() -> {
|
inUIThread(() -> {
|
||||||
ServoOptions options = new ServoOptions();
|
ServoOptions options = new ServoOptions();
|
||||||
|
@ -210,12 +248,12 @@ public class ServoSurface {
|
||||||
options.width = mWidth;
|
options.width = mWidth;
|
||||||
options.height = mHeight;
|
options.height = mHeight;
|
||||||
options.density = 1;
|
options.density = 1;
|
||||||
options.url = mInitialUri == null ? null : mInitialUri;
|
options.url = mInitialUri;
|
||||||
options.logStr = mServoLog;
|
options.logStr = mServoLog;
|
||||||
options.enableLogs = true;
|
options.enableLogs = true;
|
||||||
options.enableSubpixelTextAntialiasing = false;
|
options.enableSubpixelTextAntialiasing = false;
|
||||||
|
|
||||||
mServo = new Servo(options, this, surface, mClient, mActivity);
|
mServo = new Servo(options, this, mSurface, mClient, mActivity);
|
||||||
});
|
});
|
||||||
|
|
||||||
Looper.loop();
|
Looper.loop();
|
||||||
|
|
|
@ -70,6 +70,11 @@ public class ServoView extends GLSurfaceView
|
||||||
init(context);
|
init(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void onDetachedFromWindow() {
|
||||||
|
mServo.requestShutdown();
|
||||||
|
super.onDetachedFromWindow();
|
||||||
|
}
|
||||||
|
|
||||||
private void init(Context context) {
|
private void init(Context context) {
|
||||||
mActivity = (Activity) context;
|
mActivity = (Activity) context;
|
||||||
setFocusable(true);
|
setFocusable(true);
|
||||||
|
@ -136,6 +141,11 @@ public class ServoView extends GLSurfaceView
|
||||||
public void makeCurrent() {
|
public void makeCurrent() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void finalizeShutdown() {
|
||||||
|
// shutdown has been requested and completed.
|
||||||
|
mServo.deinit();
|
||||||
|
}
|
||||||
|
|
||||||
public void inGLThread(Runnable f) {
|
public void inGLThread(Runnable f) {
|
||||||
queueEvent(f);
|
queueEvent(f);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue