Compare commits

..

2 Commits

Author SHA1 Message Date
Romain Vimont
7cd1e39766 Toggle "raw key events" via shortcut
The "raw key events" mode may need to be toggled while scrcpy is
running (e.g. to type text then play a game).

Remove the command-line argument and bind Ctrl+k to toggle the mode.
2018-08-15 21:07:50 +02:00
Romain Vimont
9de332bbe5 Add "raw key events" mode
Add a command-line option to enable "raw key events" mode
(-k,--raw-key-events).

This disable text inputs and forwards "text" key events (which are
not forwarded by default).

This is helpful for gaming:
<https://github.com/Genymobile/scrcpy/issues/87>
<https://github.com/Genymobile/scrcpy/issues/127>
2018-08-15 20:29:07 +02:00
16 changed files with 142 additions and 207 deletions

View File

@@ -312,12 +312,6 @@ To show physical touches while scrcpy is running:
scrcpy -t
```
The app may be started directly in fullscreen:
```
scrcpy -f
```
To run without installing:
```bash
@@ -344,12 +338,15 @@ To run without installing:
| turn screen on | _Right-click²_ |
| paste computer clipboard to device | `Ctrl`+`v` |
| enable/disable FPS counter (on stdout) | `Ctrl`+`i` |
| toggle "raw key events" mode ([#87]) | `Ctrl`+`k` |
| install APK from computer | drag & drop APK file |
| push file to `/sdcard/` | drag & drop non-APK file |
_¹Double-click on black borders to remove them._
_²Right-click turns the screen on if it was off, presses BACK otherwise._
[#87]: https://github.com/Genymobile/scrcpy/issues/87
## Why _scrcpy_?

View File

@@ -18,25 +18,9 @@ static inline const char *get_adb_command() {
return adb_command;
}
static void show_adb_err_msg(enum process_result err) {
switch (err) {
case PROCESS_ERROR_GENERIC:
LOGE("Failed to execute adb");
break;
case PROCESS_ERROR_MISSING_BINARY:
LOGE("'adb' command not found (make it accessible from your PATH "
"or define its full path in the ADB environment variable)");
break;
case PROCESS_SUCCESS:
/* do nothing */
break;
}
}
process_t adb_execute(const char *serial, const char *const adb_cmd[], int len) {
const char *cmd[len + 4];
int i;
process_t process;
cmd[0] = get_adb_command();
if (serial) {
cmd[1] = "-s";
@@ -48,12 +32,7 @@ process_t adb_execute(const char *serial, const char *const adb_cmd[], int len)
memcpy(&cmd[i], adb_cmd, len * sizeof(const char *));
cmd[len + i] = NULL;
enum process_result r = cmd_execute(cmd[0], cmd, &process);
if (r != PROCESS_SUCCESS) {
show_adb_err_msg(r);
return PROCESS_NONE;
}
return process;
return cmd_execute(cmd[0], cmd);
}
process_t adb_forward(const char *serial, uint16_t local_port, const char *device_socket_name) {

View File

@@ -32,13 +32,7 @@
#endif
# define NO_EXIT_CODE -1
enum process_result {
PROCESS_SUCCESS,
PROCESS_ERROR_GENERIC,
PROCESS_ERROR_MISSING_BINARY,
};
enum process_result cmd_execute(const char *path, const char *const argv[], process_t *process);
process_t cmd_execute(const char *path, const char *const argv[]);
SDL_bool cmd_terminate(process_t pid);
SDL_bool cmd_simple_wait(process_t pid, exit_code_t *exit_code);

View File

@@ -70,7 +70,84 @@ static enum android_metastate convert_meta_state(SDL_Keymod mod) {
return autocomplete_metastate(metastate);
}
static SDL_bool convert_keycode(SDL_Keycode from, enum android_keycode *to, Uint16 mod) {
// only used if raw_key_events is enabled
static SDL_bool convert_text_keycode(SDL_Keycode from, enum android_keycode *to) {
switch (from) {
MAP(SDLK_SPACE, AKEYCODE_SPACE);
MAP(SDLK_HASH, AKEYCODE_POUND);
MAP(SDLK_QUOTE, AKEYCODE_APOSTROPHE);
MAP(SDLK_ASTERISK, AKEYCODE_STAR);
MAP(SDLK_COMMA, AKEYCODE_COMMA);
MAP(SDLK_MINUS, AKEYCODE_MINUS);
MAP(SDLK_PERIOD, AKEYCODE_PERIOD);
MAP(SDLK_SLASH, AKEYCODE_SLASH);
MAP(SDLK_0, AKEYCODE_0);
MAP(SDLK_1, AKEYCODE_1);
MAP(SDLK_2, AKEYCODE_2);
MAP(SDLK_3, AKEYCODE_3);
MAP(SDLK_4, AKEYCODE_4);
MAP(SDLK_5, AKEYCODE_5);
MAP(SDLK_6, AKEYCODE_6);
MAP(SDLK_7, AKEYCODE_7);
MAP(SDLK_8, AKEYCODE_8);
MAP(SDLK_9, AKEYCODE_9);
MAP(SDLK_SEMICOLON, AKEYCODE_SEMICOLON);
MAP(SDLK_EQUALS, AKEYCODE_EQUALS);
MAP(SDLK_AT, AKEYCODE_AT);
MAP(SDLK_LEFTBRACKET, AKEYCODE_LEFT_BRACKET);
MAP(SDLK_BACKSLASH, AKEYCODE_BACKSLASH);
MAP(SDLK_RIGHTBRACKET, AKEYCODE_RIGHT_BRACKET);
MAP(SDLK_BACKQUOTE, AKEYCODE_GRAVE);
MAP(SDLK_a, AKEYCODE_A);
MAP(SDLK_b, AKEYCODE_B);
MAP(SDLK_c, AKEYCODE_C);
MAP(SDLK_d, AKEYCODE_D);
MAP(SDLK_e, AKEYCODE_E);
MAP(SDLK_f, AKEYCODE_F);
MAP(SDLK_g, AKEYCODE_G);
MAP(SDLK_h, AKEYCODE_H);
MAP(SDLK_i, AKEYCODE_I);
MAP(SDLK_j, AKEYCODE_J);
MAP(SDLK_k, AKEYCODE_K);
MAP(SDLK_l, AKEYCODE_L);
MAP(SDLK_m, AKEYCODE_M);
MAP(SDLK_n, AKEYCODE_N);
MAP(SDLK_o, AKEYCODE_O);
MAP(SDLK_p, AKEYCODE_P);
MAP(SDLK_q, AKEYCODE_Q);
MAP(SDLK_r, AKEYCODE_R);
MAP(SDLK_s, AKEYCODE_S);
MAP(SDLK_t, AKEYCODE_T);
MAP(SDLK_u, AKEYCODE_U);
MAP(SDLK_v, AKEYCODE_V);
MAP(SDLK_w, AKEYCODE_W);
MAP(SDLK_x, AKEYCODE_X);
MAP(SDLK_y, AKEYCODE_Y);
MAP(SDLK_z, AKEYCODE_Z);
MAP(SDLK_KP_ENTER, AKEYCODE_NUMPAD_ENTER);
MAP(SDLK_KP_1, AKEYCODE_NUMPAD_1);
MAP(SDLK_KP_2, AKEYCODE_NUMPAD_2);
MAP(SDLK_KP_3, AKEYCODE_NUMPAD_3);
MAP(SDLK_KP_4, AKEYCODE_NUMPAD_4);
MAP(SDLK_KP_5, AKEYCODE_NUMPAD_5);
MAP(SDLK_KP_6, AKEYCODE_NUMPAD_6);
MAP(SDLK_KP_7, AKEYCODE_NUMPAD_7);
MAP(SDLK_KP_8, AKEYCODE_NUMPAD_8);
MAP(SDLK_KP_9, AKEYCODE_NUMPAD_9);
MAP(SDLK_KP_0, AKEYCODE_NUMPAD_0);
MAP(SDLK_KP_DIVIDE, AKEYCODE_NUMPAD_DIVIDE);
MAP(SDLK_KP_MULTIPLY, AKEYCODE_NUMPAD_MULTIPLY);
MAP(SDLK_KP_MINUS, AKEYCODE_NUMPAD_SUBTRACT);
MAP(SDLK_KP_PLUS, AKEYCODE_NUMPAD_ADD);
MAP(SDLK_KP_PERIOD, AKEYCODE_NUMPAD_DOT);
MAP(SDLK_KP_EQUALS, AKEYCODE_NUMPAD_EQUALS);
MAP(SDLK_KP_LEFTPAREN, AKEYCODE_NUMPAD_LEFT_PAREN);
MAP(SDLK_KP_RIGHTPAREN, AKEYCODE_NUMPAD_RIGHT_PAREN);
FAIL;
}
}
static SDL_bool convert_keycode(SDL_Keycode from, enum android_keycode *to) {
switch (from) {
MAP(SDLK_RETURN, AKEYCODE_ENTER);
MAP(SDLK_KP_ENTER, AKEYCODE_NUMPAD_ENTER);
@@ -86,39 +163,6 @@ static SDL_bool convert_keycode(SDL_Keycode from, enum android_keycode *to, Uint
MAP(SDLK_LEFT, AKEYCODE_DPAD_LEFT);
MAP(SDLK_DOWN, AKEYCODE_DPAD_DOWN);
MAP(SDLK_UP, AKEYCODE_DPAD_UP);
}
if (mod & (KMOD_LALT | KMOD_RALT | KMOD_LGUI | KMOD_RGUI)) {
return SDL_FALSE;
}
// if ALT and META are not pressed, also handle letters and space
switch (from) {
MAP(SDLK_a, AKEYCODE_A);
MAP(SDLK_b, AKEYCODE_B);
MAP(SDLK_c, AKEYCODE_C);
MAP(SDLK_d, AKEYCODE_D);
MAP(SDLK_e, AKEYCODE_E);
MAP(SDLK_f, AKEYCODE_F);
MAP(SDLK_g, AKEYCODE_G);
MAP(SDLK_h, AKEYCODE_H);
MAP(SDLK_i, AKEYCODE_I);
MAP(SDLK_j, AKEYCODE_J);
MAP(SDLK_k, AKEYCODE_K);
MAP(SDLK_l, AKEYCODE_L);
MAP(SDLK_m, AKEYCODE_M);
MAP(SDLK_n, AKEYCODE_N);
MAP(SDLK_o, AKEYCODE_O);
MAP(SDLK_p, AKEYCODE_P);
MAP(SDLK_q, AKEYCODE_Q);
MAP(SDLK_r, AKEYCODE_R);
MAP(SDLK_s, AKEYCODE_S);
MAP(SDLK_t, AKEYCODE_T);
MAP(SDLK_u, AKEYCODE_U);
MAP(SDLK_v, AKEYCODE_V);
MAP(SDLK_w, AKEYCODE_W);
MAP(SDLK_x, AKEYCODE_X);
MAP(SDLK_y, AKEYCODE_Y);
MAP(SDLK_z, AKEYCODE_Z);
MAP(SDLK_SPACE, AKEYCODE_SPACE);
FAIL;
}
}
@@ -152,19 +196,22 @@ static enum android_motionevent_buttons convert_mouse_buttons(Uint32 state) {
}
SDL_bool input_key_from_sdl_to_android(const SDL_KeyboardEvent *from,
struct control_event *to) {
struct control_event *to,
SDL_bool raw_key_events) {
to->type = CONTROL_EVENT_TYPE_KEYCODE;
if (!convert_keycode_action(from->type, &to->keycode_event.action)) {
return SDL_FALSE;
}
Uint16 mod = from->keysym.mod;
if (!convert_keycode(from->keysym.sym, &to->keycode_event.keycode, mod)) {
return SDL_FALSE;
if (!convert_keycode(from->keysym.sym, &to->keycode_event.keycode)) {
if (!raw_key_events ||
!convert_text_keycode(from->keysym.sym, &to->keycode_event.keycode)) {
return SDL_FALSE;
}
}
to->keycode_event.metastate = convert_meta_state(mod);
to->keycode_event.metastate = convert_meta_state(from->keysym.mod);
return SDL_TRUE;
}

View File

@@ -16,7 +16,9 @@ struct complete_mouse_wheel_event {
};
SDL_bool input_key_from_sdl_to_android(const SDL_KeyboardEvent *from,
struct control_event *to);
struct control_event *to,
SDL_bool raw_key_events);
SDL_bool mouse_button_from_sdl_to_android(const SDL_MouseButtonEvent *from,
struct size screen_size,
struct control_event *to);

View File

@@ -1,6 +1,5 @@
#include "input_manager.h"
#include <SDL2/SDL_assert.h>
#include "convert.h"
#include "lock_util.h"
#include "log.h"
@@ -130,10 +129,8 @@ static void clipboard_paste(struct controller *controller) {
void input_manager_process_text_input(struct input_manager *input_manager,
const SDL_TextInputEvent *event) {
char c = event->text[0];
if (isalpha(c) || c == ' ') {
SDL_assert(event->text[1] == '\0');
// letters and space are handled as raw key event
if (input_manager->raw_key_events) {
// we will forward the raw key events instead
return;
}
struct control_event control_event;
@@ -223,13 +220,21 @@ void input_manager_process_key(struct input_manager *input_manager,
switch_fps_counter_state(input_manager->frames);
}
return;
case SDLK_k:
if (!repeat && event->type == SDL_KEYDOWN) {
input_manager->raw_key_events ^= SDL_TRUE; // toggle
LOGI("Raw key events mode %s",
input_manager->raw_key_events ? "enabled" : "disabled");
}
return;
}
return;
}
struct control_event control_event;
if (input_key_from_sdl_to_android(event, &control_event)) {
SDL_bool raw_key_events = input_manager->raw_key_events;
if (input_key_from_sdl_to_android(event, &control_event, raw_key_events)) {
if (!controller_push_event(input_manager->controller, &control_event)) {
LOGW("Cannot send control event");
}

View File

@@ -11,6 +11,7 @@ struct input_manager {
struct controller *controller;
struct frames *frames;
struct screen *screen;
SDL_bool raw_key_events;
};
void input_manager_process_text_input(struct input_manager *input_manager,

View File

@@ -11,7 +11,6 @@
struct args {
const char *serial;
const char *crop;
SDL_bool fullscreen;
SDL_bool help;
SDL_bool version;
SDL_bool show_touches;
@@ -37,9 +36,6 @@ static void usage(const char *arg0) {
" (typically, portrait for a phone, landscape for a tablet).\n"
" Any --max-size value is computed on the cropped size.\n"
"\n"
" -f, --fullscreen\n"
" Start in fullscreen.\n"
"\n"
" -h, --help\n"
" Print this help.\n"
"\n"
@@ -204,7 +200,6 @@ static SDL_bool parse_args(struct args *args, int argc, char *argv[]) {
static const struct option long_options[] = {
{"bit-rate", required_argument, NULL, 'b'},
{"crop", required_argument, NULL, 'c'},
{"fullscreen", no_argument, NULL, 'f'},
{"help", no_argument, NULL, 'h'},
{"max-size", required_argument, NULL, 'm'},
{"port", required_argument, NULL, 'p'},
@@ -214,7 +209,7 @@ static SDL_bool parse_args(struct args *args, int argc, char *argv[]) {
{NULL, 0, NULL, 0 },
};
int c;
while ((c = getopt_long(argc, argv, "b:c:fhm:p:s:tv", long_options, NULL)) != -1) {
while ((c = getopt_long(argc, argv, "b:c:hm:p:s:tv", long_options, NULL)) != -1) {
switch (c) {
case 'b':
if (!parse_bit_rate(optarg, &args->bit_rate)) {
@@ -224,9 +219,6 @@ static SDL_bool parse_args(struct args *args, int argc, char *argv[]) {
case 'c':
args->crop = optarg;
break;
case 'f':
args->fullscreen = SDL_TRUE;
break;
case 'h':
args->help = SDL_TRUE;
break;
@@ -313,7 +305,6 @@ int main(int argc, char *argv[]) {
.max_size = args.max_size,
.bit_rate = args.bit_rate,
.show_touches = args.show_touches,
.fullscreen = args.fullscreen,
};
int res = scrcpy(&options) ? 0 : 1;

View File

@@ -35,6 +35,7 @@ static struct input_manager input_manager = {
.controller = &controller,
.frames = &frames,
.screen = &screen,
.raw_key_events = SDL_FALSE,
};
#if defined(__APPLE__) || defined(__WINDOWS__)
@@ -223,10 +224,6 @@ SDL_bool scrcpy(const struct scrcpy_options *options) {
show_touches_waited = SDL_TRUE;
}
if (options->fullscreen) {
screen_switch_fullscreen(&screen);
}
ret = event_loop();
LOGD("quit...");

View File

@@ -10,7 +10,6 @@ struct scrcpy_options {
Uint16 max_size;
Uint32 bit_rate;
SDL_bool show_touches;
SDL_bool fullscreen;
};
SDL_bool scrcpy(const struct scrcpy_options *options);

View File

@@ -1,7 +1,5 @@
#include "command.h"
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <stdlib.h>
#include <sys/types.h>
@@ -9,65 +7,18 @@
#include <unistd.h>
#include "log.h"
enum process_result cmd_execute(const char *path, const char *const argv[], pid_t *pid) {
int fd[2];
if (pipe(fd) == -1) {
perror("pipe");
return PROCESS_ERROR_GENERIC;
}
enum process_result ret = PROCESS_SUCCESS;
*pid = fork();
if (*pid == -1) {
pid_t cmd_execute(const char *path, const char *const argv[]) {
pid_t pid = fork();
if (pid == -1) {
perror("fork");
ret = PROCESS_ERROR_GENERIC;
goto end;
return -1;
}
if (*pid > 0) {
// parent close write side
close(fd[1]);
fd[1] = -1;
// wait for EOF or receive errno from child
if (read(fd[0], &ret, sizeof(ret)) == -1) {
perror("read");
ret = PROCESS_ERROR_GENERIC;
goto end;
}
} else if (*pid == 0) {
// child close read side
close(fd[0]);
if (fcntl(fd[1], F_SETFD, FD_CLOEXEC) == 0) {
execvp(path, (char *const *)argv);
if (errno == ENOENT) {
ret = PROCESS_ERROR_MISSING_BINARY;
} else {
ret = PROCESS_ERROR_GENERIC;
}
perror("exec");
} else {
perror("fcntl");
ret = PROCESS_ERROR_GENERIC;
}
// send ret to the parent
if (write(fd[1], &ret, sizeof(ret)) == -1) {
perror("write");
}
// close write side before exiting
close(fd[1]);
if (pid == 0) {
execvp(path, (char *const *)argv);
perror("exec");
_exit(1);
}
end:
if (fd[0] != -1) {
close(fd[0]);
}
if (fd[1] != -1) {
close(fd[1]);
}
return ret;
return pid;
}
SDL_bool cmd_terminate(pid_t pid) {

View File

@@ -4,7 +4,7 @@
#include "log.h"
#include "str_util.h"
enum process_result cmd_execute(const char *path, const char *const argv[], HANDLE *handle) {
HANDLE cmd_execute(const char *path, const char *const argv[]) {
STARTUPINFO si;
PROCESS_INFORMATION pi;
memset(&si, 0, sizeof(si));
@@ -18,8 +18,7 @@ enum process_result cmd_execute(const char *path, const char *const argv[], HAND
size_t ret = xstrjoin(cmd, argv, ' ', sizeof(cmd));
if (ret >= sizeof(cmd)) {
LOGE("Command too long (%" PRIsizet " chars)", sizeof(cmd) - 1);
*handle = NULL;
return PROCESS_ERROR_GENERIC;
return NULL;
}
#ifdef WINDOWS_NOCONSOLE
@@ -28,15 +27,10 @@ enum process_result cmd_execute(const char *path, const char *const argv[], HAND
int flags = 0;
#endif
if (!CreateProcess(NULL, cmd, NULL, NULL, FALSE, flags, NULL, NULL, &si, &pi)) {
*handle = NULL;
if (GetLastError() == ERROR_FILE_NOT_FOUND) {
return PROCESS_ERROR_MISSING_BINARY;
}
return PROCESS_ERROR_GENERIC;
return NULL;
}
*handle = pi.hProcess;
return PROCESS_SUCCESS;
return pi.hProcess;
}
SDL_bool cmd_terminate(HANDLE handle) {

View File

@@ -5,9 +5,9 @@ import android.net.LocalSocket;
import android.net.LocalSocketAddress;
import java.io.Closeable;
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
public final class DesktopConnection implements Closeable {
@@ -18,14 +18,14 @@ public final class DesktopConnection implements Closeable {
private final LocalSocket socket;
private final InputStream inputStream;
private final FileDescriptor fd;
private final OutputStream outputStream;
private final ControlEventReader reader = new ControlEventReader();
private DesktopConnection(LocalSocket socket) throws IOException {
this.socket = socket;
inputStream = socket.getInputStream();
fd = socket.getFileDescriptor();
outputStream = socket.getOutputStream();
}
private static LocalSocket connect(String abstractName) throws IOException {
@@ -78,11 +78,11 @@ public final class DesktopConnection implements Closeable {
buffer[DEVICE_NAME_FIELD_LENGTH + 1] = (byte) width;
buffer[DEVICE_NAME_FIELD_LENGTH + 2] = (byte) (height >> 8);
buffer[DEVICE_NAME_FIELD_LENGTH + 3] = (byte) height;
IO.writeFully(fd, buffer, 0, buffer.length);
outputStream.write(buffer, 0, buffer.length);
}
public FileDescriptor getFd() {
return fd;
public OutputStream getOutputStream() {
return outputStream;
}
public ControlEvent receiveControlEvent() throws IOException {

View File

@@ -1,31 +0,0 @@
package com.genymobile.scrcpy;
import android.system.ErrnoException;
import android.system.Os;
import android.system.OsConstants;
import java.io.FileDescriptor;
import java.io.IOException;
import java.nio.ByteBuffer;
public class IO {
private IO() {
// not instantiable
}
public static void writeFully(FileDescriptor fd, ByteBuffer from) throws IOException {
while (from.hasRemaining()) {
try {
Os.write(fd, from);
} catch (ErrnoException e) {
if (e.errno != OsConstants.EINTR) {
throw new IOException(e);
}
}
}
}
public static void writeFully(FileDescriptor fd, byte[] buffer, int offset, int len) throws IOException {
writeFully(fd, ByteBuffer.wrap(buffer, offset, len));
}
}

View File

@@ -9,8 +9,8 @@ import android.media.MediaFormat;
import android.os.IBinder;
import android.view.Surface;
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -48,7 +48,7 @@ public class ScreenEncoder implements Device.RotationListener {
return rotationChanged.getAndSet(false);
}
public void streamScreen(Device device, FileDescriptor fd) throws IOException {
public void streamScreen(Device device, OutputStream outputStream) throws IOException {
MediaFormat format = createFormat(bitRate, frameRate, iFrameInterval);
device.setRotationListener(this);
boolean alive;
@@ -64,7 +64,7 @@ public class ScreenEncoder implements Device.RotationListener {
setDisplaySurface(display, surface, contentRect, videoRect);
codec.start();
try {
alive = encode(codec, fd);
alive = encode(codec, outputStream);
} finally {
codec.stop();
destroyDisplay(display);
@@ -77,7 +77,9 @@ public class ScreenEncoder implements Device.RotationListener {
}
}
private boolean encode(MediaCodec codec, FileDescriptor fd) throws IOException {
private boolean encode(MediaCodec codec, OutputStream outputStream) throws IOException {
@SuppressWarnings("checkstyle:MagicNumber")
byte[] buf = new byte[bitRate / 8]; // may contain up to 1 second of video
boolean eof = false;
MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
while (!consumeRotationChange() && !eof) {
@@ -89,8 +91,15 @@ public class ScreenEncoder implements Device.RotationListener {
break;
}
if (outputBufferId >= 0) {
ByteBuffer codecBuffer = codec.getOutputBuffer(outputBufferId);
IO.writeFully(fd, codecBuffer);
ByteBuffer outputBuffer = codec.getOutputBuffer(outputBufferId);
while (outputBuffer.hasRemaining()) {
int remaining = outputBuffer.remaining();
int len = Math.min(buf.length, remaining);
// the outputBuffer is probably direct (it has no underlying array), and LocalSocket does not expose channels,
// so we must copy the data locally to write them manually to the output stream
outputBuffer.get(buf, 0, len);
outputStream.write(buf, 0, len);
}
}
} finally {
if (outputBufferId >= 0) {

View File

@@ -21,7 +21,7 @@ public final class Server {
try {
// synchronous
screenEncoder.streamScreen(device, connection.getFd());
screenEncoder.streamScreen(device, connection.getOutputStream());
} catch (IOException e) {
// this is expected on close
Ln.d("Screen streaming stopped");