diff --git a/app/src/input_manager.c b/app/src/input_manager.c index 14ee7e40..1bb32920 100644 --- a/app/src/input_manager.c +++ b/app/src/input_manager.c @@ -7,33 +7,6 @@ #include "util/lock.h" #include "util/log.h" -// Convert window coordinates (as provided by SDL_GetMouseState() to renderer -// coordinates (as provided in SDL mouse events) -// -// See my question: -// -static void -convert_to_renderer_coordinates(SDL_Renderer *renderer, int *x, int *y) { - SDL_Rect viewport; - float scale_x, scale_y; - SDL_RenderGetViewport(renderer, &viewport); - SDL_RenderGetScale(renderer, &scale_x, &scale_y); - *x = (int) (*x / scale_x) - viewport.x; - *y = (int) (*y / scale_y) - viewport.y; -} - -static struct point -get_mouse_point(struct screen *screen) { - int x; - int y; - SDL_GetMouseState(&x, &y); - convert_to_renderer_coordinates(screen->renderer, &x, &y); - return (struct point) { - .x = x, - .y = y, - }; -} - static const int ACTION_DOWN = 1; static const int ACTION_UP = 1 << 1; @@ -487,11 +460,17 @@ convert_touch(const SDL_TouchFingerEvent *from, struct screen *screen, to->inject_touch_event.pointer_id = from->fingerId; to->inject_touch_event.position.screen_size = screen->frame_size; + + int ww; + int wh; + SDL_GL_GetDrawableSize(screen->window, &ww, &wh); + // SDL touch event coordinates are normalized in the range [0; 1] - float x = from->x * screen->content_size.width; - float y = from->y * screen->content_size.height; + int32_t x = from->x * ww; + int32_t y = from->y * wh; to->inject_touch_event.position.point = screen_convert_to_frame_coords(screen, x, y); + to->inject_touch_event.pressure = from->pressure; to->inject_touch_event.buttons = 0; return true; @@ -508,13 +487,6 @@ input_manager_process_touch(struct input_manager *im, } } -static bool -is_outside_device_screen(struct input_manager *im, int x, int y) -{ - return x < 0 || x >= im->screen->content_size.width || - y < 0 || y >= im->screen->content_size.height; -} - static bool convert_mouse_button(const SDL_MouseButtonEvent *from, struct screen *screen, struct control_msg *to) { @@ -552,10 +524,14 @@ input_manager_process_mouse_button(struct input_manager *im, action_home(im->controller, ACTION_DOWN | ACTION_UP); return; } + // double-click on black borders resize to fit the device screen if (event->button == SDL_BUTTON_LEFT && event->clicks == 2) { - bool outside = - is_outside_device_screen(im, event->x, event->y); + int x = event->x; + int y = event->y; + SDL_Rect *r = &im->screen->rect; + bool outside = x < r->x || x >= r->x + r->w + || y < r->y || y >= r->y + r->h; if (outside) { screen_resize_to_fit(im->screen); return; @@ -579,9 +555,15 @@ input_manager_process_mouse_button(struct input_manager *im, static bool convert_mouse_wheel(const SDL_MouseWheelEvent *from, struct screen *screen, struct control_msg *to) { + + // mouse_x and mouse_y are expressed in pixels relative to the window + int mouse_x; + int mouse_y; + SDL_GetMouseState(&mouse_x, &mouse_y); + struct position position = { .screen_size = screen->frame_size, - .point = get_mouse_point(screen), + .point = screen_convert_to_frame_coords(screen, mouse_x, mouse_y), }; to->type = CONTROL_MSG_TYPE_INJECT_SCROLL_EVENT; diff --git a/app/src/screen.c b/app/src/screen.c index ea94abfd..6598e4df 100644 --- a/app/src/screen.c +++ b/app/src/screen.c @@ -175,6 +175,43 @@ get_initial_optimal_size(struct size content_size, uint16_t req_width, return window_size; } +static void +update_content_rect(struct screen *screen) { + int dw; + int dh; + SDL_GL_GetDrawableSize(screen->window, &dw, &dh); + + struct size content_size = screen->content_size; + // The drawable size is the window size * the HiDPI scale + struct size drawable_size = {dw, dh}; + + SDL_Rect *rect = &screen->rect; + + if (is_optimal_size(drawable_size, content_size)) { + rect->x = 0; + rect->y = 0; + rect->w = drawable_size.width; + rect->h = drawable_size.height; + return; + } + + bool keep_width = content_size.width * drawable_size.height + > content_size.height * drawable_size.width; + if (keep_width) { + rect->x = 0; + rect->w = drawable_size.width; + rect->h = drawable_size.width * content_size.height + / content_size.width; + rect->y = (drawable_size.height - rect->h) / 2; + } else { + rect->y = 0; + rect->h = drawable_size.height; + rect->w = drawable_size.height * content_size.width + / content_size.height; + rect->x = (drawable_size.width - rect->w) / 2; + } +} + void screen_init(struct screen *screen) { *screen = (struct screen) SCREEN_INITIALIZER; @@ -264,13 +301,6 @@ screen_init_rendering(struct screen *screen, const char *window_title, const char *renderer_name = r ? NULL : renderer_info.name; LOGI("Renderer: %s", renderer_name ? renderer_name : "(unknown)"); - if (SDL_RenderSetLogicalSize(screen->renderer, content_size.width, - content_size.height)) { - LOGE("Could not set renderer logical size: %s", SDL_GetError()); - screen_destroy(screen); - return false; - } - // starts with "opengl" screen->use_opengl = renderer_name && !strncmp(renderer_name, "opengl", 6); if (screen->use_opengl) { @@ -314,6 +344,8 @@ screen_init_rendering(struct screen *screen, const char *window_title, return false; } + update_content_rect(screen); + screen->windowed_window_size = window_size; return true; @@ -348,13 +380,6 @@ screen_set_rotation(struct screen *screen, unsigned rotation) { struct size new_content_size = get_rotated_size(screen->frame_size, rotation); - if (SDL_RenderSetLogicalSize(screen->renderer, - new_content_size.width, - new_content_size.height)) { - LOGE("Could not set renderer logical size: %s", SDL_GetError()); - return; - } - struct size windowed_size = get_windowed_window_size(screen); struct size target_size = { .width = (uint32_t) windowed_size.width * new_content_size.width @@ -369,6 +394,7 @@ screen_set_rotation(struct screen *screen, unsigned rotation) { screen->rotation = rotation; LOGI("Display rotation set to %u", rotation); + update_content_rect(screen); screen_render(screen); } @@ -377,18 +403,11 @@ static bool prepare_for_frame(struct screen *screen, struct size new_frame_size) { if (screen->frame_size.width != new_frame_size.width || screen->frame_size.height != new_frame_size.height) { - struct size new_content_size = - get_rotated_size(new_frame_size, screen->rotation); - if (SDL_RenderSetLogicalSize(screen->renderer, - new_content_size.width, - new_content_size.height)) { - LOGE("Could not set renderer logical size: %s", SDL_GetError()); - return false; - } - // frame dimension changed, destroy texture SDL_DestroyTexture(screen->texture); + struct size new_content_size = + get_rotated_size(new_frame_size, screen->rotation); struct size content_size = screen->content_size; struct size windowed_size = get_windowed_window_size(screen); struct size target_size = { @@ -402,6 +421,7 @@ prepare_for_frame(struct screen *screen, struct size new_frame_size) { screen->frame_size = new_frame_size; screen->content_size = new_content_size; + update_content_rect(screen); LOGI("New texture: %" PRIu16 "x%" PRIu16, screen->frame_size.width, screen->frame_size.height); @@ -451,7 +471,7 @@ void screen_render(struct screen *screen) { SDL_RenderClear(screen->renderer); if (screen->rotation == 0) { - SDL_RenderCopy(screen->renderer, screen->texture, NULL, NULL); + SDL_RenderCopy(screen->renderer, screen->texture, NULL, &screen->rect); } else { // rotation in RenderCopyEx() is clockwise, while screen->rotation is // counterclockwise (to be consistent with --lock-video-orientation) @@ -461,12 +481,14 @@ screen_render(struct screen *screen) { SDL_Rect *dstrect = NULL; SDL_Rect rect; if (screen->rotation & 1) { - struct size size = screen->content_size; - rect.x = (size.width - size.height) / 2; - rect.y = (size.height - size.width) / 2; - rect.w = size.height; - rect.h = size.width; + rect.x = screen->rect.x + (screen->rect.w - screen->rect.h) / 2; + rect.y = screen->rect.y + (screen->rect.h - screen->rect.w) / 2; + rect.w = screen->rect.h; + rect.h = screen->rect.w; dstrect = ▭ + } else { + assert(screen->rotation == 2); + dstrect = &screen->rect; } SDL_RenderCopyEx(screen->renderer, screen->texture, NULL, dstrect, @@ -487,6 +509,7 @@ screen_switch_fullscreen(struct screen *screen) { apply_windowed_size(screen); LOGD("Switched to %s mode", screen->fullscreen ? "fullscreen" : "windowed"); + update_content_rect(screen); screen_render(screen); } @@ -530,6 +553,7 @@ screen_handle_window_event(struct screen *screen, const SDL_WindowEvent *event) { switch (event->event) { case SDL_WINDOWEVENT_EXPOSED: + update_content_rect(screen); screen_render(screen); break; case SDL_WINDOWEVENT_SIZE_CHANGED: @@ -545,6 +569,7 @@ screen_handle_window_event(struct screen *screen, // window is maximized or fullscreen is enabled. screen->windowed_window_size = get_window_size(screen->window); } + update_content_rect(screen); screen_render(screen); break; case SDL_WINDOWEVENT_MAXIMIZED: @@ -574,6 +599,17 @@ screen_convert_to_frame_coords(struct screen *screen, int32_t x, int32_t y) { int32_t w = screen->content_size.width; int32_t h = screen->content_size.height; + + // take the HiDPI scaling (dw/ww and dh/wh) into account + int ww, wh, dw, dh; + SDL_GetWindowSize(screen->window, &ww, &wh); + SDL_GL_GetDrawableSize(screen->window, &dw, &dh); + + // scale (64 bits for intermediate multiplications) + x = (int64_t) (x - screen->rect.x) * w * dw / (screen->rect.w * ww); + y = (int64_t) (y - screen->rect.y) * h * dh / (screen->rect.h * wh); + + // rotate struct point result; switch (rotation) { case 0: diff --git a/app/src/screen.h b/app/src/screen.h index 85514279..89cefd9e 100644 --- a/app/src/screen.h +++ b/app/src/screen.h @@ -28,6 +28,8 @@ struct screen { struct size windowed_window_size_backup; // client rotation: 0, 1, 2 or 3 (x90 degrees counterclockwise) unsigned rotation; + // rectangle of the content (excluding black borders) + struct SDL_Rect rect; bool has_frame; bool fullscreen; bool maximized; @@ -58,6 +60,12 @@ struct screen { .height = 0, \ }, \ .rotation = 0, \ + .rect = { \ + .x = 0, \ + .y = 0, \ + .w = 0, \ + .h = 0, \ + }, \ .has_frame = false, \ .fullscreen = false, \ .maximized = false, \