Customize shortcut modifier

Add --shortcut-mod, and use Alt as default modifier.

This paves the way to forward the Ctrl key to the device.
shortcutmod
Romain Vimont 4 years ago
parent 63cb93d7d7
commit 1b76d9fd78

@ -354,7 +354,7 @@ scrcpy --fullscreen
scrcpy -f # short version
```
Fullscreen can then be toggled dynamically with `Ctrl`+`f`.
Fullscreen can then be toggled dynamically with `MOD`+`f`.
#### Rotation
@ -370,17 +370,17 @@ Possibles values are:
- `2`: 180 degrees
- `3`: 90 degrees clockwise
The rotation can also be changed dynamically with `Ctrl`+`←` _(left)_ and
`Ctrl`+`→` _(right)_.
The rotation can also be changed dynamically with `MOD`+`←` _(left)_ and
`MOD`+`→` _(right)_.
Note that _scrcpy_ manages 3 different rotations:
- `Ctrl`+`r` requests the device to switch between portrait and landscape (the
- `MOD`+`r` requests the device to switch between portrait and landscape (the
current running app may refuse, if it does support the requested
orientation).
- `--lock-video-orientation` changes the mirroring orientation (the orientation
of the video sent from the device to the computer). This affects the
recording.
- `--rotation` (or `Ctrl`+`←`/`Ctrl`+`→`) rotates only the window content. This
- `--rotation` (or `MOD`+`←`/`MOD`+`→`) rotates only the window content. This
affects only the display, not the recording.
@ -437,9 +437,9 @@ scrcpy --turn-screen-off
scrcpy -S
```
Or by pressing `Ctrl`+`o` at any time.
Or by pressing `MOD`+`o` at any time.
To turn it back on, press `Ctrl`+`Shift`+`o` (or `POWER`, `Ctrl`+`p`).
To turn it back on, press `MOD`+`Shift`+`o` (or `POWER`, `MOD`+`p`).
It can be useful to also prevent the device to sleep:
@ -494,7 +494,7 @@ scrcpy --disable-screensaver
#### Rotate device screen
Press `Ctrl`+`r` to switch between portrait and landscape modes.
Press `MOD`+`r` to switch between portrait and landscape modes.
Note that it rotates only if the application in foreground supports the
requested orientation.
@ -504,10 +504,10 @@ requested orientation.
It is possible to synchronize clipboards between the computer and the device, in
both directions:
- `Ctrl`+`c` copies the device clipboard to the computer clipboard;
- `Ctrl`+`Shift`+`v` copies the computer clipboard to the device clipboard (and
- `MOD`+`c` copies the device clipboard to the computer clipboard;
- `MOD`+`Shift`+`v` copies the computer clipboard to the device clipboard (and
pastes if the device runs Android >= 7);
- `Ctrl`+`v` _pastes_ the computer clipboard as a sequence of text events (but
- `MOD`+`v` _pastes_ the computer clipboard as a sequence of text events (but
breaks non-ASCII characters).
Moreover, any time the Android clipboard changes, it is automatically
@ -571,30 +571,48 @@ Also see [issue #14].
## Shortcuts
In the following list, `MOD` is the shortcut modifier. By default, it's (left)
`Alt`.
It can be changed using `--shortcut-mod`. Possible keys are `lctrl`, `rctrl`,
`lalt`, `ralt`, `lsuper` and `rsuper`. For example:
```bash
# use RCtrl for shortcuts
scrcpy --shortcut-mod=rctrl
# use either LCtrl+LAlt or LSuper for shortcuts
scrcpy --shortcut-mod=lctrl+lalt,lsuper
```
_[Super] is typically the "Windows" or "Cmd" key._
[Super]: https://en.wikipedia.org/wiki/Super_key_(keyboard_button)
| Action | Shortcut
| ------------------------------------------- |:-----------------------------
| Switch fullscreen mode | `Ctrl`+`f`
| Rotate display left | `Ctrl`+`←` _(left)_
| Rotate display right | `Ctrl`+`→` _(right)_
| Resize window to 1:1 (pixel-perfect) | `Ctrl`+`g`
| Resize window to remove black borders | `Ctrl`+`x` \| _Double-click¹_
| Click on `HOME` | `Ctrl`+`h` \| _Middle-click_
| Click on `BACK` | `Ctrl`+`b` \| _Right-click²_
| Click on `APP_SWITCH` | `Ctrl`+`s`
| Click on `MENU` | `Ctrl`+`m`
| Click on `VOLUME_UP` | `Ctrl`+`↑` _(up)_
| Click on `VOLUME_DOWN` | `Ctrl`+`↓` _(down)_
| Click on `POWER` | `Ctrl`+`p`
| Switch fullscreen mode | `MOD`+`f`
| Rotate display left | `MOD`+`←` _(left)_
| Rotate display right | `MOD`+`→` _(right)_
| Resize window to 1:1 (pixel-perfect) | `MOD`+`g`
| Resize window to remove black borders | `MOD`+`x` \| _Double-click¹_
| Click on `HOME` | `MOD`+`h` \| _Middle-click_
| Click on `BACK` | `MOD`+`b` \| _Right-click²_
| Click on `APP_SWITCH` | `MOD`+`s`
| Click on `MENU` | `MOD`+`m`
| Click on `VOLUME_UP` | `MOD`+`↑` _(up)_
| Click on `VOLUME_DOWN` | `MOD`+`↓` _(down)_
| Click on `POWER` | `MOD`+`p`
| Power on | _Right-click²_
| Turn device screen off (keep mirroring) | `Ctrl`+`o`
| Turn device screen on | `Ctrl`+`Shift`+`o`
| Rotate device screen | `Ctrl`+`r`
| Expand notification panel | `Ctrl`+`n`
| Collapse notification panel | `Ctrl`+`Shift`+`n`
| Copy device clipboard to computer | `Ctrl`+`c`
| Paste computer clipboard to device | `Ctrl`+`v`
| Copy computer clipboard to device and paste | `Ctrl`+`Shift`+`v`
| Enable/disable FPS counter (on stdout) | `Ctrl`+`i`
| Turn device screen off (keep mirroring) | `MOD`+`o`
| Turn device screen on | `MOD`+`Shift`+`o`
| Rotate device screen | `MOD`+`r`
| Expand notification panel | `MOD`+`n`
| Collapse notification panel | `MOD`+`Shift`+`n`
| Copy device clipboard to computer | `MOD`+`c`
| Paste computer clipboard to device | `MOD`+`v`
| Copy computer clipboard to device and paste | `MOD`+`Shift`+`v`
| Enable/disable FPS counter (on stdout) | `MOD`+`i`
_¹Double-click on black borders to remove them._
_²Right-click turns the screen on if it was off, presses BACK otherwise._

@ -186,7 +186,7 @@ if get_option('buildtype') == 'debug'
exe = executable(t[0], t[1],
include_directories: src_dir,
dependencies: dependencies,
c_args: ['-DSDL_MAIN_HANDLED'])
c_args: ['-DSDL_MAIN_HANDLED', '-DSC_TEST'])
test(t[0], exe)
endforeach
endif

@ -149,6 +149,16 @@ Set the initial display rotation. Possibles values are 0, 1, 2 and 3. Each incre
.BI "\-s, \-\-serial " number
The device serial number. Mandatory only if several devices are connected to adb.
.TP
.BI "\-\-shortcut\-mod " key[+...]][,...]
Specify the modifiers to use for scrcpy shortcuts. Possible keys are "lctrl", "rctrl", "lalt", "ralt", "lsuper" and "rsuper".
A shortcut can consist in several keys, separated by '+'. Several shortcuts can be specified, separated by ','.
For example, to use either LCtrl+LAlt or LSuper for scrcpy shortcuts, pass "lctrl+lalt,lsuper".
Default is "lalt" (left-Alt).
.TP
.B \-S, \-\-turn\-screen\-off
Turn the device screen off immediately.
@ -207,52 +217,55 @@ Default is 0 (automatic).\n
.SH SHORTCUTS
In the following list, MOD is the shortcut modifier. By default, it's (left)
Alt, but it can be configured by \-\-shortcut-mod.
.TP
.B Ctrl+f
.B MOD+f
Switch fullscreen mode
.TP
.B Ctrl+Left
.B MOD+Left
Rotate display left
.TP
.B Ctrl+Right
.B MOD+Right
Rotate display right
.TP
.B Ctrl+g
.B MOD+g
Resize window to 1:1 (pixel\-perfect)
.TP
.B Ctrl+x, Double\-click on black borders
.B MOD+x, Double\-click on black borders
Resize window to remove black borders
.TP
.B Ctrl+h, Home, Middle\-click
.B MOD+h, Home, Middle\-click
Click on HOME
.TP
.B Ctrl+b, Ctrl+Backspace, Right\-click (when screen is on)
.B MOD+b, MOD+Backspace, Right\-click (when screen is on)
Click on BACK
.TP
.B Ctrl+s
.B MOD+s
Click on APP_SWITCH
.TP
.B Ctrl+m
.B MOD+m
Click on MENU
.TP
.B Ctrl+Up
.B MOD+Up
Click on VOLUME_UP
.TP
.B Ctrl+Down
.B MOD+Down
Click on VOLUME_DOWN
.TP
.B Ctrl+p
.B MOD+p
Click on POWER (turn screen on/off)
.TP
@ -260,39 +273,39 @@ Click on POWER (turn screen on/off)
Turn screen on
.TP
.B Ctrl+o
.B MOD+o
Turn device screen off (keep mirroring)
.TP
.B Ctrl+Shift+o
.B MOD+Shift+o
Turn device screen on
.TP
.B Ctrl+r
.B MOD+r
Rotate device screen
.TP
.B Ctrl+n
.B MOD+n
Expand notification panel
.TP
.B Ctrl+Shift+n
.B MOD+Shift+n
Collapse notification panel
.TP
.B Ctrl+c
.B MOD+c
Copy device clipboard to computer
.TP
.B Ctrl+v
.B MOD+v
Paste computer clipboard to device
.TP
.B Ctrl+Shift+v
.B MOD+Shift+v
Copy computer clipboard to device (and paste if the device runs Android >= 7)
.TP
.B Ctrl+i
.B MOD+i
Enable/disable FPS counter (print frames/second in logs)
.TP

@ -138,6 +138,19 @@ scrcpy_print_usage(const char *arg0) {
" The device serial number. Mandatory only if several devices\n"
" are connected to adb.\n"
"\n"
" --shortcut-mod key[+...]][,...]\n"
" Specify the modifiers to use for scrcpy shortcuts.\n"
" Possible keys are \"lctrl\", \"rctrl\", \"lalt\", \"ralt\",\n"
" \"lsuper\" and \"rsuper\".\n"
"\n"
" A shortcut can consist in several keys, separated by '+'.\n"
" Several shortcuts can be specified, separated by ','.\n"
"\n"
" For example, to use either LCtrl+LAlt or LSuper for scrcpy\n"
" shortcuts, pass \"lctrl+lalt,lsuper\".\n"
"\n"
" Default is \"lalt\" (left-Alt).\n"
"\n"
" -S, --turn-screen-off\n"
" Turn the device screen off immediately.\n"
"\n"
@ -185,75 +198,78 @@ scrcpy_print_usage(const char *arg0) {
"\n"
"Shortcuts:\n"
"\n"
" Ctrl+f\n"
" In the following list, MOD is the shortcut modifier. By default,\n"
" it's (left) Alt, but it can be configured by --shortcut-mod.\n"
"\n"
" MOD+f\n"
" Switch fullscreen mode\n"
"\n"
" Ctrl+Left\n"
" MOD+Left\n"
" Rotate display left\n"
"\n"
" Ctrl+Right\n"
" MOD+Right\n"
" Rotate display right\n"
"\n"
" Ctrl+g\n"
" MOD+g\n"
" Resize window to 1:1 (pixel-perfect)\n"
"\n"
" Ctrl+x\n"
" MOD+x\n"
" Double-click on black borders\n"
" Resize window to remove black borders\n"
"\n"
" Ctrl+h\n"
" MOD+h\n"
" Middle-click\n"
" Click on HOME\n"
"\n"
" Ctrl+b\n"
" Ctrl+Backspace\n"
" MOD+b\n"
" MOD+Backspace\n"
" Right-click (when screen is on)\n"
" Click on BACK\n"
"\n"
" Ctrl+s\n"
" MOD+s\n"
" Click on APP_SWITCH\n"
"\n"
" Ctrl+m\n"
" MOD+m\n"
" Click on MENU\n"
"\n"
" Ctrl+Up\n"
" MOD+Up\n"
" Click on VOLUME_UP\n"
"\n"
" Ctrl+Down\n"
" MOD+Down\n"
" Click on VOLUME_DOWN\n"
"\n"
" Ctrl+p\n"
" MOD+p\n"
" Click on POWER (turn screen on/off)\n"
"\n"
" Right-click (when screen is off)\n"
" Power on\n"
"\n"
" Ctrl+o\n"
" MOD+o\n"
" Turn device screen off (keep mirroring)\n"
"\n"
" Ctrl+Shift+o\n"
" MOD+Shift+o\n"
" Turn device screen on\n"
"\n"
" Ctrl+r\n"
" MOD+r\n"
" Rotate device screen\n"
"\n"
" Ctrl+n\n"
" MOD+n\n"
" Expand notification panel\n"
"\n"
" Ctrl+Shift+n\n"
" MOD+Shift+n\n"
" Collapse notification panel\n"
"\n"
" Ctrl+c\n"
" MOD+c\n"
" Copy device clipboard to computer\n"
"\n"
" Ctrl+v\n"
" MOD+v\n"
" Paste computer clipboard to device\n"
"\n"
" Ctrl+Shift+v\n"
" MOD+Shift+v\n"
" Copy computer clipboard to device (and paste if the device\n"
" runs Android >= 7)\n"
"\n"
" Ctrl+i\n"
" MOD+i\n"
" Enable/disable FPS counter (print frames/second in logs)\n"
"\n"
" Drag & drop APK file\n"
@ -475,6 +491,101 @@ parse_log_level(const char *s, enum sc_log_level *log_level) {
return false;
}
// item is a list of mod keys separated by '+' (e.g. "lctrl+lalt")
// returns a bitwise-or of SC_MOD_* constants (or 0 on error)
static unsigned
parse_shortcut_mods_item(const char *item, size_t len) {
unsigned mod = 0;
for (;;) {
char *plus = strchr(item, '+');
// strchr() does not consider the "len" parameter, to it could find an
// occurrence too far in the string (there is no strnchr())
bool has_plus = plus && plus < item + len;
assert(!has_plus || plus > item);
size_t key_len = has_plus ? (size_t) (plus - item) : len;
#define STREQ(literal, s, len) \
((sizeof(literal)-1 == len) && !memcmp(literal, s, len))
if (STREQ("lctrl", item, key_len)) {
mod |= SC_MOD_LCTRL;
} else if (STREQ("rctrl", item, key_len)) {
mod |= SC_MOD_RCTRL;
} else if (STREQ("lalt", item, key_len)) {
mod |= SC_MOD_LALT;
} else if (STREQ("ralt", item, key_len)) {
mod |= SC_MOD_RALT;
} else if (STREQ("lsuper", item, key_len)) {
mod |= SC_MOD_LSUPER;
} else if (STREQ("rsuper", item, key_len)) {
mod |= SC_MOD_RSUPER;
} else {
LOGW("Unknown modifier key: %.*s", (int) key_len, item);
return 0;
}
#undef STREQ
if (!has_plus) {
break;
}
item = plus + 1;
assert(len >= key_len + 1);
len -= key_len + 1;
}
return mod;
}
static bool
parse_shortcut_mods(const char *s, struct sc_shortcut_mods *mods) {
unsigned count = 0;
unsigned current = 0;
// LCtrl+LAlt or RCtrl or LCtrl+RSuper: "lctrl+lalt,rctrl,lctrl+rsuper"
for (;;) {
char *comma = strchr(s, ',');
if (comma && count == SC_MAX_SHORTCUT_MODS - 1) {
assert(count < SC_MAX_SHORTCUT_MODS);
LOGW("Too many shortcut modifiers alternatives");
return false;
}
assert(!comma || comma > s);
size_t limit = comma ? (size_t) (comma - s) : strlen(s);
unsigned mod = parse_shortcut_mods_item(s, limit);
if (!mod) {
LOGE("Invalid modifier keys: %.*s", (int) limit, s);
return false;
}
mods->data[current++] = mod;
++count;
if (!comma) {
break;
}
s = comma + 1;
}
mods->count = count;
return true;
}
#ifdef SC_TEST
// expose the function to unit-tests
bool
sc_parse_shortcut_mods(const char *s, struct sc_shortcut_mods *mods) {
return parse_shortcut_mods(s, mods);
}
#endif
static bool
parse_record_format(const char *optarg, enum sc_record_format *format) {
if (!strcmp(optarg, "mp4")) {
@ -526,6 +637,7 @@ guess_record_format(const char *filename) {
#define OPT_CODEC_OPTIONS 1018
#define OPT_FORCE_ADB_FORWARD 1019
#define OPT_DISABLE_SCREENSAVER 1020
#define OPT_SHORTCUT_MOD 1021
bool
scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) {
@ -558,6 +670,7 @@ scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) {
OPT_RENDER_EXPIRED_FRAMES},
{"rotation", required_argument, NULL, OPT_ROTATION},
{"serial", required_argument, NULL, 's'},
{"shortcut-mod", required_argument, NULL, OPT_SHORTCUT_MOD},
{"show-touches", no_argument, NULL, 't'},
{"stay-awake", no_argument, NULL, 'w'},
{"turn-screen-off", no_argument, NULL, 'S'},
@ -721,6 +834,11 @@ scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) {
case OPT_DISABLE_SCREENSAVER:
opts->disable_screensaver = true;
break;
case OPT_SHORTCUT_MOD:
if (!parse_shortcut_mods(optarg, &opts->shortcut_mods)) {
return false;
}
break;
default:
// getopt prints the error message on stderr
return false;

@ -18,4 +18,9 @@ scrcpy_print_usage(const char *arg0);
bool
scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]);
#ifdef SC_TEST
bool
sc_parse_shortcut_mods(const char *s, struct sc_shortcut_mods *mods);
#endif
#endif

@ -1,6 +1,7 @@
#include "input_manager.h"
#include <assert.h>
#include <SDL2/SDL_keycode.h>
#include "config.h"
#include "event_converter.h"
@ -10,6 +11,64 @@
static const int ACTION_DOWN = 1;
static const int ACTION_UP = 1 << 1;
#define SC_SDL_SHORTCUT_MODS_MASK (KMOD_CTRL | KMOD_ALT | KMOD_GUI)
static inline uint16_t
to_sdl_mod(unsigned mod) {
uint16_t sdl_mod = 0;
if (mod & SC_MOD_LCTRL) {
sdl_mod |= KMOD_LCTRL;
}
if (mod & SC_MOD_RCTRL) {
sdl_mod |= KMOD_RCTRL;
}
if (mod & SC_MOD_LALT) {
sdl_mod |= KMOD_LALT;
}
if (mod & SC_MOD_RALT) {
sdl_mod |= KMOD_RALT;
}
if (mod & SC_MOD_LSUPER) {
sdl_mod |= KMOD_LGUI;
}
if (mod & SC_MOD_RSUPER) {
sdl_mod |= KMOD_RGUI;
}
return sdl_mod;
}
static bool
is_shortcut_mod(struct input_manager *im, uint16_t sdl_mod) {
// keep only the relevant modifier keys
sdl_mod &= SC_SDL_SHORTCUT_MODS_MASK;
assert(im->sdl_shortcut_mods.count);
assert(im->sdl_shortcut_mods.count < SC_MAX_SHORTCUT_MODS);
for (unsigned i = 0; i < im->sdl_shortcut_mods.count; ++i) {
if (im->sdl_shortcut_mods.data[i] == sdl_mod) {
return true;
}
}
return false;
}
void
input_manager_init(struct input_manager *im, bool prefer_text,
const struct sc_shortcut_mods *shortcut_mods)
{
im->prefer_text = prefer_text;
assert(shortcut_mods->count);
assert(shortcut_mods->count < SC_MAX_SHORTCUT_MODS);
for (unsigned i = 0; i < shortcut_mods->count; ++i) {
uint16_t sdl_mod = to_sdl_mod(shortcut_mods->data[i]);
assert(sdl_mod);
im->sdl_shortcut_mods.data[i] = sdl_mod;
}
im->sdl_shortcut_mods.count = shortcut_mods->count;
}
static void
send_keycode(struct controller *controller, enum android_keycode keycode,
int actions, const char *name) {
@ -258,22 +317,13 @@ input_manager_process_key(struct input_manager *im,
const SDL_KeyboardEvent *event,
bool control) {
// control: indicates the state of the command-line option --no-control
// ctrl: the Ctrl key
bool ctrl = event->keysym.mod & (KMOD_LCTRL | KMOD_RCTRL);
bool alt = event->keysym.mod & (KMOD_LALT | KMOD_RALT);
bool meta = event->keysym.mod & (KMOD_LGUI | KMOD_RGUI);
if (alt || meta) {
// no shortcuts involve Alt or Meta, and they must not be forwarded to
// the device
return;
}
bool smod = is_shortcut_mod(im, event->keysym.mod);
struct controller *controller = im->controller;
// capture all Ctrl events
if (ctrl) {
// The shortcut modifier is pressed
if (smod) {
SDL_Keycode keycode = event->keysym.sym;
bool down = event->type == SDL_KEYDOWN;
int action = down ? ACTION_DOWN : ACTION_UP;
@ -281,33 +331,33 @@ input_manager_process_key(struct input_manager *im,
bool shift = event->keysym.mod & (KMOD_LSHIFT | KMOD_RSHIFT);
switch (keycode) {
case SDLK_h:
if (control && ctrl && !shift && !repeat) {
if (control && !shift && !repeat) {
action_home(controller, action);
}
return;
case SDLK_b: // fall-through
case SDLK_BACKSPACE:
if (control && ctrl && !shift && !repeat) {
if (control && !shift && !repeat) {
action_back(controller, action);
}
return;
case SDLK_s:
if (control && ctrl && !shift && !repeat) {
if (control && !shift && !repeat) {
action_app_switch(controller, action);
}
return;
case SDLK_m:
if (control && ctrl && !shift && !repeat) {
if (control && !shift && !repeat) {
action_menu(controller, action);
}
return;
case SDLK_p:
if (control && ctrl && !shift && !repeat) {
if (control && !shift && !repeat) {
action_power(controller, action);
}
return;
case SDLK_o:
if (control && ctrl && !repeat && down) {
if (control && !repeat && down) {
enum screen_power_mode mode = shift
? SCREEN_POWER_MODE_NORMAL
: SCREEN_POWER_MODE_OFF;
@ -315,34 +365,34 @@ input_manager_process_key(struct input_manager *im,
}
return;
case SDLK_DOWN:
if (control && ctrl && !shift) {
if (control && !shift) {
// forward repeated events
action_volume_down(controller, action);
}
return;
case SDLK_UP:
if (control && ctrl && !shift) {
if (control && !shift) {
// forward repeated events
action_volume_up(controller, action);
}
return;
case SDLK_LEFT:
if (ctrl && !shift && !repeat && down) {
if (!shift && !repeat && down) {
rotate_client_left(im->screen);
}
return;
case SDLK_RIGHT:
if (ctrl && !shift && !repeat && down) {
if (!shift && !repeat && down) {
rotate_client_right(im->screen);
}
return;
case SDLK_c:
if (control && ctrl && !shift && !repeat && down) {
if (control && !shift && !repeat && down) {
request_device_clipboard(controller);
}
return;
case SDLK_v:
if (control && ctrl && !repeat && down) {
if (control && !repeat && down) {
if (shift) {
// store the text in the device clipboard and paste
set_device_clipboard(controller, true);
@ -353,29 +403,29 @@ input_manager_process_key(struct input_manager *im,
}
return;
case SDLK_f:
if (ctrl && !shift && !repeat && down) {
if (!shift && !repeat && down) {
screen_switch_fullscreen(im->screen);
}
return;
case SDLK_x:
if (ctrl && !shift && !repeat && down) {
if (!shift && !repeat && down) {
screen_resize_to_fit(im->screen);
}
return;
case SDLK_g:
if (ctrl && !shift && !repeat && down) {
if (!shift && !repeat && down) {
screen_resize_to_pixel_perfect(im->screen);
}
return;
case SDLK_i:
if (ctrl && !shift && !repeat && down) {
if (!shift && !repeat && down) {
struct fps_counter *fps_counter =
im->video_buffer->fps_counter;
switch_fps_counter_state(fps_counter);
}
return;
case SDLK_n:
if (control && ctrl && !repeat && down) {
if (control && !repeat && down) {
if (shift) {
collapse_notification_panel(controller);
} else {
@ -384,7 +434,7 @@ input_manager_process_key(struct input_manager *im,
}
return;
case SDLK_r:
if (control && ctrl && !shift && !repeat && down) {
if (control && !shift && !repeat && down) {
rotate_device(controller);
}
return;

@ -3,12 +3,15 @@
#include <stdbool.h>
#include <SDL2/SDL.h>
#include "config.h"
#include "common.h"
#include "controller.h"
#include "fps_counter.h"
#include "video_buffer.h"
#include "scrcpy.h"
#include "screen.h"
#include "video_buffer.h"
struct input_manager {
struct controller *controller;
@ -20,8 +23,17 @@ struct input_manager {
unsigned repeat;
bool prefer_text;
struct {
unsigned data[SC_MAX_SHORTCUT_MODS];
unsigned count;
} sdl_shortcut_mods;
};
void
input_manager_init(struct input_manager *im, bool prefer_text,
const struct sc_shortcut_mods *shortcut_mods);
void
input_manager_process_text_input(struct input_manager *im,
const SDL_TextInputEvent *event);

@ -49,7 +49,13 @@ static struct input_manager input_manager = {
.video_buffer = &video_buffer,
.screen = &screen,
.repeat = 0,
.prefer_text = false, // initialized later
// initialized later
.prefer_text = false,
.sdl_shortcut_mods = {
.data = {0},
.count = 0,
},
};
#ifdef _WIN32
@ -437,7 +443,8 @@ scrcpy(const struct scrcpy_options *options) {
}
}
input_manager.prefer_text = options->prefer_text;
input_manager_init(&input_manager, options->prefer_text,
&options->shortcut_mods);
ret = event_loop(options->display, options->control);
LOGD("quit...");

@ -20,6 +20,22 @@ enum sc_record_format {
SC_RECORD_FORMAT_MKV,
};
#define SC_MAX_SHORTCUT_MODS 8
enum sc_shortcut_mod {
SC_MOD_LCTRL = 1 << 0,
SC_MOD_RCTRL = 1 << 1,
SC_MOD_LALT = 1 << 2,
SC_MOD_RALT = 1 << 3,
SC_MOD_LSUPER = 1 << 4,
SC_MOD_RSUPER = 1 << 5,
};
struct sc_shortcut_mods {
unsigned data[SC_MAX_SHORTCUT_MODS];
unsigned count;
};
struct sc_port_range {
uint16_t first;
uint16_t last;
@ -38,6 +54,7 @@ struct scrcpy_options {
enum sc_log_level log_level;
enum sc_record_format record_format;
struct sc_port_range port_range;
struct sc_shortcut_mods shortcut_mods;
uint16_t max_size;
uint32_t bit_rate;
uint16_t max_fps;
@ -77,6 +94,10 @@ struct scrcpy_options {
.first = DEFAULT_LOCAL_PORT_RANGE_FIRST, \
.last = DEFAULT_LOCAL_PORT_RANGE_LAST, \
}, \
.shortcut_mods = { \
.data = {SC_MOD_LALT}, \
.count = 1, \
}, \
.max_size = DEFAULT_MAX_SIZE, \
.bit_rate = DEFAULT_BIT_RATE, \
.max_fps = 0, \

@ -3,6 +3,7 @@
#include "cli.h"
#include "common.h"
#include "scrcpy.h"
static void test_flag_version(void) {
struct scrcpy_cli_args args = {
@ -122,6 +123,43 @@ static void test_options2(void) {
assert(opts->record_format == SC_RECORD_FORMAT_MP4);
}
static void test_parse_shortcut_mods(void) {
struct sc_shortcut_mods mods;
bool ok;
ok = sc_parse_shortcut_mods("lctrl", &mods);
assert(ok);
assert(mods.count == 1);
assert(mods.data[0] == SC_MOD_LCTRL);
ok = sc_parse_shortcut_mods("lctrl+lalt", &mods);
assert(ok);
assert(mods.count == 1);
assert(mods.data[0] == (SC_MOD_LCTRL | SC_MOD_LALT));
ok = sc_parse_shortcut_mods("rctrl,lalt", &mods);
assert(ok);
assert(mods.count == 2);
assert(mods.data[0] == SC_MOD_RCTRL);
assert(mods.data[1] == SC_MOD_LALT);
ok = sc_parse_shortcut_mods("lsuper,rsuper+lalt,lctrl+rctrl+ralt", &mods);
assert(ok);
assert(mods.count == 3);
assert(mods.data[0] == SC_MOD_LSUPER);
assert(mods.data[1] == (SC_MOD_RSUPER | SC_MOD_LALT));
assert(mods.data[2] == (SC_MOD_LCTRL | SC_MOD_RCTRL | SC_MOD_RALT));
ok = sc_parse_shortcut_mods("", &mods);
assert(!ok);
ok = sc_parse_shortcut_mods("lctrl+", &mods);
assert(!ok);
ok = sc_parse_shortcut_mods("lctrl,", &mods);
assert(!ok);
}
int main(int argc, char *argv[]) {
(void) argc;
(void) argv;
@ -130,5 +168,6 @@ int main(int argc, char *argv[]) {
test_flag_help();
test_options();
test_options2();
test_parse_shortcut_mods();
return 0;
};

Loading…
Cancel
Save