scrcpy.c 7.3 KB
Newer Older
R
Romain Vimont 已提交
1
#include "scrcpy.h"
R
Romain Vimont 已提交
2

R
Romain Vimont 已提交
3 4
#include <stdio.h>
#include <string.h>
R
Romain Vimont 已提交
5 6
#include <unistd.h>
#include <libavformat/avformat.h>
R
Romain Vimont 已提交
7
#include <sys/time.h>
R
Romain Vimont 已提交
8 9
#include <SDL2/SDL.h>

R
Romain Vimont 已提交
10 11
#include "command.h"
#include "common.h"
R
Romain Vimont 已提交
12
#include "controller.h"
R
Romain Vimont 已提交
13
#include "decoder.h"
14
#include "device.h"
R
Romain Vimont 已提交
15 16
#include "events.h"
#include "frames.h"
R
Romain Vimont 已提交
17
#include "fpscounter.h"
18
#include "inputmanager.h"
R
Romain Vimont 已提交
19
#include "log.h"
R
Romain Vimont 已提交
20
#include "lockutil.h"
21
#include "net.h"
22
#include "screen.h"
R
Romain Vimont 已提交
23
#include "server.h"
R
Romain Vimont 已提交
24
#include "tinyxpm.h"
25
#include "installer.h"
R
Romain Vimont 已提交
26

27
static struct server server = SERVER_INITIALIZER;
28
static struct screen screen = SCREEN_INITIALIZER;
R
Romain Vimont 已提交
29 30 31
static struct frames frames;
static struct decoder decoder;
static struct controller controller;
32
static struct installer installer;
R
Romain Vimont 已提交
33

34 35
static struct input_manager input_manager = {
    .controller = &controller,
36
    .frames = &frames,
37 38 39
    .screen = &screen,
};

40 41 42 43 44 45 46 47 48 49
#if defined(__APPLE__) || defined(__WINDOWS__)
# define CONTINUOUS_RESIZING_WORKAROUND
#endif

#ifdef CONTINUOUS_RESIZING_WORKAROUND
// On Windows and MacOS, resizing blocks the event loop, so resizing events are
// not triggered. As a workaround, handle them in an event handler.
//
// <https://bugzilla.libsdl.org/show_bug.cgi?id=2077>
// <https://stackoverflow.com/a/40693139/1987178>
R
Romain Vimont 已提交
50
static int event_watcher(void *data, SDL_Event *event) {
51 52 53 54 55 56 57 58
    if (event->type == SDL_WINDOWEVENT && event->window.event == SDL_WINDOWEVENT_RESIZED) {
        // called from another thread, not very safe, but it's a workaround!
        screen_render(&screen);
    }
    return 0;
}
#endif

R
Romain Vimont 已提交
59
static void event_loop(void) {
60
#ifdef CONTINUOUS_RESIZING_WORKAROUND
R
Romain Vimont 已提交
61
    SDL_AddEventWatch(event_watcher, NULL);
62
#endif
R
Romain Vimont 已提交
63 64 65
    SDL_Event event;
    while (SDL_WaitEvent(&event)) {
        switch (event.type) {
R
Romain Vimont 已提交
66
            case EVENT_DECODER_STOPPED:
R
Romain Vimont 已提交
67
                LOGD("Video decoder stopped");
R
Romain Vimont 已提交
68
                return;
R
Romain Vimont 已提交
69
            case SDL_QUIT:
R
Romain Vimont 已提交
70
                LOGD("User requested to quit");
R
Romain Vimont 已提交
71
                return;
R
Romain Vimont 已提交
72
            case EVENT_NEW_FRAME:
R
Romain Vimont 已提交
73 74 75 76 77
                if (!screen.has_frame) {
                    screen.has_frame = SDL_TRUE;
                    // this is the very first frame, show the window
                    screen_show_window(&screen);
                }
R
Romain Vimont 已提交
78
                if (!screen_update_frame(&screen, &frames)) {
R
Romain Vimont 已提交
79 80 81 82 83
                    return;
                }
                break;
            case SDL_WINDOWEVENT:
                switch (event.window.event) {
R
Romain Vimont 已提交
84 85 86 87
                    case SDL_WINDOWEVENT_EXPOSED:
                    case SDL_WINDOWEVENT_SIZE_CHANGED:
                        screen_render(&screen);
                        break;
R
Romain Vimont 已提交
88 89
                }
                break;
R
Romain Vimont 已提交
90
            case SDL_TEXTINPUT:
91
                input_manager_process_text_input(&input_manager, &event.text);
R
Romain Vimont 已提交
92 93 94
                break;
            case SDL_KEYDOWN:
            case SDL_KEYUP:
95
                input_manager_process_key(&input_manager, &event.key);
R
Romain Vimont 已提交
96 97
                break;
            case SDL_MOUSEMOTION:
98
                input_manager_process_mouse_motion(&input_manager, &event.motion);
R
Romain Vimont 已提交
99
                break;
R
Romain Vimont 已提交
100
            case SDL_MOUSEWHEEL:
101
                input_manager_process_mouse_wheel(&input_manager, &event.wheel);
R
Romain Vimont 已提交
102 103
                break;
            case SDL_MOUSEBUTTONDOWN:
R
Romain Vimont 已提交
104
            case SDL_MOUSEBUTTONUP:
105
                input_manager_process_mouse_button(&input_manager, &event.button);
R
Romain Vimont 已提交
106
                break;
107 108 109
            case SDL_DROPFILE:
                installer_install_apk(&installer, event.drop.file);
                break;
R
Romain Vimont 已提交
110 111 112 113
        }
    }
}

114
static process_t set_show_touches_enabled(const char *serial, SDL_bool enabled) {
115 116 117 118
    const char *value = enabled ? "1" : "0";
    const char *const adb_cmd[] = {
        "shell", "settings", "put", "system", "show_touches", value
    };
119 120 121 122 123 124
    return adb_execute(serial, adb_cmd, ARRAY_LEN(adb_cmd));
}

static void wait_show_touches(process_t process) {
    // reap the process, ignore the result
    process_check_success(process, "show_touches");
125 126
}

127 128
SDL_bool scrcpy(const struct scrcpy_options *options) {
    if (!server_start(&server, options->serial, options->port,
R
Romain Vimont 已提交
129
                      options->max_size, options->bit_rate, options->crop)) {
R
Romain Vimont 已提交
130 131 132
        return SDL_FALSE;
    }

R
Romain Vimont 已提交
133
    process_t proc_show_touches = PROCESS_NONE;
134 135 136 137 138 139 140
    SDL_bool show_touches_waited;
    if (options->show_touches) {
        LOGI("Enable show_touches");
        proc_show_touches = set_show_touches_enabled(options->serial, SDL_TRUE);
        show_touches_waited = SDL_FALSE;
    }

141 142
    SDL_bool ret = SDL_TRUE;

143 144 145 146
    if (!SDL_SetHint(SDL_HINT_NO_SIGNAL_HANDLERS, "1")) {
        LOGW("Cannot request to keep default signal handlers");
    }

R
Romain Vimont 已提交
147 148 149 150 151
    if (!sdl_init_and_configure()) {
        ret = SDL_FALSE;
        goto finally_destroy_server;
    }

152
    socket_t device_socket = server_connect_to(&server);
153
    if (device_socket == INVALID_SOCKET) {
R
Romain Vimont 已提交
154
        server_stop(&server);
155 156
        ret = SDL_FALSE;
        goto finally_destroy_server;
R
Romain Vimont 已提交
157 158 159
    }

    char device_name[DEVICE_NAME_FIELD_LENGTH];
160
    struct size frame_size;
R
Romain Vimont 已提交
161 162 163 164

    // screenrecord does not send frames when the screen content does not change
    // therefore, we transmit the screen size before the video stream, to be able
    // to init the window immediately
165
    if (!device_read_info(device_socket, device_name, &frame_size)) {
R
Romain Vimont 已提交
166
        server_stop(&server);
167 168
        ret = SDL_FALSE;
        goto finally_destroy_server;
R
Romain Vimont 已提交
169 170 171
    }

    if (!frames_init(&frames)) {
R
Romain Vimont 已提交
172
        server_stop(&server);
173 174
        ret = SDL_FALSE;
        goto finally_destroy_server;
R
Romain Vimont 已提交
175 176
    }

177 178 179 180 181 182
    if (!installer_init(&installer, server.serial)) {
        ret = SDL_FALSE;
        server_stop(&server);
        goto finally_destroy_frames;
    }

R
Romain Vimont 已提交
183
    decoder_init(&decoder, &frames, device_socket);
R
Romain Vimont 已提交
184 185 186 187 188

    // now we consumed the header values, the socket receives the video stream
    // start the decoder
    if (!decoder_start(&decoder)) {
        ret = SDL_FALSE;
R
Romain Vimont 已提交
189
        server_stop(&server);
190
        goto finally_destroy_installer;
R
Romain Vimont 已提交
191 192 193 194
    }

    if (!controller_init(&controller, device_socket)) {
        ret = SDL_FALSE;
195
        goto finally_stop_decoder;
R
Romain Vimont 已提交
196 197 198 199
    }

    if (!controller_start(&controller)) {
        ret = SDL_FALSE;
200
        goto finally_destroy_controller;
R
Romain Vimont 已提交
201 202
    }

203
    if (!screen_init_rendering(&screen, device_name, frame_size)) {
R
Romain Vimont 已提交
204
        ret = SDL_FALSE;
205
        goto finally_stop_and_join_controller;
R
Romain Vimont 已提交
206
    }
R
Romain Vimont 已提交
207

208
    if (options->show_touches) {
209 210
        wait_show_touches(proc_show_touches);
        show_touches_waited = SDL_TRUE;
211
    }
R
Romain Vimont 已提交
212

213
    event_loop();
R
Romain Vimont 已提交
214
    LOGD("quit...");
215

216 217
    screen_destroy(&screen);

218
finally_stop_and_join_controller:
R
Romain Vimont 已提交
219 220
    controller_stop(&controller);
    controller_join(&controller);
221
finally_destroy_controller:
R
Romain Vimont 已提交
222
    controller_destroy(&controller);
223
finally_stop_decoder:
224
    decoder_stop(&decoder);
225
    // stop the server before decoder_join() to wake up the decoder
R
Romain Vimont 已提交
226
    server_stop(&server);
R
Romain Vimont 已提交
227
    decoder_join(&decoder);
228 229 230 231
finally_destroy_installer:
    installer_stop(&installer);
    installer_join(&installer);
    installer_destroy(&installer);
232
finally_destroy_frames:
R
Romain Vimont 已提交
233
    frames_destroy(&frames);
234
finally_destroy_server:
235 236 237 238 239 240 241 242 243 244
    if (options->show_touches) {
        if (!show_touches_waited) {
            // wait the process which enabled "show touches"
            wait_show_touches(proc_show_touches);
        }
        LOGI("Disable show_touches");
        proc_show_touches = set_show_touches_enabled(options->serial, SDL_FALSE);
        wait_show_touches(proc_show_touches);
    }

245
    server_destroy(&server);
R
Romain Vimont 已提交
246

R
Romain Vimont 已提交
247
    return ret;
R
Romain Vimont 已提交
248
}