Add shortcuts to rotate display
Add Ctrl+Left and Ctrl+Right shortcuts to rotate the display (the content of the scrcpy window). Contrary to --lock-video-orientation, the rotation has no impact on recording, and can be changed dynamically (and immediately). Fixes #218 <https://github.com/Genymobile/scrcpy/issues/218>
This commit is contained in:
parent
fd63e7eb5a
commit
d48b375a1d
6 changed files with 152 additions and 27 deletions
|
@ -491,6 +491,8 @@ Also see [issue #14].
|
||||||
| Action | Shortcut | Shortcut (macOS)
|
| Action | Shortcut | Shortcut (macOS)
|
||||||
| -------------------------------------- |:----------------------------- |:-----------------------------
|
| -------------------------------------- |:----------------------------- |:-----------------------------
|
||||||
| Switch fullscreen mode | `Ctrl`+`f` | `Cmd`+`f`
|
| Switch fullscreen mode | `Ctrl`+`f` | `Cmd`+`f`
|
||||||
|
| Rotate display left | `Ctrl`+`←` _(left)_ | `Cmd`+`←` _(left)_
|
||||||
|
| Rotate display right | `Ctrl`+`→` _(right)_ | `Cmd`+`→` _(right)_
|
||||||
| Resize window to 1:1 (pixel-perfect) | `Ctrl`+`g` | `Cmd`+`g`
|
| Resize window to 1:1 (pixel-perfect) | `Ctrl`+`g` | `Cmd`+`g`
|
||||||
| Resize window to remove black borders | `Ctrl`+`x` \| _Double-click¹_ | `Cmd`+`x` \| _Double-click¹_
|
| Resize window to remove black borders | `Ctrl`+`x` \| _Double-click¹_ | `Cmd`+`x` \| _Double-click¹_
|
||||||
| Click on `HOME` | `Ctrl`+`h` \| _Middle-click_ | `Ctrl`+`h` \| _Middle-click_
|
| Click on `HOME` | `Ctrl`+`h` \| _Middle-click_ | `Ctrl`+`h` \| _Middle-click_
|
||||||
|
|
|
@ -166,6 +166,14 @@ Default is 0 (automatic).\n
|
||||||
.B Ctrl+f
|
.B Ctrl+f
|
||||||
Switch fullscreen mode
|
Switch fullscreen mode
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.B Ctrl+Left
|
||||||
|
Rotate display left
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.B Ctrl+Right
|
||||||
|
Rotate display right
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
.B Ctrl+g
|
.B Ctrl+g
|
||||||
Resize window to 1:1 (pixel\-perfect)
|
Resize window to 1:1 (pixel\-perfect)
|
||||||
|
|
|
@ -145,6 +145,12 @@ scrcpy_print_usage(const char *arg0) {
|
||||||
" " CTRL_OR_CMD "+f\n"
|
" " CTRL_OR_CMD "+f\n"
|
||||||
" Switch fullscreen mode\n"
|
" Switch fullscreen mode\n"
|
||||||
"\n"
|
"\n"
|
||||||
|
" " CTRL_OR_CMD "+Left\n"
|
||||||
|
" Rotate display left\n"
|
||||||
|
"\n"
|
||||||
|
" " CTRL_OR_CMD "+Right\n"
|
||||||
|
" Rotate display right\n"
|
||||||
|
"\n"
|
||||||
" " CTRL_OR_CMD "+g\n"
|
" " CTRL_OR_CMD "+g\n"
|
||||||
" Resize window to 1:1 (pixel-perfect)\n"
|
" Resize window to 1:1 (pixel-perfect)\n"
|
||||||
"\n"
|
"\n"
|
||||||
|
|
|
@ -221,6 +221,18 @@ rotate_device(struct controller *controller) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
rotate_client_left(struct screen *screen) {
|
||||||
|
unsigned new_rotation = (screen->rotation + 1) % 4;
|
||||||
|
screen_set_rotation(screen, new_rotation);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
rotate_client_right(struct screen *screen) {
|
||||||
|
unsigned new_rotation = (screen->rotation + 3) % 4;
|
||||||
|
screen_set_rotation(screen, new_rotation);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
input_manager_process_text_input(struct input_manager *im,
|
input_manager_process_text_input(struct input_manager *im,
|
||||||
const SDL_TextInputEvent *event) {
|
const SDL_TextInputEvent *event) {
|
||||||
|
@ -351,6 +363,16 @@ input_manager_process_key(struct input_manager *im,
|
||||||
action_volume_up(controller, action);
|
action_volume_up(controller, action);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
case SDLK_LEFT:
|
||||||
|
if (cmd && !shift && down) {
|
||||||
|
rotate_client_left(im->screen);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
case SDLK_RIGHT:
|
||||||
|
if (cmd && !shift && down) {
|
||||||
|
rotate_client_right(im->screen);
|
||||||
|
}
|
||||||
|
return;
|
||||||
case SDLK_c:
|
case SDLK_c:
|
||||||
if (control && cmd && !shift && !repeat && down) {
|
if (control && cmd && !shift && !repeat && down) {
|
||||||
request_device_clipboard(controller);
|
request_device_clipboard(controller);
|
||||||
|
|
134
app/src/screen.c
134
app/src/screen.c
|
@ -15,6 +15,19 @@
|
||||||
|
|
||||||
#define DISPLAY_MARGINS 96
|
#define DISPLAY_MARGINS 96
|
||||||
|
|
||||||
|
static inline struct size
|
||||||
|
get_rotated_size(struct size size, int rotation) {
|
||||||
|
struct size rotated_size;
|
||||||
|
if (rotation & 1) {
|
||||||
|
rotated_size.width = size.height;
|
||||||
|
rotated_size.height = size.width;
|
||||||
|
} else {
|
||||||
|
rotated_size.width = size.width;
|
||||||
|
rotated_size.height = size.height;
|
||||||
|
}
|
||||||
|
return rotated_size;
|
||||||
|
}
|
||||||
|
|
||||||
// get the window size in a struct size
|
// get the window size in a struct size
|
||||||
static struct size
|
static struct size
|
||||||
get_window_size(SDL_Window *window) {
|
get_window_size(SDL_Window *window) {
|
||||||
|
@ -80,8 +93,8 @@ get_preferred_display_bounds(struct size *bounds) {
|
||||||
// - it keeps the aspect ratio
|
// - it keeps the aspect ratio
|
||||||
// - it scales down to make it fit in the display_size
|
// - it scales down to make it fit in the display_size
|
||||||
static struct size
|
static struct size
|
||||||
get_optimal_size(struct size current_size, struct size frame_size) {
|
get_optimal_size(struct size current_size, struct size content_size) {
|
||||||
if (frame_size.width == 0 || frame_size.height == 0) {
|
if (content_size.width == 0 || content_size.height == 0) {
|
||||||
// avoid division by 0
|
// avoid division by 0
|
||||||
return current_size;
|
return current_size;
|
||||||
}
|
}
|
||||||
|
@ -100,14 +113,14 @@ get_optimal_size(struct size current_size, struct size frame_size) {
|
||||||
h = MIN(current_size.height, display_size.height);
|
h = MIN(current_size.height, display_size.height);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool keep_width = frame_size.width * h > frame_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
|
||||||
h = frame_size.height * w / frame_size.width;
|
h = content_size.height * w / content_size.width;
|
||||||
} else {
|
} else {
|
||||||
// remove black borders on left and right (or none at all if it already
|
// remove black borders on left and right (or none at all if it already
|
||||||
// fits)
|
// fits)
|
||||||
w = frame_size.width * h / frame_size.height;
|
w = content_size.width * h / content_size.height;
|
||||||
}
|
}
|
||||||
|
|
||||||
// w and h must fit into 16 bits
|
// w and h must fit into 16 bits
|
||||||
|
@ -117,33 +130,33 @@ get_optimal_size(struct size current_size, struct size frame_size) {
|
||||||
|
|
||||||
// same as get_optimal_size(), but read the current size from the window
|
// same as get_optimal_size(), but read the current size from the window
|
||||||
static inline struct size
|
static inline struct size
|
||||||
get_optimal_window_size(const struct screen *screen, struct size frame_size) {
|
get_optimal_window_size(const struct screen *screen, struct size content_size) {
|
||||||
struct size windowed_size = get_windowed_window_size(screen);
|
struct size windowed_size = get_windowed_window_size(screen);
|
||||||
return get_optimal_size(windowed_size, frame_size);
|
return get_optimal_size(windowed_size, content_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
// initially, there is no current size, so use the frame size as current size
|
// initially, there is no current size, so use the frame size as current size
|
||||||
// req_width and req_height, if not 0, are the sizes requested by the user
|
// req_width and req_height, if not 0, are the sizes requested by the user
|
||||||
static inline struct size
|
static inline struct size
|
||||||
get_initial_optimal_size(struct size frame_size, uint16_t req_width,
|
get_initial_optimal_size(struct size content_size, uint16_t req_width,
|
||||||
uint16_t req_height) {
|
uint16_t req_height) {
|
||||||
struct size window_size;
|
struct size window_size;
|
||||||
if (!req_width && !req_height) {
|
if (!req_width && !req_height) {
|
||||||
window_size = get_optimal_size(frame_size, frame_size);
|
window_size = get_optimal_size(content_size, content_size);
|
||||||
} else {
|
} else {
|
||||||
if (req_width) {
|
if (req_width) {
|
||||||
window_size.width = req_width;
|
window_size.width = req_width;
|
||||||
} else {
|
} else {
|
||||||
// compute from the requested height
|
// compute from the requested height
|
||||||
window_size.width = (uint32_t) req_height * frame_size.width
|
window_size.width = (uint32_t) req_height * content_size.width
|
||||||
/ frame_size.height;
|
/ content_size.height;
|
||||||
}
|
}
|
||||||
if (req_height) {
|
if (req_height) {
|
||||||
window_size.height = req_height;
|
window_size.height = req_height;
|
||||||
} else {
|
} else {
|
||||||
// compute from the requested width
|
// compute from the requested width
|
||||||
window_size.height = (uint32_t) req_width * frame_size.height
|
window_size.height = (uint32_t) req_width * content_size.height
|
||||||
/ frame_size.width;
|
/ content_size.width;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return window_size;
|
return window_size;
|
||||||
|
@ -167,9 +180,11 @@ screen_init_rendering(struct screen *screen, const char *window_title,
|
||||||
int16_t window_x, int16_t window_y, uint16_t window_width,
|
int16_t window_x, int16_t window_y, uint16_t window_width,
|
||||||
uint16_t window_height, bool window_borderless) {
|
uint16_t window_height, bool window_borderless) {
|
||||||
screen->frame_size = frame_size;
|
screen->frame_size = frame_size;
|
||||||
|
struct size content_size =
|
||||||
|
get_rotated_size(frame_size, screen->rotation);
|
||||||
|
|
||||||
struct size window_size =
|
struct size window_size =
|
||||||
get_initial_optimal_size(frame_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 | SDL_WINDOW_RESIZABLE;
|
||||||
#ifdef HIDPI_SUPPORT
|
#ifdef HIDPI_SUPPORT
|
||||||
window_flags |= SDL_WINDOW_ALLOW_HIGHDPI;
|
window_flags |= SDL_WINDOW_ALLOW_HIGHDPI;
|
||||||
|
@ -206,8 +221,8 @@ screen_init_rendering(struct screen *screen, const char *window_title,
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (SDL_RenderSetLogicalSize(screen->renderer, frame_size.width,
|
if (SDL_RenderSetLogicalSize(screen->renderer, content_size.width,
|
||||||
frame_size.height)) {
|
content_size.height)) {
|
||||||
LOGE("Could not set renderer logical size: %s", SDL_GetError());
|
LOGE("Could not set renderer logical size: %s", SDL_GetError());
|
||||||
screen_destroy(screen);
|
screen_destroy(screen);
|
||||||
return false;
|
return false;
|
||||||
|
@ -253,13 +268,51 @@ screen_destroy(struct screen *screen) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
screen_set_rotation(struct screen *screen, unsigned rotation) {
|
||||||
|
assert(rotation < 4);
|
||||||
|
if (rotation == screen->rotation) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct size old_content_size =
|
||||||
|
get_rotated_size(screen->frame_size, screen->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
|
||||||
|
/ old_content_size.width,
|
||||||
|
.height = (uint32_t) windowed_size.height * new_content_size.height
|
||||||
|
/ old_content_size.height,
|
||||||
|
};
|
||||||
|
target_size = get_optimal_size(target_size, new_content_size);
|
||||||
|
set_window_size(screen, target_size);
|
||||||
|
|
||||||
|
screen->rotation = rotation;
|
||||||
|
LOGI("Display rotation set to %u", rotation);
|
||||||
|
|
||||||
|
screen_render(screen);
|
||||||
|
}
|
||||||
|
|
||||||
// recreate the texture and resize the window if the frame size has changed
|
// recreate the texture and resize the window if the frame size has changed
|
||||||
static bool
|
static bool
|
||||||
prepare_for_frame(struct screen *screen, struct size new_frame_size) {
|
prepare_for_frame(struct screen *screen, struct size new_frame_size) {
|
||||||
if (screen->frame_size.width != new_frame_size.width
|
if (screen->frame_size.width != new_frame_size.width
|
||||||
|| screen->frame_size.height != new_frame_size.height) {
|
|| screen->frame_size.height != new_frame_size.height) {
|
||||||
if (SDL_RenderSetLogicalSize(screen->renderer, new_frame_size.width,
|
struct size new_content_size =
|
||||||
new_frame_size.height)) {
|
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());
|
LOGE("Could not set renderer logical size: %s", SDL_GetError());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -267,14 +320,16 @@ prepare_for_frame(struct screen *screen, struct size new_frame_size) {
|
||||||
// frame dimension changed, destroy texture
|
// frame dimension changed, destroy texture
|
||||||
SDL_DestroyTexture(screen->texture);
|
SDL_DestroyTexture(screen->texture);
|
||||||
|
|
||||||
|
struct size content_size =
|
||||||
|
get_rotated_size(screen->frame_size, screen->rotation);
|
||||||
struct size windowed_size = get_windowed_window_size(screen);
|
struct size windowed_size = get_windowed_window_size(screen);
|
||||||
struct size target_size = {
|
struct size target_size = {
|
||||||
(uint32_t) windowed_size.width * new_frame_size.width
|
(uint32_t) windowed_size.width * new_content_size.width
|
||||||
/ screen->frame_size.width,
|
/ content_size.width,
|
||||||
(uint32_t) windowed_size.height * new_frame_size.height
|
(uint32_t) windowed_size.height * new_content_size.height
|
||||||
/ screen->frame_size.height,
|
/ content_size.height,
|
||||||
};
|
};
|
||||||
target_size = get_optimal_size(target_size, new_frame_size);
|
target_size = get_optimal_size(target_size, new_content_size);
|
||||||
set_window_size(screen, target_size);
|
set_window_size(screen, target_size);
|
||||||
|
|
||||||
screen->frame_size = new_frame_size;
|
screen->frame_size = new_frame_size;
|
||||||
|
@ -319,7 +374,29 @@ screen_update_frame(struct screen *screen, struct video_buffer *vb) {
|
||||||
void
|
void
|
||||||
screen_render(struct screen *screen) {
|
screen_render(struct screen *screen) {
|
||||||
SDL_RenderClear(screen->renderer);
|
SDL_RenderClear(screen->renderer);
|
||||||
SDL_RenderCopy(screen->renderer, screen->texture, NULL, NULL);
|
if (screen->rotation == 0) {
|
||||||
|
SDL_RenderCopy(screen->renderer, screen->texture, NULL, NULL);
|
||||||
|
} else {
|
||||||
|
// rotation in RenderCopyEx() is clockwise, while screen->rotation is
|
||||||
|
// counterclockwise (to be consistent with --lock-video-orientation)
|
||||||
|
int cw_rotation = (4 - screen->rotation) % 4;
|
||||||
|
double angle = 90 * cw_rotation;
|
||||||
|
|
||||||
|
SDL_Rect *dstrect = NULL;
|
||||||
|
SDL_Rect rect;
|
||||||
|
if (screen->rotation & 1) {
|
||||||
|
struct size size =
|
||||||
|
get_rotated_size(screen->frame_size, screen->rotation);
|
||||||
|
rect.x = (size.width - size.height) / 2;
|
||||||
|
rect.y = (size.height - size.width) / 2;
|
||||||
|
rect.w = size.height;
|
||||||
|
rect.h = size.width;
|
||||||
|
dstrect = ▭
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_RenderCopyEx(screen->renderer, screen->texture, NULL, dstrect,
|
||||||
|
angle, NULL, 0);
|
||||||
|
}
|
||||||
SDL_RenderPresent(screen->renderer);
|
SDL_RenderPresent(screen->renderer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -349,8 +426,10 @@ screen_resize_to_fit(struct screen *screen) {
|
||||||
screen->maximized = false;
|
screen->maximized = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct size content_size =
|
||||||
|
get_rotated_size(screen->frame_size, screen->rotation);
|
||||||
struct size optimal_size =
|
struct size optimal_size =
|
||||||
get_optimal_window_size(screen, screen->frame_size);
|
get_optimal_window_size(screen, content_size);
|
||||||
SDL_SetWindowSize(screen->window, optimal_size.width, optimal_size.height);
|
SDL_SetWindowSize(screen->window, optimal_size.width, optimal_size.height);
|
||||||
LOGD("Resized to optimal size");
|
LOGD("Resized to optimal size");
|
||||||
}
|
}
|
||||||
|
@ -366,8 +445,9 @@ screen_resize_to_pixel_perfect(struct screen *screen) {
|
||||||
screen->maximized = false;
|
screen->maximized = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
SDL_SetWindowSize(screen->window, screen->frame_size.width,
|
struct size content_size =
|
||||||
screen->frame_size.height);
|
get_rotated_size(screen->frame_size, screen->rotation);
|
||||||
|
SDL_SetWindowSize(screen->window, content_size.width, content_size.height);
|
||||||
LOGD("Resized to pixel-perfect");
|
LOGD("Resized to pixel-perfect");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,8 @@ struct screen {
|
||||||
// Since we receive the event SIZE_CHANGED before MAXIMIZED, we must be
|
// Since we receive the event SIZE_CHANGED before MAXIMIZED, we must be
|
||||||
// able to revert the size to its non-maximized value.
|
// able to revert the size to its non-maximized value.
|
||||||
struct size windowed_window_size_backup;
|
struct size windowed_window_size_backup;
|
||||||
|
// client rotation: 0, 1, 2 or 3 (x90 degrees counterclockwise)
|
||||||
|
unsigned rotation;
|
||||||
bool has_frame;
|
bool has_frame;
|
||||||
bool fullscreen;
|
bool fullscreen;
|
||||||
bool maximized;
|
bool maximized;
|
||||||
|
@ -44,6 +46,7 @@ struct screen {
|
||||||
.width = 0, \
|
.width = 0, \
|
||||||
.height = 0, \
|
.height = 0, \
|
||||||
}, \
|
}, \
|
||||||
|
.rotation = 0, \
|
||||||
.has_frame = false, \
|
.has_frame = false, \
|
||||||
.fullscreen = false, \
|
.fullscreen = false, \
|
||||||
.maximized = false, \
|
.maximized = false, \
|
||||||
|
@ -90,6 +93,10 @@ screen_resize_to_fit(struct screen *screen);
|
||||||
void
|
void
|
||||||
screen_resize_to_pixel_perfect(struct screen *screen);
|
screen_resize_to_pixel_perfect(struct screen *screen);
|
||||||
|
|
||||||
|
// set the display rotation (0, 1, 2 or 3, x90 degrees counterclockwise)
|
||||||
|
void
|
||||||
|
screen_set_rotation(struct screen *screen, unsigned rotation);
|
||||||
|
|
||||||
// react to window events
|
// react to window events
|
||||||
void
|
void
|
||||||
screen_handle_window_event(struct screen *screen, const SDL_WindowEvent *event);
|
screen_handle_window_event(struct screen *screen, const SDL_WindowEvent *event);
|
||||||
|
|
Loading…
Reference in a new issue