Compare commits
6 Commits
issue5542.
...
linux_maco
Author | SHA1 | Date | |
---|---|---|---|
e3ecc6a59f | |||
6a2f35e7dc | |||
4f74bf7f2b | |||
5c7d0717a2 | |||
f7ff391afd | |||
b901cb1c40 |
29
.github/workflows/release.yml
vendored
29
.github/workflows/release.yml
vendored
@ -85,15 +85,6 @@ jobs:
|
|||||||
build-linux-x86_64:
|
build-linux-x86_64:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Check architecture
|
|
||||||
run: |
|
|
||||||
arch=$(uname -m)
|
|
||||||
if [[ "$arch" != x86_64 ]]
|
|
||||||
then
|
|
||||||
echo "Unexpected architecture: $arch" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
@ -190,15 +181,6 @@ jobs:
|
|||||||
build-macos-aarch64:
|
build-macos-aarch64:
|
||||||
runs-on: macos-latest
|
runs-on: macos-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Check architecture
|
|
||||||
run: |
|
|
||||||
arch=$(uname -m)
|
|
||||||
if [[ "$arch" != arm64 ]]
|
|
||||||
then
|
|
||||||
echo "Unexpected architecture: $arch" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
@ -227,15 +209,6 @@ jobs:
|
|||||||
build-macos-x86_64:
|
build-macos-x86_64:
|
||||||
runs-on: macos-13
|
runs-on: macos-13
|
||||||
steps:
|
steps:
|
||||||
- name: Check architecture
|
|
||||||
run: |
|
|
||||||
arch=$(uname -m)
|
|
||||||
if [[ "$arch" != x86_64 ]]
|
|
||||||
then
|
|
||||||
echo "Unexpected architecture: $arch" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
@ -486,7 +459,7 @@ jobs:
|
|||||||
- name: Download release-macos-x86_64
|
- name: Download release-macos-x86_64
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: release-macos-x86_64
|
name: release-macos-aarch64
|
||||||
path: release/output/
|
path: release/output/
|
||||||
|
|
||||||
- name: Package server
|
- name: Package server
|
||||||
|
@ -23,20 +23,14 @@ To control the device without mirroring:
|
|||||||
scrcpy --no-video --no-audio
|
scrcpy --no-video --no-audio
|
||||||
```
|
```
|
||||||
|
|
||||||
By default, the mouse is disabled when video playback is turned off.
|
By default, mouse mode is switched to UHID if video mirroring is disabled (a
|
||||||
|
relative mouse mode is required).
|
||||||
To control the device using a relative mouse, enable UHID mouse mode:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
scrcpy --no-video --no-audio --mouse=uhid
|
|
||||||
scrcpy --no-video --no-audio -M # short version
|
|
||||||
```
|
|
||||||
|
|
||||||
To also use a UHID keyboard, set it explicitly:
|
To also use a UHID keyboard, set it explicitly:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
scrcpy --no-video --no-audio --mouse=uhid --keyboard=uhid
|
scrcpy --no-video --no-audio --keyboard=uhid
|
||||||
scrcpy --no-video --no-audio -MK # short version
|
scrcpy --no-video --no-audio -K # short version
|
||||||
```
|
```
|
||||||
|
|
||||||
To use AOA instead (over USB only):
|
To use AOA instead (over USB only):
|
||||||
|
@ -4,13 +4,13 @@ cd "$(dirname ${BASH_SOURCE[0]})"
|
|||||||
. build_common
|
. build_common
|
||||||
cd .. # root project dir
|
cd .. # root project dir
|
||||||
|
|
||||||
|
ARCH="$1"
|
||||||
if [[ $# != 1 ]]
|
if [[ $# != 1 ]]
|
||||||
then
|
then
|
||||||
echo "Syntax: $0 <arch>" >&2
|
echo "Syntax: $0 <arch>" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
ARCH="$1"
|
|
||||||
LINUX_BUILD_DIR="$WORK_DIR/build-linux-$ARCH"
|
LINUX_BUILD_DIR="$WORK_DIR/build-linux-$ARCH"
|
||||||
|
|
||||||
app/deps/adb_linux.sh
|
app/deps/adb_linux.sh
|
||||||
|
@ -4,13 +4,13 @@ cd "$(dirname ${BASH_SOURCE[0]})"
|
|||||||
. build_common
|
. build_common
|
||||||
cd .. # root project dir
|
cd .. # root project dir
|
||||||
|
|
||||||
|
ARCH="$1"
|
||||||
if [[ $# != 1 ]]
|
if [[ $# != 1 ]]
|
||||||
then
|
then
|
||||||
echo "Syntax: $0 <arch>" >&2
|
echo "Syntax: $0 <arch>" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
ARCH="$1"
|
|
||||||
MACOS_BUILD_DIR="$WORK_DIR/build-macos-$ARCH"
|
MACOS_BUILD_DIR="$WORK_DIR/build-macos-$ARCH"
|
||||||
|
|
||||||
app/deps/adb_macos.sh
|
app/deps/adb_macos.sh
|
||||||
|
@ -207,15 +207,13 @@ public final class CleanUp {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Change the power of the main display when mirroring a virtual display
|
if (displayId != Device.DISPLAY_ID_NONE && Device.isScreenOn(displayId)) {
|
||||||
int targetDisplayId = displayId != Device.DISPLAY_ID_NONE ? displayId : 0;
|
|
||||||
if (Device.isScreenOn(targetDisplayId)) {
|
|
||||||
if (powerOffScreen) {
|
if (powerOffScreen) {
|
||||||
Ln.i("Power off screen");
|
Ln.i("Power off screen");
|
||||||
Device.powerOffScreen(targetDisplayId);
|
Device.powerOffScreen(displayId);
|
||||||
} else if (restoreDisplayPower) {
|
} else if (restoreDisplayPower) {
|
||||||
Ln.i("Restoring display power");
|
Ln.i("Restoring display power");
|
||||||
Device.setDisplayPower(targetDisplayId, true);
|
Device.setDisplayPower(displayId, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,7 +21,6 @@ import android.content.IOnPrimaryClipChangedListener;
|
|||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.SystemClock;
|
import android.os.SystemClock;
|
||||||
import android.util.Pair;
|
|
||||||
import android.view.InputDevice;
|
import android.view.InputDevice;
|
||||||
import android.view.KeyCharacterMap;
|
import android.view.KeyCharacterMap;
|
||||||
import android.view.KeyEvent;
|
import android.view.KeyEvent;
|
||||||
@ -282,7 +281,7 @@ public class Controller implements AsyncProcessor, VirtualDisplayListener {
|
|||||||
setClipboard(msg.getText(), msg.getPaste(), msg.getSequence());
|
setClipboard(msg.getText(), msg.getPaste(), msg.getSequence());
|
||||||
break;
|
break;
|
||||||
case ControlMessage.TYPE_SET_DISPLAY_POWER:
|
case ControlMessage.TYPE_SET_DISPLAY_POWER:
|
||||||
if (supportsInputEvents) {
|
if (supportsInputEvents && displayId != Device.DISPLAY_ID_NONE) {
|
||||||
setDisplayPower(msg.getOn());
|
setDisplayPower(msg.getOn());
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -351,47 +350,24 @@ public class Controller implements AsyncProcessor, VirtualDisplayListener {
|
|||||||
return successCount;
|
return successCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Pair<Point, Integer> getEventPointAndDisplayId(Position position) {
|
private boolean injectTouch(int action, long pointerId, Position position, float pressure, int actionButton, int buttons) {
|
||||||
|
long now = SystemClock.uptimeMillis();
|
||||||
|
|
||||||
// it hides the field on purpose, to read it with atomic access
|
// it hides the field on purpose, to read it with atomic access
|
||||||
@SuppressWarnings("checkstyle:HiddenField")
|
@SuppressWarnings("checkstyle:HiddenField")
|
||||||
DisplayData displayData = this.displayData.get();
|
DisplayData displayData = this.displayData.get();
|
||||||
// In scrcpy, displayData should never be null (a touch event can only be generated from the client when a video frame is present).
|
assert displayData != null : "Cannot receive a touch event without a display";
|
||||||
// However, it is possible to send events without video playback when using scrcpy-server alone (except for virtual displays).
|
|
||||||
assert displayData != null || displayId != Device.DISPLAY_ID_NONE : "Cannot receive a touch event without a display";
|
|
||||||
|
|
||||||
Point point;
|
Point point = displayData.positionMapper.map(position);
|
||||||
int targetDisplayId;
|
|
||||||
if (displayData != null) {
|
|
||||||
point = displayData.positionMapper.map(position);
|
|
||||||
if (point == null) {
|
if (point == null) {
|
||||||
if (Ln.isEnabled(Ln.Level.VERBOSE)) {
|
if (Ln.isEnabled(Ln.Level.VERBOSE)) {
|
||||||
Size eventSize = position.getScreenSize();
|
Size eventSize = position.getScreenSize();
|
||||||
Size currentSize = displayData.positionMapper.getVideoSize();
|
Size currentSize = displayData.positionMapper.getVideoSize();
|
||||||
Ln.v("Ignore event generated for size " + eventSize + " (current size is " + currentSize + ")");
|
Ln.v("Ignore touch event generated for size " + eventSize + " (current size is " + currentSize + ")");
|
||||||
}
|
}
|
||||||
return null;
|
|
||||||
}
|
|
||||||
targetDisplayId = displayData.virtualDisplayId;
|
|
||||||
} else {
|
|
||||||
// No display, use the raw coordinates
|
|
||||||
point = position.getPoint();
|
|
||||||
targetDisplayId = displayId;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Pair.create(point, targetDisplayId);
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean injectTouch(int action, long pointerId, Position position, float pressure, int actionButton, int buttons) {
|
|
||||||
long now = SystemClock.uptimeMillis();
|
|
||||||
|
|
||||||
Pair<Point, Integer> pair = getEventPointAndDisplayId(position);
|
|
||||||
if (pair == null) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Point point = pair.first;
|
|
||||||
int targetDisplayId = pair.second;
|
|
||||||
|
|
||||||
int pointerIndex = pointersState.getPointerIndex(pointerId);
|
int pointerIndex = pointersState.getPointerIndex(pointerId);
|
||||||
if (pointerIndex == -1) {
|
if (pointerIndex == -1) {
|
||||||
Ln.w("Too many pointers for touch event");
|
Ln.w("Too many pointers for touch event");
|
||||||
@ -445,7 +421,7 @@ public class Controller implements AsyncProcessor, VirtualDisplayListener {
|
|||||||
// First button pressed: ACTION_DOWN
|
// First button pressed: ACTION_DOWN
|
||||||
MotionEvent downEvent = MotionEvent.obtain(lastTouchDown, now, MotionEvent.ACTION_DOWN, pointerCount, pointerProperties,
|
MotionEvent downEvent = MotionEvent.obtain(lastTouchDown, now, MotionEvent.ACTION_DOWN, pointerCount, pointerProperties,
|
||||||
pointerCoords, 0, buttons, 1f, 1f, DEFAULT_DEVICE_ID, 0, source, 0);
|
pointerCoords, 0, buttons, 1f, 1f, DEFAULT_DEVICE_ID, 0, source, 0);
|
||||||
if (!Device.injectEvent(downEvent, targetDisplayId, Device.INJECT_MODE_ASYNC)) {
|
if (!Device.injectEvent(downEvent, displayData.virtualDisplayId, Device.INJECT_MODE_ASYNC)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -456,7 +432,7 @@ public class Controller implements AsyncProcessor, VirtualDisplayListener {
|
|||||||
if (!InputManager.setActionButton(pressEvent, actionButton)) {
|
if (!InputManager.setActionButton(pressEvent, actionButton)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!Device.injectEvent(pressEvent, targetDisplayId, Device.INJECT_MODE_ASYNC)) {
|
if (!Device.injectEvent(pressEvent, displayData.virtualDisplayId, Device.INJECT_MODE_ASYNC)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -470,7 +446,7 @@ public class Controller implements AsyncProcessor, VirtualDisplayListener {
|
|||||||
if (!InputManager.setActionButton(releaseEvent, actionButton)) {
|
if (!InputManager.setActionButton(releaseEvent, actionButton)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!Device.injectEvent(releaseEvent, targetDisplayId, Device.INJECT_MODE_ASYNC)) {
|
if (!Device.injectEvent(releaseEvent, displayData.virtualDisplayId, Device.INJECT_MODE_ASYNC)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -478,7 +454,7 @@ public class Controller implements AsyncProcessor, VirtualDisplayListener {
|
|||||||
// Last button released: ACTION_UP
|
// Last button released: ACTION_UP
|
||||||
MotionEvent upEvent = MotionEvent.obtain(lastTouchDown, now, MotionEvent.ACTION_UP, pointerCount, pointerProperties,
|
MotionEvent upEvent = MotionEvent.obtain(lastTouchDown, now, MotionEvent.ACTION_UP, pointerCount, pointerProperties,
|
||||||
pointerCoords, 0, buttons, 1f, 1f, DEFAULT_DEVICE_ID, 0, source, 0);
|
pointerCoords, 0, buttons, 1f, 1f, DEFAULT_DEVICE_ID, 0, source, 0);
|
||||||
if (!Device.injectEvent(upEvent, targetDisplayId, Device.INJECT_MODE_ASYNC)) {
|
if (!Device.injectEvent(upEvent, displayData.virtualDisplayId, Device.INJECT_MODE_ASYNC)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -489,20 +465,27 @@ public class Controller implements AsyncProcessor, VirtualDisplayListener {
|
|||||||
|
|
||||||
MotionEvent event = MotionEvent.obtain(lastTouchDown, now, action, pointerCount, pointerProperties, pointerCoords, 0, buttons, 1f, 1f,
|
MotionEvent event = MotionEvent.obtain(lastTouchDown, now, action, pointerCount, pointerProperties, pointerCoords, 0, buttons, 1f, 1f,
|
||||||
DEFAULT_DEVICE_ID, 0, source, 0);
|
DEFAULT_DEVICE_ID, 0, source, 0);
|
||||||
return Device.injectEvent(event, targetDisplayId, Device.INJECT_MODE_ASYNC);
|
return Device.injectEvent(event, displayData.virtualDisplayId, Device.INJECT_MODE_ASYNC);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean injectScroll(Position position, float hScroll, float vScroll, int buttons) {
|
private boolean injectScroll(Position position, float hScroll, float vScroll, int buttons) {
|
||||||
long now = SystemClock.uptimeMillis();
|
long now = SystemClock.uptimeMillis();
|
||||||
|
|
||||||
Pair<Point, Integer> pair = getEventPointAndDisplayId(position);
|
// it hides the field on purpose, to read it with atomic access
|
||||||
if (pair == null) {
|
@SuppressWarnings("checkstyle:HiddenField")
|
||||||
|
DisplayData displayData = this.displayData.get();
|
||||||
|
assert displayData != null : "Cannot receive a scroll event without a display";
|
||||||
|
|
||||||
|
Point point = displayData.positionMapper.map(position);
|
||||||
|
if (point == null) {
|
||||||
|
if (Ln.isEnabled(Ln.Level.VERBOSE)) {
|
||||||
|
Size eventSize = position.getScreenSize();
|
||||||
|
Size currentSize = displayData.positionMapper.getVideoSize();
|
||||||
|
Ln.v("Ignore scroll event generated for size " + eventSize + " (current size is " + currentSize + ")");
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Point point = pair.first;
|
|
||||||
int targetDisplayId = pair.second;
|
|
||||||
|
|
||||||
MotionEvent.PointerProperties props = pointerProperties[0];
|
MotionEvent.PointerProperties props = pointerProperties[0];
|
||||||
props.id = 0;
|
props.id = 0;
|
||||||
|
|
||||||
@ -514,7 +497,7 @@ public class Controller implements AsyncProcessor, VirtualDisplayListener {
|
|||||||
|
|
||||||
MotionEvent event = MotionEvent.obtain(lastTouchDown, now, MotionEvent.ACTION_SCROLL, 1, pointerProperties, pointerCoords, 0, buttons, 1f, 1f,
|
MotionEvent event = MotionEvent.obtain(lastTouchDown, now, MotionEvent.ACTION_SCROLL, 1, pointerProperties, pointerCoords, 0, buttons, 1f, 1f,
|
||||||
DEFAULT_DEVICE_ID, 0, InputDevice.SOURCE_MOUSE, 0);
|
DEFAULT_DEVICE_ID, 0, InputDevice.SOURCE_MOUSE, 0);
|
||||||
return Device.injectEvent(event, targetDisplayId, Device.INJECT_MODE_ASYNC);
|
return Device.injectEvent(event, displayData.virtualDisplayId, Device.INJECT_MODE_ASYNC);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -708,12 +691,9 @@ public class Controller implements AsyncProcessor, VirtualDisplayListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void setDisplayPower(boolean on) {
|
private void setDisplayPower(boolean on) {
|
||||||
// Change the power of the main display when mirroring a virtual display
|
boolean setDisplayPowerOk = Device.setDisplayPower(displayId, on);
|
||||||
int targetDisplayId = displayId != Device.DISPLAY_ID_NONE ? displayId : 0;
|
|
||||||
boolean setDisplayPowerOk = Device.setDisplayPower(targetDisplayId, on);
|
|
||||||
if (setDisplayPowerOk) {
|
if (setDisplayPowerOk) {
|
||||||
// Do not keep display power off for virtual displays: MOD+p must wake up the physical device
|
keepDisplayPowerOff = !on;
|
||||||
keepDisplayPowerOff = displayId != Device.DISPLAY_ID_NONE && !on;
|
|
||||||
Ln.i("Device display turned " + (on ? "on" : "off"));
|
Ln.i("Device display turned " + (on ? "on" : "off"));
|
||||||
if (cleanUp != null) {
|
if (cleanUp != null) {
|
||||||
boolean mustRestoreOnExit = !on;
|
boolean mustRestoreOnExit = !on;
|
||||||
|
@ -40,10 +40,6 @@ public final class Device {
|
|||||||
public static final int INJECT_MODE_WAIT_FOR_RESULT = InputManager.INJECT_INPUT_EVENT_MODE_WAIT_FOR_RESULT;
|
public static final int INJECT_MODE_WAIT_FOR_RESULT = InputManager.INJECT_INPUT_EVENT_MODE_WAIT_FOR_RESULT;
|
||||||
public static final int INJECT_MODE_WAIT_FOR_FINISH = InputManager.INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH;
|
public static final int INJECT_MODE_WAIT_FOR_FINISH = InputManager.INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH;
|
||||||
|
|
||||||
// The new display power method introduced in Android 15 does not work as expected:
|
|
||||||
// <https://github.com/Genymobile/scrcpy/issues/5530>
|
|
||||||
private static final boolean USE_ANDROID_15_DISPLAY_POWER = false;
|
|
||||||
|
|
||||||
private Device() {
|
private Device() {
|
||||||
// not instantiable
|
// not instantiable
|
||||||
}
|
}
|
||||||
@ -131,7 +127,7 @@ public final class Device {
|
|||||||
public static boolean setDisplayPower(int displayId, boolean on) {
|
public static boolean setDisplayPower(int displayId, boolean on) {
|
||||||
assert displayId != Device.DISPLAY_ID_NONE;
|
assert displayId != Device.DISPLAY_ID_NONE;
|
||||||
|
|
||||||
if (USE_ANDROID_15_DISPLAY_POWER && Build.VERSION.SDK_INT >= AndroidVersions.API_35_ANDROID_15) {
|
if (Build.VERSION.SDK_INT >= AndroidVersions.API_35_ANDROID_15) {
|
||||||
return ServiceManager.getDisplayManager().requestDisplayPower(displayId, on);
|
return ServiceManager.getDisplayManager().requestDisplayPower(displayId, on);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -192,9 +192,6 @@ public final class DisplayManager {
|
|||||||
if ("onDisplayChanged".equals(method.getName())) {
|
if ("onDisplayChanged".equals(method.getName())) {
|
||||||
listener.onDisplayChanged((int) args[0]);
|
listener.onDisplayChanged((int) args[0]);
|
||||||
}
|
}
|
||||||
if ("toString".equals(method.getName())) {
|
|
||||||
return "DisplayListener";
|
|
||||||
}
|
|
||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
try {
|
try {
|
||||||
|
Reference in New Issue
Block a user