Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ctrl+n+n for settings panel #2260

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 17 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -698,10 +698,10 @@ _<kbd>[Super]</kbd> is typically the <kbd>Windows</kbd> or <kbd>Cmd</kbd> key._
| Rotate display left | <kbd>MOD</kbd>+<kbd>←</kbd> _(left)_
| Rotate display right | <kbd>MOD</kbd>+<kbd>→</kbd> _(right)_
| Resize window to 1:1 (pixel-perfect) | <kbd>MOD</kbd>+<kbd>g</kbd>
| Resize window to remove black borders | <kbd>MOD</kbd>+<kbd>w</kbd> \| _Double-click¹_
| Resize window to remove black borders | <kbd>MOD</kbd>+<kbd>w</kbd> \| _Left-Double-click¹_
| Click on `HOME` | <kbd>MOD</kbd>+<kbd>h</kbd> \| _Middle-click_
| Click on `BACK` | <kbd>MOD</kbd>+<kbd>b</kbd> \| _Right-click²_
| Click on `APP_SWITCH` | <kbd>MOD</kbd>+<kbd>s</kbd>
| Click on `APP_SWITCH` | <kbd>MOD</kbd>+<kbd>s</kbd> \| _4th-click³_
| Click on `MENU` (unlock screen) | <kbd>MOD</kbd>+<kbd>m</kbd>
| Click on `VOLUME_UP` | <kbd>MOD</kbd>+<kbd>↑</kbd> _(up)_
| Click on `VOLUME_DOWN` | <kbd>MOD</kbd>+<kbd>↓</kbd> _(down)_
Expand All @@ -710,18 +710,27 @@ _<kbd>[Super]</kbd> is typically the <kbd>Windows</kbd> or <kbd>Cmd</kbd> key._
| Turn device screen off (keep mirroring) | <kbd>MOD</kbd>+<kbd>o</kbd>
| Turn device screen on | <kbd>MOD</kbd>+<kbd>Shift</kbd>+<kbd>o</kbd>
| Rotate device screen | <kbd>MOD</kbd>+<kbd>r</kbd>
| Expand notification panel | <kbd>MOD</kbd>+<kbd>n</kbd>
| Collapse notification panel | <kbd>MOD</kbd>+<kbd>Shift</kbd>+<kbd>n</kbd>
| Copy to clipboard³ | <kbd>MOD</kbd>+<kbd>c</kbd>
| Cut to clipboard³ | <kbd>MOD</kbd>+<kbd>x</kbd>
| Synchronize clipboards and paste³ | <kbd>MOD</kbd>+<kbd>v</kbd>
| Expand notification panel | <kbd>MOD</kbd>+<kbd>n</kbd> \| _5th-click³_
| Expand settings panel | <kbd>MOD</kbd>+<kbd>n</kbd>+<kbd>n</kbd> \| _5th-double-click³_
| Collapse panels | <kbd>MOD</kbd>+<kbd>Shift</kbd>+<kbd>n</kbd>
| Copy to clipboard⁴ | <kbd>MOD</kbd>+<kbd>c</kbd>
| Cut to clipboard⁴ | <kbd>MOD</kbd>+<kbd>x</kbd>
| Synchronize clipboards and paste⁴ | <kbd>MOD</kbd>+<kbd>v</kbd>
| Inject computer clipboard text | <kbd>MOD</kbd>+<kbd>Shift</kbd>+<kbd>v</kbd>
| Enable/disable FPS counter (on stdout) | <kbd>MOD</kbd>+<kbd>i</kbd>
| Pinch-to-zoom | <kbd>Ctrl</kbd>+_click-and-move_

_¹Double-click on black borders to remove them._
_²Right-click turns the screen on if it was off, presses BACK otherwise._
_³Only on Android >= 7._
_³4th/5th mouse button, if your mouse has it_
_⁴Only on Android >= 7._

Shortcuts with repeated keys are executted by releasing and pressing the key a second time.
For example, to execute "Expand settings panel":

1. Press and keep pressing <kbd>MOD</kbd>.
2. Then double-press <kbd>n</kbd>.
3. Finally, release <kbd>MOD</kbd>.

All <kbd>Ctrl</kbd>+_key_ shortcuts are forwarded to the device, so they are
handled by the active application.
Expand Down
3 changes: 2 additions & 1 deletion app/src/control_msg.c
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,8 @@ control_msg_serialize(const struct control_msg *msg, unsigned char *buf) {
buf[1] = msg->set_screen_power_mode.mode;
return 2;
case CONTROL_MSG_TYPE_EXPAND_NOTIFICATION_PANEL:
case CONTROL_MSG_TYPE_COLLAPSE_NOTIFICATION_PANEL:
case CONTROL_MSG_TYPE_EXPAND_SETTINGS_PANEL:
case CONTROL_MSG_TYPE_COLLAPSE_PANELS:
case CONTROL_MSG_TYPE_GET_CLIPBOARD:
case CONTROL_MSG_TYPE_ROTATE_DEVICE:
// no additional data
Expand Down
3 changes: 2 additions & 1 deletion app/src/control_msg.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ enum control_msg_type {
CONTROL_MSG_TYPE_INJECT_SCROLL_EVENT,
CONTROL_MSG_TYPE_BACK_OR_SCREEN_ON,
CONTROL_MSG_TYPE_EXPAND_NOTIFICATION_PANEL,
CONTROL_MSG_TYPE_COLLAPSE_NOTIFICATION_PANEL,
CONTROL_MSG_TYPE_EXPAND_SETTINGS_PANEL,
CONTROL_MSG_TYPE_COLLAPSE_PANELS,
CONTROL_MSG_TYPE_GET_CLIPBOARD,
CONTROL_MSG_TYPE_SET_CLIPBOARD,
CONTROL_MSG_TYPE_SET_SCREEN_POWER_MODE,
Expand Down
45 changes: 38 additions & 7 deletions app/src/input_manager.c
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,10 @@ input_manager_init(struct input_manager *im,
im->sdl_shortcut_mods.count = shortcut_mods->count;

im->vfinger_down = false;

im->last_keycode = SDLK_UNKNOWN;
im->last_mod = 0;
im->key_repeat = 0;
}

static void
Expand Down Expand Up @@ -179,9 +183,19 @@ expand_notification_panel(struct controller *controller) {
}

static void
collapse_notification_panel(struct controller *controller) {
expand_settings_panel(struct controller *controller) {
struct control_msg msg;
msg.type = CONTROL_MSG_TYPE_COLLAPSE_NOTIFICATION_PANEL;
msg.type = CONTROL_MSG_TYPE_EXPAND_SETTINGS_PANEL;

if (!controller_push_msg(controller, &msg)) {
LOGW("Could not request 'expand settings panel'");
}
}

static void
collapse_panels(struct controller *controller) {
struct control_msg msg;
msg.type = CONTROL_MSG_TYPE_COLLAPSE_PANELS;

if (!controller_push_msg(controller, &msg)) {
LOGW("Could not request 'collapse notification panel'");
Expand Down Expand Up @@ -384,16 +398,27 @@ input_manager_process_key(struct input_manager *im,
// control: indicates the state of the command-line option --no-control
bool control = im->control;

bool smod = is_shortcut_mod(im, event->keysym.mod);

struct controller *controller = im->controller;

SDL_Keycode keycode = event->keysym.sym;
uint16_t mod = event->keysym.mod;
bool down = event->type == SDL_KEYDOWN;
bool ctrl = event->keysym.mod & KMOD_CTRL;
bool shift = event->keysym.mod & KMOD_SHIFT;
bool repeat = event->repeat;

bool smod = is_shortcut_mod(im, mod);

if (down && !repeat) {
if (keycode == im->last_keycode && mod == im->last_mod) {
++im->key_repeat;
} else {
im->key_repeat = 0;
im->last_keycode = keycode;
im->last_mod = mod;
}
}

// The shortcut modifier is pressed
if (smod) {
int action = down ? ACTION_DOWN : ACTION_UP;
Expand Down Expand Up @@ -498,9 +523,11 @@ input_manager_process_key(struct input_manager *im,
case SDLK_n:
if (control && !repeat && down) {
if (shift) {
collapse_notification_panel(controller);
} else {
collapse_panels(controller);
} else if (im->key_repeat == 0) {
expand_notification_panel(controller);
} else {
expand_settings_panel(controller);
}
}
return;
Expand Down Expand Up @@ -666,7 +693,11 @@ input_manager_process_mouse_button(struct input_manager *im,
return;
}
if (control && event->button == SDL_BUTTON_X2 && down) {
expand_notification_panel(im->controller);
if (event->clicks < 2) {
expand_notification_panel(im->controller);
} else {
expand_settings_panel(im->controller);
}
return;
}
if (control && event->button == SDL_BUTTON_RIGHT) {
Expand Down
7 changes: 7 additions & 0 deletions app/src/input_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,13 @@ struct input_manager {
} sdl_shortcut_mods;

bool vfinger_down;

// Tracks the number of identical consecutive shortcut key down events.
// Not to be confused with event->repeat, which counts the number of
// system-generated repeated key presses.
unsigned key_repeat;
SDL_Keycode last_keycode;
uint16_t last_mod;
};

void
Expand Down
24 changes: 20 additions & 4 deletions app/tests/test_control_msg_serialize.c
Original file line number Diff line number Diff line change
Expand Up @@ -177,17 +177,32 @@ static void test_serialize_expand_notification_panel(void) {
assert(!memcmp(buf, expected, sizeof(expected)));
}

static void test_serialize_collapse_notification_panel(void) {
static void test_serialize_expand_settings_panel(void) {
struct control_msg msg = {
.type = CONTROL_MSG_TYPE_COLLAPSE_NOTIFICATION_PANEL,
.type = CONTROL_MSG_TYPE_EXPAND_SETTINGS_PANEL,
};

unsigned char buf[CONTROL_MSG_MAX_SIZE];
size_t size = control_msg_serialize(&msg, buf);
assert(size == 1);

const unsigned char expected[] = {
CONTROL_MSG_TYPE_COLLAPSE_NOTIFICATION_PANEL,
CONTROL_MSG_TYPE_EXPAND_SETTINGS_PANEL,
};
assert(!memcmp(buf, expected, sizeof(expected)));
}

static void test_serialize_collapse_panels(void) {
struct control_msg msg = {
.type = CONTROL_MSG_TYPE_COLLAPSE_PANELS,
};

unsigned char buf[CONTROL_MSG_MAX_SIZE];
size_t size = control_msg_serialize(&msg, buf);
assert(size == 1);

const unsigned char expected[] = {
CONTROL_MSG_TYPE_COLLAPSE_PANELS,
};
assert(!memcmp(buf, expected, sizeof(expected)));
}
Expand Down Expand Up @@ -274,7 +289,8 @@ int main(int argc, char *argv[]) {
test_serialize_inject_scroll_event();
test_serialize_back_or_screen_on();
test_serialize_expand_notification_panel();
test_serialize_collapse_notification_panel();
test_serialize_expand_settings_panel();
test_serialize_collapse_panels();
test_serialize_get_clipboard();
test_serialize_set_clipboard();
test_serialize_set_screen_power_mode();
Expand Down
11 changes: 6 additions & 5 deletions server/src/main/java/com/genymobile/scrcpy/ControlMessage.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,12 @@ public final class ControlMessage {
public static final int TYPE_INJECT_SCROLL_EVENT = 3;
public static final int TYPE_BACK_OR_SCREEN_ON = 4;
public static final int TYPE_EXPAND_NOTIFICATION_PANEL = 5;
public static final int TYPE_COLLAPSE_NOTIFICATION_PANEL = 6;
public static final int TYPE_GET_CLIPBOARD = 7;
public static final int TYPE_SET_CLIPBOARD = 8;
public static final int TYPE_SET_SCREEN_POWER_MODE = 9;
public static final int TYPE_ROTATE_DEVICE = 10;
public static final int TYPE_EXPAND_SETTINGS_PANEL = 6;
public static final int TYPE_COLLAPSE_PANELS = 7;
public static final int TYPE_GET_CLIPBOARD = 8;
public static final int TYPE_SET_CLIPBOARD = 9;
public static final int TYPE_SET_SCREEN_POWER_MODE = 10;
public static final int TYPE_ROTATE_DEVICE = 11;
Comment on lines -14 to +19
Copy link
Contributor Author

@brunoais brunoais Apr 17, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not fully sure about changing the value of the constants as I did (and related) as it will make the previous server incompatible.

  1. On one hand, as I did,
    1. breaks backwards compatibility.
    2. making this change allows cleaner code on both ends.
  2. On the other hand, by just adding at the end
    1. Keeps backwards compatibility
    2. The code is less readable and maintainable.

I ended up going with 1, but I don't mind changing to 2 if you prefer.
Maybe doing as 2 and change to 1 soon, though, later.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not fully sure about changing the value of the constants as I did (and related) as it will make the previous server incompatible.

That's totally acceptable, servers are not expected to be compatible at all with any other scrcpy client version. This would add annoying constraints for absolutely no benefit for scrcpy.


private int type;
private String text;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,8 @@ public ControlMessage next() {
msg = parseSetScreenPowerMode();
break;
case ControlMessage.TYPE_EXPAND_NOTIFICATION_PANEL:
case ControlMessage.TYPE_COLLAPSE_NOTIFICATION_PANEL:
case ControlMessage.TYPE_EXPAND_SETTINGS_PANEL:
case ControlMessage.TYPE_COLLAPSE_PANELS:
case ControlMessage.TYPE_GET_CLIPBOARD:
case ControlMessage.TYPE_ROTATE_DEVICE:
msg = ControlMessage.createEmpty(type);
Expand Down
5 changes: 4 additions & 1 deletion server/src/main/java/com/genymobile/scrcpy/Controller.java
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,10 @@ private void handleEvent() throws IOException {
case ControlMessage.TYPE_EXPAND_NOTIFICATION_PANEL:
Device.expandNotificationPanel();
break;
case ControlMessage.TYPE_COLLAPSE_NOTIFICATION_PANEL:
case ControlMessage.TYPE_EXPAND_SETTINGS_PANEL:
Device.expandSettingsPanel();
break;
case ControlMessage.TYPE_COLLAPSE_PANELS:
Device.collapsePanels();
break;
case ControlMessage.TYPE_GET_CLIPBOARD:
Expand Down
4 changes: 4 additions & 0 deletions server/src/main/java/com/genymobile/scrcpy/Device.java
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,10 @@ public static void expandNotificationPanel() {
SERVICE_MANAGER.getStatusBarManager().expandNotificationsPanel();
}

public static void expandSettingsPanel() {
SERVICE_MANAGER.getStatusBarManager().expandSettingsPanel();
}

public static void collapsePanels() {
SERVICE_MANAGER.getStatusBarManager().collapsePanels();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public class ActivityManager {

private final IInterface manager;
private Method getContentProviderExternalMethod;
private boolean getContentProviderExternalMethodLegacy;
private boolean getContentProviderExternalMethodNewVersion = true;
private Method removeContentProviderExternalMethod;

public ActivityManager(IInterface manager) {
Expand All @@ -29,7 +29,7 @@ private Method getGetContentProviderExternalMethod() throws NoSuchMethodExceptio
} catch (NoSuchMethodException e) {
// old version
getContentProviderExternalMethod = manager.getClass().getMethod("getContentProviderExternal", String.class, int.class, IBinder.class);
getContentProviderExternalMethodLegacy = true;
getContentProviderExternalMethodNewVersion = false;
}
}
return getContentProviderExternalMethod;
Expand All @@ -46,7 +46,7 @@ private ContentProvider getContentProviderExternal(String name, IBinder token) {
try {
Method method = getGetContentProviderExternalMethod();
Object[] args;
if (!getContentProviderExternalMethodLegacy) {
if (getContentProviderExternalMethodNewVersion) {
// new version
args = new Object[]{name, ServiceManager.USER_ID, token, null};
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ public class StatusBarManager {

private final IInterface manager;
private Method expandNotificationsPanelMethod;
private Method expandSettingsPanelMethod;
private boolean expandSettingsPanelMethodNewVersion = true;
private Method collapsePanelsMethod;

public StatusBarManager(IInterface manager) {
Expand All @@ -24,6 +26,20 @@ private Method getExpandNotificationsPanelMethod() throws NoSuchMethodException
return expandNotificationsPanelMethod;
}

private Method getExpandSettingsPanel() throws NoSuchMethodException {
if (expandSettingsPanelMethod == null) {
try {
// Since Android 7: https://android.googlesource.com/platform/frameworks/base.git/+/a9927325eda025504d59bb6594fee8e240d95b01%5E%21/
expandSettingsPanelMethod = manager.getClass().getMethod("expandSettingsPanel", String.class);
} catch (NoSuchMethodException e) {
// old version
expandSettingsPanelMethod = manager.getClass().getMethod("expandSettingsPanel");
expandSettingsPanelMethodNewVersion = false;
}
}
return expandSettingsPanelMethod;
}

private Method getCollapsePanelsMethod() throws NoSuchMethodException {
if (collapsePanelsMethod == null) {
collapsePanelsMethod = manager.getClass().getMethod("collapsePanels");
Expand All @@ -40,6 +56,21 @@ public void expandNotificationsPanel() {
}
}

public void expandSettingsPanel() {
try {
Method method = getExpandSettingsPanel();
if (expandSettingsPanelMethodNewVersion) {
// new version
method.invoke(manager, (Object) null);
} else {
// old version
method.invoke(manager);
}
} catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException e) {
Ln.e("Could not invoke method", e);
}
}

public void collapsePanels() {
try {
Method method = getCollapsePanelsMethod();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -182,19 +182,35 @@ public void testParseExpandNotificationPanelEvent() throws IOException {
}

@Test
public void testParseCollapseNotificationPanelEvent() throws IOException {
public void testParseExpandSettingsPanelEvent() throws IOException {
ControlMessageReader reader = new ControlMessageReader();

ByteArrayOutputStream bos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(bos);
dos.writeByte(ControlMessage.TYPE_COLLAPSE_NOTIFICATION_PANEL);
dos.writeByte(ControlMessage.TYPE_EXPAND_SETTINGS_PANEL);

byte[] packet = bos.toByteArray();

reader.readFrom(new ByteArrayInputStream(packet));
ControlMessage event = reader.next();

Assert.assertEquals(ControlMessage.TYPE_COLLAPSE_NOTIFICATION_PANEL, event.getType());
Assert.assertEquals(ControlMessage.TYPE_EXPAND_SETTINGS_PANEL, event.getType());
}

@Test
public void testParseCollapsePanelsEvent() throws IOException {
ControlMessageReader reader = new ControlMessageReader();

ByteArrayOutputStream bos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(bos);
dos.writeByte(ControlMessage.TYPE_COLLAPSE_PANELS);

byte[] packet = bos.toByteArray();

reader.readFrom(new ByteArrayInputStream(packet));
ControlMessage event = reader.next();

Assert.assertEquals(ControlMessage.TYPE_COLLAPSE_PANELS, event.getType());
}

@Test
Expand Down