Compare commits
8 Commits
Author | SHA1 | Date | |
---|---|---|---|
c59a3c3169 | |||
2780e0bd7b | |||
6c6607d404 | |||
988174805c | |||
f90dc216d1 | |||
97fa77c76c | |||
baa10ed0a3 | |||
2ed2247e8f |
@ -2,7 +2,7 @@
|
||||
source for the project. Do not download releases from random websites, even if
|
||||
their name contains `scrcpy`.**
|
||||
|
||||
# scrcpy (v3.0.1)
|
||||
# scrcpy (v3.0.2)
|
||||
|
||||
<img src="app/data/icon.svg" width="128" height="128" alt="scrcpy" align="right" />
|
||||
|
||||
|
@ -57,6 +57,7 @@ _scrcpy() {
|
||||
--no-mipmaps
|
||||
--no-mouse-hover
|
||||
--no-power-on
|
||||
--no-vd-destroy-content
|
||||
--no-vd-system-decorations
|
||||
--no-video
|
||||
--no-video-playback
|
||||
|
@ -63,6 +63,7 @@ arguments=(
|
||||
'--no-mipmaps[Disable the generation of mipmaps]'
|
||||
'--no-mouse-hover[Do not forward mouse hover events]'
|
||||
'--no-power-on[Do not power on the device on start]'
|
||||
'--no-vd-destroy-content[Disable virtual display "destroy content on removal" flag]'
|
||||
'--no-vd-system-decorations[Disable virtual display system decorations flag]'
|
||||
'--no-video[Disable video forwarding]'
|
||||
'--no-video-playback[Disable video playback]'
|
||||
|
@ -13,7 +13,7 @@ BEGIN
|
||||
VALUE "LegalCopyright", "Romain Vimont, Genymobile"
|
||||
VALUE "OriginalFilename", "scrcpy.exe"
|
||||
VALUE "ProductName", "scrcpy"
|
||||
VALUE "ProductVersion", "3.0"
|
||||
VALUE "ProductVersion", "3.0.2"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
|
@ -369,6 +369,12 @@ Do not forward mouse hover (mouse motion without any clicks) events.
|
||||
.B \-\-no\-power\-on
|
||||
Do not power on the device on start.
|
||||
|
||||
.TP
|
||||
.B \-\-no\-vd\-destroy\-content
|
||||
Disable virtual display "destroy content on removal" flag.
|
||||
|
||||
With this option, when the virtual display is closed, the running apps are moved to the main display rather than being destroyed.
|
||||
|
||||
.TP
|
||||
.B \-\-no\-vd\-system\-decorations
|
||||
Disable virtual display system decorations flag.
|
||||
|
@ -110,6 +110,7 @@ enum {
|
||||
OPT_CAPTURE_ORIENTATION,
|
||||
OPT_ANGLE,
|
||||
OPT_NO_VD_SYSTEM_DECORATIONS,
|
||||
OPT_NO_VD_DESTROY_CONTENT,
|
||||
};
|
||||
|
||||
struct sc_option {
|
||||
@ -659,6 +660,15 @@ static const struct sc_option options[] = {
|
||||
.longopt = "no-power-on",
|
||||
.text = "Do not power on the device on start.",
|
||||
},
|
||||
{
|
||||
.longopt_id = OPT_NO_VD_DESTROY_CONTENT,
|
||||
.longopt = "no-vd-destroy-content",
|
||||
.text = "Disable virtual display \"destroy content on removal\" "
|
||||
"flag.\n"
|
||||
"With this option, when the virtual display is closed, the "
|
||||
"running apps are moved to the main display rather than being "
|
||||
"destroyed.",
|
||||
},
|
||||
{
|
||||
.longopt_id = OPT_NO_VD_SYSTEM_DECORATIONS,
|
||||
.longopt = "no-vd-system-decorations",
|
||||
@ -2705,8 +2715,11 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
|
||||
case OPT_ANGLE:
|
||||
opts->angle = optarg;
|
||||
break;
|
||||
case OPT_NO_VD_DESTROY_CONTENT:
|
||||
opts->vd_destroy_content = false;
|
||||
break;
|
||||
case OPT_NO_VD_SYSTEM_DECORATIONS:
|
||||
opts->vd_system_decorations = optarg;
|
||||
opts->vd_system_decorations = false;
|
||||
break;
|
||||
default:
|
||||
// getopt prints the error message on stderr
|
||||
|
@ -108,6 +108,7 @@ const struct scrcpy_options scrcpy_options_default = {
|
||||
.new_display = NULL,
|
||||
.start_app = NULL,
|
||||
.angle = NULL,
|
||||
.vd_destroy_content = true,
|
||||
.vd_system_decorations = true,
|
||||
};
|
||||
|
||||
|
@ -310,6 +310,7 @@ struct scrcpy_options {
|
||||
bool audio_dup;
|
||||
const char *new_display; // [<width>x<height>][/<dpi>] parsed by the server
|
||||
const char *start_app;
|
||||
bool vd_destroy_content;
|
||||
bool vd_system_decorations;
|
||||
};
|
||||
|
||||
|
@ -458,6 +458,7 @@ scrcpy(struct scrcpy_options *options) {
|
||||
.power_on = options->power_on,
|
||||
.kill_adb_on_close = options->kill_adb_on_close,
|
||||
.camera_high_speed = options->camera_high_speed,
|
||||
.vd_destroy_content = options->vd_destroy_content,
|
||||
.vd_system_decorations = options->vd_system_decorations,
|
||||
.list = options->list,
|
||||
};
|
||||
|
@ -377,6 +377,9 @@ execute_server(struct sc_server *server,
|
||||
VALIDATE_STRING(params->new_display);
|
||||
ADD_PARAM("new_display=%s", params->new_display);
|
||||
}
|
||||
if (!params->vd_destroy_content) {
|
||||
ADD_PARAM("vd_destroy_content=false");
|
||||
}
|
||||
if (!params->vd_system_decorations) {
|
||||
ADD_PARAM("vd_system_decorations=false");
|
||||
}
|
||||
|
@ -69,6 +69,7 @@ struct sc_server_params {
|
||||
bool power_on;
|
||||
bool kill_adb_on_close;
|
||||
bool camera_high_speed;
|
||||
bool vd_destroy_content;
|
||||
bool vd_system_decorations;
|
||||
uint8_t list;
|
||||
};
|
||||
|
@ -233,10 +233,10 @@ install` must be run as root)._
|
||||
|
||||
#### Option 2: Use prebuilt server
|
||||
|
||||
- [`scrcpy-server-v3.0.1`][direct-scrcpy-server]
|
||||
<sub>SHA-256: `86c4ef31f5acb060a24d5d63d8dd262ef83384e19ae5f9ad78e6408a50743d17`</sub>
|
||||
- [`scrcpy-server-v3.0.2`][direct-scrcpy-server]
|
||||
<sub>SHA-256: `e19fe024bfa3367809494407ad6ca809a6f6e77dac95e99f85ba75144e0ba35d`</sub>
|
||||
|
||||
[direct-scrcpy-server]: https://github.com/Genymobile/scrcpy/releases/download/v3.0.1/scrcpy-server-v3.0.1
|
||||
[direct-scrcpy-server]: https://github.com/Genymobile/scrcpy/releases/download/v3.0.2/scrcpy-server-v3.0.2
|
||||
|
||||
Download the prebuilt server somewhere, and specify its path during the Meson
|
||||
configuration:
|
||||
|
@ -6,11 +6,11 @@
|
||||
|
||||
Download a static build of the [latest release]:
|
||||
|
||||
- [`scrcpy-linux-x86_64-v3.0.1.tar.gz`][direct-linux-x86_64] (x86_64)
|
||||
<sub>SHA-256: `6cb7fb16efbe3afd6db19b1ee31ee9f6e104a4735dc1f41c4a478cabbeac3f77`</sub>
|
||||
- [`scrcpy-linux-x86_64-v3.0.2.tar.gz`][direct-linux-x86_64] (x86_64)
|
||||
<sub>SHA-256: `20b69dcd379bb7d7208bf1e4858cf04162fc856697be0e6c03863d7b3c1e734a`</sub>
|
||||
|
||||
[latest release]: https://github.com/Genymobile/scrcpy/releases/latest
|
||||
[direct-linux-x86_64]: https://github.com/Genymobile/scrcpy/releases/download/v3.0.1/scrcpy-linux-x86_64-v3.0.1.tar.gz
|
||||
[direct-linux-x86_64]: https://github.com/Genymobile/scrcpy/releases/download/v3.0.2/scrcpy-linux-x86_64-v3.0.2.tar.gz
|
||||
|
||||
and extract it.
|
||||
|
||||
|
12
doc/macos.md
12
doc/macos.md
@ -6,15 +6,15 @@
|
||||
|
||||
Download a static build of the [latest release]:
|
||||
|
||||
- [`scrcpy-macos-aarch64-v3.0.1.tar.gz`][direct-macos-aarch64] (aarch64)
|
||||
<sub>SHA-256: `e1af70898b6881b3e714ee0e15a7664bfab5eda3ea27c101163a09a36e1df753`</sub>
|
||||
- [`scrcpy-macos-aarch64-v3.0.2.tar.gz`][direct-macos-aarch64] (aarch64)
|
||||
<sub>SHA-256: `811ba2f4e856146bdd161e24c3490d78efbec2339ca783fac791d041c0aecfb6`</sub>
|
||||
|
||||
- [`scrcpy-macos-x86_64-v3.0.1.tar.gz`][direct-macos-x86_64] (x86_64)
|
||||
<sub>SHA-256: `d6f9fad290e0142a6dfb0a405a8d1bfbe1698bbb146c1c0c33e38da53762e442`</sub>
|
||||
- [`scrcpy-macos-x86_64-v3.0.2.tar.gz`][direct-macos-x86_64] (x86_64)
|
||||
<sub>SHA-256: `8effff54dca3a3e46eaaec242771a13a7f81af2e18670b3d0d8ed6b461bb4f79`</sub>
|
||||
|
||||
[latest release]: https://github.com/Genymobile/scrcpy/releases/latest
|
||||
[direct-macos-aarch64]: https://github.com/Genymobile/scrcpy/releases/download/v3.0.1/scrcpy-macos-aarch64-v3.0.1.tar.gz
|
||||
[direct-macos-x86_64]: https://github.com/Genymobile/scrcpy/releases/download/v3.0.1/scrcpy-macos-x86_64-v3.0.1.tar.gz
|
||||
[direct-macos-aarch64]: https://github.com/Genymobile/scrcpy/releases/download/v3.0.2/scrcpy-macos-aarch64-v3.0.2.tar.gz
|
||||
[direct-macos-x86_64]: https://github.com/Genymobile/scrcpy/releases/download/v3.0.2/scrcpy-macos-x86_64-v3.0.2.tar.gz
|
||||
|
||||
and extract it.
|
||||
|
||||
|
@ -50,3 +50,14 @@ any default launcher UI available in virtual displays.
|
||||
|
||||
Note that if no app is started, no content will be rendered, so no video frame
|
||||
will be produced at all.
|
||||
|
||||
|
||||
## Destroy on close
|
||||
|
||||
By default, when the virtual display is closed, the running apps are destroyed.
|
||||
|
||||
To move them to the main display instead, use:
|
||||
|
||||
```
|
||||
scrcpy --new-display --no-vd-destroy-content
|
||||
```
|
||||
|
@ -6,14 +6,14 @@
|
||||
|
||||
Download the [latest release]:
|
||||
|
||||
- [`scrcpy-win64-v3.0.1.zip`][direct-win64] (64-bit)
|
||||
<sub>SHA-256: `2d2485cead6bb9d80ec337a660a571fc4b3c2e15034ad73c6a2867442206a5f4`</sub>
|
||||
- [`scrcpy-win32-v3.0.1.zip`][direct-win32] (32-bit)
|
||||
<sub>SHA-256: `43296f8bf34dd408c65463d45ca367febe68cec3ae34b78917a8f3ecbf321829`</sub>
|
||||
- [`scrcpy-win64-v3.0.2.zip`][direct-win64] (64-bit)
|
||||
<sub>SHA-256: `f0de59f5d46127c87cd822d39d6665e016b86db4cd048101b262f6adb6766832`</sub>
|
||||
- [`scrcpy-win32-v3.0.2.zip`][direct-win32] (32-bit)
|
||||
<sub>SHA-256: `8db8d4984d642012c55802de71f507f8ff9f68a8cfed456d7a1982d47e065f64`</sub>
|
||||
|
||||
[latest release]: https://github.com/Genymobile/scrcpy/releases/latest
|
||||
[direct-win64]: https://github.com/Genymobile/scrcpy/releases/download/v3.0.1/scrcpy-win64-v3.0.1.zip
|
||||
[direct-win32]: https://github.com/Genymobile/scrcpy/releases/download/v3.0.1/scrcpy-win32-v3.0.1.zip
|
||||
[direct-win64]: https://github.com/Genymobile/scrcpy/releases/download/v3.0.2/scrcpy-win64-v3.0.2.zip
|
||||
[direct-win32]: https://github.com/Genymobile/scrcpy/releases/download/v3.0.2/scrcpy-win32-v3.0.2.zip
|
||||
|
||||
and extract it.
|
||||
|
||||
|
@ -2,8 +2,8 @@
|
||||
set -e
|
||||
|
||||
BUILDDIR=build-auto
|
||||
PREBUILT_SERVER_URL=https://github.com/Genymobile/scrcpy/releases/download/v3.0.1/scrcpy-server-v3.0.1
|
||||
PREBUILT_SERVER_SHA256=86c4ef31f5acb060a24d5d63d8dd262ef83384e19ae5f9ad78e6408a50743d17
|
||||
PREBUILT_SERVER_URL=https://github.com/Genymobile/scrcpy/releases/download/v3.0.2/scrcpy-server-v3.0.2
|
||||
PREBUILT_SERVER_SHA256=e19fe024bfa3367809494407ad6ca809a6f6e77dac95e99f85ba75144e0ba35d
|
||||
|
||||
echo "[scrcpy] Downloading prebuilt server..."
|
||||
wget "$PREBUILT_SERVER_URL" -O scrcpy-server
|
||||
|
@ -1,5 +1,5 @@
|
||||
project('scrcpy', 'c',
|
||||
version: '3.0',
|
||||
version: '3.0.2',
|
||||
meson_version: '>= 0.48',
|
||||
default_options: [
|
||||
'c_std=c11',
|
||||
|
@ -7,8 +7,8 @@ android {
|
||||
applicationId "com.genymobile.scrcpy"
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 35
|
||||
versionCode 30000
|
||||
versionName "3.0"
|
||||
versionCode 30002
|
||||
versionName "3.0.2"
|
||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
buildTypes {
|
||||
|
@ -12,7 +12,7 @@
|
||||
set -e
|
||||
|
||||
SCRCPY_DEBUG=false
|
||||
SCRCPY_VERSION_NAME=3.0
|
||||
SCRCPY_VERSION_NAME=3.0.2
|
||||
|
||||
PLATFORM=${ANDROID_PLATFORM:-35}
|
||||
BUILD_TOOLS=${ANDROID_BUILD_TOOLS:-35.0.0}
|
||||
|
@ -10,6 +10,8 @@ import android.os.BatteryManager;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Handle the cleanup of scrcpy, even if the main process is killed.
|
||||
@ -24,6 +26,7 @@ public final class CleanUp {
|
||||
private boolean pendingRestoreDisplayPower;
|
||||
|
||||
private Thread thread;
|
||||
private boolean interrupted;
|
||||
|
||||
private CleanUp(Options options) {
|
||||
thread = new Thread(() -> runCleanUp(options), "cleanup");
|
||||
@ -34,8 +37,10 @@ public final class CleanUp {
|
||||
return new CleanUp(options);
|
||||
}
|
||||
|
||||
public void interrupt() {
|
||||
thread.interrupt();
|
||||
public synchronized void interrupt() {
|
||||
// Do not use thread.interrupt() because only the wait() call must be interrupted, not Command.exec()
|
||||
interrupted = true;
|
||||
notify();
|
||||
}
|
||||
|
||||
public void join() throws InterruptedException {
|
||||
@ -97,25 +102,29 @@ public final class CleanUp {
|
||||
|
||||
try {
|
||||
run(displayId, restoreStayOn, disableShowTouches, powerOffScreen, restoreScreenOffTimeout);
|
||||
} catch (InterruptedException e) {
|
||||
// ignore
|
||||
} catch (IOException e) {
|
||||
Ln.e("Clean up I/O exception", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void run(int displayId, int restoreStayOn, boolean disableShowTouches, boolean powerOffScreen, int restoreScreenOffTimeout)
|
||||
throws IOException, InterruptedException {
|
||||
String[] cmd = {
|
||||
"app_process",
|
||||
"/",
|
||||
CleanUp.class.getName(),
|
||||
String.valueOf(displayId),
|
||||
String.valueOf(restoreStayOn),
|
||||
String.valueOf(disableShowTouches),
|
||||
String.valueOf(powerOffScreen),
|
||||
String.valueOf(restoreScreenOffTimeout),
|
||||
};
|
||||
throws IOException {
|
||||
|
||||
List<String> cmd = new ArrayList<>();
|
||||
if (new File("/system/bin/setsid").exists()) {
|
||||
cmd.add("/system/bin/setsid");
|
||||
} else if (new File("/system/bin/nohup").exists()) {
|
||||
cmd.add("/system/bin/nohup");
|
||||
}
|
||||
|
||||
cmd.add("app_process");
|
||||
cmd.add("/");
|
||||
cmd.add(CleanUp.class.getName());
|
||||
cmd.add(String.valueOf(displayId));
|
||||
cmd.add(String.valueOf(restoreStayOn));
|
||||
cmd.add(String.valueOf(disableShowTouches));
|
||||
cmd.add(String.valueOf(powerOffScreen));
|
||||
cmd.add(String.valueOf(restoreScreenOffTimeout));
|
||||
|
||||
ProcessBuilder builder = new ProcessBuilder(cmd);
|
||||
builder.environment().put("CLASSPATH", Server.SERVER_PATH);
|
||||
@ -126,8 +135,15 @@ public final class CleanUp {
|
||||
int localPendingChanges;
|
||||
boolean localPendingRestoreDisplayPower;
|
||||
synchronized (this) {
|
||||
while (pendingChanges == 0) {
|
||||
wait();
|
||||
while (!interrupted && pendingChanges == 0) {
|
||||
try {
|
||||
wait();
|
||||
} catch (InterruptedException e) {
|
||||
throw new AssertionError("Clean up thread MUST NOT be interrupted");
|
||||
}
|
||||
}
|
||||
if (interrupted) {
|
||||
break;
|
||||
}
|
||||
localPendingChanges = pendingChanges;
|
||||
localPendingRestoreDisplayPower = pendingRestoreDisplayPower;
|
||||
|
@ -60,6 +60,7 @@ public class Options {
|
||||
private boolean powerOn = true;
|
||||
|
||||
private NewDisplay newDisplay;
|
||||
private boolean vdDestroyContent = true;
|
||||
private boolean vdSystemDecorations = true;
|
||||
|
||||
private Orientation.Lock captureOrientationLock = Orientation.Lock.Unlocked;
|
||||
@ -233,6 +234,10 @@ public class Options {
|
||||
return captureOrientationLock;
|
||||
}
|
||||
|
||||
public boolean getVDDestroyContent() {
|
||||
return vdDestroyContent;
|
||||
}
|
||||
|
||||
public boolean getVDSystemDecorations() {
|
||||
return vdSystemDecorations;
|
||||
}
|
||||
@ -466,6 +471,9 @@ public class Options {
|
||||
case "new_display":
|
||||
options.newDisplay = parseNewDisplay(value);
|
||||
break;
|
||||
case "vd_destroy_content":
|
||||
options.vdDestroyContent = Boolean.parseBoolean(value);
|
||||
break;
|
||||
case "vd_system_decorations":
|
||||
options.vdSystemDecorations = Boolean.parseBoolean(value);
|
||||
break;
|
||||
|
@ -53,6 +53,7 @@ public class NewDisplayCapture extends SurfaceCapture {
|
||||
private final boolean captureOrientationLocked;
|
||||
private final Orientation captureOrientation;
|
||||
private final float angle;
|
||||
private final boolean vdDestroyContent;
|
||||
private final boolean vdSystemDecorations;
|
||||
|
||||
private VirtualDisplay virtualDisplay;
|
||||
@ -73,6 +74,7 @@ public class NewDisplayCapture extends SurfaceCapture {
|
||||
this.captureOrientation = options.getCaptureOrientation();
|
||||
assert captureOrientation != null;
|
||||
this.angle = options.getAngle();
|
||||
this.vdDestroyContent = options.getVDDestroyContent();
|
||||
this.vdSystemDecorations = options.getVDSystemDecorations();
|
||||
}
|
||||
|
||||
@ -167,8 +169,10 @@ public class NewDisplayCapture extends SurfaceCapture {
|
||||
int flags = VIRTUAL_DISPLAY_FLAG_PUBLIC
|
||||
| VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY
|
||||
| VIRTUAL_DISPLAY_FLAG_SUPPORTS_TOUCH
|
||||
| VIRTUAL_DISPLAY_FLAG_ROTATES_WITH_CONTENT
|
||||
| VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL;
|
||||
| VIRTUAL_DISPLAY_FLAG_ROTATES_WITH_CONTENT;
|
||||
if (vdDestroyContent) {
|
||||
flags |= VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL;
|
||||
}
|
||||
if (vdSystemDecorations) {
|
||||
flags |= VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS;
|
||||
}
|
||||
|
@ -124,15 +124,9 @@ public class ScreenCapture extends SurfaceCapture {
|
||||
inputSize = videoSize;
|
||||
}
|
||||
|
||||
int virtualDisplayId;
|
||||
PositionMapper positionMapper;
|
||||
try {
|
||||
virtualDisplay = ServiceManager.getDisplayManager()
|
||||
.createVirtualDisplay("scrcpy", inputSize.getWidth(), inputSize.getHeight(), displayId, surface);
|
||||
virtualDisplayId = virtualDisplay.getDisplay().getDisplayId();
|
||||
|
||||
// The positions are relative to the virtual display, not the original display (so use inputSize, not deviceSize!)
|
||||
positionMapper = PositionMapper.create(videoSize, transform, inputSize);
|
||||
Ln.d("Display: using DisplayManager API");
|
||||
} catch (Exception displayManagerException) {
|
||||
try {
|
||||
@ -140,11 +134,7 @@ public class ScreenCapture extends SurfaceCapture {
|
||||
|
||||
Size deviceSize = displayInfo.getSize();
|
||||
int layerStack = displayInfo.getLayerStack();
|
||||
|
||||
setDisplaySurface(display, surface, deviceSize.toRect(), inputSize.toRect(), layerStack);
|
||||
virtualDisplayId = displayId;
|
||||
|
||||
positionMapper = PositionMapper.create(videoSize, transform, deviceSize);
|
||||
Ln.d("Display: using SurfaceControl API");
|
||||
} catch (Exception surfaceControlException) {
|
||||
Ln.e("Could not create display using DisplayManager", displayManagerException);
|
||||
@ -154,6 +144,18 @@ public class ScreenCapture extends SurfaceCapture {
|
||||
}
|
||||
|
||||
if (vdListener != null) {
|
||||
int virtualDisplayId;
|
||||
PositionMapper positionMapper;
|
||||
if (virtualDisplay == null || displayId == 0) {
|
||||
// Surface control or main display: send all events to the original display, relative to the device size
|
||||
Size deviceSize = displayInfo.getSize();
|
||||
positionMapper = PositionMapper.create(videoSize, transform, deviceSize);
|
||||
virtualDisplayId = displayId;
|
||||
} else {
|
||||
// The positions are relative to the virtual display, not the original display (so use inputSize, not deviceSize!)
|
||||
positionMapper = PositionMapper.create(videoSize, transform, inputSize);
|
||||
virtualDisplayId = virtualDisplay.getDisplay().getDisplayId();
|
||||
}
|
||||
vdListener.onNewVirtualDisplay(virtualDisplayId, positionMapper);
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user