disconnected

This commit is contained in:
Romain Vimont
2026-01-25 16:00:22 +01:00
parent 43f71abe80
commit 643ea64b63
6 changed files with 137 additions and 13 deletions

BIN
app/data/disconnected.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

View File

@@ -20,6 +20,7 @@ enum {
SC_EVENT_CONTROLLER_ERROR,
SC_EVENT_AOA_OPEN_ERROR,
SC_EVENT_DISCONNECTED_ICON_LOADED,
SC_EVENT_DISCONNECTED_TIMEOUT,
};
bool

View File

@@ -6,6 +6,7 @@
#include <SDL3/SDL_surface.h>
#define SC_ICON_FILENAME_SCRCPY "scrcpy.png"
#define SC_ICON_FILENAME_DISCONNECTED "disconnected.png"
SDL_Surface *
sc_icon_load(const char *filename);

View File

@@ -167,23 +167,41 @@ sdl_configure(bool video_playback, bool disable_screensaver) {
}
static enum scrcpy_exit_code
event_loop(struct scrcpy *s, bool has_screen) {
event_loop(struct scrcpy *s, bool has_screen, bool disconnected) {
SDL_Event event;
while (SDL_WaitEvent(&event)) {
switch (event.type) {
case SC_EVENT_DEVICE_DISCONNECTED:
if (disconnected) {
break;
}
LOGW("Device disconnected");
if (has_screen && !sc_screen_handle_event(&s->screen, &event)) {
return SCRCPY_EXIT_FAILURE;
}
return SCRCPY_EXIT_DISCONNECTED;
case SC_EVENT_DEMUXER_ERROR:
if (disconnected) {
break;
}
LOGE("Demuxer error");
return SCRCPY_EXIT_FAILURE;
case SC_EVENT_CONTROLLER_ERROR:
if (disconnected) {
break;
}
LOGE("Controller error");
return SCRCPY_EXIT_FAILURE;
case SC_EVENT_RECORDER_ERROR:
if (disconnected) {
break;
}
LOGE("Recorder error");
return SCRCPY_EXIT_FAILURE;
case SC_EVENT_AOA_OPEN_ERROR:
if (disconnected) {
break;
}
LOGE("AOA open error");
return SCRCPY_EXIT_FAILURE;
case SC_EVENT_TIME_LIMIT_REACHED:
@@ -192,6 +210,9 @@ event_loop(struct scrcpy *s, bool has_screen) {
case SDL_EVENT_QUIT:
LOGD("User requested to quit");
return SCRCPY_EXIT_SUCCESS;
case SC_EVENT_DISCONNECTED_TIMEOUT:
LOGD("Closing after device disconnection");
return SCRCPY_EXIT_DISCONNECTED;
case SC_EVENT_RUN_ON_MAIN_THREAD: {
sc_runnable_fn run = event.user.data1;
void *userdata = event.user.data2;
@@ -945,16 +966,9 @@ aoa_complete:
}
}
ret = event_loop(s, options->window);
ret = event_loop(s, options->window, false);
terminate_event_loop();
LOGD("quit...");
if (options->window) {
// Close the window immediately on closing, because screen_destroy()
// may only be called once the video demuxer thread is joined (it may
// take time)
sc_screen_hide_window(&s->screen);
}
bool disconnected = ret == SCRCPY_EXIT_DISCONNECTED;
end:
if (timeout_started) {
@@ -999,6 +1013,24 @@ end:
sc_server_stop(&s->server);
}
if (options->window && ret != SCRCPY_EXIT_DISCONNECTED) {
// Close the window immediately, because sc_screen_destroy() may only be
// called once the video demuxer thread is joined (it may take time)
sc_screen_hide_window(&s->screen);
}
if (screen_initialized && options->window) {
if (disconnected) {
ret = event_loop(s, options->window, true);
sc_screen_interrupt_disconnect(&s->screen);
}
LOGD("Quit...");
// Close the window immediately, because sc_screen_destroy() may only be
// called once the video demuxer thread is joined (it may take time)
sc_screen_hide_window(&s->screen);
}
if (timeout_started) {
sc_timeout_join(&s->timeout);
}

View File

@@ -137,7 +137,7 @@ sc_screen_is_relative_mode(struct sc_screen *screen) {
static void
sc_screen_update_content_rect(struct sc_screen *screen) {
// Only upscale video frames, not icon
bool can_upscale = screen->video;
bool can_upscale = screen->video && !screen->disconnected;
struct sc_size render_size =
sc_sdl_get_render_output_size(screen->renderer);
@@ -163,7 +163,9 @@ sc_screen_render(struct sc_screen *screen, bool update_content_rect) {
bool ok = false;
SDL_Texture *texture = screen->tex.texture;
if (!texture) {
LOGW("No texture to render");
if (!screen->disconnected) {
LOGW("No texture to render");
}
goto end;
}
@@ -299,6 +301,8 @@ sc_screen_init(struct sc_screen *screen,
screen->paused = false;
screen->resume_frame = NULL;
screen->orientation = SC_ORIENTATION_0;
screen->disconnected = false;
screen->disconnect_started = false;
screen->video = params->video;
screen->camera = params->camera;
@@ -556,9 +560,19 @@ sc_screen_interrupt(struct sc_screen *screen) {
sc_fps_counter_interrupt(&screen->fps_counter);
}
void
sc_screen_interrupt_disconnect(struct sc_screen *screen) {
if (screen->disconnect_started) {
sc_disconnect_interrupt(&screen->disconnect);
}
}
void
sc_screen_join(struct sc_screen *screen) {
sc_fps_counter_join(&screen->fps_counter);
if (screen->disconnect_started) {
sc_disconnect_join(&screen->disconnect);
}
}
void
@@ -566,6 +580,9 @@ sc_screen_destroy(struct sc_screen *screen) {
#ifndef NDEBUG
assert(!screen->open);
#endif
if (screen->disconnect_started) {
sc_disconnect_destroy(&screen->disconnect);
}
sc_texture_destroy(&screen->tex);
av_frame_free(&screen->frame);
#ifdef SC_DISPLAY_FORCE_OPENGL_CORE_PROFILE
@@ -805,12 +822,37 @@ sc_screen_resize_to_pixel_perfect(struct sc_screen *screen) {
content_size.height);
}
static void
sc_disconnect_on_icon_loaded(struct sc_disconnect *d, SDL_Surface *icon,
void *userdata) {
(void) d;
(void) userdata;
bool ok = sc_push_event_with_data(SC_EVENT_DISCONNECTED_ICON_LOADED, icon);
if (!ok) {
sc_icon_destroy(icon);
}
}
static void
sc_disconnect_on_timeout(struct sc_disconnect *d, void *userdata) {
(void) d;
(void) userdata;
bool ok = sc_push_event(SC_EVENT_DISCONNECTED_TIMEOUT);
(void) ok; // ignore failure
}
bool
sc_screen_handle_event(struct sc_screen *screen, const SDL_Event *event) {
// !video implies !has_video_window
assert(screen->video || !screen->has_video_window);
switch (event->type) {
case SC_EVENT_NEW_FRAME: {
if (screen->disconnected) {
// ignore
return true;
}
bool ok = sc_screen_update_frame(screen);
if (!ok) {
LOGE("Frame update failed\n");
@@ -845,6 +887,44 @@ sc_screen_handle_event(struct sc_screen *screen, const SDL_Event *event) {
sc_screen_render(screen, true);
}
return true;
case SC_EVENT_DEVICE_DISCONNECTED:
if (screen->disconnected) {
return true;
}
screen->disconnected = true;
sc_texture_reset(&screen->tex);
sc_screen_render(screen, true);
sc_tick deadline = sc_tick_now() + SC_TICK_FROM_SEC(2);
static const struct sc_disconnect_callbacks cbs = {
.on_icon_loaded = sc_disconnect_on_icon_loaded,
.on_timeout = sc_disconnect_on_timeout,
};
bool ok =
sc_disconnect_start(&screen->disconnect, deadline, &cbs, NULL);
if (ok) {
screen->disconnect_started = true;
}
// else not fatal
return true;
case SC_EVENT_DISCONNECTED_ICON_LOADED: {
SDL_Surface *icon_disconnected = event->user.data1;
assert(icon_disconnected);
bool ok = sc_texture_set_from_surface(&screen->tex, icon_disconnected);
if (ok) {
screen->content_size.width = icon_disconnected->w;
screen->content_size.height = icon_disconnected->h;
sc_screen_render(screen, true);
} else {
// not fatal
LOGE("Could not set disconnected icon");
}
sc_icon_destroy(icon_disconnected);
return true;
}
}
if (sc_screen_is_relative_mode(screen)

View File

@@ -12,6 +12,7 @@
#include "controller.h"
#include "coords.h"
#include "disconnect.h"
#include "fps_counter.h"
#include "frame_buffer.h"
#include "input_manager.h"
@@ -77,6 +78,10 @@ struct sc_screen {
bool paused;
AVFrame *resume_frame;
bool disconnected;
bool disconnect_started;
struct sc_disconnect disconnect;
};
struct sc_screen_params {
@@ -116,10 +121,15 @@ bool
sc_screen_init(struct sc_screen *screen, const struct sc_screen_params *params);
// request to interrupt any inner thread
// must be called before screen_join()
// must be called before sc_screen_join()
void
sc_screen_interrupt(struct sc_screen *screen);
// request to interrupt the disconnected state (before closing the window)
// must be called before sc_screen_join();
void
sc_screen_interrupt_disconnect(struct sc_screen *screen);
// join any inner thread
void
sc_screen_join(struct sc_screen *screen);