From 45fe6b602b4c050c5b1fba87cec7160093052af3 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Sun, 7 Apr 2024 16:01:26 +0200 Subject: [PATCH] Add scrcpy window without video playback Add the possibility to solely control the device without screen mirroring: scrcpy --no-video --no-audio This is different from OTG mode, which does not require USB debugging at all. Here, the standard mode is used but with the possibility to disable video playback. By default, always open a window (even without video playback), and add an option --no-window. Fixes #4727 Fixes #4793 PR #4868 --- app/scrcpy.1 | 4 ++ app/src/cli.c | 51 ++++++++++++++--- app/src/display.c | 36 +++++++++++- app/src/display.h | 3 +- app/src/input_manager.c | 59 ++++++++++--------- app/src/options.c | 1 + app/src/options.h | 1 + app/src/scrcpy.c | 26 +++++---- app/src/screen.c | 123 ++++++++++++++++++++++++++++++++++------ app/src/screen.h | 4 ++ 10 files changed, 243 insertions(+), 65 deletions(-) diff --git a/app/scrcpy.1 b/app/scrcpy.1 index eb09f530..f9ef3498 100644 --- a/app/scrcpy.1 +++ b/app/scrcpy.1 @@ -316,6 +316,10 @@ Disable video forwarding. .B \-\-no\-video\-playback Disable video playback on the computer. +.TP +.B \-\-no\-window +Disable scrcpy window. Implies --no-video-playback and --no-control. + .TP .BI "\-\-orientation " value Same as --display-orientation=value --record-orientation=value. diff --git a/app/src/cli.c b/app/src/cli.c index b1cc62ac..0caeea5c 100644 --- a/app/src/cli.c +++ b/app/src/cli.c @@ -97,6 +97,7 @@ enum { OPT_MOUSE, OPT_HID_KEYBOARD_DEPRECATED, OPT_HID_MOUSE_DEPRECATED, + OPT_NO_WINDOW, }; struct sc_option { @@ -566,6 +567,12 @@ static const struct sc_option options[] = { .longopt = "no-video-playback", .text = "Disable video playback on the computer.", }, + { + .longopt_id = OPT_NO_WINDOW, + .longopt = "no-window", + .text = "Disable scrcpy window. Implies --no-video-playback and " + "--no-control.", + }, { .longopt_id = OPT_ORIENTATION, .longopt = "orientation", @@ -2486,6 +2493,9 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[], case OPT_CAMERA_HIGH_SPEED: opts->camera_high_speed = true; break; + case OPT_NO_WINDOW: + opts->window = false; + break; default: // getopt prints the error message on stderr return false; @@ -2523,6 +2533,12 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[], v4l2 = !!opts->v4l2_device; #endif + if (!opts->window) { + // Without window, there cannot be any video playback or control + opts->video_playback = false; + opts->control = false; + } + if (!opts->video) { opts->video_playback = false; // Do not power on the device on start if video capture is disabled @@ -2544,8 +2560,8 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[], opts->audio = false; } - if (!opts->video && !opts->audio && !otg) { - LOGE("No video, no audio, no OTG: nothing to do"); + if (!opts->video && !opts->audio && !opts->control && !otg) { + LOGE("No video, no audio, no control, no OTG: nothing to do"); return false; } @@ -2569,6 +2585,11 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[], #ifdef HAVE_V4L2 if (v4l2) { + if (!opts->video) { + LOGE("V4L2 sink requires video capture, but --no-video was set."); + return false; + } + if (opts->lock_video_orientation == SC_LOCK_VIDEO_ORIENTATION_UNLOCKED) { LOGI("Video orientation is locked for v4l2 sink. " @@ -2588,13 +2609,25 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[], } #endif - if (opts->keyboard_input_mode == SC_KEYBOARD_INPUT_MODE_AUTO) { - opts->keyboard_input_mode = otg ? SC_KEYBOARD_INPUT_MODE_AOA - : SC_KEYBOARD_INPUT_MODE_SDK; - } - if (opts->mouse_input_mode == SC_MOUSE_INPUT_MODE_AUTO) { - opts->mouse_input_mode = otg ? SC_MOUSE_INPUT_MODE_AOA - : SC_MOUSE_INPUT_MODE_SDK; + if (opts->control) { + if (opts->keyboard_input_mode == SC_KEYBOARD_INPUT_MODE_AUTO) { + opts->keyboard_input_mode = otg ? SC_KEYBOARD_INPUT_MODE_AOA + : SC_KEYBOARD_INPUT_MODE_SDK; + } + if (opts->mouse_input_mode == SC_MOUSE_INPUT_MODE_AUTO) { + if (otg) { + opts->mouse_input_mode = SC_MOUSE_INPUT_MODE_AOA; + } else if (!opts->video_playback) { + LOGI("No video mirroring, mouse mode switched to UHID"); + opts->mouse_input_mode = SC_MOUSE_INPUT_MODE_UHID; + } else { + opts->mouse_input_mode = SC_MOUSE_INPUT_MODE_SDK; + } + } else if (opts->mouse_input_mode == SC_MOUSE_INPUT_MODE_SDK + && !opts->video_playback) { + LOGE("SDK mouse mode requires video playback. Try --mouse=uhid."); + return false; + } } if (otg) { diff --git a/app/src/display.c b/app/src/display.c index 25c23265..9f5fb0c6 100644 --- a/app/src/display.c +++ b/app/src/display.c @@ -5,8 +5,30 @@ #include "util/log.h" +static bool +sc_display_init_novideo_icon(struct sc_display *display, + SDL_Surface *icon_novideo) { + assert(icon_novideo); + + if (SDL_RenderSetLogicalSize(display->renderer, + icon_novideo->w, icon_novideo->h)) { + LOGW("Could not set renderer logical size: %s", SDL_GetError()); + // don't fail + } + + display->texture = SDL_CreateTextureFromSurface(display->renderer, + icon_novideo); + if (!display->texture) { + LOGE("Could not create texture: %s", SDL_GetError()); + return false; + } + + return true; +} + bool -sc_display_init(struct sc_display *display, SDL_Window *window, bool mipmaps) { +sc_display_init(struct sc_display *display, SDL_Window *window, + SDL_Surface *icon_novideo, bool mipmaps) { display->renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED); if (!display->renderer) { @@ -68,6 +90,18 @@ sc_display_init(struct sc_display *display, SDL_Window *window, bool mipmaps) { display->pending.frame = NULL; display->has_frame = false; + if (icon_novideo) { + // Without video, set a static scrcpy icon as window content + bool ok = sc_display_init_novideo_icon(display, icon_novideo); + if (!ok) { +#ifdef SC_DISPLAY_FORCE_OPENGL_CORE_PROFILE + SDL_GL_DeleteContext(display->gl_context); +#endif + SDL_DestroyRenderer(display->renderer); + return false; + } + } + return true; } diff --git a/app/src/display.h b/app/src/display.h index 590715ee..064bb7bf 100644 --- a/app/src/display.h +++ b/app/src/display.h @@ -44,7 +44,8 @@ enum sc_display_result { }; bool -sc_display_init(struct sc_display *display, SDL_Window *window, bool mipmaps); +sc_display_init(struct sc_display *display, SDL_Window *window, + SDL_Surface *icon_novideo, bool mipmaps); void sc_display_destroy(struct sc_display *display); diff --git a/app/src/input_manager.c b/app/src/input_manager.c index cb606d40..3a5fc6ed 100644 --- a/app/src/input_manager.c +++ b/app/src/input_manager.c @@ -403,6 +403,7 @@ sc_input_manager_process_key(struct sc_input_manager *im, // controller is NULL if --no-control is requested bool control = im->controller; bool paused = im->screen->paused; + bool video = im->screen->video; SDL_Keycode keycode = event->keysym.sym; uint16_t mod = event->keysym.mod; @@ -462,13 +463,13 @@ sc_input_manager_process_key(struct sc_input_manager *im, } return; case SDLK_z: - if (down && !repeat) { + if (video && down && !repeat) { sc_screen_set_paused(im->screen, !shift); } return; case SDLK_DOWN: if (shift) { - if (!repeat && down) { + if (video && !repeat && down) { apply_orientation_transform(im, SC_ORIENTATION_FLIP_180); } @@ -479,7 +480,7 @@ sc_input_manager_process_key(struct sc_input_manager *im, return; case SDLK_UP: if (shift) { - if (!repeat && down) { + if (video && !repeat && down) { apply_orientation_transform(im, SC_ORIENTATION_FLIP_180); } @@ -489,7 +490,7 @@ sc_input_manager_process_key(struct sc_input_manager *im, } return; case SDLK_LEFT: - if (!repeat && down) { + if (video && !repeat && down) { if (shift) { apply_orientation_transform(im, SC_ORIENTATION_FLIP_0); @@ -500,7 +501,7 @@ sc_input_manager_process_key(struct sc_input_manager *im, } return; case SDLK_RIGHT: - if (!repeat && down) { + if (video && !repeat && down) { if (shift) { apply_orientation_transform(im, SC_ORIENTATION_FLIP_0); @@ -533,22 +534,22 @@ sc_input_manager_process_key(struct sc_input_manager *im, } return; case SDLK_f: - if (!shift && !repeat && down) { + if (video && !shift && !repeat && down) { sc_screen_switch_fullscreen(im->screen); } return; case SDLK_w: - if (!shift && !repeat && down) { + if (video && !shift && !repeat && down) { sc_screen_resize_to_fit(im->screen); } return; case SDLK_g: - if (!shift && !repeat && down) { + if (video && !shift && !repeat && down) { sc_screen_resize_to_pixel_perfect(im->screen); } return; case SDLK_i: - if (!shift && !repeat && down) { + if (video && !shift && !repeat && down) { switch_fps_counter_state(im); } return; @@ -625,6 +626,23 @@ sc_input_manager_process_key(struct sc_input_manager *im, im->kp->ops->process_key(im->kp, &evt, ack_to_wait); } +static struct sc_position +sc_input_manager_get_position(struct sc_input_manager *im, int32_t x, + int32_t y) { + if (im->mp->relative_mode) { + // No absolute position + return (struct sc_position) { + .screen_size = {0, 0}, + .point = {0, 0}, + }; + } + + return (struct sc_position) { + .screen_size = im->screen->frame_size, + .point = sc_screen_convert_window_to_frame_coords(im->screen, x, y), + }; +} + static void sc_input_manager_process_mouse_motion(struct sc_input_manager *im, const SDL_MouseMotionEvent *event) { @@ -634,12 +652,7 @@ sc_input_manager_process_mouse_motion(struct sc_input_manager *im, } struct sc_mouse_motion_event evt = { - .position = { - .screen_size = im->screen->frame_size, - .point = sc_screen_convert_window_to_frame_coords(im->screen, - event->x, - event->y), - }, + .position = sc_input_manager_get_position(im, event->x, event->y), .pointer_id = im->forward_all_clicks ? POINTER_ID_MOUSE : POINTER_ID_GENERIC_FINGER, .xrel = event->xrel, @@ -735,7 +748,8 @@ sc_input_manager_process_mouse_button(struct sc_input_manager *im, } // double-click on black borders resize to fit the device screen - if (event->button == SDL_BUTTON_LEFT && event->clicks == 2) { + bool video = im->screen->video; + if (video && event->button == SDL_BUTTON_LEFT && event->clicks == 2) { int32_t x = event->x; int32_t y = event->y; sc_screen_hidpi_scale_coords(im->screen, &x, &y); @@ -759,12 +773,7 @@ sc_input_manager_process_mouse_button(struct sc_input_manager *im, uint32_t sdl_buttons_state = SDL_GetMouseState(NULL, NULL); struct sc_mouse_click_event evt = { - .position = { - .screen_size = im->screen->frame_size, - .point = sc_screen_convert_window_to_frame_coords(im->screen, - event->x, - event->y), - }, + .position = sc_input_manager_get_position(im, event->x, event->y), .action = sc_action_from_sdl_mousebutton_type(event->type), .button = sc_mouse_button_from_sdl(event->button), .pointer_id = im->forward_all_clicks ? POINTER_ID_MOUSE @@ -839,11 +848,7 @@ sc_input_manager_process_mouse_wheel(struct sc_input_manager *im, uint32_t buttons = SDL_GetMouseState(&mouse_x, &mouse_y); struct sc_mouse_scroll_event evt = { - .position = { - .screen_size = im->screen->frame_size, - .point = sc_screen_convert_window_to_frame_coords(im->screen, - mouse_x, mouse_y), - }, + .position = sc_input_manager_get_position(im, mouse_x, mouse_y), #if SDL_VERSION_ATLEAST(2, 0, 18) .hscroll = CLAMP(event->preciseX, -1.0f, 1.0f), .vscroll = CLAMP(event->preciseY, -1.0f, 1.0f), diff --git a/app/src/options.c b/app/src/options.c index 7a885aa5..d6bf9158 100644 --- a/app/src/options.c +++ b/app/src/options.c @@ -89,6 +89,7 @@ const struct scrcpy_options scrcpy_options_default = { .kill_adb_on_close = false, .camera_high_speed = false, .list = 0, + .window = true, }; enum sc_orientation diff --git a/app/src/options.h b/app/src/options.h index 5445e7c8..1fb61ddf 100644 --- a/app/src/options.h +++ b/app/src/options.h @@ -279,6 +279,7 @@ struct scrcpy_options { #define SC_OPTION_LIST_CAMERAS 0x4 #define SC_OPTION_LIST_CAMERA_SIZES 0x8 uint8_t list; + bool window; }; extern const struct scrcpy_options scrcpy_options_default; diff --git a/app/src/scrcpy.c b/app/src/scrcpy.c index 537562f4..5e7b19fd 100644 --- a/app/src/scrcpy.c +++ b/app/src/scrcpy.c @@ -408,7 +408,7 @@ scrcpy(struct scrcpy_options *options) { return SCRCPY_EXIT_FAILURE; } - if (options->video_playback) { + if (options->window) { // Set hints before starting the server thread to avoid race conditions // in SDL sdl_set_hints(options->render_driver); @@ -430,7 +430,7 @@ scrcpy(struct scrcpy_options *options) { assert(!options->video_playback || options->video); assert(!options->audio_playback || options->audio); - if (options->video_playback || + if (options->window || (options->control && options->clipboard_autosync)) { // Initialize the video subsystem even if --no-video or // --no-video-playback is passed so that clipboard synchronization @@ -684,11 +684,12 @@ scrcpy(struct scrcpy_options *options) { // There is a controller if and only if control is enabled assert(options->control == !!controller); - if (options->video_playback) { + if (options->window) { const char *window_title = options->window_title ? options->window_title : info->device_name; struct sc_screen_params screen_params = { + .video = options->video_playback, .controller = controller, .fp = fp, .kp = kp, @@ -710,12 +711,15 @@ scrcpy(struct scrcpy_options *options) { .start_fps_counter = options->start_fps_counter, }; - struct sc_frame_source *src = &s->video_decoder.frame_source; - if (options->display_buffer) { - sc_delay_buffer_init(&s->display_buffer, options->display_buffer, - true); - sc_frame_source_add_sink(src, &s->display_buffer.frame_sink); - src = &s->display_buffer.frame_source; + struct sc_frame_source *src; + if (options->video_playback) { + src = &s->video_decoder.frame_source; + if (options->display_buffer) { + sc_delay_buffer_init(&s->display_buffer, + options->display_buffer, true); + sc_frame_source_add_sink(src, &s->display_buffer.frame_sink); + src = &s->display_buffer.frame_source; + } } if (!sc_screen_init(&s->screen, &screen_params)) { @@ -723,7 +727,9 @@ scrcpy(struct scrcpy_options *options) { } screen_initialized = true; - sc_frame_source_add_sink(src, &s->screen.frame_sink); + if (options->video_playback) { + sc_frame_source_add_sink(src, &s->screen.frame_sink); + } } if (options->audio_playback) { diff --git a/app/src/screen.c b/app/src/screen.c index 351eb3fb..56f13f99 100644 --- a/app/src/screen.c +++ b/app/src/screen.c @@ -205,6 +205,8 @@ sc_screen_toggle_mouse_capture(struct sc_screen *screen) { static void sc_screen_update_content_rect(struct sc_screen *screen) { + assert(screen->video); + int dw; int dh; SDL_GL_GetDrawableSize(screen->window, &dw, &dh); @@ -246,6 +248,8 @@ sc_screen_update_content_rect(struct sc_screen *screen) { // changed, so that the content rectangle is recomputed static void sc_screen_render(struct sc_screen *screen, bool update_content_rect) { + assert(screen->video); + if (update_content_rect) { sc_screen_update_content_rect(screen); } @@ -255,6 +259,13 @@ sc_screen_render(struct sc_screen *screen, bool update_content_rect) { (void) res; // any error already logged } +static void +sc_screen_render_novideo(struct sc_screen *screen) { + enum sc_display_result res = + sc_display_render(&screen->display, NULL, SC_ORIENTATION_0); + (void) res; // any error already logged +} + #if defined(__APPLE__) || defined(__WINDOWS__) # define CONTINUOUS_RESIZING_WORKAROUND #endif @@ -268,6 +279,8 @@ sc_screen_render(struct sc_screen *screen, bool update_content_rect) { static int event_watcher(void *data, SDL_Event *event) { struct sc_screen *screen = data; + assert(screen->video); + if (event->type == SDL_WINDOWEVENT && event->window.event == SDL_WINDOWEVENT_RESIZED) { // In practice, it seems to always be called from the same thread in @@ -326,6 +339,7 @@ sc_screen_frame_sink_close(struct sc_frame_sink *sink) { static bool sc_screen_frame_sink_push(struct sc_frame_sink *sink, const AVFrame *frame) { struct sc_screen *screen = DOWNCAST(sink); + assert(screen->video); bool previous_skipped; bool ok = sc_frame_buffer_push(&screen->fb, frame, &previous_skipped); @@ -364,6 +378,9 @@ sc_screen_init(struct sc_screen *screen, screen->mouse_capture_key_pressed = 0; screen->paused = false; screen->resume_frame = NULL; + screen->orientation = SC_ORIENTATION_0; + + screen->video = params->video; screen->req.x = params->window_x; screen->req.y = params->window_y; @@ -381,41 +398,75 @@ sc_screen_init(struct sc_screen *screen, goto error_destroy_frame_buffer; } - screen->orientation = params->orientation; - if (screen->orientation != SC_ORIENTATION_0) { - LOGI("Initial display orientation set to %s", - sc_orientation_get_name(screen->orientation)); + if (screen->video) { + screen->orientation = params->orientation; + if (screen->orientation != SC_ORIENTATION_0) { + LOGI("Initial display orientation set to %s", + sc_orientation_get_name(screen->orientation)); + } } - uint32_t window_flags = SDL_WINDOW_HIDDEN - | SDL_WINDOW_RESIZABLE - | SDL_WINDOW_ALLOW_HIGHDPI; + uint32_t window_flags = SDL_WINDOW_ALLOW_HIGHDPI; if (params->always_on_top) { window_flags |= SDL_WINDOW_ALWAYS_ON_TOP; } if (params->window_borderless) { window_flags |= SDL_WINDOW_BORDERLESS; } + if (params->video) { + // The window will be shown on first frame + window_flags |= SDL_WINDOW_HIDDEN + | SDL_WINDOW_RESIZABLE; + } + + const char *title = params->window_title; + assert(title); + + int x = SDL_WINDOWPOS_UNDEFINED; + int y = SDL_WINDOWPOS_UNDEFINED; + int width = 256; + int height = 256; + if (params->window_x != SC_WINDOW_POSITION_UNDEFINED) { + x = params->window_x; + } + if (params->window_y != SC_WINDOW_POSITION_UNDEFINED) { + y = params->window_y; + } + if (params->window_width) { + width = params->window_width; + } + if (params->window_height) { + height = params->window_height; + } // The window will be positioned and sized on first video frame - screen->window = - SDL_CreateWindow(params->window_title, 0, 0, 0, 0, window_flags); + screen->window = SDL_CreateWindow(title, x, y, width, height, window_flags); if (!screen->window) { LOGE("Could not create window: %s", SDL_GetError()); goto error_destroy_fps_counter; } - ok = sc_display_init(&screen->display, screen->window, params->mipmaps); - if (!ok) { - goto error_destroy_window; - } - SDL_Surface *icon = scrcpy_icon_load(); if (icon) { SDL_SetWindowIcon(screen->window, icon); - scrcpy_icon_destroy(icon); - } else { + } else if (params->video) { + // just a warning LOGW("Could not load icon"); + } else { + // without video, the icon is used as window content, it must be present + LOGE("Could not load icon"); + goto error_destroy_fps_counter; + } + + SDL_Surface *icon_novideo = params->video ? NULL : icon; + bool mipmaps = params->video && params->mipmaps; + ok = sc_display_init(&screen->display, screen->window, icon_novideo, + mipmaps); + if (icon) { + scrcpy_icon_destroy(icon); + } + if (!ok) { + goto error_destroy_window; } screen->frame = av_frame_alloc(); @@ -439,7 +490,9 @@ sc_screen_init(struct sc_screen *screen, sc_input_manager_init(&screen->im, &im_params); #ifdef CONTINUOUS_RESIZING_WORKAROUND - SDL_AddEventWatch(event_watcher, screen); + if (screen->video) { + SDL_AddEventWatch(event_watcher, screen); + } #endif static const struct sc_frame_sink_ops ops = { @@ -454,6 +507,11 @@ sc_screen_init(struct sc_screen *screen, screen->open = false; #endif + if (!screen->video && sc_screen_is_relative_mode(screen)) { + // Capture mouse immediately if video mirroring is disabled + sc_screen_set_mouse_capture(screen, true); + } + return true; error_destroy_display: @@ -524,6 +582,8 @@ sc_screen_destroy(struct sc_screen *screen) { static void resize_for_content(struct sc_screen *screen, struct sc_size old_content_size, struct sc_size new_content_size) { + assert(screen->video); + struct sc_size window_size = get_window_size(screen); struct sc_size target_size = { .width = (uint32_t) window_size.width * new_content_size.width @@ -537,6 +597,8 @@ resize_for_content(struct sc_screen *screen, struct sc_size old_content_size, static void set_content_size(struct sc_screen *screen, struct sc_size new_content_size) { + assert(screen->video); + if (!screen->fullscreen && !screen->maximized && !screen->minimized) { resize_for_content(screen, screen->content_size, new_content_size); } else if (!screen->resize_pending) { @@ -551,6 +613,8 @@ set_content_size(struct sc_screen *screen, struct sc_size new_content_size) { static void apply_pending_resize(struct sc_screen *screen) { + assert(screen->video); + assert(!screen->fullscreen); assert(!screen->maximized); assert(!screen->minimized); @@ -564,6 +628,8 @@ apply_pending_resize(struct sc_screen *screen) { void sc_screen_set_orientation(struct sc_screen *screen, enum sc_orientation orientation) { + assert(screen->video); + if (orientation == screen->orientation) { return; } @@ -598,6 +664,8 @@ sc_screen_init_size(struct sc_screen *screen) { // recreate the texture and resize the window if the frame size has changed static enum sc_display_result prepare_for_frame(struct sc_screen *screen, struct sc_size new_frame_size) { + assert(screen->video); + if (screen->frame_size.width == new_frame_size.width && screen->frame_size.height == new_frame_size.height) { return SC_DISPLAY_RESULT_OK; @@ -617,6 +685,8 @@ prepare_for_frame(struct sc_screen *screen, struct sc_size new_frame_size) { static bool sc_screen_apply_frame(struct sc_screen *screen) { + assert(screen->video); + sc_fps_counter_add_rendered_frame(&screen->fps_counter); AVFrame *frame = screen->frame; @@ -656,6 +726,8 @@ sc_screen_apply_frame(struct sc_screen *screen) { static bool sc_screen_update_frame(struct sc_screen *screen) { + assert(screen->video); + if (screen->paused) { if (!screen->resume_frame) { screen->resume_frame = av_frame_alloc(); @@ -677,6 +749,8 @@ sc_screen_update_frame(struct sc_screen *screen) { void sc_screen_set_paused(struct sc_screen *screen, bool paused) { + assert(screen->video); + if (!paused && !screen->paused) { // nothing to do return; @@ -704,6 +778,8 @@ sc_screen_set_paused(struct sc_screen *screen, bool paused) { void sc_screen_switch_fullscreen(struct sc_screen *screen) { + assert(screen->video); + uint32_t new_mode = screen->fullscreen ? 0 : SDL_WINDOW_FULLSCREEN_DESKTOP; if (SDL_SetWindowFullscreen(screen->window, new_mode)) { LOGW("Could not switch fullscreen mode: %s", SDL_GetError()); @@ -721,6 +797,8 @@ sc_screen_switch_fullscreen(struct sc_screen *screen) { void sc_screen_resize_to_fit(struct sc_screen *screen) { + assert(screen->video); + if (screen->fullscreen || screen->maximized || screen->minimized) { return; } @@ -745,6 +823,8 @@ sc_screen_resize_to_fit(struct sc_screen *screen) { void sc_screen_resize_to_pixel_perfect(struct sc_screen *screen) { + assert(screen->video); + if (screen->fullscreen || screen->minimized) { return; } @@ -788,6 +868,13 @@ sc_screen_handle_event(struct sc_screen *screen, const SDL_Event *event) { return true; } case SDL_WINDOWEVENT: + if (!screen->video + && event->window.event == SDL_WINDOWEVENT_EXPOSED) { + sc_screen_render_novideo(screen); + } + + // !video implies !has_frame + assert(screen->video || !screen->has_frame); if (!screen->has_frame) { // Do nothing return true; @@ -891,6 +978,8 @@ sc_screen_handle_event(struct sc_screen *screen, const SDL_Event *event) { struct sc_point sc_screen_convert_drawable_to_frame_coords(struct sc_screen *screen, int32_t x, int32_t y) { + assert(screen->video); + enum sc_orientation orientation = screen->orientation; int32_t w = screen->content_size.width; diff --git a/app/src/screen.h b/app/src/screen.h index 361ce455..3e205cdc 100644 --- a/app/src/screen.h +++ b/app/src/screen.h @@ -26,6 +26,8 @@ struct sc_screen { bool open; // track the open/close state to assert correct behavior #endif + bool video; + struct sc_display display; struct sc_input_manager im; struct sc_frame_buffer fb; @@ -70,6 +72,8 @@ struct sc_screen { }; struct sc_screen_params { + bool video; + struct sc_controller *controller; struct sc_file_pusher *fp; struct sc_key_processor *kp;