Skip to content

Commit e40532a

Browse files
committed
Manually position and scale the content
Position and scale the content "manually" instead of relying on the renderer "logical size". This avoids possible rounding differences between the computed window size and the content size, causing one row or column of black pixels on the bottom or on the right. This also avoids HiDPI scale issues, by computing the scaling manually. This will also enable to draw items at their expected size on the screen (unscaled). Fixes #15 <#15>
1 parent d860ad4 commit e40532a

File tree

4 files changed

+125
-77
lines changed

4 files changed

+125
-77
lines changed

app/src/input_manager.c

+22-39
Original file line numberDiff line numberDiff line change
@@ -7,33 +7,6 @@
77
#include "util/lock.h"
88
#include "util/log.h"
99

10-
// Convert window coordinates (as provided by SDL_GetMouseState() to renderer
11-
// coordinates (as provided in SDL mouse events)
12-
//
13-
// See my question:
14-
// <https://stackoverflow.com/questions/49111054/how-to-get-mouse-position-on-mouse-wheel-event>
15-
static void
16-
convert_to_renderer_coordinates(SDL_Renderer *renderer, int *x, int *y) {
17-
SDL_Rect viewport;
18-
float scale_x, scale_y;
19-
SDL_RenderGetViewport(renderer, &viewport);
20-
SDL_RenderGetScale(renderer, &scale_x, &scale_y);
21-
*x = (int) (*x / scale_x) - viewport.x;
22-
*y = (int) (*y / scale_y) - viewport.y;
23-
}
24-
25-
static struct point
26-
get_mouse_point(struct screen *screen) {
27-
int x;
28-
int y;
29-
SDL_GetMouseState(&x, &y);
30-
convert_to_renderer_coordinates(screen->renderer, &x, &y);
31-
return (struct point) {
32-
.x = x,
33-
.y = y,
34-
};
35-
}
36-
3710
static const int ACTION_DOWN = 1;
3811
static const int ACTION_UP = 1 << 1;
3912

@@ -487,11 +460,17 @@ convert_touch(const SDL_TouchFingerEvent *from, struct screen *screen,
487460

488461
to->inject_touch_event.pointer_id = from->fingerId;
489462
to->inject_touch_event.position.screen_size = screen->frame_size;
463+
464+
int ww;
465+
int wh;
466+
SDL_GL_GetDrawableSize(screen->window, &ww, &wh);
467+
490468
// SDL touch event coordinates are normalized in the range [0; 1]
491-
float x = from->x * screen->content_size.width;
492-
float y = from->y * screen->content_size.height;
469+
int32_t x = from->x * ww;
470+
int32_t y = from->y * wh;
493471
to->inject_touch_event.position.point =
494472
screen_convert_to_frame_coords(screen, x, y);
473+
495474
to->inject_touch_event.pressure = from->pressure;
496475
to->inject_touch_event.buttons = 0;
497476
return true;
@@ -508,13 +487,6 @@ input_manager_process_touch(struct input_manager *im,
508487
}
509488
}
510489

511-
static bool
512-
is_outside_device_screen(struct input_manager *im, int x, int y)
513-
{
514-
return x < 0 || x >= im->screen->content_size.width ||
515-
y < 0 || y >= im->screen->content_size.height;
516-
}
517-
518490
static bool
519491
convert_mouse_button(const SDL_MouseButtonEvent *from, struct screen *screen,
520492
struct control_msg *to) {
@@ -552,10 +524,15 @@ input_manager_process_mouse_button(struct input_manager *im,
552524
action_home(im->controller, ACTION_DOWN | ACTION_UP);
553525
return;
554526
}
527+
555528
// double-click on black borders resize to fit the device screen
556529
if (event->button == SDL_BUTTON_LEFT && event->clicks == 2) {
557-
bool outside =
558-
is_outside_device_screen(im, event->x, event->y);
530+
int32_t x = event->x;
531+
int32_t y = event->y;
532+
screen_hidpi_scale_coords(im->screen, &x, &y);
533+
SDL_Rect *r = &im->screen->rect;
534+
bool outside = x < r->x || x >= r->x + r->w
535+
|| y < r->y || y >= r->y + r->h;
559536
if (outside) {
560537
screen_resize_to_fit(im->screen);
561538
return;
@@ -579,9 +556,15 @@ input_manager_process_mouse_button(struct input_manager *im,
579556
static bool
580557
convert_mouse_wheel(const SDL_MouseWheelEvent *from, struct screen *screen,
581558
struct control_msg *to) {
559+
560+
// mouse_x and mouse_y are expressed in pixels relative to the window
561+
int mouse_x;
562+
int mouse_y;
563+
SDL_GetMouseState(&mouse_x, &mouse_y);
564+
582565
struct position position = {
583566
.screen_size = screen->frame_size,
584-
.point = get_mouse_point(screen),
567+
.point = screen_convert_to_frame_coords(screen, mouse_x, mouse_y),
585568
};
586569

587570
to->type = CONTROL_MSG_TYPE_INJECT_SCROLL_EVENT;

app/src/scrcpy.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ event_watcher(void *data, SDL_Event *event) {
136136
&& event->window.event == SDL_WINDOWEVENT_RESIZED) {
137137
// In practice, it seems to always be called from the same thread in
138138
// that specific case. Anyway, it's just a workaround.
139-
screen_render(&screen);
139+
screen_render(&screen, true);
140140
}
141141
return 0;
142142
}

app/src/screen.c

+83-36
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,43 @@ get_initial_optimal_size(struct size content_size, uint16_t req_width,
156156
return window_size;
157157
}
158158

159+
static void
160+
screen_update_content_rect(struct screen *screen) {
161+
int dw;
162+
int dh;
163+
SDL_GL_GetDrawableSize(screen->window, &dw, &dh);
164+
165+
struct size content_size = screen->content_size;
166+
// The drawable size is the window size * the HiDPI scale
167+
struct size drawable_size = {dw, dh};
168+
169+
SDL_Rect *rect = &screen->rect;
170+
171+
if (is_optimal_size(drawable_size, content_size)) {
172+
rect->x = 0;
173+
rect->y = 0;
174+
rect->w = drawable_size.width;
175+
rect->h = drawable_size.height;
176+
return;
177+
}
178+
179+
bool keep_width = content_size.width * drawable_size.height
180+
> content_size.height * drawable_size.width;
181+
if (keep_width) {
182+
rect->x = 0;
183+
rect->w = drawable_size.width;
184+
rect->h = drawable_size.width * content_size.height
185+
/ content_size.width;
186+
rect->y = (drawable_size.height - rect->h) / 2;
187+
} else {
188+
rect->y = 0;
189+
rect->h = drawable_size.height;
190+
rect->w = drawable_size.height * content_size.width
191+
/ content_size.height;
192+
rect->x = (drawable_size.width - rect->w) / 2;
193+
}
194+
}
195+
159196
void
160197
screen_init(struct screen *screen) {
161198
*screen = (struct screen) SCREEN_INITIALIZER;
@@ -245,13 +282,6 @@ screen_init_rendering(struct screen *screen, const char *window_title,
245282
const char *renderer_name = r ? NULL : renderer_info.name;
246283
LOGI("Renderer: %s", renderer_name ? renderer_name : "(unknown)");
247284

248-
if (SDL_RenderSetLogicalSize(screen->renderer, content_size.width,
249-
content_size.height)) {
250-
LOGE("Could not set renderer logical size: %s", SDL_GetError());
251-
screen_destroy(screen);
252-
return false;
253-
}
254-
255285
// starts with "opengl"
256286
screen->use_opengl = renderer_name && !strncmp(renderer_name, "opengl", 6);
257287
if (screen->use_opengl) {
@@ -295,6 +325,8 @@ screen_init_rendering(struct screen *screen, const char *window_title,
295325
return false;
296326
}
297327

328+
screen_update_content_rect(screen);
329+
298330
return true;
299331
}
300332

@@ -365,40 +397,30 @@ screen_set_rotation(struct screen *screen, unsigned rotation) {
365397
struct size new_content_size =
366398
get_rotated_size(screen->frame_size, rotation);
367399

368-
if (SDL_RenderSetLogicalSize(screen->renderer,
369-
new_content_size.width,
370-
new_content_size.height)) {
371-
LOGE("Could not set renderer logical size: %s", SDL_GetError());
372-
return;
373-
}
374-
375400
set_content_size(screen, new_content_size);
401+
376402
screen->rotation = rotation;
377403
LOGI("Display rotation set to %u", rotation);
378404

379-
screen_render(screen);
405+
screen_render(screen, true);
380406
}
381407

382408
// recreate the texture and resize the window if the frame size has changed
383409
static bool
384410
prepare_for_frame(struct screen *screen, struct size new_frame_size) {
385411
if (screen->frame_size.width != new_frame_size.width
386412
|| screen->frame_size.height != new_frame_size.height) {
387-
struct size new_content_size =
388-
get_rotated_size(new_frame_size, screen->rotation);
389-
if (SDL_RenderSetLogicalSize(screen->renderer,
390-
new_content_size.width,
391-
new_content_size.height)) {
392-
LOGE("Could not set renderer logical size: %s", SDL_GetError());
393-
return false;
394-
}
395-
396413
// frame dimension changed, destroy texture
397414
SDL_DestroyTexture(screen->texture);
398415

399-
set_content_size(screen, new_content_size);
400416
screen->frame_size = new_frame_size;
401417

418+
struct size new_content_size =
419+
get_rotated_size(new_frame_size, screen->rotation);
420+
set_content_size(screen, new_content_size);
421+
422+
screen_update_content_rect(screen);
423+
402424
LOGI("New texture: %" PRIu16 "x%" PRIu16,
403425
screen->frame_size.width, screen->frame_size.height);
404426
screen->texture = create_texture(screen);
@@ -439,15 +461,19 @@ screen_update_frame(struct screen *screen, struct video_buffer *vb) {
439461
update_texture(screen, frame);
440462
mutex_unlock(vb->mutex);
441463

442-
screen_render(screen);
464+
screen_render(screen, false);
443465
return true;
444466
}
445467

446468
void
447-
screen_render(struct screen *screen) {
469+
screen_render(struct screen *screen, bool update_content_rect) {
470+
if (update_content_rect) {
471+
screen_update_content_rect(screen);
472+
}
473+
448474
SDL_RenderClear(screen->renderer);
449475
if (screen->rotation == 0) {
450-
SDL_RenderCopy(screen->renderer, screen->texture, NULL, NULL);
476+
SDL_RenderCopy(screen->renderer, screen->texture, NULL, &screen->rect);
451477
} else {
452478
// rotation in RenderCopyEx() is clockwise, while screen->rotation is
453479
// counterclockwise (to be consistent with --lock-video-orientation)
@@ -457,12 +483,14 @@ screen_render(struct screen *screen) {
457483
SDL_Rect *dstrect = NULL;
458484
SDL_Rect rect;
459485
if (screen->rotation & 1) {
460-
struct size size = screen->content_size;
461-
rect.x = (size.width - size.height) / 2;
462-
rect.y = (size.height - size.width) / 2;
463-
rect.w = size.height;
464-
rect.h = size.width;
486+
rect.x = screen->rect.x + (screen->rect.w - screen->rect.h) / 2;
487+
rect.y = screen->rect.y + (screen->rect.h - screen->rect.w) / 2;
488+
rect.w = screen->rect.h;
489+
rect.h = screen->rect.w;
465490
dstrect = &rect;
491+
} else {
492+
assert(screen->rotation == 2);
493+
dstrect = &screen->rect;
466494
}
467495

468496
SDL_RenderCopyEx(screen->renderer, screen->texture, NULL, dstrect,
@@ -485,7 +513,7 @@ screen_switch_fullscreen(struct screen *screen) {
485513
}
486514

487515
LOGD("Switched to %s mode", screen->fullscreen ? "fullscreen" : "windowed");
488-
screen_render(screen);
516+
screen_render(screen, true);
489517
}
490518

491519
void
@@ -523,10 +551,10 @@ screen_handle_window_event(struct screen *screen,
523551
const SDL_WindowEvent *event) {
524552
switch (event->event) {
525553
case SDL_WINDOWEVENT_EXPOSED:
526-
screen_render(screen);
554+
screen_render(screen, true);
527555
break;
528556
case SDL_WINDOWEVENT_SIZE_CHANGED:
529-
screen_render(screen);
557+
screen_render(screen, true);
530558
break;
531559
case SDL_WINDOWEVENT_MAXIMIZED:
532560
screen->maximized = true;
@@ -552,6 +580,13 @@ screen_convert_to_frame_coords(struct screen *screen, int32_t x, int32_t y) {
552580

553581
int32_t w = screen->content_size.width;
554582
int32_t h = screen->content_size.height;
583+
584+
screen_hidpi_scale_coords(screen, &x, &y);
585+
586+
x = (int64_t) (x - screen->rect.x) * w / screen->rect.w;
587+
y = (int64_t) (y - screen->rect.y) * h / screen->rect.h;
588+
589+
// rotate
555590
struct point result;
556591
switch (rotation) {
557592
case 0:
@@ -574,3 +609,15 @@ screen_convert_to_frame_coords(struct screen *screen, int32_t x, int32_t y) {
574609
}
575610
return result;
576611
}
612+
613+
void
614+
screen_hidpi_scale_coords(struct screen *screen, int32_t *x, int32_t *y) {
615+
// take the HiDPI scaling (dw/ww and dh/wh) into account
616+
int ww, wh, dw, dh;
617+
SDL_GetWindowSize(screen->window, &ww, &wh);
618+
SDL_GL_GetDrawableSize(screen->window, &dw, &dh);
619+
620+
// scale for HiDPI (64 bits for intermediate multiplications)
621+
*x = (int64_t) *x * dw / ww;
622+
*y = (int64_t) *y * dh / wh;
623+
}

app/src/screen.h

+19-1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ struct screen {
2929

3030
// client rotation: 0, 1, 2 or 3 (x90 degrees counterclockwise)
3131
unsigned rotation;
32+
// rectangle of the content (excluding black borders)
33+
struct SDL_Rect rect;
3234
bool has_frame;
3335
bool fullscreen;
3436
bool maximized;
@@ -56,6 +58,12 @@ struct screen {
5658
.height = 0, \
5759
}, \
5860
.rotation = 0, \
61+
.rect = { \
62+
.x = 0, \
63+
.y = 0, \
64+
.w = 0, \
65+
.h = 0, \
66+
}, \
5967
.has_frame = false, \
6068
.fullscreen = false, \
6169
.maximized = false, \
@@ -89,8 +97,11 @@ bool
8997
screen_update_frame(struct screen *screen, struct video_buffer *vb);
9098

9199
// render the texture to the renderer
100+
//
101+
// Set the update_content_rect flag if the window or content size may have
102+
// changed, so that the content rectangle is recomputed
92103
void
93-
screen_render(struct screen *screen);
104+
screen_render(struct screen *screen, bool update_content_rect);
94105

95106
// switch the fullscreen mode
96107
void
@@ -117,4 +128,11 @@ screen_handle_window_event(struct screen *screen, const SDL_WindowEvent *event);
117128
struct point
118129
screen_convert_to_frame_coords(struct screen *screen, int32_t x, int32_t y);
119130

131+
// Convert coordinates from window to drawable.
132+
// Events are expressed in window coordinates, but content is expressed in
133+
// drawable coordinates. They are the same if HiDPI scaling is 1, but differ
134+
// otherwise.
135+
void
136+
screen_hidpi_scale_coords(struct screen *screen, int32_t *x, int32_t *y);
137+
120138
#endif

0 commit comments

Comments
 (0)