Compare commits

..

3 Commits

Author SHA1 Message Date
Romain Vimont
7ce63499d9 Always request fullscreen and maximized state
The window state may be changed by the window manager without scrcpy
being aware of it (for example the fullscreen mode on macOS).

To avoid problems, request the window state to SDL when needed.
2020-05-07 18:20:27 +02:00
Romain Vimont
e2d5f0e7fc Send scroll events as a touchscreen
Scroll events were sent with a mouse input device. When scrolling on a
list, this could cause the whole list to be focused, and drawn with the
focus color as background.

Send scroll events with a touchscreen input device instead (like motion
events).

Fixes #1362 <https://github.com/Genymobile/scrcpy/issues/1362>
2020-05-07 15:08:11 +02:00
Romain Vimont
ead7ee4a03 Revert "Improve resizing workaround"
This reverts commit 92cb3a6661, which
broke the fullscreen/maximized restoration size on Windows.

Fixes #1346 <https://github.com/Genymobile/scrcpy/issues/1346>
2020-05-06 01:10:25 +02:00
9 changed files with 95 additions and 172 deletions

View File

@@ -109,10 +109,10 @@ static int
event_watcher(void *data, SDL_Event *event) {
(void) data;
if (event->type == SDL_WINDOWEVENT
&& event->window.event == SDL_WINDOWEVENT_SIZE_CHANGED) {
&& event->window.event == SDL_WINDOWEVENT_RESIZED) {
// In practice, it seems to always be called from the same thread in
// that specific case. Anyway, it's just a workaround.
screen_handle_window_event(&screen, &event->window);
screen_render(&screen);
}
return 0;
}

View File

@@ -15,6 +15,18 @@
#define DISPLAY_MARGINS 96
static bool
is_maximized(const struct screen *screen) {
uint32_t flags = SDL_GetWindowFlags(screen->window);
return !!(flags & SDL_WINDOW_MAXIMIZED);
}
static bool
is_fullscreen(const struct screen *screen) {
uint32_t flags = SDL_GetWindowFlags(screen->window);
return !!(flags & (SDL_WINDOW_FULLSCREEN | SDL_WINDOW_FULLSCREEN_DESKTOP));
}
static inline struct size
get_rotated_size(struct size size, int rotation) {
struct size rotated_size;
@@ -44,7 +56,7 @@ get_window_size(SDL_Window *window) {
// get the windowed window size
static struct size
get_windowed_window_size(const struct screen *screen) {
if (screen->fullscreen || screen->maximized) {
if (is_fullscreen(screen) || is_maximized(screen)) {
return screen->windowed_window_size;
}
return get_window_size(screen->window);
@@ -53,7 +65,7 @@ get_windowed_window_size(const struct screen *screen) {
// apply the windowed window size if fullscreen and maximized are disabled
static void
apply_windowed_size(struct screen *screen) {
if (!screen->fullscreen && !screen->maximized) {
if (!is_fullscreen(screen) && !is_maximized(screen)) {
SDL_SetWindowSize(screen->window, screen->windowed_window_size.width,
screen->windowed_window_size.height);
}
@@ -471,28 +483,27 @@ screen_render(struct screen *screen) {
void
screen_switch_fullscreen(struct screen *screen) {
uint32_t new_mode = screen->fullscreen ? 0 : SDL_WINDOW_FULLSCREEN_DESKTOP;
bool was_fullscreen = is_fullscreen(screen);
uint32_t new_mode = was_fullscreen ? 0 : SDL_WINDOW_FULLSCREEN_DESKTOP;
if (SDL_SetWindowFullscreen(screen->window, new_mode)) {
LOGW("Could not switch fullscreen mode: %s", SDL_GetError());
return;
}
screen->fullscreen = !screen->fullscreen;
apply_windowed_size(screen);
LOGD("Switched to %s mode", screen->fullscreen ? "fullscreen" : "windowed");
LOGD("Switched to %s mode", was_fullscreen ? "windowed" : "fullscreen");
screen_render(screen);
}
void
screen_resize_to_fit(struct screen *screen) {
if (screen->fullscreen) {
if (is_fullscreen(screen)) {
return;
}
if (screen->maximized) {
if (is_maximized(screen)) {
SDL_RestoreWindow(screen->window);
screen->maximized = false;
}
struct size optimal_size =
@@ -504,13 +515,12 @@ screen_resize_to_fit(struct screen *screen) {
void
screen_resize_to_pixel_perfect(struct screen *screen) {
if (screen->fullscreen) {
if (is_fullscreen(screen)) {
return;
}
if (screen->maximized) {
if (is_maximized(screen)) {
SDL_RestoreWindow(screen->window);
screen->maximized = false;
}
struct size content_size = screen->content_size;
@@ -527,7 +537,7 @@ screen_handle_window_event(struct screen *screen,
screen_render(screen);
break;
case SDL_WINDOWEVENT_SIZE_CHANGED:
if (!screen->fullscreen && !screen->maximized) {
if (!is_fullscreen(screen) && !is_maximized(screen)) {
// Backup the previous size: if we receive the MAXIMIZED event,
// then the new size must be ignored (it's the maximized size).
// We could not rely on the window flags due to race conditions
@@ -552,10 +562,8 @@ screen_handle_window_event(struct screen *screen,
screen->windowed_window_size_backup.width = 0;
screen->windowed_window_size_backup.height = 0;
#endif
screen->maximized = true;
break;
case SDL_WINDOWEVENT_RESTORED:
screen->maximized = false;
apply_windowed_size(screen);
break;
}

View File

@@ -29,8 +29,6 @@ struct screen {
// client rotation: 0, 1, 2 or 3 (x90 degrees counterclockwise)
unsigned rotation;
bool has_frame;
bool fullscreen;
bool maximized;
bool no_window;
bool mipmaps;
};
@@ -59,8 +57,6 @@ struct screen {
}, \
.rotation = 0, \
.has_frame = false, \
.fullscreen = false, \
.maximized = false, \
.no_window = false, \
.mipmaps = false, \
}

View File

@@ -231,17 +231,22 @@ enable_tunnel_any_port(struct server *server, struct port_range port_range) {
static process_t
execute_server(struct server *server, const struct server_params *params) {
process_t result = PROCESS_NONE;
char *cmd[128];
int i = 0;
cmd[i++] = "shell";
cmd[i++] = "CLASSPATH=" DEVICE_SERVER_PATH;
cmd[i++] = "app_process";
char max_size_string[6];
char bit_rate_string[11];
char max_fps_string[6];
char lock_video_orientation_string[3];
char display_id_string[6];
sprintf(max_size_string, "%"PRIu16, params->max_size);
sprintf(bit_rate_string, "%"PRIu32, params->bit_rate);
sprintf(max_fps_string, "%"PRIu16, params->max_fps);
sprintf(lock_video_orientation_string, "%"PRIi8, params->lock_video_orientation);
sprintf(display_id_string, "%"PRIu16, params->display_id);
const char *const cmd[] = {
"shell",
"CLASSPATH=" DEVICE_SERVER_PATH,
"app_process",
#ifdef SERVER_DEBUGGER
# define SERVER_DEBUGGER_PORT "5005"
cmd[i++] =
# ifdef SERVER_DEBUGGER_METHOD_NEW
/* Android 9 and above */
"-XjdwpProvider:internal -XjdwpOptions:transport=dt_socket,suspend=y,server=y,address="
@@ -249,38 +254,23 @@ execute_server(struct server *server, const struct server_params *params) {
/* Android 8 and below */
"-agentlib:jdwp=transport=dt_socket,suspend=y,server=y,address="
# endif
SERVER_DEBUGGER_PORT;
SERVER_DEBUGGER_PORT,
#endif
cmd[i++] = "/"; // unused
cmd[i++] = "com.genymobile.scrcpy.Server";
cmd[i++] = SCRCPY_VERSION;
int dyn_index = i; // from there, the strings are allocated
#define ADD_PARAM(fmt, ...) \
cmd[i] = sc_asprintf(fmt, ## __VA_ARGS__); \
if (!cmd[i++]) { \
goto end; \
}
#define STRBOOL(p) (p ? "true" : "false")
ADD_PARAM("max_size=%"PRIu16, params->max_size);
ADD_PARAM("bit_rate=%"PRIu32, params->bit_rate);
ADD_PARAM("max_fps=%"PRIu16, params->max_fps);
ADD_PARAM("lock_video_orientation=%"PRIi8, params->lock_video_orientation);
ADD_PARAM("tunnel_forward=%s", STRBOOL(server->tunnel_forward));
ADD_PARAM("crop=%s", params->crop ? params->crop : "");
// always send frame meta (packet boundaries + timestamp)
ADD_PARAM("send_frame_meta=true");
ADD_PARAM("control=%s", STRBOOL(params->control));
ADD_PARAM("display_id=%"PRIu16, params->display_id);
ADD_PARAM("show_touches=%s", STRBOOL(params->show_touches));
ADD_PARAM("stay_awake=%s", STRBOOL(params->stay_awake));
#undef ADD_PARAM
#undef STRBOOL
"/", // unused
"com.genymobile.scrcpy.Server",
SCRCPY_VERSION,
max_size_string,
bit_rate_string,
max_fps_string,
lock_video_orientation_string,
server->tunnel_forward ? "true" : "false",
params->crop ? params->crop : "-",
"true", // always send frame meta (packet boundaries + timestamp)
params->control ? "true" : "false",
display_id_string,
params->show_touches ? "true" : "false",
params->stay_awake ? "true" : "false",
};
#ifdef SERVER_DEBUGGER
LOGI("Server debugger waiting for a client on device port "
SERVER_DEBUGGER_PORT "...");
@@ -292,14 +282,7 @@ execute_server(struct server *server, const struct server_params *params) {
// Port: 5005
// Then click on "Debug"
#endif
result = adb_execute(server->serial, (const char **) cmd, i);
end:
for (int j = i; j > dyn_index; --j) {
free(cmd[j - 1]);
}
return result;
return adb_execute(server->serial, cmd, sizeof(cmd) / sizeof(cmd[0]));
}
static socket_t

View File

@@ -1,9 +1,7 @@
#include "str_util.h"
#include <assert.h>
#include <errno.h>
#include <limits.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
@@ -197,33 +195,3 @@ utf8_from_wide_char(const wchar_t *ws) {
}
#endif
char *
sc_asprintf(const char *fmt, ...) {
va_list va;
va_start(va, fmt);
char *s = sc_vasprintf(fmt, va);
va_end(va);
return s;
}
char *
sc_vasprintf(const char *fmt, va_list ap) {
va_list va;
va_copy(va, ap);
int len = vsnprintf(NULL, 0, fmt, va);
va_end(va);
char *str = malloc(len + 1);
if (!str) {
return NULL;
}
va_copy(va, ap);
int len2 = vsprintf(str, fmt, va);
(void) len2;
assert(len == len2);
va_end(va);
return str;
}

View File

@@ -1,10 +1,8 @@
#ifndef STRUTIL_H
#define STRUTIL_H
#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include "config.h"
@@ -59,13 +57,4 @@ char *
utf8_from_wide_char(const wchar_t *s);
#endif
// compatibility function similar to asprintf()
// (but returning the resulting string for convenience)
char *
sc_asprintf(const char *fmt, ...)
__attribute__((format(printf, 1, 2)));
char *
sc_vasprintf(const char *fmt, va_list ap);
#endif

View File

@@ -215,7 +215,7 @@ public class Controller {
MotionEvent event = MotionEvent
.obtain(lastTouchDown, now, MotionEvent.ACTION_SCROLL, 1, pointerProperties, pointerCoords, 0, 0, 1f, 1f, DEVICE_ID_VIRTUAL, 0,
InputDevice.SOURCE_MOUSE, 0);
InputDevice.SOURCE_TOUCHSCREEN, 0);
return injectEvent(event);
}

View File

@@ -6,7 +6,7 @@ public class Options {
private int maxSize;
private int bitRate;
private int maxFps;
private int lockedVideoOrientation = -1;
private int lockedVideoOrientation;
private boolean tunnelForward;
private Rect crop;
private boolean sendFrameMeta; // send PTS so that the client may record properly

View File

@@ -109,73 +109,52 @@ public final class Server {
"The server version (" + BuildConfig.VERSION_NAME + ") does not match the client " + "(" + clientVersion + ")");
}
final int expectedParameters = 12;
if (args.length != expectedParameters) {
throw new IllegalArgumentException("Expecting " + expectedParameters + " parameters");
}
Options options = new Options();
for (int i = 1; i < args.length; ++i) {
String arg = args[i];
int equalIndex = arg.indexOf('=');
if (equalIndex == -1) {
throw new IllegalArgumentException("Invalid key=value pair: \"" + arg + "\"");
}
String key = arg.substring(0, equalIndex);
String value = arg.substring(equalIndex + 1);
int maxSize = Integer.parseInt(args[1]) & ~7; // multiple of 8
options.setMaxSize(maxSize);
switch (key) {
case "max_size":
int maxSize = Integer.parseInt(value) & ~7; // multiple of 8
options.setMaxSize(maxSize);
break;
case "bit_rate":
int bitRate = Integer.parseInt(value);
options.setBitRate(bitRate);
break;
case "max_fps":
int maxFps = Integer.parseInt(value);
options.setMaxFps(maxFps);
break;
case "lock_video_orientation":
int lockedVideoOrientation = Integer.parseInt(value);
options.setLockedVideoOrientation(lockedVideoOrientation);
break;
case "tunnel_forward":
// use "adb forward" instead of "adb tunnel"? (so the server must listen)
boolean tunnelForward = Boolean.parseBoolean(value);
options.setTunnelForward(tunnelForward);
break;
case "crop":
Rect crop = parseCrop(value);
options.setCrop(crop);
break;
case "send_frame_meta":
boolean sendFrameMeta = Boolean.parseBoolean(value);
options.setSendFrameMeta(sendFrameMeta);
break;
case "control":
boolean control = Boolean.parseBoolean(value);
options.setControl(control);
break;
case "display_id":
int displayId = Integer.parseInt(value);
options.setDisplayId(displayId);
break;
case "show_touches":
boolean showTouches = Boolean.parseBoolean(value);
options.setShowTouches(showTouches);
break;
case "stay_awake":
boolean stayAwake = Boolean.parseBoolean(value);
options.setStayAwake(stayAwake);
break;
default:
throw new IllegalArgumentException("Unknown parameter: " + key);
}
}
int bitRate = Integer.parseInt(args[2]);
options.setBitRate(bitRate);
int maxFps = Integer.parseInt(args[3]);
options.setMaxFps(maxFps);
int lockedVideoOrientation = Integer.parseInt(args[4]);
options.setLockedVideoOrientation(lockedVideoOrientation);
// use "adb forward" instead of "adb tunnel"? (so the server must listen)
boolean tunnelForward = Boolean.parseBoolean(args[5]);
options.setTunnelForward(tunnelForward);
Rect crop = parseCrop(args[6]);
options.setCrop(crop);
boolean sendFrameMeta = Boolean.parseBoolean(args[7]);
options.setSendFrameMeta(sendFrameMeta);
boolean control = Boolean.parseBoolean(args[8]);
options.setControl(control);
int displayId = Integer.parseInt(args[9]);
options.setDisplayId(displayId);
boolean showTouches = Boolean.parseBoolean(args[10]);
options.setShowTouches(showTouches);
boolean stayAwake = Boolean.parseBoolean(args[11]);
options.setStayAwake(stayAwake);
return options;
}
private static Rect parseCrop(String crop) {
if (crop.isEmpty()) {
if ("-".equals(crop)) {
return null;
}
// input format: "width:height:x:y"