Compare commits

...

6 Commits

Author SHA1 Message Date
2e425e6605 Fix HiDPI issues on secondary screen
Reset the renderer and texture on display scaling change.

FIXME problem with texture (see FIXME in code)

Co-authored-by: Louis Kruger <louisk@gmail.com>
2020-04-24 23:50:42 +02:00
f3b7712ea2 Add a mechanism to report errors on event handling
This paves the way to workaround HiDPI issues, which may recreate the
renderer and texture (which may fail) on window event.
2020-04-24 23:35:42 +02:00
04a31ef7b9 Remove HIDPI_SUPPORT compilation flag
We never need to build without HiDPI support.
2020-04-24 23:02:58 +02:00
a14840a515 Fix typo in comments 2020-04-24 23:01:58 +02:00
8581d6850b Stabilize auto-resize
The window dimensions are integers, so resizing to fit the content may
not be exact.

When computing the optimal size, it could cause to reduce alternatively
the width and height by few pixels, making the "optimal size" unstable.

To avoid this problem, check if the optimal size is already correct
either by keeping the width or the height.
2020-04-24 22:52:02 +02:00
92cb3a6661 Improve resizing workaround
Call the same method as when the event is received on the event loop, so
that the behavior is the same in both cases.
2020-04-24 21:36:25 +02:00
5 changed files with 115 additions and 15 deletions

View File

@ -116,9 +116,6 @@ conf.set('DEFAULT_LOCK_VIDEO_ORIENTATION', '-1') # -1: unlocked
# overridden by option --bit-rate # overridden by option --bit-rate
conf.set('DEFAULT_BIT_RATE', '8000000') # 8Mbps conf.set('DEFAULT_BIT_RATE', '8000000') # 8Mbps
# enable High DPI support
conf.set('HIDPI_SUPPORT', get_option('hidpi_support'))
# disable console on Windows # disable console on Windows
conf.set('WINDOWS_NOCONSOLE', get_option('windows_noconsole')) conf.set('WINDOWS_NOCONSOLE', get_option('windows_noconsole'))

View File

@ -109,9 +109,10 @@ static int
event_watcher(void *data, SDL_Event *event) { event_watcher(void *data, SDL_Event *event) {
(void) data; (void) data;
if (event->type == SDL_WINDOWEVENT if (event->type == SDL_WINDOWEVENT
&& event->window.event == SDL_WINDOWEVENT_RESIZED) { && event->window.event == SDL_WINDOWEVENT_SIZE_CHANGED) {
// called from another thread, not very safe, but it's a workaround! // In practice, it seems to always be called from the same thread in
screen_render(&screen); // that specific case. Anyway, it's just a workaround.
screen_handle_window_event(&screen, &event->window);
} }
return 0; return 0;
} }
@ -127,6 +128,7 @@ enum event_result {
EVENT_RESULT_CONTINUE, EVENT_RESULT_CONTINUE,
EVENT_RESULT_STOPPED_BY_USER, EVENT_RESULT_STOPPED_BY_USER,
EVENT_RESULT_STOPPED_BY_EOS, EVENT_RESULT_STOPPED_BY_EOS,
EVENT_RESULT_STOPPED_BY_ERROR,
}; };
static enum event_result static enum event_result
@ -149,7 +151,9 @@ handle_event(SDL_Event *event, bool control) {
} }
break; break;
case SDL_WINDOWEVENT: case SDL_WINDOWEVENT:
screen_handle_window_event(&screen, &event->window); if (!screen_handle_window_event(&screen, &event->window)) {
return EVENT_RESULT_STOPPED_BY_ERROR;
}
break; break;
case SDL_TEXTINPUT: case SDL_TEXTINPUT:
if (!control) { if (!control) {
@ -221,6 +225,9 @@ event_loop(bool display, bool control) {
case EVENT_RESULT_STOPPED_BY_EOS: case EVENT_RESULT_STOPPED_BY_EOS:
LOGW("Device disconnected"); LOGW("Device disconnected");
return false; return false;
case EVENT_RESULT_STOPPED_BY_ERROR:
LOGC("Stopping due to unrecoverable error");
return false;
case EVENT_RESULT_CONTINUE: case EVENT_RESULT_CONTINUE:
break; break;
} }

View File

@ -113,6 +113,13 @@ get_optimal_size(struct size current_size, struct size content_size) {
h = MIN(current_size.height, display_size.height); h = MIN(current_size.height, display_size.height);
} }
if (h == w * content_size.height / content_size.width
|| w == h * content_size.width / content_size.height) {
// The size is already optimal, if we ignore rounding errors due to
// integer window dimensions
return (struct size) {w, h};
}
bool keep_width = content_size.width * h > content_size.height * w; bool keep_width = content_size.width * h > content_size.height * w;
if (keep_width) { if (keep_width) {
// remove black borders on top and bottom // remove black borders on top and bottom
@ -210,10 +217,9 @@ screen_init_rendering(struct screen *screen, const char *window_title,
struct size window_size = struct size window_size =
get_initial_optimal_size(content_size, window_width, window_height); get_initial_optimal_size(content_size, window_width, window_height);
uint32_t window_flags = SDL_WINDOW_HIDDEN | SDL_WINDOW_RESIZABLE; uint32_t window_flags = SDL_WINDOW_HIDDEN
#ifdef HIDPI_SUPPORT | SDL_WINDOW_RESIZABLE
window_flags |= SDL_WINDOW_ALLOW_HIGHDPI; | SDL_WINDOW_ALLOW_HIGHDPI;
#endif
if (always_on_top) { if (always_on_top) {
#ifdef SCRCPY_SDL_HAS_WINDOW_ALWAYS_ON_TOP #ifdef SCRCPY_SDL_HAS_WINDOW_ALWAYS_ON_TOP
window_flags |= SDL_WINDOW_ALWAYS_ON_TOP; window_flags |= SDL_WINDOW_ALWAYS_ON_TOP;
@ -258,7 +264,7 @@ screen_init_rendering(struct screen *screen, const char *window_title,
return false; return false;
} }
// stats with "opengl" // starts with "opengl"
screen->use_opengl = renderer_name && !strncmp(renderer_name, "opengl", 6); screen->use_opengl = renderer_name && !strncmp(renderer_name, "opengl", 6);
if (screen->use_opengl) { if (screen->use_opengl) {
struct sc_opengl *gl = &screen->gl; struct sc_opengl *gl = &screen->gl;
@ -303,6 +309,11 @@ screen_init_rendering(struct screen *screen, const char *window_title,
screen->windowed_window_size = window_size; screen->windowed_window_size = window_size;
struct scale_ratio *rw = &screen->scale.w;
struct scale_ratio *rh = &screen->scale.h;
SDL_GetWindowSize(screen->window, &rw->window, &rh->window);
SDL_GL_GetDrawableSize(screen->window, &rw->drawable, &rh->drawable);
return true; return true;
} }
@ -512,11 +523,74 @@ screen_resize_to_pixel_perfect(struct screen *screen) {
content_size.height); content_size.height);
} }
void bool
screen_fix_hidpi(struct screen *screen) {
// If the scale ratio has changed, recreate the renderer to fix HiDPI issues
// <https://github.com/Genymobile/scrcpy/issues/15>
int ww, wh, dw, dh;
SDL_GetWindowSize(screen->window, &ww, &wh);
SDL_GL_GetDrawableSize(screen->window, &dw, &dh);
struct scale_ratio *rw = &screen->scale.w;
struct scale_ratio *rh = &screen->scale.h;
if (ww * rw->drawable == dw * rw->window &&
wh * rh->drawable == dh * rh->window) {
// same ratio, both horizontally and vertically, nothing to do
return true;
}
// update the current scale
rw->window = ww;
rh->window = wh;
rw->drawable = dw;
rh->drawable = dh;
if (screen->texture) {
SDL_DestroyTexture(screen->texture);
}
if (screen->renderer) {
SDL_DestroyRenderer(screen->renderer);
}
screen->renderer = SDL_CreateRenderer(screen->window, -1,
SDL_RENDERER_ACCELERATED);
if (!screen->renderer) {
LOGC("Could not create renderer: %s", SDL_GetError());
return false;
}
struct size content_size = screen->content_size;
if (SDL_RenderSetLogicalSize(screen->renderer, content_size.width,
content_size.height)) {
LOGE("Could not set renderer logical size: %s", SDL_GetError());
SDL_DestroyRenderer(screen->renderer);
return false;
}
// FIXME this is wrong, we must update the last frame to the new texture,
// but we don't have it anymore!
screen->texture = create_texture(screen);
if (!screen->texture) {
LOGC("Could not create texture: %s", SDL_GetError());
SDL_DestroyRenderer(screen->renderer);
return false;
}
LOGD("Renderer and texture reset");
return true;
}
bool
screen_handle_window_event(struct screen *screen, screen_handle_window_event(struct screen *screen,
const SDL_WindowEvent *event) { const SDL_WindowEvent *event) {
switch (event->event) { switch (event->event) {
case SDL_WINDOWEVENT_EXPOSED: case SDL_WINDOWEVENT_EXPOSED:
if (!screen_fix_hidpi(screen)) {
// unrecoverable
return false;
}
screen_render(screen); screen_render(screen);
break; break;
case SDL_WINDOWEVENT_SIZE_CHANGED: case SDL_WINDOWEVENT_SIZE_CHANGED:
@ -552,6 +626,8 @@ screen_handle_window_event(struct screen *screen,
apply_windowed_size(screen); apply_windowed_size(screen);
break; break;
} }
return true;
} }
struct point struct point

View File

@ -13,6 +13,11 @@
struct video_buffer; struct video_buffer;
struct scale_ratio {
int window;
int drawable;
};
struct screen { struct screen {
SDL_Window *window; SDL_Window *window;
SDL_Renderer *renderer; SDL_Renderer *renderer;
@ -33,6 +38,11 @@ struct screen {
bool maximized; bool maximized;
bool no_window; bool no_window;
bool mipmaps; bool mipmaps;
struct {
struct scale_ratio w;
struct scale_ratio h;
} scale;
}; };
#define SCREEN_INITIALIZER { \ #define SCREEN_INITIALIZER { \
@ -63,6 +73,16 @@ struct screen {
.maximized = false, \ .maximized = false, \
.no_window = false, \ .no_window = false, \
.mipmaps = false, \ .mipmaps = false, \
.scale = { \
.w = { \
.window = 0, \
.drawable = 0, \
}, \
.h = { \
.window = 0, \
.drawable = 0, \
}, \
} \
} }
// initialize default values // initialize default values
@ -111,7 +131,8 @@ void
screen_set_rotation(struct screen *screen, unsigned rotation); screen_set_rotation(struct screen *screen, unsigned rotation);
// react to window events // react to window events
void // return true on success, false on unrecoverable error
bool
screen_handle_window_event(struct screen *screen, const SDL_WindowEvent *event); screen_handle_window_event(struct screen *screen, const SDL_WindowEvent *event);
// convert point from window coordinates to frame coordinates // convert point from window coordinates to frame coordinates

View File

@ -4,6 +4,5 @@ option('crossbuild_windows', type: 'boolean', value: false, description: 'Build
option('windows_noconsole', type: 'boolean', value: false, description: 'Disable console on Windows (pass -mwindows flag)') option('windows_noconsole', type: 'boolean', value: false, description: 'Disable console on Windows (pass -mwindows flag)')
option('prebuilt_server', type: 'string', description: 'Path of the prebuilt server') option('prebuilt_server', type: 'string', description: 'Path of the prebuilt server')
option('portable', type: 'boolean', value: false, description: 'Use scrcpy-server from the same directory as the scrcpy executable') option('portable', type: 'boolean', value: false, description: 'Use scrcpy-server from the same directory as the scrcpy executable')
option('hidpi_support', type: 'boolean', value: true, description: 'Enable High DPI support')
option('server_debugger', type: 'boolean', value: false, description: 'Run a server debugger and wait for a client to be attached') option('server_debugger', type: 'boolean', value: false, description: 'Run a server debugger and wait for a client to be attached')
option('server_debugger_method', type: 'combo', choices: ['old', 'new'], value: 'new', description: 'Select the debugger method (Android < 9: "old", Android >= 9: "new")') option('server_debugger_method', type: 'combo', choices: ['old', 'new'], value: 'new', description: 'Select the debugger method (Android < 9: "old", Android >= 9: "new")')