Compare commits

..

5 Commits

Author SHA1 Message Date
Romain Vimont
9648b7a387 Handle down/up actions for BACK or POWER ON
Pressing right-click generates either BACK or POWER ON, depending on the
current screen state.

Forward DOWN and UP events separately to the device, to generate key
events accordingly.

Ref #1062 <https://github.com/Genymobile/scrcpy/issues/1062>
2020-01-08 22:48:22 +01:00
Romain Vimont
149702753f Handle down/up actions separately for HOME
Pressing middle-click generated both the DOWN and UP keyevent HOME.

Instead, generate DOWN on button pressed and UP on button released.

Ref #1062 <https://github.com/Genymobile/scrcpy/issues/1062>
2020-01-08 22:13:10 +01:00
Romain Vimont
83d48267a7 Accept --max-fps before Android 10
KEY_MAX_FPS_TO_ENCODER existed privately before Android 10:
<https://github.com/Genymobile/scrcpy/issues/488#issuecomment-567321437>
2019-12-19 11:52:09 +01:00
Romain Vimont
db6252e52b Simplify net.c
The platform-specific code for net.c was implemented in sys/*/net.c.

But the differences are quite limited, so use ifdef-blocks in the single
net.c instead.
2019-12-15 22:04:09 +01:00
Romain Vimont
229eeb24a2 Merge pull request #1002 into dev
Fix utf-8 char path in windows

<https://github.com/Genymobile/scrcpy/pull/1002>
2019-12-15 13:24:17 +01:00
16 changed files with 131 additions and 80 deletions

View File

@@ -137,12 +137,14 @@ scrcpy -b 2M # short version
#### Limit frame rate
On devices with Android >= 10, the capture frame rate can be limited:
The capture frame rate can be limited:
```bash
scrcpy --max-fps 15
```
This is officially supported since Android 10, but may work on earlier versions.
#### Crop
The device screen may be cropped to mirror only part of the screen.

View File

@@ -76,11 +76,9 @@ cc = meson.get_compiler('c')
if host_machine.system() == 'windows'
src += [ 'src/sys/win/command.c' ]
src += [ 'src/sys/win/net.c' ]
dependencies += cc.find_library('ws2_32')
else
src += [ 'src/sys/unix/command.c' ]
src += [ 'src/sys/unix/net.c' ]
endif
conf = configuration_data()

View File

@@ -43,7 +43,7 @@ Print this help.
.TP
.BI "\-\-max\-fps " value
Limit the framerate of screen capture (only supported on devices with Android >= 10).
Limit the framerate of screen capture (officially supported since Android 10, but may work on earlier versions).
.TP
.BI "\-m, \-\-max\-size " value

View File

@@ -42,8 +42,8 @@ scrcpy_print_usage(const char *arg0) {
" Print this help.\n"
"\n"
" --max-fps value\n"
" Limit the frame rate of screen capture (only supported on\n"
" devices with Android >= 10).\n"
" Limit the frame rate of screen capture (officially supported\n"
" since Android 10, but may work on earlier versions).\n"
"\n"
" -m, --max-size value\n"
" Limit both the width and height of the video to value. The\n"

View File

@@ -75,6 +75,8 @@ control_msg_serialize(const struct control_msg *msg, unsigned char *buf) {
buf[1] = msg->set_screen_power_mode.mode;
return 2;
case CONTROL_MSG_TYPE_BACK_OR_SCREEN_ON:
buf[1] = msg->back_or_screen_on.action;
return 2;
case CONTROL_MSG_TYPE_EXPAND_NOTIFICATION_PANEL:
case CONTROL_MSG_TYPE_COLLAPSE_NOTIFICATION_PANEL:
case CONTROL_MSG_TYPE_GET_CLIPBOARD:

View File

@@ -66,6 +66,9 @@ struct control_msg {
struct {
enum screen_power_mode mode;
} set_screen_power_mode;
struct {
enum android_keyevent_action action;
} back_or_screen_on;
};
};

View File

@@ -99,9 +99,14 @@ action_menu(struct controller *controller, int actions) {
// turn the screen on if it was off, press BACK otherwise
static void
press_back_or_turn_screen_on(struct controller *controller) {
press_back_or_turn_screen_on(struct controller *controller, int action) {
assert(action == ACTION_DOWN || action == ACTION_UP);
struct control_msg msg;
msg.type = CONTROL_MSG_TYPE_BACK_OR_SCREEN_ON;
msg.back_or_screen_on.action = action == ACTION_DOWN
? AKEY_EVENT_ACTION_DOWN
: AKEY_EVENT_ACTION_UP;
if (!controller_push_msg(controller, &msg)) {
LOGW("Could not request 'press back or turn screen on'");
@@ -521,31 +526,36 @@ input_manager_process_mouse_button(struct input_manager *im,
// simulated from touch events, so it's a duplicate
return;
}
if (event->type == SDL_MOUSEBUTTONDOWN) {
if (control && event->button == SDL_BUTTON_RIGHT) {
press_back_or_turn_screen_on(im->controller);
// double-click on black borders resize to fit the device screen
if (event->type == SDL_MOUSEBUTTONDOWN
&& event->button == SDL_BUTTON_LEFT
&& event->clicks == 2) {
bool outside = is_outside_device_screen(im, event->x, event->y);
if (outside) {
screen_resize_to_fit(im->screen);
return;
}
if (control && event->button == SDL_BUTTON_MIDDLE) {
action_home(im->controller, ACTION_DOWN | ACTION_UP);
return;
}
// double-click on black borders resize to fit the device screen
if (event->button == SDL_BUTTON_LEFT && event->clicks == 2) {
bool outside =
is_outside_device_screen(im, event->x, event->y);
if (outside) {
screen_resize_to_fit(im->screen);
return;
}
}
// otherwise, send the click event to the device
}
if (!control) {
return;
}
if (event->button == SDL_BUTTON_MIDDLE) {
int action = event->type == SDL_MOUSEBUTTONDOWN ? ACTION_DOWN
: ACTION_UP;
action_home(im->controller, action);
return;
}
if (event->button == SDL_BUTTON_RIGHT) {
int action = event->type == SDL_MOUSEBUTTONDOWN ? ACTION_DOWN
: ACTION_UP;
press_back_or_turn_screen_on(im->controller, action);
return;
}
struct control_msg msg;
if (convert_mouse_button(event, im->screen, &msg)) {
if (!controller_push_msg(im->controller, &msg)) {

View File

@@ -1,21 +0,0 @@
#include "util/net.h"
#include <unistd.h>
#include "config.h"
bool
net_init(void) {
// do nothing
return true;
}
void
net_cleanup(void) {
// do nothing
}
bool
net_close(socket_t socket) {
return !close(socket);
}

View File

@@ -1,25 +0,0 @@
#include "util/net.h"
#include "config.h"
#include "util/log.h"
bool
net_init(void) {
WSADATA wsa;
int res = WSAStartup(MAKEWORD(2, 2), &wsa) < 0;
if (res < 0) {
LOGC("WSAStartup failed with error %d", res);
return false;
}
return true;
}
void
net_cleanup(void) {
WSACleanup();
}
bool
net_close(socket_t socket) {
return !closesocket(socket);
}

View File

@@ -1,6 +1,7 @@
#include "net.h"
#include <stdio.h>
#include <SDL2/SDL_platform.h>
#include "config.h"
#include "log.h"
@@ -115,3 +116,32 @@ bool
net_shutdown(socket_t socket, int how) {
return !shutdown(socket, how);
}
bool
net_init(void) {
#ifdef __WINDOWS__
WSADATA wsa;
int res = WSAStartup(MAKEWORD(2, 2), &wsa) < 0;
if (res < 0) {
LOGC("WSAStartup failed with error %d", res);
return false;
}
#endif
return true;
}
void
net_cleanup(void) {
#ifdef __WINDOWS__
WSACleanup();
#endif
}
bool
net_close(socket_t socket) {
#ifdef __WINDOWS__
return !closesocket(socket);
#else
return !close(socket);
#endif
}

View File

@@ -140,14 +140,18 @@ static void test_serialize_inject_scroll_event(void) {
static void test_serialize_back_or_screen_on(void) {
struct control_msg msg = {
.type = CONTROL_MSG_TYPE_BACK_OR_SCREEN_ON,
.back_or_screen_on = {
.action = AKEY_EVENT_ACTION_UP,
},
};
unsigned char buf[CONTROL_MSG_SERIALIZED_MAX_SIZE];
int size = control_msg_serialize(&msg, buf);
assert(size == 1);
assert(size == 2);
const unsigned char expected[] = {
CONTROL_MSG_TYPE_BACK_OR_SCREEN_ON,
AKEY_EVENT_ACTION_UP,
};
assert(!memcmp(buf, expected, sizeof(expected)));
}

View File

@@ -85,6 +85,13 @@ public final class ControlMessage {
return msg;
}
public static ControlMessage createBackOrScreenOn(int action) {
ControlMessage msg = new ControlMessage();
msg.type = TYPE_BACK_OR_SCREEN_ON;
msg.action = action;
return msg;
}
public static ControlMessage createEmpty(int type) {
ControlMessage msg = new ControlMessage();
msg.type = type;

View File

@@ -13,6 +13,7 @@ public class ControlMessageReader {
private static final int INJECT_TOUCH_EVENT_PAYLOAD_LENGTH = 21;
private static final int INJECT_SCROLL_EVENT_PAYLOAD_LENGTH = 20;
private static final int SET_SCREEN_POWER_MODE_PAYLOAD_LENGTH = 1;
private static final int BACK_OR_SCREEN_ON_PAYLOAD_LENGTH = 1;
public static final int TEXT_MAX_LENGTH = 300;
public static final int CLIPBOARD_TEXT_MAX_LENGTH = 4093;
@@ -73,6 +74,8 @@ public class ControlMessageReader {
msg = parseSetScreenPowerMode();
break;
case ControlMessage.TYPE_BACK_OR_SCREEN_ON:
msg = parseBackOrScreenOn();
break;
case ControlMessage.TYPE_EXPAND_NOTIFICATION_PANEL:
case ControlMessage.TYPE_COLLAPSE_NOTIFICATION_PANEL:
case ControlMessage.TYPE_GET_CLIPBOARD:
@@ -164,6 +167,14 @@ public class ControlMessageReader {
return ControlMessage.createSetScreenPowerMode(mode);
}
private ControlMessage parseBackOrScreenOn() {
if (buffer.remaining() < BACK_OR_SCREEN_ON_PAYLOAD_LENGTH) {
return null;
}
int action = toUnsigned(buffer.get());
return ControlMessage.createBackOrScreenOn(action);
}
private static Position readPosition(ByteBuffer buffer) {
int x = buffer.getInt();
int y = buffer.getInt();

View File

@@ -26,6 +26,9 @@ public class Controller {
private final MotionEvent.PointerProperties[] pointerProperties = new MotionEvent.PointerProperties[PointersState.MAX_POINTERS];
private final MotionEvent.PointerCoords[] pointerCoords = new MotionEvent.PointerCoords[PointersState.MAX_POINTERS];
private boolean backPressed;
private boolean powerPressed;
public Controller(Device device, DesktopConnection connection) {
this.device = device;
this.connection = connection;
@@ -88,7 +91,7 @@ public class Controller {
injectScroll(msg.getPosition(), msg.getHScroll(), msg.getVScroll());
break;
case ControlMessage.TYPE_BACK_OR_SCREEN_ON:
pressBackOrTurnScreenOn();
pressBackOrTurnScreenOn(msg.getAction());
break;
case ControlMessage.TYPE_EXPAND_NOTIFICATION_PANEL:
device.expandNotificationPanel();
@@ -223,8 +226,33 @@ public class Controller {
return device.injectInputEvent(event, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
}
private boolean pressBackOrTurnScreenOn() {
int keycode = device.isScreenOn() ? KeyEvent.KEYCODE_BACK : KeyEvent.KEYCODE_POWER;
return injectKeycode(keycode);
private boolean pressBackOrTurnScreenOn(int action) {
int keycode = -1;
if (action == KeyEvent.ACTION_UP) {
// if BACK or POWER were pressed, this action is the corresponding release event (regardless of the screen state)
if (backPressed) {
keycode = KeyEvent.KEYCODE_BACK;
backPressed = false;
} else if (powerPressed) {
keycode = KeyEvent.KEYCODE_POWER;
powerPressed = false;
}
}
if (keycode == -1) {
keycode = device.isScreenOn() ? KeyEvent.KEYCODE_BACK : KeyEvent.KEYCODE_POWER;
}
if (action == KeyEvent.ACTION_DOWN) {
if (keycode == KeyEvent.KEYCODE_BACK) {
backPressed = true;
powerPressed = false;
} else {
backPressed = false;
powerPressed = true;
}
}
return injectKeyEvent(action, keycode, 0, 0);
}
}

View File

@@ -19,6 +19,7 @@ public class ScreenEncoder implements Device.RotationListener {
private static final int DEFAULT_I_FRAME_INTERVAL = 10; // seconds
private static final int REPEAT_FRAME_DELAY_US = 100_000; // repeat after 100ms
private static final String KEY_MAX_FPS_TO_ENCODER = "max-fps-to-encoder";
private static final int NO_PTS = -1;
@@ -150,11 +151,10 @@ public class ScreenEncoder implements Device.RotationListener {
// display the very first frame, and recover from bad quality when no new frames
format.setLong(MediaFormat.KEY_REPEAT_PREVIOUS_FRAME_AFTER, REPEAT_FRAME_DELAY_US); // µs
if (maxFps > 0) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
format.setFloat(MediaFormat.KEY_MAX_FPS_TO_ENCODER, maxFps);
} else {
Ln.w("Max FPS is only supported since Android 10, the option has been ignored");
}
// The key existed privately before Android 10:
// <https://android.googlesource.com/platform/frameworks/base/+/625f0aad9f7a259b6881006ad8710adce57d1384%5E%21/>
// <https://github.com/Genymobile/scrcpy/issues/488#issuecomment-567321437>
format.setFloat(KEY_MAX_FPS_TO_ENCODER, maxFps);
}
return format;
}

View File

@@ -145,6 +145,7 @@ public class ControlMessageReaderTest {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(bos);
dos.writeByte(ControlMessage.TYPE_BACK_OR_SCREEN_ON);
dos.writeByte(KeyEvent.ACTION_UP);
byte[] packet = bos.toByteArray();
@@ -152,6 +153,7 @@ public class ControlMessageReaderTest {
ControlMessage event = reader.next();
Assert.assertEquals(ControlMessage.TYPE_BACK_OR_SCREEN_ON, event.getType());
Assert.assertEquals(KeyEvent.ACTION_UP, event.getAction());
}
@Test