diff --git a/server/src/main/java/com/genymobile/scrcpy/opengl/OpenGLRunner.java b/server/src/main/java/com/genymobile/scrcpy/opengl/OpenGLRunner.java index 86bd1859..06bcd7eb 100644 --- a/server/src/main/java/com/genymobile/scrcpy/opengl/OpenGLRunner.java +++ b/server/src/main/java/com/genymobile/scrcpy/opengl/OpenGLRunner.java @@ -1,6 +1,7 @@ package com.genymobile.scrcpy.opengl; import com.genymobile.scrcpy.device.Size; +import com.genymobile.scrcpy.util.Threads; import android.graphics.SurfaceTexture; import android.opengl.EGL14; @@ -15,6 +16,7 @@ import android.os.Handler; import android.os.HandlerThread; import android.view.Surface; +import java.util.concurrent.Callable; import java.util.concurrent.Semaphore; public final class OpenGLRunner { @@ -80,31 +82,17 @@ public final class OpenGLRunner { public Surface start(Size inputSize, Size outputSize, Surface outputSurface) throws OpenGLException { initOnce(); - // Simulate CompletableFuture, but working for all Android versions - final Semaphore sem = new Semaphore(0); - Throwable[] throwableRef = new Throwable[1]; - // The whole OpenGL execution must be performed on a Handler, so that SurfaceTexture.setOnFrameAvailableListener() works correctly. // See - handler.post(() -> { - try { - run(inputSize, outputSize, outputSurface); - } catch (Throwable throwable) { - throwableRef[0] = throwable; - } finally { - sem.release(); - } - }); - try { - sem.acquire(); - } catch (InterruptedException e) { - // Behave as if this method call was synchronous - Thread.currentThread().interrupt(); - } - - Throwable throwable = throwableRef[0]; - if (throwable != null) { + Threads.executeSynchronouslyOn(handler, new Callable() { + @Override + public Void call() throws Exception { + run(inputSize, outputSize, outputSurface); + return null; + } + }); + } catch (Throwable throwable) { if (throwable instanceof OpenGLException) { throw (OpenGLException) throwable; } diff --git a/server/src/main/java/com/genymobile/scrcpy/util/Threads.java b/server/src/main/java/com/genymobile/scrcpy/util/Threads.java new file mode 100644 index 00000000..c561d1ef --- /dev/null +++ b/server/src/main/java/com/genymobile/scrcpy/util/Threads.java @@ -0,0 +1,43 @@ +package com.genymobile.scrcpy.util; + +import android.os.Handler; + +import java.util.concurrent.Callable; +import java.util.concurrent.Semaphore; + +public final class Threads { + private Threads() { + // not instantiable + } + + public static T executeSynchronouslyOn(Handler handler, Callable callable) throws Throwable { + // Simulate CompletableFuture, but working for all Android versions + final Semaphore sem = new Semaphore(0); + @SuppressWarnings("unchecked") + T[] resultRef = (T[]) new Object[1]; + Throwable[] throwableRef = new Throwable[1]; + + handler.post(() -> { + try { + resultRef[0] = callable.call(); + } catch (Throwable throwable) { + throwableRef[0] = throwable; + } finally { + sem.release(); + } + }); + + try { + sem.acquire(); + } catch (InterruptedException e) { + // Behave as if this method call was synchronous + Thread.currentThread().interrupt(); + } + + if (throwableRef[0] != null) { + throw throwableRef[0]; + } + + return resultRef[0]; + } +}