Skip to content

Commit

Permalink
Implement device screen off while mirroring
Browse files Browse the repository at this point in the history
Add two shortcuts:
 - Ctrl+o to turn the device screen off while mirroring
 - Ctrl+Shift+o to turn it back on

On power on (either via the POWER key or BACK while screen is off), both
the device screen and the mirror are turned on.

<#175>
  • Loading branch information
rom1v committed May 31, 2019
1 parent 3ee9560 commit 12a3bb2
Show file tree
Hide file tree
Showing 12 changed files with 144 additions and 3 deletions.
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,9 @@ you are interested, see [issue 14].
| click on `VOLUME_UP` | `Ctrl`+`` _(up)_ (`Cmd`+`` on MacOS) |
| click on `VOLUME_DOWN` | `Ctrl`+`` _(down)_ (`Cmd`+`` on MacOS) |
| click on `POWER` | `Ctrl`+`p` |
| turn screen on | _Right-click²_ |
| power on | _Right-click²_ |
| turn device screen off (keep mirroring)| `Ctrl`+`o` |
| turn device screen on | `Ctrl`+`Shift`+`o` |
| expand notification panel | `Ctrl`+`n` |
| collapse notification panel | `Ctrl`+`Shift`+`n` |
| copy device clipboard to computer | `Ctrl`+`c` |
Expand Down
3 changes: 3 additions & 0 deletions app/src/control_msg.c
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ control_msg_serialize(const struct control_msg *msg, unsigned char *buf) {
&buf[1]);
return 1 + len;
}
case CONTROL_MSG_TYPE_SET_SCREEN_POWER_MODE:
buf[1] = msg->set_screen_power_mode.mode;
return 2;
case CONTROL_MSG_TYPE_BACK_OR_SCREEN_ON:
case CONTROL_MSG_TYPE_EXPAND_NOTIFICATION_PANEL:
case CONTROL_MSG_TYPE_COLLAPSE_NOTIFICATION_PANEL:
Expand Down
10 changes: 10 additions & 0 deletions app/src/control_msg.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,13 @@ enum control_msg_type {
CONTROL_MSG_TYPE_COLLAPSE_NOTIFICATION_PANEL,
CONTROL_MSG_TYPE_GET_CLIPBOARD,
CONTROL_MSG_TYPE_SET_CLIPBOARD,
CONTROL_MSG_TYPE_SET_SCREEN_POWER_MODE,
};

enum screen_power_mode {
// see <https://android.googlesource.com/platform/frameworks/base.git/+/pie-release-2/core/java/android/view/SurfaceControl.java#305>
SCREEN_POWER_MODE_OFF = 0,
SCREEN_POWER_MODE_NORMAL = 2,
};

struct control_msg {
Expand All @@ -50,6 +57,9 @@ struct control_msg {
struct {
char *text; // owned, to be freed by SDL_free()
} set_clipboard;
struct {
enum screen_power_mode mode;
} set_screen_power_mode;
};
};

Expand Down
20 changes: 20 additions & 0 deletions app/src/input_manager.c
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,18 @@ set_device_clipboard(struct controller *controller) {
}
}

static void
set_screen_power_mode(struct controller *controller,
enum screen_power_mode mode) {
struct control_msg msg;
msg.type = CONTROL_MSG_TYPE_SET_SCREEN_POWER_MODE;
msg.set_screen_power_mode.mode = mode;

if (!controller_push_msg(controller, &msg)) {
LOGW("Cannot request 'set screen power mode'");
}
}

static void
switch_fps_counter_state(struct video_buffer *vb) {
mutex_lock(vb->mutex);
Expand Down Expand Up @@ -263,6 +275,14 @@ input_manager_process_key(struct input_manager *input_manager,
action_power(input_manager->controller, action);
}
return;
case SDLK_o:
if (control && ctrl && !meta && event->type == SDL_KEYDOWN) {
enum screen_power_mode mode = shift
? SCREEN_POWER_MODE_NORMAL
: SCREEN_POWER_MODE_OFF;
set_screen_power_mode(input_manager->controller, mode);
}
return;
case SDLK_DOWN:
#ifdef __APPLE__
if (control && !ctrl && meta && !shift) {
Expand Down
8 changes: 7 additions & 1 deletion app/src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,13 @@ static void usage(const char *arg0) {
" click on POWER (turn screen on/off)\n"
"\n"
" Right-click (when screen is off)\n"
" turn screen on\n"
" power on\n"
"\n"
" Ctrl+o\n"
" turn device screen off (keep mirroring)\n"
"\n"
" Ctrl+Shift+o\n"
" turn device screen on\n"
"\n"
" Ctrl+n\n"
" expand notification panel\n"
Expand Down
20 changes: 20 additions & 0 deletions app/tests/test_control_msg_serialize.c
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,25 @@ static void test_serialize_set_clipboard(void) {
assert(!memcmp(buf, expected, sizeof(expected)));
}

static void test_serialize_set_screen_power_mode(void) {
struct control_msg msg = {
.type = CONTROL_MSG_TYPE_SET_SCREEN_POWER_MODE,
.set_screen_power_mode = {
.mode = SCREEN_POWER_MODE_NORMAL,
},
};

unsigned char buf[CONTROL_MSG_SERIALIZED_MAX_SIZE];
int size = control_msg_serialize(&msg, buf);
assert(size == 2);

const unsigned char expected[] = {
CONTROL_MSG_TYPE_SET_SCREEN_POWER_MODE,
0x02, // SCREEN_POWER_MODE_NORMAL
};
assert(!memcmp(buf, expected, sizeof(expected)));
}

int main(void) {
test_serialize_inject_keycode();
test_serialize_inject_text();
Expand All @@ -224,5 +243,6 @@ int main(void) {
test_serialize_collapse_notification_panel();
test_serialize_get_clipboard();
test_serialize_set_clipboard();
test_serialize_set_screen_power_mode();
return 0;
}
15 changes: 14 additions & 1 deletion server/src/main/java/com/genymobile/scrcpy/ControlMessage.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.genymobile.scrcpy;

import com.genymobile.scrcpy.wrappers.SurfaceControl;

/**
* Union of all supported event types, identified by their {@code type}.
*/
Expand All @@ -14,11 +16,12 @@ public final class ControlMessage {
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;

private int type;
private String text;
private int metaState; // KeyEvent.META_*
private int action; // KeyEvent.ACTION_* or MotionEvent.ACTION_*
private int action; // KeyEvent.ACTION_* or MotionEvent.ACTION_* or POWER_MODE_*
private int keycode; // KeyEvent.KEYCODE_*
private int buttons; // MotionEvent.BUTTON_*
private Position position;
Expand Down Expand Up @@ -69,6 +72,16 @@ public static ControlMessage createSetClipboard(String text) {
return event;
}

/**
* @param mode one of the {@code Device.SCREEN_POWER_MODE_*} constants
*/
public static ControlMessage createSetScreenPowerMode(int mode) {
ControlMessage event = new ControlMessage();
event.type = TYPE_SET_SCREEN_POWER_MODE;
event.action = mode;
return event;
}

public static ControlMessage createEmpty(int type) {
ControlMessage event = new ControlMessage();
event.type = type;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ public class ControlMessageReader {
private static final int INJECT_KEYCODE_PAYLOAD_LENGTH = 9;
private static final int INJECT_MOUSE_EVENT_PAYLOAD_LENGTH = 17;
private static final int INJECT_SCROLL_EVENT_PAYLOAD_LENGTH = 20;
private static final int SET_SCREEN_POWER_MODE_PAYLOAD_LENGTH = 1;

public static final int TEXT_MAX_LENGTH = 300;
public static final int CLIPBOARD_TEXT_MAX_LENGTH = 4093;
Expand Down Expand Up @@ -67,6 +68,9 @@ public ControlMessage next() {
case ControlMessage.TYPE_SET_CLIPBOARD:
msg = parseSetClipboard();
break;
case ControlMessage.TYPE_SET_SCREEN_POWER_MODE:
msg = parseSetScreenPowerMode();
break;
case ControlMessage.TYPE_BACK_OR_SCREEN_ON:
case ControlMessage.TYPE_EXPAND_NOTIFICATION_PANEL:
case ControlMessage.TYPE_COLLAPSE_NOTIFICATION_PANEL:
Expand Down Expand Up @@ -144,6 +148,14 @@ private ControlMessage parseSetClipboard() {
return ControlMessage.createSetClipboard(text);
}

private ControlMessage parseSetScreenPowerMode() {
if (buffer.remaining() < SET_SCREEN_POWER_MODE_PAYLOAD_LENGTH) {
return null;
}
int mode = buffer.get();
return ControlMessage.createSetScreenPowerMode(mode);
}

private static Position readPosition(ByteBuffer buffer) {
int x = buffer.getInt();
int y = buffer.getInt();
Expand Down
3 changes: 3 additions & 0 deletions server/src/main/java/com/genymobile/scrcpy/Controller.java
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,9 @@ private void handleEvent() throws IOException {
case ControlMessage.TYPE_SET_CLIPBOARD:
device.setClipboardText(msg.getText());
break;
case ControlMessage.TYPE_SET_SCREEN_POWER_MODE:
device.setScreenPowerMode(msg.getAction());
break;
default:
// do nothing
}
Expand Down
14 changes: 14 additions & 0 deletions server/src/main/java/com/genymobile/scrcpy/Device.java
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
package com.genymobile.scrcpy;

import com.genymobile.scrcpy.wrappers.ServiceManager;
import com.genymobile.scrcpy.wrappers.SurfaceControl;

import android.graphics.Rect;
import android.os.Build;
import android.os.IBinder;
import android.os.RemoteException;
import android.view.IRotationWatcher;
import android.view.InputEvent;

public final class Device {

public static final int POWER_MODE_OFF = SurfaceControl.POWER_MODE_OFF;
public static final int POWER_MODE_NORMAL = SurfaceControl.POWER_MODE_NORMAL;

public interface RotationListener {
void onRotationChanged(int rotation);
}
Expand Down Expand Up @@ -152,6 +157,15 @@ public void setClipboardText(String text) {
Ln.i("Device clipboard set");
}

/**
* @param mode one of the {@code SCREEN_POWER_MODE_*} constants
*/
public void setScreenPowerMode(int mode) {
IBinder d = SurfaceControl.getBuiltInDisplay(0);
SurfaceControl.setDisplayPowerMode(d, mode);
Ln.i("Device screen turned " + (mode == Device.POWER_MODE_OFF ? "off" : "on"));
}

static Rect flipRect(Rect crop) {
return new Rect(crop.top, crop.left, crop.bottom, crop.right);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ public final class SurfaceControl {

private static final Class<?> CLASS;

// see <https://android.googlesource.com/platform/frameworks/base.git/+/pie-release-2/core/java/android/view/SurfaceControl.java#305>
public static final int POWER_MODE_OFF = 0;
public static final int POWER_MODE_NORMAL = 2;

static {
try {
CLASS = Class.forName("android.view.SurfaceControl");
Expand Down Expand Up @@ -71,6 +75,22 @@ public static IBinder createDisplay(String name, boolean secure) {
}
}

public static IBinder getBuiltInDisplay(int builtInDisplayId) {
try {
return (IBinder) CLASS.getMethod("getBuiltInDisplay", int.class).invoke(null, builtInDisplayId);
} catch (Exception e) {
throw new AssertionError(e);
}
}

public static void setDisplayPowerMode(IBinder displayToken, int mode) {
try {
CLASS.getMethod("setDisplayPowerMode", IBinder.class, int.class).invoke(null, displayToken, mode);
} catch (Exception e) {
throw new AssertionError(e);
}
}

public static void destroyDisplay(IBinder displayToken) {
try {
CLASS.getMethod("destroyDisplay", IBinder.class).invoke(null, displayToken);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,24 @@ public void testParseSetClipboardEvent() throws IOException {
Assert.assertEquals("testé", event.getText());
}

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

ByteArrayOutputStream bos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(bos);
dos.writeByte(ControlMessage.TYPE_SET_SCREEN_POWER_MODE);
dos.writeByte(Device.POWER_MODE_NORMAL);

byte[] packet = bos.toByteArray();

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

Assert.assertEquals(ControlMessage.TYPE_SET_SCREEN_POWER_MODE, event.getType());
Assert.assertEquals(Device.POWER_MODE_NORMAL, event.getAction());
}

@Test
public void testMultiEvents() throws IOException {
ControlMessageReader reader = new ControlMessageReader();
Expand Down

0 comments on commit 12a3bb2

Please sign in to comment.