mirror of
https://github.com/barry-ran/QtScrcpy.git
synced 2025-07-30 20:48:39 +00:00
update:同步scrcpy
This commit is contained in:
parent
b92199dea3
commit
2ec0a36f08
20 changed files with 277 additions and 113 deletions
|
@ -105,7 +105,8 @@ void Dialog::on_startServerBtn_clicked()
|
||||||
quint32 bitRate = ui->bitRateBox->currentText().trimmed().toUInt();
|
quint32 bitRate = ui->bitRateBox->currentText().trimmed().toUInt();
|
||||||
// this is ok that "native" toUshort is 0
|
// this is ok that "native" toUshort is 0
|
||||||
quint16 videoSize = ui->videoSizeBox->currentText().trimmed().toUShort();
|
quint16 videoSize = ui->videoSizeBox->currentText().trimmed().toUShort();
|
||||||
m_videoForm = new VideoForm(ui->serialBox->currentText().trimmed(), videoSize, bitRate, absFilePath);
|
m_videoForm = new VideoForm(ui->serialBox->currentText().trimmed(), videoSize, bitRate,
|
||||||
|
absFilePath, ui->closeScreenCheck->isChecked());
|
||||||
if (ui->alwaysTopCheck->isChecked()) {
|
if (ui->alwaysTopCheck->isChecked()) {
|
||||||
m_videoForm->staysOnTop();
|
m_videoForm->staysOnTop();
|
||||||
}
|
}
|
||||||
|
@ -238,3 +239,15 @@ void Dialog::on_alwaysTopCheck_stateChanged(int arg1)
|
||||||
m_videoForm->staysOnTop(false);
|
m_videoForm->staysOnTop(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Dialog::on_closeScreenCheck_stateChanged(int arg1)
|
||||||
|
{
|
||||||
|
if (!m_videoForm) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (ui->closeScreenCheck->isChecked()) {
|
||||||
|
m_videoForm->setScreenPowerMode(ControlMsg::SPM_OFF);
|
||||||
|
} else {
|
||||||
|
m_videoForm->setScreenPowerMode(ControlMsg::SPM_NORMAL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -43,6 +43,8 @@ private slots:
|
||||||
|
|
||||||
void on_alwaysTopCheck_stateChanged(int arg1);
|
void on_alwaysTopCheck_stateChanged(int arg1);
|
||||||
|
|
||||||
|
void on_closeScreenCheck_stateChanged(int arg1);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool checkAdbRun();
|
bool checkAdbRun();
|
||||||
void initUI();
|
void initUI();
|
||||||
|
|
|
@ -230,6 +230,13 @@
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="2" column="1" colspan="2">
|
||||||
|
<widget class="QCheckBox" name="closeScreenCheck">
|
||||||
|
<property name="text">
|
||||||
|
<string>close screen</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
|
|
@ -12,21 +12,21 @@ ControlMsg::ControlMsg(ControlMsgType controlMsgType)
|
||||||
ControlMsg::~ControlMsg()
|
ControlMsg::~ControlMsg()
|
||||||
{
|
{
|
||||||
if (CMT_SET_CLIPBOARD == m_data.type
|
if (CMT_SET_CLIPBOARD == m_data.type
|
||||||
&& Q_NULLPTR != m_data.setClipboardMsg.text) {
|
&& Q_NULLPTR != m_data.setClipboard.text) {
|
||||||
delete m_data.setClipboardMsg.text;
|
delete m_data.setClipboard.text;
|
||||||
m_data.setClipboardMsg.text = Q_NULLPTR;
|
m_data.setClipboard.text = Q_NULLPTR;
|
||||||
} else if (CMT_INJECT_TEXT == m_data.type
|
} else if (CMT_INJECT_TEXT == m_data.type
|
||||||
&& Q_NULLPTR != m_data.injectTextMsg.text){
|
&& Q_NULLPTR != m_data.injectText.text){
|
||||||
delete m_data.injectTextMsg.text;
|
delete m_data.injectText.text;
|
||||||
m_data.injectTextMsg.text = Q_NULLPTR;
|
m_data.injectText.text = Q_NULLPTR;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ControlMsg::setInjectKeycodeMsgData(AndroidKeyeventAction action, AndroidKeycode keycode, AndroidMetastate metastate)
|
void ControlMsg::setInjectKeycodeMsgData(AndroidKeyeventAction action, AndroidKeycode keycode, AndroidMetastate metastate)
|
||||||
{
|
{
|
||||||
m_data.injectKeycodeMsg.action = action;
|
m_data.injectKeycode.action = action;
|
||||||
m_data.injectKeycodeMsg.keycode = keycode;
|
m_data.injectKeycode.keycode = keycode;
|
||||||
m_data.injectKeycodeMsg.metastate = metastate;
|
m_data.injectKeycode.metastate = metastate;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ControlMsg::setInjectTextMsgData(QString& text)
|
void ControlMsg::setInjectTextMsgData(QString& text)
|
||||||
|
@ -37,30 +37,30 @@ void ControlMsg::setInjectTextMsgData(QString& text)
|
||||||
text = text.left(CONTROL_MSG_TEXT_MAX_LENGTH);
|
text = text.left(CONTROL_MSG_TEXT_MAX_LENGTH);
|
||||||
}
|
}
|
||||||
QByteArray tmp = text.toUtf8();
|
QByteArray tmp = text.toUtf8();
|
||||||
m_data.injectTextMsg.text = new char[tmp.length() + 1];
|
m_data.injectText.text = new char[tmp.length() + 1];
|
||||||
memcpy(m_data.injectTextMsg.text, tmp.data(), tmp.length());
|
memcpy(m_data.injectText.text, tmp.data(), tmp.length());
|
||||||
m_data.injectTextMsg.text[tmp.length()] = '\0';
|
m_data.injectText.text[tmp.length()] = '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
void ControlMsg::setInjectMouseMsgData(AndroidMotioneventAction action, AndroidMotioneventButtons buttons, QRect position)
|
void ControlMsg::setInjectMouseMsgData(AndroidMotioneventAction action, AndroidMotioneventButtons buttons, QRect position)
|
||||||
{
|
{
|
||||||
m_data.injectMouseMsg.action = action;
|
m_data.injectMouse.action = action;
|
||||||
m_data.injectMouseMsg.buttons = buttons;
|
m_data.injectMouse.buttons = buttons;
|
||||||
m_data.injectMouseMsg.position = position;
|
m_data.injectMouse.position = position;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ControlMsg::setInjectTouchMsgData(quint32 id, AndroidMotioneventAction action, QRect position)
|
void ControlMsg::setInjectTouchMsgData(quint32 id, AndroidMotioneventAction action, QRect position)
|
||||||
{
|
{
|
||||||
m_data.injectTouchMsg.action = action;
|
m_data.injectTouch.action = action;
|
||||||
m_data.injectTouchMsg.id = id;
|
m_data.injectTouch.id = id;
|
||||||
m_data.injectTouchMsg.position = position;
|
m_data.injectTouch.position = position;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ControlMsg::setInjectScrollMsgData(QRect position, qint32 hScroll, qint32 vScroll)
|
void ControlMsg::setInjectScrollMsgData(QRect position, qint32 hScroll, qint32 vScroll)
|
||||||
{
|
{
|
||||||
m_data.injectScrollMsg.position = position;
|
m_data.injectScroll.position = position;
|
||||||
m_data.injectScrollMsg.hScroll = hScroll;
|
m_data.injectScroll.hScroll = hScroll;
|
||||||
m_data.injectScrollMsg.vScroll = vScroll;
|
m_data.injectScroll.vScroll = vScroll;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ControlMsg::setSetClipboardMsgData(QString &text)
|
void ControlMsg::setSetClipboardMsgData(QString &text)
|
||||||
|
@ -73,9 +73,14 @@ void ControlMsg::setSetClipboardMsgData(QString &text)
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray tmp = text.toUtf8();
|
QByteArray tmp = text.toUtf8();
|
||||||
m_data.setClipboardMsg.text = new char[tmp.length() + 1];
|
m_data.setClipboard.text = new char[tmp.length() + 1];
|
||||||
memcpy(m_data.setClipboardMsg.text, tmp.data(), tmp.length());
|
memcpy(m_data.setClipboard.text, tmp.data(), tmp.length());
|
||||||
m_data.setClipboardMsg.text[tmp.length()] = '\0';
|
m_data.setClipboard.text[tmp.length()] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
void ControlMsg::setSetScreenPowerModeData(ControlMsg::ScreenPowerMode mode)
|
||||||
|
{
|
||||||
|
m_data.setScreenPowerMode.mode = mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ControlMsg::writePosition(QBuffer &buffer, const QRect& value)
|
void ControlMsg::writePosition(QBuffer &buffer, const QRect& value)
|
||||||
|
@ -95,32 +100,35 @@ QByteArray ControlMsg::serializeData()
|
||||||
|
|
||||||
switch (m_data.type) {
|
switch (m_data.type) {
|
||||||
case CMT_INJECT_KEYCODE:
|
case CMT_INJECT_KEYCODE:
|
||||||
buffer.putChar(m_data.injectKeycodeMsg.action);
|
buffer.putChar(m_data.injectKeycode.action);
|
||||||
BufferUtil::write32(buffer, m_data.injectKeycodeMsg.keycode);
|
BufferUtil::write32(buffer, m_data.injectKeycode.keycode);
|
||||||
BufferUtil::write32(buffer, m_data.injectKeycodeMsg.metastate);
|
BufferUtil::write32(buffer, m_data.injectKeycode.metastate);
|
||||||
break;
|
break;
|
||||||
case CMT_INJECT_TEXT:
|
case CMT_INJECT_TEXT:
|
||||||
BufferUtil::write16(buffer, strlen(m_data.injectTextMsg.text));
|
BufferUtil::write16(buffer, strlen(m_data.injectText.text));
|
||||||
buffer.write(m_data.injectTextMsg.text, strlen(m_data.injectTextMsg.text));
|
buffer.write(m_data.injectText.text, strlen(m_data.injectText.text));
|
||||||
break;
|
break;
|
||||||
case CMT_INJECT_MOUSE:
|
case CMT_INJECT_MOUSE:
|
||||||
buffer.putChar(m_data.injectMouseMsg.action);
|
buffer.putChar(m_data.injectMouse.action);
|
||||||
BufferUtil::write32(buffer, m_data.injectMouseMsg.buttons);
|
BufferUtil::write32(buffer, m_data.injectMouse.buttons);
|
||||||
writePosition(buffer, m_data.injectMouseMsg.position);
|
writePosition(buffer, m_data.injectMouse.position);
|
||||||
break;
|
break;
|
||||||
case CMT_INJECT_TOUCH:
|
case CMT_INJECT_TOUCH:
|
||||||
buffer.putChar(m_data.injectTouchMsg.id);
|
buffer.putChar(m_data.injectTouch.id);
|
||||||
buffer.putChar(m_data.injectTouchMsg.action);
|
buffer.putChar(m_data.injectTouch.action);
|
||||||
writePosition(buffer, m_data.injectTouchMsg.position);
|
writePosition(buffer, m_data.injectTouch.position);
|
||||||
break;
|
break;
|
||||||
case CMT_INJECT_SCROLL:
|
case CMT_INJECT_SCROLL:
|
||||||
writePosition(buffer, m_data.injectScrollMsg.position);
|
writePosition(buffer, m_data.injectScroll.position);
|
||||||
BufferUtil::write32(buffer, m_data.injectScrollMsg.hScroll);
|
BufferUtil::write32(buffer, m_data.injectScroll.hScroll);
|
||||||
BufferUtil::write32(buffer, m_data.injectScrollMsg.vScroll);
|
BufferUtil::write32(buffer, m_data.injectScroll.vScroll);
|
||||||
break;
|
break;
|
||||||
case CMT_SET_CLIPBOARD:
|
case CMT_SET_CLIPBOARD:
|
||||||
BufferUtil::write16(buffer, strlen(m_data.setClipboardMsg.text));
|
BufferUtil::write16(buffer, strlen(m_data.setClipboard.text));
|
||||||
buffer.write(m_data.setClipboardMsg.text, strlen(m_data.setClipboardMsg.text));
|
buffer.write(m_data.setClipboard.text, strlen(m_data.setClipboard.text));
|
||||||
|
break;
|
||||||
|
case CMT_SET_SCREEN_POWER_MODE:
|
||||||
|
buffer.putChar(m_data.setScreenPowerMode.mode);
|
||||||
break;
|
break;
|
||||||
case CMT_BACK_OR_SCREEN_ON:
|
case CMT_BACK_OR_SCREEN_ON:
|
||||||
case CMT_EXPAND_NOTIFICATION_PANEL:
|
case CMT_EXPAND_NOTIFICATION_PANEL:
|
||||||
|
|
|
@ -26,9 +26,16 @@ public:
|
||||||
CMT_COLLAPSE_NOTIFICATION_PANEL,
|
CMT_COLLAPSE_NOTIFICATION_PANEL,
|
||||||
CMT_GET_CLIPBOARD,
|
CMT_GET_CLIPBOARD,
|
||||||
CMT_SET_CLIPBOARD,
|
CMT_SET_CLIPBOARD,
|
||||||
|
CMT_SET_SCREEN_POWER_MODE,
|
||||||
|
|
||||||
CMT_INJECT_TOUCH,
|
CMT_INJECT_TOUCH,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum ScreenPowerMode {
|
||||||
|
// see <https://android.googlesource.com/platform/frameworks/base.git/+/pie-release-2/core/java/android/view/SurfaceControl.java#305>
|
||||||
|
SPM_OFF = 0,
|
||||||
|
SPM_NORMAL = 2,
|
||||||
|
};
|
||||||
|
|
||||||
ControlMsg(ControlMsgType controlMsgType);
|
ControlMsg(ControlMsgType controlMsgType);
|
||||||
virtual ~ControlMsg();
|
virtual ~ControlMsg();
|
||||||
|
@ -42,6 +49,7 @@ public:
|
||||||
void setInjectTouchMsgData(quint32 id, AndroidMotioneventAction action, QRect position);
|
void setInjectTouchMsgData(quint32 id, AndroidMotioneventAction action, QRect position);
|
||||||
void setInjectScrollMsgData(QRect position, qint32 hScroll, qint32 vScroll);
|
void setInjectScrollMsgData(QRect position, qint32 hScroll, qint32 vScroll);
|
||||||
void setSetClipboardMsgData(QString& text);
|
void setSetClipboardMsgData(QString& text);
|
||||||
|
void setSetScreenPowerModeData(ControlMsg::ScreenPowerMode mode);
|
||||||
|
|
||||||
QByteArray serializeData();
|
QByteArray serializeData();
|
||||||
|
|
||||||
|
@ -56,28 +64,31 @@ private:
|
||||||
AndroidKeyeventAction action;
|
AndroidKeyeventAction action;
|
||||||
AndroidKeycode keycode;
|
AndroidKeycode keycode;
|
||||||
AndroidMetastate metastate;
|
AndroidMetastate metastate;
|
||||||
} injectKeycodeMsg;
|
} injectKeycode;
|
||||||
struct {
|
struct {
|
||||||
char* text = Q_NULLPTR;
|
char* text = Q_NULLPTR;
|
||||||
} injectTextMsg;
|
} injectText;
|
||||||
struct {
|
struct {
|
||||||
AndroidMotioneventAction action;
|
AndroidMotioneventAction action;
|
||||||
AndroidMotioneventButtons buttons;
|
AndroidMotioneventButtons buttons;
|
||||||
QRect position;
|
QRect position;
|
||||||
} injectMouseMsg;
|
} injectMouse;
|
||||||
struct {
|
struct {
|
||||||
quint32 id;
|
quint32 id;
|
||||||
AndroidMotioneventAction action;
|
AndroidMotioneventAction action;
|
||||||
QRect position;
|
QRect position;
|
||||||
} injectTouchMsg;
|
} injectTouch;
|
||||||
struct {
|
struct {
|
||||||
QRect position;
|
QRect position;
|
||||||
qint32 hScroll;
|
qint32 hScroll;
|
||||||
qint32 vScroll;
|
qint32 vScroll;
|
||||||
} injectScrollMsg;
|
} injectScroll;
|
||||||
struct {
|
struct {
|
||||||
char *text = Q_NULLPTR;
|
char *text = Q_NULLPTR;
|
||||||
} setClipboardMsg;
|
} setClipboard;
|
||||||
|
struct {
|
||||||
|
ScreenPowerMode mode;
|
||||||
|
} setScreenPowerMode;
|
||||||
};
|
};
|
||||||
|
|
||||||
ControlMsgData(){}
|
ControlMsgData(){}
|
||||||
|
|
|
@ -41,6 +41,7 @@ void Receiver::processMsg(DeviceMsg *deviceMsg)
|
||||||
switch (deviceMsg->type()) {
|
switch (deviceMsg->type()) {
|
||||||
case DeviceMsg::DMT_GET_CLIPBOARD:
|
case DeviceMsg::DMT_GET_CLIPBOARD:
|
||||||
{
|
{
|
||||||
|
qInfo("Device clipboard copied");
|
||||||
QClipboard *board = QApplication::clipboard();
|
QClipboard *board = QApplication::clipboard();
|
||||||
QString text;
|
QString text;
|
||||||
deviceMsg->getClipboardMsgData(text);
|
deviceMsg->getClipboardMsgData(text);
|
||||||
|
|
|
@ -65,7 +65,7 @@ bool Server::pushServer()
|
||||||
if (m_workProcess.isRuning()) {
|
if (m_workProcess.isRuning()) {
|
||||||
m_workProcess.kill();
|
m_workProcess.kill();
|
||||||
}
|
}
|
||||||
m_workProcess.push(m_serial, getServerPath(), DEVICE_SERVER_PATH);
|
m_workProcess.push(m_params.serial, getServerPath(), DEVICE_SERVER_PATH);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,7 +74,7 @@ bool Server::enableTunnelReverse()
|
||||||
if (m_workProcess.isRuning()) {
|
if (m_workProcess.isRuning()) {
|
||||||
m_workProcess.kill();
|
m_workProcess.kill();
|
||||||
}
|
}
|
||||||
m_workProcess.reverse(m_serial, SOCKET_NAME, m_localPort);
|
m_workProcess.reverse(m_params.serial, SOCKET_NAME, m_params.localPort);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,7 +89,7 @@ bool Server::disableTunnelReverse()
|
||||||
sender()->deleteLater();
|
sender()->deleteLater();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
adb->reverseRemove(m_serial, SOCKET_NAME);
|
adb->reverseRemove(m_params.serial, SOCKET_NAME);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,7 +98,7 @@ bool Server::enableTunnelForward()
|
||||||
if (m_workProcess.isRuning()) {
|
if (m_workProcess.isRuning()) {
|
||||||
m_workProcess.kill();
|
m_workProcess.kill();
|
||||||
}
|
}
|
||||||
m_workProcess.forward(m_serial, m_localPort, SOCKET_NAME);
|
m_workProcess.forward(m_params.serial, m_params.localPort, SOCKET_NAME);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
bool Server::disableTunnelForward()
|
bool Server::disableTunnelForward()
|
||||||
|
@ -112,7 +112,7 @@ bool Server::disableTunnelForward()
|
||||||
sender()->deleteLater();
|
sender()->deleteLater();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
adb->forwardRemove(m_serial, m_localPort);
|
adb->forwardRemove(m_params.serial, m_params.localPort);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -127,31 +127,27 @@ bool Server::execute()
|
||||||
args << "app_process";
|
args << "app_process";
|
||||||
args << "/"; // unused;
|
args << "/"; // unused;
|
||||||
args << "com.genymobile.scrcpy.Server";
|
args << "com.genymobile.scrcpy.Server";
|
||||||
args << QString::number(m_maxSize);
|
args << QString::number(m_params.maxSize);
|
||||||
args << QString::number(m_bitRate);
|
args << QString::number(m_params.bitRate);
|
||||||
args << (m_tunnelForward ? "true" : "false");
|
args << (m_tunnelForward ? "true" : "false");
|
||||||
if (m_crop.isEmpty()) {
|
if (m_params.crop.isEmpty()) {
|
||||||
args << "-";
|
args << "-";
|
||||||
} else {
|
} else {
|
||||||
args << m_crop;
|
args << m_params.crop;
|
||||||
}
|
}
|
||||||
args << (m_sendFrameMeta ? "true" : "false");
|
args << (m_params.sendFrameMeta ? "true" : "false");
|
||||||
|
args << (m_params.control ? "true" : "false");
|
||||||
|
|
||||||
// adb -s P7C0218510000537 shell CLASSPATH=/data/local/tmp/scrcpy-server.jar app_process / com.genymobile.scrcpy.Server 0 8000000 false
|
// adb -s P7C0218510000537 shell CLASSPATH=/data/local/tmp/scrcpy-server.jar app_process / com.genymobile.scrcpy.Server 0 8000000 false
|
||||||
// mark: crop input format: "width:height:x:y" or - for no crop, for example: "100:200:0:0"
|
// mark: crop input format: "width:height:x:y" or - for no crop, for example: "100:200:0:0"
|
||||||
// 这条adb命令是阻塞运行的,m_serverProcess进程不会退出了
|
// 这条adb命令是阻塞运行的,m_serverProcess进程不会退出了
|
||||||
m_serverProcess.execute(m_serial, args);
|
m_serverProcess.execute(m_params.serial, args);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Server::start(const QString& serial, quint16 localPort, quint16 maxSize, quint32 bitRate, const QString& crop, bool sendFrameMeta)
|
bool Server::start(Server::ServerParams params)
|
||||||
{
|
{
|
||||||
m_serial = serial;
|
m_params = params;
|
||||||
m_localPort = localPort;
|
|
||||||
m_maxSize = maxSize;
|
|
||||||
m_bitRate = bitRate;
|
|
||||||
m_crop = crop;
|
|
||||||
m_sendFrameMeta = sendFrameMeta;
|
|
||||||
|
|
||||||
m_serverStartStep = SSS_PUSH;
|
m_serverStartStep = SSS_PUSH;
|
||||||
return startServerByStep();
|
return startServerByStep();
|
||||||
}
|
}
|
||||||
|
@ -179,7 +175,7 @@ bool Server::connectTo()
|
||||||
|
|
||||||
// video socket
|
// video socket
|
||||||
m_videoSocket = new VideoSocket();
|
m_videoSocket = new VideoSocket();
|
||||||
m_videoSocket->connectToHost(QHostAddress::LocalHost, m_localPort);
|
m_videoSocket->connectToHost(QHostAddress::LocalHost, m_params.localPort);
|
||||||
if (!m_videoSocket->waitForConnected(1000)) {
|
if (!m_videoSocket->waitForConnected(1000)) {
|
||||||
stop();
|
stop();
|
||||||
qWarning("video socket connect to server failed");
|
qWarning("video socket connect to server failed");
|
||||||
|
@ -206,7 +202,7 @@ bool Server::connectTo()
|
||||||
|
|
||||||
// control socket
|
// control socket
|
||||||
m_controlSocket = new QTcpSocket();
|
m_controlSocket = new QTcpSocket();
|
||||||
m_controlSocket->connectToHost(QHostAddress::LocalHost, m_localPort);
|
m_controlSocket->connectToHost(QHostAddress::LocalHost, m_params.localPort);
|
||||||
if (!m_controlSocket->waitForConnected(1000)) {
|
if (!m_controlSocket->waitForConnected(1000)) {
|
||||||
stop();
|
stop();
|
||||||
qWarning("control socket connect to server failed");
|
qWarning("control socket connect to server failed");
|
||||||
|
@ -294,8 +290,8 @@ bool Server::startServerByStep()
|
||||||
// client can listen before starting the server app, so there is no need to
|
// client can listen before starting the server app, so there is no need to
|
||||||
// try to connect until the server socket is listening on the device.
|
// try to connect until the server socket is listening on the device.
|
||||||
m_serverSocket.setMaxPendingConnections(2);
|
m_serverSocket.setMaxPendingConnections(2);
|
||||||
if (!m_serverSocket.listen(QHostAddress::LocalHost, m_localPort)) {
|
if (!m_serverSocket.listen(QHostAddress::LocalHost, m_params.localPort)) {
|
||||||
qCritical(QString("Could not listen on port %1").arg(m_localPort).toStdString().c_str());
|
qCritical(QString("Could not listen on port %1").arg(m_params.localPort).toStdString().c_str());
|
||||||
m_serverStartStep = SSS_NULL;
|
m_serverStartStep = SSS_NULL;
|
||||||
if (m_tunnelForward) {
|
if (m_tunnelForward) {
|
||||||
disableTunnelForward();
|
disableTunnelForward();
|
||||||
|
|
|
@ -22,10 +22,20 @@ class Server : public QObject
|
||||||
SSS_RUNNING,
|
SSS_RUNNING,
|
||||||
};
|
};
|
||||||
public:
|
public:
|
||||||
|
struct ServerParams {
|
||||||
|
QString serial = "";
|
||||||
|
quint16 localPort = 27183;
|
||||||
|
quint16 maxSize = 0;
|
||||||
|
quint32 bitRate = 8000000;
|
||||||
|
QString crop = "-";
|
||||||
|
bool sendFrameMeta = false;
|
||||||
|
bool control = true;
|
||||||
|
};
|
||||||
|
|
||||||
explicit Server(QObject *parent = nullptr);
|
explicit Server(QObject *parent = nullptr);
|
||||||
virtual ~Server();
|
virtual ~Server();
|
||||||
|
|
||||||
bool start(const QString& serial, quint16 localPort, quint16 maxSize, quint32 bitRate, const QString& crop, bool sendFrameMeta = false);
|
bool start(Server::ServerParams params);
|
||||||
bool connectTo();
|
bool connectTo();
|
||||||
|
|
||||||
VideoSocket* getVideoSocket();
|
VideoSocket* getVideoSocket();
|
||||||
|
@ -61,21 +71,16 @@ private:
|
||||||
private:
|
private:
|
||||||
QString m_serverPath = "";
|
QString m_serverPath = "";
|
||||||
AdbProcess m_workProcess;
|
AdbProcess m_workProcess;
|
||||||
QString m_serial = "";
|
|
||||||
AdbProcess m_serverProcess;
|
AdbProcess m_serverProcess;
|
||||||
TcpServer m_serverSocket; // only used if !tunnel_forward
|
TcpServer m_serverSocket; // only used if !tunnel_forward
|
||||||
QPointer<VideoSocket> m_videoSocket = Q_NULLPTR;
|
QPointer<VideoSocket> m_videoSocket = Q_NULLPTR;
|
||||||
QPointer<QTcpSocket> m_controlSocket = Q_NULLPTR;
|
QPointer<QTcpSocket> m_controlSocket = Q_NULLPTR;
|
||||||
quint16 m_localPort = 0;
|
|
||||||
bool m_tunnelEnabled = false;
|
bool m_tunnelEnabled = false;
|
||||||
bool m_tunnelForward = false; // use "adb forward" instead of "adb reverse"
|
bool m_tunnelForward = false; // use "adb forward" instead of "adb reverse"
|
||||||
bool m_sendFrameMeta = false;
|
|
||||||
quint16 m_maxSize = 0;
|
|
||||||
quint32 m_bitRate = 0;
|
|
||||||
QString m_crop = "";
|
|
||||||
quint32 m_acceptTimeoutTimer = 0;
|
quint32 m_acceptTimeoutTimer = 0;
|
||||||
QString m_deviceName = "";
|
QString m_deviceName = "";
|
||||||
QSize m_deviceSize = QSize();
|
QSize m_deviceSize = QSize();
|
||||||
|
ServerParams m_params;
|
||||||
|
|
||||||
SERVER_START_STEP m_serverStartStep = SSS_NULL;
|
SERVER_START_STEP m_serverStartStep = SSS_NULL;
|
||||||
};
|
};
|
||||||
|
|
|
@ -145,7 +145,7 @@ static qint32 readPacketWithMeta(void *opaque, uint8_t *buf, int bufSize) {
|
||||||
quint8 header[HEADER_SIZE];
|
quint8 header[HEADER_SIZE];
|
||||||
qint32 r = stream->recvData(header, HEADER_SIZE);
|
qint32 r = stream->recvData(header, HEADER_SIZE);
|
||||||
if (r == -1) {
|
if (r == -1) {
|
||||||
return AVERROR(errno);
|
return errno ? AVERROR(errno) : AVERROR_EOF;
|
||||||
}
|
}
|
||||||
if (r == 0) {
|
if (r == 0) {
|
||||||
return AVERROR_EOF;
|
return AVERROR_EOF;
|
||||||
|
@ -173,7 +173,7 @@ static qint32 readPacketWithMeta(void *opaque, uint8_t *buf, int bufSize) {
|
||||||
|
|
||||||
qint32 r = stream->recvData(buf, bufSize);
|
qint32 r = stream->recvData(buf, bufSize);
|
||||||
if (r == -1) {
|
if (r == -1) {
|
||||||
return AVERROR(errno);
|
return errno ? AVERROR(errno) : AVERROR_EOF;
|
||||||
}
|
}
|
||||||
if (r == 0) {
|
if (r == 0) {
|
||||||
return AVERROR_EOF;
|
return AVERROR_EOF;
|
||||||
|
|
|
@ -23,7 +23,7 @@
|
||||||
#include "controlmsg.h"
|
#include "controlmsg.h"
|
||||||
#include "mousetap/mousetap.h"
|
#include "mousetap/mousetap.h"
|
||||||
|
|
||||||
VideoForm::VideoForm(const QString& serial, quint16 maxSize, quint32 bitRate, const QString& fileName, QWidget *parent) :
|
VideoForm::VideoForm(const QString& serial, quint16 maxSize, quint32 bitRate, const QString& fileName, bool closeScreen, QWidget *parent) :
|
||||||
QWidget(parent),
|
QWidget(parent),
|
||||||
ui(new Ui::videoForm),
|
ui(new Ui::videoForm),
|
||||||
m_serial(serial),
|
m_serial(serial),
|
||||||
|
@ -33,6 +33,7 @@ VideoForm::VideoForm(const QString& serial, quint16 maxSize, quint32 bitRate, co
|
||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
initUI();
|
initUI();
|
||||||
|
|
||||||
|
m_closeScreen = closeScreen;
|
||||||
m_server = new Server();
|
m_server = new Server();
|
||||||
m_vb = new VideoBuffer();
|
m_vb = new VideoBuffer();
|
||||||
m_vb->init();
|
m_vb->init();
|
||||||
|
@ -55,7 +56,15 @@ VideoForm::VideoForm(const QString& serial, quint16 maxSize, quint32 bitRate, co
|
||||||
// only one devices, serial can be null
|
// only one devices, serial can be null
|
||||||
// mark: crop input format: "width:height:x:y" or - for no crop, for example: "100:200:0:0"
|
// mark: crop input format: "width:height:x:y" or - for no crop, for example: "100:200:0:0"
|
||||||
// sendFrameMeta for recorder mp4
|
// sendFrameMeta for recorder mp4
|
||||||
m_server->start(m_serial, 27183, m_maxSize, m_bitRate, "-", sendFrameMeta);
|
Server::ServerParams params;
|
||||||
|
params.serial = m_serial;
|
||||||
|
params.localPort = 27183;
|
||||||
|
params.maxSize = m_maxSize;
|
||||||
|
params.bitRate = m_bitRate;
|
||||||
|
params.crop = "-";
|
||||||
|
params.sendFrameMeta = sendFrameMeta;
|
||||||
|
params.control = true;
|
||||||
|
m_server->start(params);
|
||||||
});
|
});
|
||||||
|
|
||||||
updateShowSize(size());
|
updateShowSize(size());
|
||||||
|
@ -177,6 +186,10 @@ void VideoForm::initSignals()
|
||||||
|
|
||||||
// init controller
|
// init controller
|
||||||
m_inputConvert.setControlSocket(m_server->getControlSocket());
|
m_inputConvert.setControlSocket(m_server->getControlSocket());
|
||||||
|
|
||||||
|
if (m_closeScreen) {
|
||||||
|
setScreenPowerMode(ControlMsg::SPM_OFF);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -404,6 +417,16 @@ void VideoForm::postTextInput(QString& text)
|
||||||
m_inputConvert.sendControlMsg(controlMsg);
|
m_inputConvert.sendControlMsg(controlMsg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VideoForm::setScreenPowerMode(ControlMsg::ScreenPowerMode mode)
|
||||||
|
{
|
||||||
|
ControlMsg* controlMsg = new ControlMsg(ControlMsg::CMT_SET_SCREEN_POWER_MODE);
|
||||||
|
if (!controlMsg) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
controlMsg->setSetScreenPowerModeData(mode);
|
||||||
|
m_inputConvert.sendControlMsg(controlMsg);
|
||||||
|
}
|
||||||
|
|
||||||
void VideoForm::staysOnTop(bool top)
|
void VideoForm::staysOnTop(bool top)
|
||||||
{
|
{
|
||||||
bool needShow = false;
|
bool needShow = false;
|
||||||
|
|
|
@ -24,7 +24,7 @@ class VideoForm : public QWidget
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit VideoForm(const QString& serial, quint16 maxSize = 720, quint32 bitRate = 8000000, const QString& fileName = "", QWidget *parent = 0);
|
explicit VideoForm(const QString& serial, quint16 maxSize = 720, quint32 bitRate = 8000000, const QString& fileName = "", bool closeScreen = false, QWidget *parent = 0);
|
||||||
~VideoForm();
|
~VideoForm();
|
||||||
|
|
||||||
void switchFullScreen();
|
void switchFullScreen();
|
||||||
|
@ -43,6 +43,7 @@ public:
|
||||||
void setDeviceClipboard();
|
void setDeviceClipboard();
|
||||||
void clipboardPaste();
|
void clipboardPaste();
|
||||||
void postTextInput(QString& text);
|
void postTextInput(QString& text);
|
||||||
|
void setScreenPowerMode(ControlMsg::ScreenPowerMode mode);
|
||||||
|
|
||||||
void staysOnTop(bool top = true);
|
void staysOnTop(bool top = true);
|
||||||
|
|
||||||
|
@ -89,6 +90,7 @@ private:
|
||||||
Recorder* m_recorder = Q_NULLPTR;
|
Recorder* m_recorder = Q_NULLPTR;
|
||||||
QTime m_startTimeCount;
|
QTime m_startTimeCount;
|
||||||
QPointer<QWidget> m_loadingWidget;
|
QPointer<QWidget> m_loadingWidget;
|
||||||
|
bool m_closeScreen = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // VIDEOFORM_H
|
#endif // VIDEOFORM_H
|
||||||
|
|
8
TODO.txt
8
TODO.txt
|
@ -5,11 +5,17 @@ Mac
|
||||||
|
|
||||||
中文输入(server需要改为apk,作为一个输入法,暂不实现)
|
中文输入(server需要改为apk,作为一个输入法,暂不实现)
|
||||||
|
|
||||||
最后更新scrcpy 7764a836f1ee02a4540cfc4118c20729018daaac
|
最后同步scrcpy b91ecf52256da73f5c8dca04fb82c13ec826cbd7
|
||||||
|
|
||||||
b35733edb6df2a00b6af9b1c98627d344c377963 鼠标事件相关系列
|
b35733edb6df2a00b6af9b1c98627d344c377963 鼠标事件相关系列
|
||||||
|
|
||||||
只录制不启动窗口(先重构,目前启动流程在videoform里)
|
只录制不启动窗口(先重构,目前启动流程在videoform里)
|
||||||
|
跳过帧改为动态配置,而不是静态编译 https://github.com/Genymobile/scrcpy/commit/ebccb9f6cc111e8acfbe10d656cac5c1f1b744a0
|
||||||
|
复制粘贴问题对比
|
||||||
|
复制粘贴中文测试
|
||||||
|
国际化
|
||||||
|
单独线程打印帧率 https://github.com/Genymobile/scrcpy/commit/e2a272bf99ecf48fcb050177113f903b3fb323c4
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
mark:
|
mark:
|
||||||
|
|
|
@ -14,14 +14,15 @@ public final class ControlMessage {
|
||||||
public static final int TYPE_COLLAPSE_NOTIFICATION_PANEL = 6;
|
public static final int TYPE_COLLAPSE_NOTIFICATION_PANEL = 6;
|
||||||
public static final int TYPE_GET_CLIPBOARD = 7;
|
public static final int TYPE_GET_CLIPBOARD = 7;
|
||||||
public static final int TYPE_SET_CLIPBOARD = 8;
|
public static final int TYPE_SET_CLIPBOARD = 8;
|
||||||
|
public static final int TYPE_SET_SCREEN_POWER_MODE = 9;
|
||||||
|
|
||||||
public static final int TYPE_INJECT_TOUCH = 9;
|
public static final int TYPE_INJECT_TOUCH = 10;
|
||||||
|
|
||||||
|
|
||||||
private int type;
|
private int type;
|
||||||
private String text;
|
private String text;
|
||||||
private int metaState; // KeyEvent.META_*
|
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 keycode; // KeyEvent.KEYCODE_*
|
||||||
private int buttons; // MotionEvent.BUTTON_*
|
private int buttons; // MotionEvent.BUTTON_*
|
||||||
private int id;
|
private int id;
|
||||||
|
@ -82,6 +83,16 @@ public final class ControlMessage {
|
||||||
return event;
|
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) {
|
public static ControlMessage createEmpty(int type) {
|
||||||
ControlMessage event = new ControlMessage();
|
ControlMessage event = new ControlMessage();
|
||||||
event.type = type;
|
event.type = type;
|
||||||
|
|
|
@ -12,6 +12,7 @@ public class ControlMessageReader {
|
||||||
private static final int INJECT_MOUSE_PAYLOAD_LENGTH = 13;
|
private static final int INJECT_MOUSE_PAYLOAD_LENGTH = 13;
|
||||||
private static final int INJECT_SCROLL_PAYLOAD_LENGTH = 16;
|
private static final int INJECT_SCROLL_PAYLOAD_LENGTH = 16;
|
||||||
private static final int INJECT_TOUCH_PAYLOAD_LENGTH = 10;
|
private static final int INJECT_TOUCH_PAYLOAD_LENGTH = 10;
|
||||||
|
private static final int SET_SCREEN_POWER_MODE_PAYLOAD_LENGTH = 1;
|
||||||
|
|
||||||
public static final int TEXT_MAX_LENGTH = 300;
|
public static final int TEXT_MAX_LENGTH = 300;
|
||||||
public static final int CLIPBOARD_TEXT_MAX_LENGTH = 4093;
|
public static final int CLIPBOARD_TEXT_MAX_LENGTH = 4093;
|
||||||
|
@ -50,43 +51,46 @@ public class ControlMessageReader {
|
||||||
}
|
}
|
||||||
int savedPosition = buffer.position();
|
int savedPosition = buffer.position();
|
||||||
int type = buffer.get();
|
int type = buffer.get();
|
||||||
ControlMessage controlEvent;
|
ControlMessage msg;
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case ControlMessage.TYPE_INJECT_KEYCODE:
|
case ControlMessage.TYPE_INJECT_KEYCODE:
|
||||||
controlEvent = parseInjectKeycode();
|
msg = parseInjectKeycode();
|
||||||
break;
|
break;
|
||||||
case ControlMessage.TYPE_INJECT_TEXT:
|
case ControlMessage.TYPE_INJECT_TEXT:
|
||||||
controlEvent = parseInjectText();
|
msg = parseInjectText();
|
||||||
break;
|
break;
|
||||||
case ControlMessage.TYPE_INJECT_MOUSE:
|
case ControlMessage.TYPE_INJECT_MOUSE:
|
||||||
controlEvent = parseInjectMouse();
|
msg = parseInjectMouse();
|
||||||
break;
|
break;
|
||||||
case ControlMessage.TYPE_INJECT_TOUCH:
|
case ControlMessage.TYPE_INJECT_TOUCH:
|
||||||
controlEvent = parseInjectMouseTouch();
|
msg = parseInjectMouseTouch();
|
||||||
break;
|
break;
|
||||||
case ControlMessage.TYPE_INJECT_SCROLL:
|
case ControlMessage.TYPE_INJECT_SCROLL:
|
||||||
controlEvent = parseInjectScroll();
|
msg = parseInjectScroll();
|
||||||
break;
|
break;
|
||||||
case ControlMessage.TYPE_SET_CLIPBOARD:
|
case ControlMessage.TYPE_SET_CLIPBOARD:
|
||||||
controlEvent = parseSetClipboard();
|
msg = parseSetClipboard();
|
||||||
|
break;
|
||||||
|
case ControlMessage.TYPE_SET_SCREEN_POWER_MODE:
|
||||||
|
msg = parseSetScreenPowerMode();
|
||||||
break;
|
break;
|
||||||
case ControlMessage.TYPE_BACK_OR_SCREEN_ON:
|
case ControlMessage.TYPE_BACK_OR_SCREEN_ON:
|
||||||
case ControlMessage.TYPE_EXPAND_NOTIFICATION_PANEL:
|
case ControlMessage.TYPE_EXPAND_NOTIFICATION_PANEL:
|
||||||
case ControlMessage.TYPE_COLLAPSE_NOTIFICATION_PANEL:
|
case ControlMessage.TYPE_COLLAPSE_NOTIFICATION_PANEL:
|
||||||
case ControlMessage.TYPE_GET_CLIPBOARD:
|
case ControlMessage.TYPE_GET_CLIPBOARD:
|
||||||
controlEvent = ControlMessage.createEmpty(type);
|
msg = ControlMessage.createEmpty(type);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
Ln.w("Unknown event type: " + type);
|
Ln.w("Unknown event type: " + type);
|
||||||
controlEvent = null;
|
msg = null;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (controlEvent == null) {
|
if (msg == null) {
|
||||||
// failure, reset savedPosition
|
// failure, reset savedPosition
|
||||||
buffer.position(savedPosition);
|
buffer.position(savedPosition);
|
||||||
}
|
}
|
||||||
return controlEvent;
|
return msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ControlMessage parseInjectKeycode() {
|
private ControlMessage parseInjectKeycode() {
|
||||||
|
@ -157,6 +161,14 @@ public class ControlMessageReader {
|
||||||
return ControlMessage.createSetClipboard(text);
|
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) {
|
private static Position readPosition(ByteBuffer buffer) {
|
||||||
int x = toUnsigned(buffer.getShort());
|
int x = toUnsigned(buffer.getShort());
|
||||||
int y = toUnsigned(buffer.getShort());
|
int y = toUnsigned(buffer.getShort());
|
||||||
|
|
|
@ -102,9 +102,21 @@ public class Controller {
|
||||||
return sender;
|
return sender;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("checkstyle:MagicNumber")
|
||||||
public void control() throws IOException {
|
public void control() throws IOException {
|
||||||
// on start, turn screen on
|
// on start, power on the device
|
||||||
turnScreenOn();
|
if (!device.isScreenOn()) {
|
||||||
|
injectKeycode(KeyEvent.KEYCODE_POWER);
|
||||||
|
|
||||||
|
// dirty hack
|
||||||
|
// After POWER is injected, the device is powered on asynchronously.
|
||||||
|
// To turn the device screen off while mirroring, the client will send a message that
|
||||||
|
// would be handled before the device is actually powered on, so its effect would
|
||||||
|
// be "canceled" once the device is turned back on.
|
||||||
|
// Adding this delay prevents to handle the message before the device is actually
|
||||||
|
// powered on.
|
||||||
|
SystemClock.sleep(500);
|
||||||
|
}
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
handleEvent();
|
handleEvent();
|
||||||
|
@ -145,6 +157,9 @@ public class Controller {
|
||||||
case ControlMessage.TYPE_SET_CLIPBOARD:
|
case ControlMessage.TYPE_SET_CLIPBOARD:
|
||||||
device.setClipboardText(msg.getText());
|
device.setClipboardText(msg.getText());
|
||||||
break;
|
break;
|
||||||
|
case ControlMessage.TYPE_SET_SCREEN_POWER_MODE:
|
||||||
|
device.setScreenPowerMode(msg.getAction());
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
// do nothing
|
// do nothing
|
||||||
}
|
}
|
||||||
|
@ -309,10 +324,6 @@ public class Controller {
|
||||||
return device.injectInputEvent(event, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
|
return device.injectInputEvent(event, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean turnScreenOn() {
|
|
||||||
return device.isScreenOn() || injectKeycode(KeyEvent.KEYCODE_POWER);
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean pressBackOrTurnScreenOn() {
|
private boolean pressBackOrTurnScreenOn() {
|
||||||
int keycode = device.isScreenOn() ? KeyEvent.KEYCODE_BACK : KeyEvent.KEYCODE_POWER;
|
int keycode = device.isScreenOn() ? KeyEvent.KEYCODE_BACK : KeyEvent.KEYCODE_POWER;
|
||||||
return injectKeycode(keycode);
|
return injectKeycode(keycode);
|
||||||
|
|
|
@ -1,16 +1,21 @@
|
||||||
package com.genymobile.scrcpy;
|
package com.genymobile.scrcpy;
|
||||||
|
|
||||||
import com.genymobile.scrcpy.wrappers.ServiceManager;
|
import com.genymobile.scrcpy.wrappers.ServiceManager;
|
||||||
|
import com.genymobile.scrcpy.wrappers.SurfaceControl;
|
||||||
|
|
||||||
import android.graphics.Point;
|
import android.graphics.Point;
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
|
import android.os.IBinder;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.view.IRotationWatcher;
|
import android.view.IRotationWatcher;
|
||||||
import android.view.InputEvent;
|
import android.view.InputEvent;
|
||||||
|
|
||||||
public final class Device {
|
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 {
|
public interface RotationListener {
|
||||||
void onRotationChanged(int rotation);
|
void onRotationChanged(int rotation);
|
||||||
}
|
}
|
||||||
|
@ -190,6 +195,16 @@ public final class Device {
|
||||||
|
|
||||||
public void setClipboardText(String text) {
|
public void setClipboardText(String text) {
|
||||||
serviceManager.getClipboardManager().setText(text);
|
serviceManager.getClipboardManager().setText(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) {
|
static Rect flipRect(Rect crop) {
|
||||||
|
|
|
@ -8,7 +8,8 @@ import android.util.Log;
|
||||||
*/
|
*/
|
||||||
public final class Ln {
|
public final class Ln {
|
||||||
|
|
||||||
private static final String TAG = "scrcpy";
|
private static final String TAG = "qtscrcpy";
|
||||||
|
private static final String PREFIX = "[server] ";
|
||||||
|
|
||||||
enum Level {
|
enum Level {
|
||||||
DEBUG,
|
DEBUG,
|
||||||
|
@ -30,28 +31,28 @@ public final class Ln {
|
||||||
public static void d(String message) {
|
public static void d(String message) {
|
||||||
if (isEnabled(Level.DEBUG)) {
|
if (isEnabled(Level.DEBUG)) {
|
||||||
Log.d(TAG, message);
|
Log.d(TAG, message);
|
||||||
System.out.println("DEBUG: " + message);
|
System.out.println(PREFIX + "DEBUG: " + message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void i(String message) {
|
public static void i(String message) {
|
||||||
if (isEnabled(Level.INFO)) {
|
if (isEnabled(Level.INFO)) {
|
||||||
Log.i(TAG, message);
|
Log.i(TAG, message);
|
||||||
System.out.println("INFO: " + message);
|
System.out.println(PREFIX + "INFO: " + message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void w(String message) {
|
public static void w(String message) {
|
||||||
if (isEnabled(Level.WARN)) {
|
if (isEnabled(Level.WARN)) {
|
||||||
Log.w(TAG, message);
|
Log.w(TAG, message);
|
||||||
System.out.println("WARN: " + message);
|
System.out.println(PREFIX + "WARN: " + message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void e(String message, Throwable throwable) {
|
public static void e(String message, Throwable throwable) {
|
||||||
if (isEnabled(Level.ERROR)) {
|
if (isEnabled(Level.ERROR)) {
|
||||||
Log.e(TAG, message, throwable);
|
Log.e(TAG, message, throwable);
|
||||||
System.out.println("ERROR: " + message);
|
System.out.println(PREFIX + "ERROR: " + message);
|
||||||
if (throwable != null) {
|
if (throwable != null) {
|
||||||
throwable.printStackTrace();
|
throwable.printStackTrace();
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ public class Options {
|
||||||
private boolean tunnelForward;
|
private boolean tunnelForward;
|
||||||
private Rect crop;
|
private Rect crop;
|
||||||
private boolean sendFrameMeta;
|
private boolean sendFrameMeta;
|
||||||
|
private boolean control;
|
||||||
|
|
||||||
public int getMaxSize() {
|
public int getMaxSize() {
|
||||||
return maxSize;
|
return maxSize;
|
||||||
|
@ -48,4 +49,12 @@ public class Options {
|
||||||
public void setSendFrameMeta(boolean sendFrameMeta) {
|
public void setSendFrameMeta(boolean sendFrameMeta) {
|
||||||
this.sendFrameMeta = sendFrameMeta;
|
this.sendFrameMeta = sendFrameMeta;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean getControl() {
|
||||||
|
return control;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setControl(boolean control) {
|
||||||
|
this.control = control;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,11 +20,13 @@ public final class Server {
|
||||||
try (DesktopConnection connection = DesktopConnection.open(device, tunnelForward)) {
|
try (DesktopConnection connection = DesktopConnection.open(device, tunnelForward)) {
|
||||||
ScreenEncoder screenEncoder = new ScreenEncoder(options.getSendFrameMeta(), options.getBitRate());
|
ScreenEncoder screenEncoder = new ScreenEncoder(options.getSendFrameMeta(), options.getBitRate());
|
||||||
|
|
||||||
Controller controller = new Controller(device, connection);
|
if (options.getControl()) {
|
||||||
|
Controller controller = new Controller(device, connection);
|
||||||
|
|
||||||
// asynchronous
|
// asynchronous
|
||||||
startController(controller);
|
startController(controller);
|
||||||
startDeviceMessageSender(controller.getSender());
|
startDeviceMessageSender(controller.getSender());
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// synchronous
|
// synchronous
|
||||||
|
@ -66,7 +68,7 @@ public final class Server {
|
||||||
|
|
||||||
@SuppressWarnings("checkstyle:MagicNumber")
|
@SuppressWarnings("checkstyle:MagicNumber")
|
||||||
private static Options createOptions(String... args) {
|
private static Options createOptions(String... args) {
|
||||||
if (args.length != 5) {
|
if (args.length != 6) {
|
||||||
throw new IllegalArgumentException("Expecting 5 parameters");
|
throw new IllegalArgumentException("Expecting 5 parameters");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,6 +90,9 @@ public final class Server {
|
||||||
boolean sendFrameMeta = Boolean.parseBoolean(args[4]);
|
boolean sendFrameMeta = Boolean.parseBoolean(args[4]);
|
||||||
options.setSendFrameMeta(sendFrameMeta);
|
options.setSendFrameMeta(sendFrameMeta);
|
||||||
|
|
||||||
|
boolean control = Boolean.parseBoolean(args[5]);
|
||||||
|
options.setControl(control);
|
||||||
|
|
||||||
return options;
|
return options;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ package com.genymobile.scrcpy.wrappers;
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
|
import android.os.Build;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.view.Surface;
|
import android.view.Surface;
|
||||||
|
|
||||||
|
@ -10,6 +11,10 @@ public final class SurfaceControl {
|
||||||
|
|
||||||
private static final Class<?> CLASS;
|
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 {
|
static {
|
||||||
try {
|
try {
|
||||||
CLASS = Class.forName("android.view.SurfaceControl");
|
CLASS = Class.forName("android.view.SurfaceControl");
|
||||||
|
@ -71,6 +76,27 @@ public final class SurfaceControl {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static IBinder getBuiltInDisplay(int builtInDisplayId) {
|
||||||
|
try {
|
||||||
|
// the method signature has changed in Android Q
|
||||||
|
// <https://github.com/Genymobile/scrcpy/issues/586>
|
||||||
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
|
||||||
|
return (IBinder) CLASS.getMethod("getBuiltInDisplay", int.class).invoke(null, builtInDisplayId);
|
||||||
|
}
|
||||||
|
return (IBinder) CLASS.getMethod("getPhysicalDisplayToken", long.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) {
|
public static void destroyDisplay(IBinder displayToken) {
|
||||||
try {
|
try {
|
||||||
CLASS.getMethod("destroyDisplay", IBinder.class).invoke(null, displayToken);
|
CLASS.getMethod("destroyDisplay", IBinder.class).invoke(null, displayToken);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue