Skip to content

Commit

Permalink
Add shortcuts to rotate client window
Browse files Browse the repository at this point in the history
Add Ctrl+Left and Ctrl+Right to rotate the scrcpy window.

Contrary to --lock-video-orientation, the rotation has no impact on
recording, and can be changed dynamically (and immediately).

Fixes #218 <#218>
  • Loading branch information
rom1v committed Apr 7, 2020
1 parent d30593e commit 8f707d9
Show file tree
Hide file tree
Showing 6 changed files with 150 additions and 27 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -491,6 +491,8 @@ Also see [issue #14].
| Action | Shortcut | Shortcut (macOS)
| -------------------------------------- |:----------------------------- |:-----------------------------
| Switch fullscreen mode | `Ctrl`+`f` | `Cmd`+`f`
| Rotate window left | `Ctrl`+`` _(left)_ | `Cmd`+`` _(left)_
| Rotate window right | `Ctrl`+`` _(right)_ | `Cmd`+`` _(right)_
| 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¹_
| Click on `HOME` | `Ctrl`+`h` \| _Middle-click_ | `Ctrl`+`h` \| _Middle-click_
Expand Down
8 changes: 8 additions & 0 deletions app/scrcpy.1
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,14 @@ Default is 0 (automatic).\n
.B Ctrl+f
switch fullscreen mode

.TP
.B Ctrl+Left
rotate window left

.TP
.B Ctrl+Right
rotate window right

.TP
.B Ctrl+g
resize window to 1:1 (pixel\-perfect)
Expand Down
6 changes: 6 additions & 0 deletions app/src/cli.c
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,12 @@ scrcpy_print_usage(const char *arg0) {
" " CTRL_OR_CMD "+f\n"
" switch fullscreen mode\n"
"\n"
" " CTRL_OR_CMD "+Left\n"
" rotate window left\n"
"\n"
" " CTRL_OR_CMD "+Right\n"
" rotate window right\n"
"\n"
" " CTRL_OR_CMD "+g\n"
" resize window to 1:1 (pixel-perfect)\n"
"\n"
Expand Down
22 changes: 22 additions & 0 deletions app/src/input_manager.c
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,18 @@ rotate_device(struct controller *controller) {
}
}

static void
rotate_client_left(struct screen *screen) {
unsigned new_rotation = (screen->rotation + 3) % 4;
screen_set_rotation(screen, new_rotation);
}

static void
rotate_client_right(struct screen *screen) {
unsigned new_rotation = (screen->rotation + 1) % 4;
screen_set_rotation(screen, new_rotation);
}

void
input_manager_process_text_input(struct input_manager *im,
const SDL_TextInputEvent *event) {
Expand Down Expand Up @@ -351,6 +363,16 @@ input_manager_process_key(struct input_manager *im,
action_volume_up(controller, action);
}
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:
if (control && cmd && !shift && !repeat && down) {
request_device_clipboard(controller);
Expand Down
132 changes: 105 additions & 27 deletions app/src/screen.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,19 @@

#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
static struct size
get_window_size(SDL_Window *window) {
Expand Down Expand Up @@ -80,8 +93,8 @@ get_preferred_display_bounds(struct size *bounds) {
// - it keeps the aspect ratio
// - it scales down to make it fit in the display_size
static struct size
get_optimal_size(struct size current_size, struct size frame_size) {
if (frame_size.width == 0 || frame_size.height == 0) {
get_optimal_size(struct size current_size, struct size content_size) {
if (content_size.width == 0 || content_size.height == 0) {
// avoid division by 0
return current_size;
}
Expand All @@ -100,14 +113,14 @@ get_optimal_size(struct size current_size, struct size frame_size) {
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) {
// remove black borders on top and bottom
h = frame_size.height * w / frame_size.width;
h = content_size.height * w / content_size.width;
} else {
// remove black borders on left and right (or none at all if it already
// 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
Expand All @@ -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
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);
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
// req_width and req_height, if not 0, are the sizes requested by the user
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) {
struct size window_size;
if (!req_width && !req_height) {
window_size = get_optimal_size(frame_size, frame_size);
window_size = get_optimal_size(content_size, content_size);
} else {
if (req_width) {
window_size.width = req_width;
} else {
// compute from the requested height
window_size.width = (uint32_t) req_height * frame_size.width
/ frame_size.height;
window_size.width = (uint32_t) req_height * content_size.width
/ content_size.height;
}
if (req_height) {
window_size.height = req_height;
} else {
// compute from the requested width
window_size.height = (uint32_t) req_width * frame_size.height
/ frame_size.width;
window_size.height = (uint32_t) req_width * content_size.height
/ content_size.width;
}
}
return window_size;
Expand All @@ -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,
uint16_t window_height, bool window_borderless) {
screen->frame_size = frame_size;
struct size content_size =
get_rotated_size(frame_size, screen->rotation);

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;
#ifdef HIDPI_SUPPORT
window_flags |= SDL_WINDOW_ALLOW_HIGHDPI;
Expand Down Expand Up @@ -206,8 +221,8 @@ screen_init_rendering(struct screen *screen, const char *window_title,
return false;
}

if (SDL_RenderSetLogicalSize(screen->renderer, frame_size.width,
frame_size.height)) {
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;
Expand Down Expand Up @@ -253,28 +268,68 @@ 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("Client rotation set to %u", rotation);

screen_render(screen);
}

// recreate the texture and resize the window if the frame size has changed
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) {
if (SDL_RenderSetLogicalSize(screen->renderer, new_frame_size.width,
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 content_size =
get_rotated_size(screen->frame_size, screen->rotation);
struct size windowed_size = get_windowed_window_size(screen);
struct size target_size = {
(uint32_t) windowed_size.width * new_frame_size.width
/ screen->frame_size.width,
(uint32_t) windowed_size.height * new_frame_size.height
/ screen->frame_size.height,
(uint32_t) windowed_size.width * new_content_size.width
/ content_size.width,
(uint32_t) windowed_size.height * new_content_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);

screen->frame_size = new_frame_size;
Expand Down Expand Up @@ -319,7 +374,27 @@ screen_update_frame(struct screen *screen, struct video_buffer *vb) {
void
screen_render(struct screen *screen) {
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 {
double angle = 90 * screen->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 = &rect;
}

// rotation in RenderCopyEx() is clockwise
SDL_RenderCopyEx(screen->renderer, screen->texture, NULL, dstrect,
angle, NULL, 0);
}
SDL_RenderPresent(screen->renderer);
}

Expand Down Expand Up @@ -349,8 +424,10 @@ screen_resize_to_fit(struct screen *screen) {
screen->maximized = false;
}

struct size content_size =
get_rotated_size(screen->frame_size, screen->rotation);
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);
LOGD("Resized to optimal size");
}
Expand All @@ -366,8 +443,9 @@ screen_resize_to_pixel_perfect(struct screen *screen) {
screen->maximized = false;
}

SDL_SetWindowSize(screen->window, screen->frame_size.width,
screen->frame_size.height);
struct size content_size =
get_rotated_size(screen->frame_size, screen->rotation);
SDL_SetWindowSize(screen->window, content_size.width, content_size.height);
LOGD("Resized to pixel-perfect");
}

Expand Down
7 changes: 7 additions & 0 deletions app/src/screen.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ struct screen {
// Since we receive the event SIZE_CHANGED before MAXIMIZED, we must be
// able to revert the size to its non-maximized value.
struct size windowed_window_size_backup;
// client rotation: 0, 1, 2 or 3 (x90 degrees clockwise)
unsigned rotation;
bool has_frame;
bool fullscreen;
bool maximized;
Expand All @@ -44,6 +46,7 @@ struct screen {
.width = 0, \
.height = 0, \
}, \
.rotation = 0, \
.has_frame = false, \
.fullscreen = false, \
.maximized = false, \
Expand Down Expand Up @@ -90,6 +93,10 @@ screen_resize_to_fit(struct screen *screen);
void
screen_resize_to_pixel_perfect(struct screen *screen);

// change the client rotation (0, 1, 2 or 3, x90 degrees clockwise)
void
screen_set_rotation(struct screen *screen, unsigned rotation);

// react to window events
void
screen_handle_window_event(struct screen *screen, const SDL_WindowEvent *event);
Expand Down

0 comments on commit 8f707d9

Please sign in to comment.