├── .gitignore
├── LICENSE
├── README.md
├── RemoteControl.pro
├── image.qrc
├── image
├── winIcon.ico
└── winIcon.png
├── qml.qrc
├── qml
├── ControlledPage.qml
├── ControllerPage.qml
├── GlowRectangle.qml
├── MyButton.qml
├── MySystemTray.qml
├── MyToolTip.qml
└── main.qml
├── qml_qmlcache.qrc
└── src
├── dxgimanager.cpp
├── dxgimanager.h
├── framelesswindow.cpp
├── framelesswindow.h
├── imageprovider.cpp
├── imageprovider.h
├── main.cpp
├── networkapi.cpp
├── networkapi.h
├── remoteevent.h
├── systemapi.cpp
├── systemapi.h
├── tcp
├── controlled.cpp
├── controlled.h
├── controller.cpp
├── controller.h
├── protocol.cpp
├── protocol.h
├── socket.cpp
└── socket.h
└── udp
├── connection.cpp
├── connection.h
├── controlled.cpp
├── controlled.h
├── controller.cpp
├── controller.h
├── protocol.cpp
├── protocol.h
├── socket.cpp
└── socket.h
/.gitignore:
--------------------------------------------------------------------------------
1 | debug/
2 | release/
3 | android-build/
4 | *.user
5 | *.Debug
6 | *.Release
7 | Makefile
8 | *.stash
9 | *.rc
10 | *.exe
11 | *.qmlc
12 | *.so-deployment-settings.json
13 | *.so
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 mengps
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # RemoteControl
2 |
3 | 尝试做一个远程控制的简单工具
4 |
5 | 一般为局域网环境下使用(热点)
6 |
7 | ------
8 |
9 | `说明` 使用两种协议传输 [可选TCP/UDP(默认)]
10 |
11 | ```
12 | 默认使用UDP来传输屏幕图像,额外的TCP连接来传输EVENT,包括连接状态
13 |
14 | 要使用TCP,通过在pro中添加 DEFINES += USE_TCP
15 | ```
16 |
17 | `说明` 使用三种截屏方式 [可选DXGI/D3D/GDI(默认,且Qt自带)]
18 |
19 | ```
20 | 通过在pro中添加:
21 |
22 | DEFINES += USE_DXGI 或者 DEFINES += USE_GDI 或者 DEFINES += USE_D3D
23 |
24 | 因为DXGI仅在 Win8 以上提供,考虑到 Win8 的使用量,所以 Win10 以上可使用
25 | ```
26 | ------
27 | ### 关于Windows和Android
28 |
29 | 要知道的是,因为不能完全跨平台,某些功能难以实现(水平不够)
30 |
31 | 并且界面也需要做一些调整,很麻烦
32 |
33 | ------
34 |
35 | ### 开发环境
36 |
37 | 使用Qt/Qml开发
38 |
39 | Windows 10,Qt 5.13.0
40 |
41 | 基本未使用高版本特性,保证`Qt Version >= 5.0`即可,建议是`Qt 5.5 ~ 5.7`
42 |
43 | ------
44 |
45 | ### 后续改进
46 |
47 | - 使用RTP传输
48 |
49 | - 不再传输图像,而是码流
50 |
51 | ------
52 |
53 | ### 许可证
54 |
55 | 使用 `MIT LICENSE`
56 |
57 | ------
58 |
59 | `注意` 目前传输的不是码流而是JPG图像
60 |
61 | `注意` 该软件仅用于学习,而不适合作为实际软件使用
--------------------------------------------------------------------------------
/RemoteControl.pro:
--------------------------------------------------------------------------------
1 | QT += quick gui
2 | CONFIG += c++11
3 |
4 | DEFINES += USE_TCP
5 | #else use udp
6 |
7 | #choose one of them USE_DXGI / USE_GDI / USE_D3D
8 | DEFINES += USE_DXGI
9 | #DEFINES += USE_GDI
10 | #DEFINES += USE_D3D
11 |
12 | INCLUDEPATH += ../RemoteControl/src
13 |
14 | HEADERS += \
15 | src/framelesswindow.h \
16 | src/networkapi.h \
17 | src/remoteevent.h \
18 | src/imageprovider.h \
19 | src/systemapi.h
20 |
21 | SOURCES += \
22 | src/framelesswindow.cpp \
23 | src/main.cpp \
24 | src/networkapi.cpp \
25 | src/imageprovider.cpp \
26 | src/systemapi.cpp
27 |
28 | if (contains(DEFINES, USE_TCP)){
29 | message("Use Tcp")
30 | HEADERS += \
31 | src/tcp/controlled.h \
32 | src/tcp/controller.h \
33 | src/tcp/protocol.h \
34 | src/tcp/socket.h
35 |
36 | SOURCES += \
37 | src/tcp/controlled.cpp \
38 | src/tcp/controller.cpp \
39 | src/tcp/protocol.cpp \
40 | src/tcp/socket.cpp
41 |
42 | INCLUDEPATH += src/tcp
43 | }else{
44 | message("Use Udp")
45 | HEADERS += \
46 | src/udp/connection.h \
47 | src/udp/controlled.h \
48 | src/udp/controller.h \
49 | src/udp/protocol.h \
50 | src/udp/socket.h
51 |
52 | SOURCES += \
53 | src/udp/connection.cpp \
54 | src/udp/controlled.cpp \
55 | src/udp/controller.cpp \
56 | src/udp/protocol.cpp \
57 | src/udp/socket.cpp
58 |
59 | INCLUDEPATH += src/udp
60 | }
61 |
62 | win32{
63 | message("Platform: Win32")
64 | QT += winextras
65 | LIBS += -lGdi32
66 | RC_ICONS += image/winIcon.ico
67 | contains(DEFINES, USE_D3D){
68 | message("Duplication interface: D3D")
69 | HEADERS += src/dxgimanager.h
70 | LIBS += -ld3d9 -lD3dx9
71 | }
72 | contains(DEFINES, USE_DXGI){
73 | message("Duplication interface: DXGI")
74 | SOURCES += src/dxgimanager.cpp
75 | LIBS += -lD3D11 -lDXGI
76 | }
77 | }else{
78 | message ("Platform: Android")
79 | }
80 |
81 | # The following define makes your compiler emit warnings if you use
82 | # any Qt feature that has been marked deprecated (the exact warnings
83 | # depend on your compiler). Refer to the documentation for the
84 | # deprecated API to know how to port your code away from it.
85 | DEFINES += QT_DEPRECATED_WARNINGS
86 |
87 | # You can also make your code fail to compile if it uses deprecated APIs.
88 | # In order to do so, uncomment the following line.
89 | # You can also select to disable deprecated APIs only up to a certain version of Qt.
90 | #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
91 |
92 | RESOURCES += \
93 | qml.qrc \
94 | image.qrc
95 |
96 | # Default rules for deployment.
97 | qnx: target.path = /tmp/$${TARGET}/bin
98 | else: unix:!android: target.path = /opt/$${TARGET}/bin
99 | !isEmpty(target.path): INSTALLS += target
100 |
--------------------------------------------------------------------------------
/image.qrc:
--------------------------------------------------------------------------------
1 |
2 |
3 | image/winIcon.png
4 |
5 |
6 |
--------------------------------------------------------------------------------
/image/winIcon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mengps/RemoteControl/f78d76504f8ec742a7acf2fef3e97be68b4d2f48/image/winIcon.ico
--------------------------------------------------------------------------------
/image/winIcon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mengps/RemoteControl/f78d76504f8ec742a7acf2fef3e97be68b4d2f48/image/winIcon.png
--------------------------------------------------------------------------------
/qml.qrc:
--------------------------------------------------------------------------------
1 |
2 |
3 | qml/main.qml
4 | qml/GlowRectangle.qml
5 | qml/MyButton.qml
6 | qml/MyToolTip.qml
7 | qml/ControlledPage.qml
8 | qml/ControllerPage.qml
9 | qml/MySystemTray.qml
10 |
11 |
12 |
--------------------------------------------------------------------------------
/qml/ControlledPage.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.12
2 | import Qt.labs.platform 1.1
3 |
4 | Item {
5 | id: root
6 |
7 | property var systemTray: undefined;
8 |
9 | Component.onCompleted: {
10 | var component = Qt.createComponent("MySystemTray.qml");
11 | var incubator = component.incubateObject(root);
12 | if (incubator.status !== Component.Ready) {
13 | incubator.onStatusChanged = function(status) {
14 | if (status === Component.Ready)
15 | root.systemTray = incubator.object;
16 | }
17 | }
18 | }
19 |
20 | Column {
21 | anchors.centerIn: parent
22 | spacing: 32
23 |
24 | Item {
25 | width: 300
26 | height: 30
27 |
28 | Text {
29 | anchors.centerIn: parent
30 | font.pointSize: 13
31 | color: "red"
32 | text: qsTr("正在被远程控制")
33 | }
34 | }
35 |
36 | Item {
37 | width: 300
38 | height: 30
39 |
40 | MyButton {
41 | id: disconnectButton
42 | anchors.centerIn: parent
43 | heightMargin: 8
44 | text: qsTr(" <-断开连接-> ")
45 | onClicked: controlled.finish();
46 | }
47 | }
48 |
49 | Item {
50 | width: 300
51 | height: 30
52 |
53 | MyButton {
54 | id: hidetButton
55 | anchors.centerIn: parent
56 | heightMargin: 8
57 | text: qsTr(" <-最小化托盘-> ")
58 | onClicked: {
59 | mainWindow.hide();
60 | if (root.systemTray != undefined)
61 | root.systemTray.showMessage(qsTr("提示"), qsTr("已最小化到托盘!"));
62 | }
63 | }
64 | }
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/qml/ControllerPage.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.12
2 |
3 | Item {
4 | id: root
5 |
6 | Image {
7 | id: image
8 | anchors.fill: parent
9 | sourceSize: Qt.size(parent.width, parent.height)
10 | cache: false
11 |
12 | property int frame: 0;
13 |
14 | Text {
15 | id: fpsText
16 | anchors.left: parent.left
17 | anchors.leftMargin: 50
18 | anchors.top: parent.top
19 | anchors.topMargin: 20
20 | font.pointSize: 20
21 | color: "red"
22 | }
23 |
24 | Timer {
25 | interval: 1000
26 | running: true
27 | repeat:true
28 | onTriggered: {
29 | fpsText.text = "FPS :" + image.frame;
30 | image.frame = 0;
31 | }
32 | }
33 | }
34 |
35 | Connections {
36 | target: controller
37 | onNeedUpdate: {
38 | image.frame++;
39 | image.source = "image://screen/" + Date.now();
40 | }
41 | }
42 |
43 | MouseArea {
44 | id: controllerArea
45 | anchors.fill: parent
46 | onPressed: {
47 | let ratio = Qt.point(mouse.x / root.width, mouse.y / root.height);
48 | controller.mousePressed(ratio);
49 | }
50 | onReleased: {
51 | let ratio = Qt.point(mouse.x / root.width, mouse.y / root.height);
52 | controller.mouseReleased(ratio);
53 | }
54 | onPositionChanged: {
55 | let ratio = Qt.point(mouse.x / root.width, mouse.y / root.height);
56 | controller.mouseMoved(ratio);
57 | }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/qml/GlowRectangle.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.12
2 | import QtGraphicalEffects 1.12
3 |
4 | Item {
5 | id: root
6 |
7 | property alias color: backRect.color;
8 | property alias radius: backRect.radius;
9 | property alias gradient: backRect.gradient;
10 | property alias spread: backEffect.spread;
11 | property alias glowColor: backEffect.color;
12 | property alias glowRadius: backEffect.glowRadius;
13 |
14 | RectangularGlow {
15 | id: backEffect
16 | antialiasing: true
17 | anchors.fill: backRect
18 | glowRadius: 1
19 | spread: 0.2
20 | cornerRadius: backRect.radius + glowRadius
21 | }
22 |
23 | Rectangle {
24 | id: backRect
25 | antialiasing: true
26 | anchors.fill: parent
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/qml/MyButton.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.12
2 |
3 | GlowRectangle {
4 | id: root
5 | width: text.width + widthMargin * 2
6 | height: text.height + heightMargin * 2
7 | radius: 6
8 | glowRadius: 6
9 | color: hovered ? hoverColor : Qt.lighter(hoverColor, 1.18);
10 |
11 | property alias text: text.text;
12 | property alias fontSize: text.font.pointSize;
13 | property bool hovered: false;
14 | property int widthMargin: 10;
15 | property int heightMargin: 6;
16 | property color textColor: text.color;
17 | property color hoverColor: "#A0A0A0";
18 |
19 | signal clicked();
20 | signal pressed();
21 | signal released();
22 | signal entered();
23 | signal exited();
24 |
25 | Text {
26 | id: text
27 | color: "#333"
28 | x: widthMargin
29 | y: heightMargin
30 | }
31 |
32 | MouseArea {
33 | id: mouseArea
34 | anchors.fill: parent
35 | hoverEnabled: true
36 | onPressed: {
37 | root.pressed();
38 | text.x += 1;
39 | text.y += 1;
40 | }
41 | onReleased: {
42 | root.released();
43 | root.clicked();
44 | text.x -= 1;
45 | text.y -= 1;
46 | }
47 | onEntered: root.hovered = true;
48 | onExited: root.hovered = false;
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/qml/MySystemTray.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.12
2 | import Qt.labs.platform 1.1
3 |
4 | SystemTrayIcon {
5 | visible: true
6 | iconSource: "qrc:/image/winIcon.png"
7 | tooltip: qsTr("远程控制后台运行中...")
8 |
9 | function activeWindow() {
10 | mainWindow.show();
11 | mainWindow.raise();
12 | mainWindow.requestActivate();
13 | }
14 |
15 | onActivated: activeWindow();
16 | }
17 |
--------------------------------------------------------------------------------
/qml/MyToolTip.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.12
2 | import QtQuick.Controls 2.12
3 |
4 | ToolTip {
5 | id: root
6 | font.pointSize: 9
7 | font.family: "微软雅黑"
8 | opacity: 0
9 | background: Rectangle {
10 | radius: 4
11 | color: "#AAFFFFFF"
12 | border.color: "#888"
13 | border.width: 1
14 | }
15 |
16 | NumberAnimation {
17 | id: animation
18 | target: root
19 | running: false
20 | property: "opacity"
21 | from: 0
22 | to: 1
23 | duration: 700
24 | easing.type: Easing.InOutQuad
25 | }
26 |
27 | onVisibleChanged: if (visible) animation.restart();
28 | }
29 |
--------------------------------------------------------------------------------
/qml/main.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.12
2 | import QtQuick.Window 2.12
3 | import QtQuick.Controls 2.12
4 | import an.window 1.0
5 |
6 | FramelessWindow {
7 | id: mainWindow
8 |
9 | visible: true
10 | width: mobile ? Screen.desktopAvailableWidth : 480
11 | height: mobile ? Screen.desktopAvailableHeight : 320
12 | minimumWidth: 480
13 | minimumHeight: 320
14 | title: qsTr("远程控制")
15 | color: "#D4E6FF"
16 | movable: !mobile
17 | resizable: !mobile
18 | Component.onCompleted: {
19 | stackView.push(mainPage);
20 | }
21 |
22 | property bool mobile: Qt.platform.os == "android";
23 | property bool connected: false;
24 | property string localIpAddress: NetworkApi.getLocalIpAddress();
25 |
26 | Connections {
27 | target: controlled
28 | onConnected: {
29 | mainWindow.connected = true;
30 | stackView.push("ControlledPage.qml");
31 | }
32 | onDisconnected: {
33 | mainWindow.connected = false;
34 | stackView.pop();
35 | }
36 | }
37 |
38 | Connections {
39 | target: controller
40 | onConnected: {
41 | mainWindow.connected = true;
42 | stackView.push("ControllerPage.qml");
43 | }
44 | onDisconnected: {
45 | mainWindow.connected = false;
46 | stackView.pop();
47 | }
48 | }
49 |
50 | GlowRectangle {
51 | id: windowTitle
52 | clip: true
53 | radius: 5
54 | z: 5
55 | height: mobile && connected ? 0 : 40; //如果是手机,并且已经连接就不显示
56 | width: parent.width
57 | color: "#F4D1B4"
58 | glowColor: color
59 | anchors.horizontalCenter: parent.horizontalCenter
60 | anchors.top: parent.top
61 | anchors.topMargin: mobile && connected ? 0 : -radius
62 |
63 | Text {
64 | anchors.horizontalCenter: parent.horizontalCenter
65 | anchors.bottom: parent.bottom
66 | anchors.bottomMargin: 10
67 | text: qsTr("远程控制")
68 | }
69 |
70 | MyButton {
71 | id: minButton
72 | visible: enabled
73 | enabled: !mobile
74 | fontSize: 12
75 | widthMargin: 8
76 | anchors.right: maxButton.left
77 | anchors.rightMargin: 6
78 | anchors.top: maxButton.top
79 | text: " - "
80 | onClicked: mainWindow.showMinimized();
81 | }
82 |
83 | MyButton {
84 | id: maxButton
85 | visible: enabled
86 | enabled: !mobile
87 | fontSize: 12
88 | widthMargin: 8
89 | anchors.right: closeButton.left
90 | anchors.rightMargin: 6
91 | anchors.top: closeButton.top
92 | text: " + "
93 | onClicked: mainWindow.showMaximized();
94 | }
95 |
96 | MyButton {
97 | id: closeButton
98 | fontSize: 12
99 | widthMargin: 8
100 | anchors.right: parent.right
101 | anchors.top: parent.top
102 | text: " x "
103 | onClicked:
104 | {
105 | if (stackView.depth == 1)
106 | mainWindow.close();
107 | else controller.finish();
108 | }
109 | }
110 | }
111 |
112 | StackView {
113 | id: stackView
114 | width: parent.width
115 | anchors.top: windowTitle.bottom
116 | anchors.bottom: parent.bottom
117 | }
118 |
119 | Component {
120 | id: mainPage
121 |
122 | Item {
123 |
124 | Column {
125 | anchors.centerIn: parent
126 | spacing: 32
127 |
128 | Item {
129 | width: 300
130 | height: 30
131 |
132 | Text {
133 | id: ipAddressText
134 | anchors.centerIn: parent
135 | font.pointSize: 13
136 | color: "red"
137 | text: qsTr("本机IP地址:" + localIpAddress)
138 | }
139 | }
140 |
141 | Item {
142 | width: 300
143 | height: 30
144 |
145 | Item {
146 | width: remoteIpAddressText.width + remoteIpAddressEdit.width
147 | height: 30
148 | anchors.horizontalCenter: parent.horizontalCenter
149 |
150 | Text {
151 | id: remoteIpAddressText
152 | anchors.verticalCenter: parent.verticalCenter
153 | text: qsTr("远程IP地址:")
154 | }
155 |
156 | TextField {
157 | id: remoteIpAddressEdit
158 | anchors.verticalCenter: parent.verticalCenter
159 | anchors.left: remoteIpAddressText.right
160 | anchors.rightMargin: 10
161 | width: 150
162 | height: parent.height
163 | selectByMouse: true
164 | placeholderText: qsTr("输入IPv4/IPv6地址")
165 | background: Rectangle {
166 | radius: 6
167 | border.color: "#09A3DC"
168 | }
169 | validator: RegExpValidator {
170 | //IP正则
171 | regExp: /(2(5[0-5]|[0-4]\d))|[0-1]?\d{1,2}(\.((2(5[0-5]|[0-4]\d))|[0-1]?\d{1,2})){3}/
172 | }
173 | }
174 | }
175 | }
176 |
177 | Item {
178 | width: 300
179 | height: 30
180 |
181 | MyButton {
182 | id: connectButton
183 | anchors.horizontalCenter: parent.horizontalCenter
184 | heightMargin: 8
185 | text: qsTr(" <-连接-> ")
186 | onClicked: controller.requestNewConnection(remoteIpAddressEdit.text)
187 | }
188 | }
189 | }
190 | }
191 | }
192 | }
193 |
--------------------------------------------------------------------------------
/qml_qmlcache.qrc:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | qml/main.qml
5 | qml/GlowRectangle.qml
6 | qml/MyButton.qml
7 | qml/MyToolTip.qml
8 | qml/ControlledPage.qml
9 | qml/ControllerPage.qml
10 | qml/MySystemTray.qml
11 |
12 |
13 |
--------------------------------------------------------------------------------
/src/dxgimanager.cpp:
--------------------------------------------------------------------------------
1 | #include "dxgimanager.h"
2 | #include
3 |
4 | Texture::~Texture()
5 | {
6 | m_texture->Release();
7 | }
8 |
9 | DxgiTextureStaging::DxgiTextureStaging(ID3D11Device *device, ID3D11DeviceContext *context)
10 | : m_device(device), m_context(context)
11 | {
12 |
13 | }
14 |
15 | DxgiTextureStaging::~DxgiTextureStaging()
16 | {
17 | m_device->Release();
18 | m_context->Release();
19 | }
20 |
21 | QPixmap DxgiTextureStaging::copyToImage(IDXGIResource *res)
22 | {
23 | D3D11_TEXTURE2D_DESC desc;
24 | ID3D11Texture2D *textrueRes = nullptr;
25 | HRESULT hr = res->QueryInterface(__uuidof(ID3D11Texture2D), reinterpret_cast(&textrueRes));
26 | if (FAILED(hr)) {
27 | qDebug() << "Failed to ID3D11Texture2D result =" << hex << uint(hr);
28 | return QPixmap();
29 | }
30 | textrueRes->GetDesc(&desc);
31 |
32 | D3D11_TEXTURE2D_DESC texDesc;
33 | ZeroMemory(&texDesc, sizeof(texDesc));
34 | texDesc.Width = desc.Width;
35 | texDesc.Height = desc.Height;
36 | texDesc.MipLevels = 1;
37 | texDesc.ArraySize = 1;
38 | texDesc.SampleDesc.Count = 1;
39 | texDesc.SampleDesc.Quality = 0;
40 | texDesc.Usage = D3D11_USAGE_STAGING;
41 | texDesc.Format = desc.Format;
42 | texDesc.BindFlags = 0;
43 | texDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
44 | texDesc.MiscFlags = 0;
45 | m_device->CreateTexture2D(&texDesc, nullptr, &m_texture);
46 | m_context->CopyResource(m_texture, textrueRes);
47 |
48 | IDXGISurface1 *surface = nullptr;
49 | hr = m_texture->QueryInterface(__uuidof(IDXGISurface1), reinterpret_cast(&surface));
50 | if (FAILED(hr)) {
51 | qDebug() << "Failed to QueryInterface IDXGISurface1 ErrorCode =" << hex << uint(hr);
52 | return QPixmap();
53 | }
54 |
55 | DXGI_MAPPED_RECT map;
56 | surface->Map(&map, DXGI_MAP_READ);
57 | QPixmap pixmap = QPixmap::fromImage(QImage(static_cast(map.pBits),
58 | int(desc.Width), int(desc.Height), QImage::Format_ARGB32));
59 | surface->Unmap();
60 | surface->Release();
61 | m_texture->Release();
62 |
63 | return pixmap;
64 | }
65 |
66 | DxgiManager::DxgiManager()
67 | {
68 |
69 | }
70 |
71 | DxgiManager::~DxgiManager()
72 | {
73 | m_duplication->Release();
74 | }
75 |
76 | bool DxgiManager::init()
77 | {
78 | ID3D11Device *d3dDevice = nullptr;
79 | ID3D11DeviceContext *d3dContext = nullptr;
80 | D3D_FEATURE_LEVEL feat = D3D_FEATURE_LEVEL_11_0;
81 | HRESULT hr = D3D11CreateDevice(nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, 0, nullptr, 0, D3D11_SDK_VERSION, &d3dDevice, &feat, &d3dContext);
82 | if (FAILED(hr)) {
83 | m_lastError = "Failed to D3D11CreateDevice ErrorCode = " + QString::number(uint(hr), 16);
84 | return false;
85 | }
86 |
87 | IDXGIDevice *dxgiDevice = nullptr;
88 | hr = d3dDevice->QueryInterface(__uuidof(IDXGIDevice), reinterpret_cast(&dxgiDevice));
89 | if(FAILED(hr)) {
90 | m_lastError = "Failed to QueryInterface IDXGIOutput6 ErrorCode = " + QString::number(uint(hr), 16);
91 | return false;
92 | }
93 |
94 | IDXGIAdapter *dxgiAdapter = nullptr;
95 | hr = dxgiDevice->GetParent(__uuidof(IDXGIAdapter), reinterpret_cast(&dxgiAdapter));
96 | dxgiDevice->Release();
97 | if (FAILED(hr)) {
98 | m_lastError = "Failed to Get IDXGIAdapter ErrorCode = " + QString::number(uint(hr), 16);
99 | return false;
100 | }
101 |
102 | IDXGIOutput *dxgiOutput = nullptr;
103 | QVector outputs;
104 | for(uint i = 0; dxgiAdapter->EnumOutputs(i, &dxgiOutput) != DXGI_ERROR_NOT_FOUND; ++i) {
105 | outputs.push_back(dxgiOutput);
106 | }
107 | dxgiAdapter->Release();
108 | if (outputs.size() > 0) dxgiOutput = outputs.at(0);
109 | else {
110 | m_lastError = "Failed to IDXGIOutput is Empty!";
111 | return false;
112 | }
113 |
114 | IDXGIOutput6 *dxgiOutput6 = nullptr;
115 | hr = dxgiOutput->QueryInterface(__uuidof(IDXGIOutput6), reinterpret_cast(&dxgiOutput6));
116 | dxgiOutput->Release();
117 | if (FAILED(hr)) {
118 | m_lastError = "Failed to QueryInterface IDXGIOutput6 ErrorCode = " + QString::number(uint(hr), 16);
119 | return false;
120 | }
121 |
122 | hr = dxgiOutput6->DuplicateOutput(d3dDevice, &m_duplication);
123 | dxgiOutput6->Release();
124 | if (FAILED(hr)) {
125 | m_lastError = "Failed to DuplicateOutput ErrorCode = " + QString::number(uint(hr), 16);
126 | return false;
127 | }
128 |
129 | DXGI_OUTDUPL_DESC desc;
130 | m_duplication->GetDesc(&desc);
131 | m_texture = new DxgiTextureStaging(d3dDevice, d3dContext);
132 | if (desc.DesktopImageInSystemMemory) {
133 | qDebug() << "Desc: CPU shared with GPU";
134 | } else {
135 | qDebug() << "Desc: CPU not shared with GPU";
136 | }
137 |
138 | return true;
139 | }
140 |
141 | QString DxgiManager::lastError() const
142 | {
143 | return m_lastError;
144 | }
145 |
146 | QPixmap DxgiManager::grabScreen()
147 | {
148 | IDXGIResource *desktopRes;
149 | DXGI_OUTDUPL_FRAME_INFO frameInfo;
150 | while (true) {
151 | HRESULT hr = m_duplication->AcquireNextFrame(100, &frameInfo, &desktopRes);
152 | if (FAILED(hr)) {
153 | m_lastError = "Failed to AcquireNextFrame ErrorCode = " + QString::number(uint(hr), 16);
154 | return QPixmap();
155 | }
156 |
157 | if (frameInfo.LastPresentTime.QuadPart) break;
158 |
159 | m_duplication->ReleaseFrame();
160 | }
161 |
162 | return m_texture->copyToImage(desktopRes);
163 | }
164 |
--------------------------------------------------------------------------------
/src/dxgimanager.h:
--------------------------------------------------------------------------------
1 | #ifndef DXGIMANAGER_H
2 | #define DXGIMANAGER_H
3 |
4 | #include
5 |
6 | #ifdef Q_OS_WIN
7 | # include
8 | # if defined (_WIN32_WINNT_WIN10) //win10 dxgi1_6
9 | # include
10 | # include
11 | # endif
12 | #endif
13 |
14 | class Texture
15 | {
16 | public:
17 | virtual ~Texture();
18 | virtual QPixmap copyToImage(IDXGIResource *res) = 0;
19 |
20 | protected:
21 | ID3D11Texture2D *m_texture = nullptr;
22 | };
23 |
24 | class DxgiTextureStaging : public Texture
25 | {
26 | public:
27 | DxgiTextureStaging(ID3D11Device *device, ID3D11DeviceContext *context);
28 | ~DxgiTextureStaging();
29 |
30 | QPixmap copyToImage(IDXGIResource *res);
31 |
32 | private:
33 | ID3D11Device *m_device = nullptr;
34 | ID3D11DeviceContext * m_context = nullptr;
35 | };
36 |
37 | class DxgiManager
38 | {
39 | public:
40 | DxgiManager();
41 | ~DxgiManager();
42 |
43 | bool init();
44 | QString lastError() const;
45 | QPixmap grabScreen();
46 |
47 | private:
48 | QString m_lastError = QString();
49 | Texture *m_texture = nullptr;
50 | IDXGIOutputDuplication *m_duplication = nullptr;
51 | };
52 |
53 | #endif // DXGIMANAGER_H
54 |
--------------------------------------------------------------------------------
/src/framelesswindow.cpp:
--------------------------------------------------------------------------------
1 | #include "framelesswindow.h"
2 |
3 | static QRect MoveArea;
4 |
5 | FramelessWindow::FramelessWindow(QWindow *parent)
6 | : QQuickWindow (parent)
7 |
8 | {
9 | setFlags(flags() | Qt::Window | Qt::FramelessWindowHint);
10 |
11 | //在这里改变默认移动区域
12 | //只有鼠标在移动区域内,才能移动窗口
13 | MoveArea = { 8, 8, width() - 16, 40 };
14 | connect(this, &QQuickWindow::widthChanged, this, [](int arg){
15 | MoveArea.setWidth(arg - 16);
16 | });
17 | }
18 |
19 | bool FramelessWindow::movable() const
20 | {
21 | return m_movable;
22 | }
23 |
24 | void FramelessWindow::setMovable(bool arg)
25 | {
26 | if (m_movable != arg) {
27 | m_movable = arg;
28 | emit movableChanged();
29 | }
30 | }
31 |
32 | bool FramelessWindow::resizable() const
33 | {
34 | return m_resizable;
35 | }
36 |
37 | void FramelessWindow::setResizable(bool arg)
38 | {
39 | if (m_resizable != arg) {
40 | m_resizable = arg;
41 | emit resizableChanged();
42 | }
43 | }
44 |
45 | void FramelessWindow::mousePressEvent(QMouseEvent *event)
46 | {
47 | m_startPos = event->globalPos();
48 | m_oldPos = position();
49 | m_oldSize = size();
50 | event->ignore();
51 |
52 | QQuickWindow::mousePressEvent(event);
53 | }
54 |
55 | void FramelessWindow::mouseReleaseEvent(QMouseEvent *event)
56 | {
57 | m_oldPos = position();
58 |
59 | QQuickWindow::mouseReleaseEvent(event);
60 | }
61 |
62 | void FramelessWindow::mouseDoubleClickEvent(QMouseEvent *event)
63 | {
64 | if (m_currentArea == Move) {
65 | if (windowState() == Qt::WindowMaximized) {
66 | showNormal();
67 | m_currentArea = Client;
68 | } else if (windowState() == Qt::WindowNoState) {
69 | showMaximized();
70 | m_currentArea = Client;
71 | }
72 | }
73 |
74 | QQuickWindow::mouseDoubleClickEvent(event);
75 | }
76 |
77 | void FramelessWindow::mouseMoveEvent(QMouseEvent *event)
78 | {
79 | if (event->buttons() & Qt::LeftButton) {
80 | if (m_movable && m_currentArea == Move) {
81 | //单独处理移动区域,这样可以更快
82 | //但是需要注意,z序更高的MouseArea仍会触发
83 | setPosition(m_oldPos - m_startPos + event->globalPos());
84 | } else if (m_resizable && m_currentArea != Move){
85 | setWindowGeometry(event->globalPos());
86 | }
87 | } else {
88 | QPoint pos = event->pos();
89 | m_currentArea = getArea(pos);
90 | if (m_resizable) setCursorIcon();
91 | }
92 |
93 | QQuickWindow::mouseMoveEvent(event);
94 | }
95 |
96 | FramelessWindow::MouseArea FramelessWindow::getArea(const QPoint &pos)
97 | {
98 | int x = pos.x();
99 | int y = pos.y();
100 | int w = width();
101 | int h = height();
102 | MouseArea area;
103 |
104 | if (x >= 0 && x <= 8 && y >= 0 && y <= 8) {
105 | area = TopLeft;
106 | } else if (x > 8 && x < (w - 8) && y >= 0 && y <= 8) {
107 | area = Top;
108 | } else if (x >=(w - 8) && x <= w && y >= 0 && y <= 8) {
109 | area = TopRight;
110 | } else if (x >= 0 && x <= 8 && y > 8 && y < (h - 8)) {
111 | area = Left;
112 | } else if (x >=(w - 8) && x <= w && y > 8 && y < (h - 8)) {
113 | area = Right;
114 | } else if (x >= 0 && x <= 8 && y >= (h - 8) && y <= h) {
115 | area = BottomLeft;
116 | } else if (x > 8 && x < (w - 8) && y >= (h - 8) && y <= h) {
117 | area = Bottom;
118 | } else if (x >=(w - 8) && x <= w && y >= (h - 8) && y <= h) {
119 | area = BottomRight;
120 | } else if (MoveArea.contains(x, y)) {
121 | area = Move;
122 | } else {
123 | area = Client;
124 | }
125 |
126 | return area;
127 | }
128 |
129 | void FramelessWindow::setWindowGeometry(const QPoint &pos)
130 | {
131 | QPoint offset = m_startPos - pos;
132 |
133 | if (offset.x() == 0 && offset.y() == 0)
134 | return;
135 |
136 | static auto set_geometry_func = [this](const QSize &size, const QPoint &pos) {
137 | QPoint temp_pos = m_oldPos;
138 | QSize temp_size = minimumSize();
139 | if (size.width() > minimumWidth()) {
140 | temp_pos.setX(pos.x());
141 | temp_size.setWidth(size.width());
142 | } else {
143 | //防止瞬间拉过头,会导致错误的计算位置,这里纠正
144 | if (pos.x() != temp_pos.x())
145 | temp_pos.setX(m_oldPos.x() + m_oldSize.width() - minimumWidth());
146 | }
147 | if (size.height() > minimumHeight()) {
148 | temp_pos.setY(pos.y());
149 | temp_size.setHeight(size.height());
150 | } else {
151 | //防止瞬间拉过头,会导致错误的计算位置,这里纠正
152 | if (pos.y() != temp_pos.y())
153 | temp_pos.setY(m_oldPos.y() + m_oldSize.height() - minimumHeight());
154 | }
155 | setGeometry(QRect(temp_pos, temp_size));
156 | update();
157 | };
158 |
159 | switch (m_currentArea) {
160 | case TopLeft:
161 | set_geometry_func(m_oldSize + QSize(offset.x(), offset.y()), m_oldPos - offset);
162 | break;
163 | case Top:
164 | set_geometry_func(m_oldSize + QSize(0, offset.y()), m_oldPos - QPoint(0, offset.y()));
165 | break;
166 | case TopRight:
167 | set_geometry_func(m_oldSize - QSize(offset.x(), -offset.y()), m_oldPos - QPoint(0, offset.y()));
168 | break;
169 | case Left:
170 | set_geometry_func(m_oldSize + QSize(offset.x(), 0), m_oldPos - QPoint(offset.x(), 0));;
171 | break;
172 | case Right:
173 | set_geometry_func(m_oldSize - QSize(offset.x(), 0), position());
174 | break;
175 | case BottomLeft:
176 | set_geometry_func(m_oldSize + QSize(offset.x(), -offset.y()), m_oldPos - QPoint(offset.x(), 0));
177 | break;
178 | case Bottom:
179 | set_geometry_func(m_oldSize + QSize(0, -offset.y()), position());
180 | break;
181 | case BottomRight:
182 | set_geometry_func(m_oldSize - QSize(offset.x(), offset.y()), position());
183 | break;
184 | default:
185 | break;
186 | }
187 | }
188 |
189 | void FramelessWindow::setCursorIcon()
190 | {
191 | static bool unset = false;
192 |
193 | switch (m_currentArea) {
194 | case TopLeft:
195 | case BottomRight:
196 | unset = true;
197 | setCursor(Qt::SizeFDiagCursor);
198 | break;
199 | case Top:
200 | case Bottom:
201 | unset = true;
202 | setCursor(Qt::SizeVerCursor);
203 | break;
204 | case TopRight:
205 | case BottomLeft:
206 | unset = true;
207 | setCursor(Qt::SizeBDiagCursor);
208 | break;
209 | case Left:
210 | case Right:
211 | unset = true;
212 | setCursor(Qt::SizeHorCursor);
213 | break;
214 | case Move:
215 | unset = true;
216 | setCursor(Qt::ArrowCursor);
217 | break;
218 | default:
219 | if (unset) {
220 | unset = false;
221 | unsetCursor();
222 | }
223 | break;
224 | }
225 | }
226 |
227 |
--------------------------------------------------------------------------------
/src/framelesswindow.h:
--------------------------------------------------------------------------------
1 | #ifndef FRAMELESSWINDOW_H
2 | #define FRAMELESSWINDOW_H
3 |
4 | #include
5 |
6 | class FramelessWindow : public QQuickWindow
7 | {
8 | Q_OBJECT
9 |
10 | Q_PROPERTY(bool movable READ movable WRITE setMovable NOTIFY movableChanged)
11 | Q_PROPERTY(bool resizable READ resizable WRITE setResizable NOTIFY resizableChanged)
12 |
13 | enum MouseArea {
14 | TopLeft = 1,
15 | Top,
16 | TopRight,
17 | Left,
18 | Move,
19 | Right,
20 | BottomLeft,
21 | Bottom,
22 | BottomRight,
23 | Client
24 | };
25 |
26 | public:
27 | explicit FramelessWindow(QWindow *parent = nullptr);
28 |
29 | bool movable() const;
30 | void setMovable(bool arg);
31 |
32 | bool resizable() const;
33 | void setResizable(bool arg);
34 |
35 | protected:
36 | void mousePressEvent(QMouseEvent *event) override;
37 | void mouseReleaseEvent(QMouseEvent *event) override;
38 | void mouseDoubleClickEvent(QMouseEvent *event) override;
39 | void mouseMoveEvent(QMouseEvent *event) override;
40 |
41 | signals:
42 | void movableChanged();
43 | void resizableChanged();
44 |
45 | private:
46 | MouseArea getArea(const QPoint &pos);
47 | void setWindowGeometry(const QPoint &pos);
48 | void setCursorIcon();
49 |
50 | bool m_movable = true;
51 | bool m_resizable = true;
52 | MouseArea m_currentArea = Move;
53 | QPoint m_startPos;
54 | QPoint m_oldPos;
55 | QSize m_oldSize;
56 | };
57 |
58 | #endif
59 |
--------------------------------------------------------------------------------
/src/imageprovider.cpp:
--------------------------------------------------------------------------------
1 | #include "imageprovider.h"
2 |
3 | ImageProvider::ImageProvider()
4 | : QQuickImageProvider(QQuickImageProvider::Pixmap)
5 | {
6 | QPixmap pixmap(100, 100);
7 | pixmap.fill(Qt::white);
8 | m_pixmap = pixmap;
9 | }
10 |
11 | QPixmap ImageProvider::requestPixmap(const QString &id, QSize *size, const QSize &requestedSize)
12 | {
13 | Q_UNUSED(id);
14 | m_pixmap.scaled(requestedSize);
15 | if (size) *size = QSize(requestedSize);
16 |
17 | return m_pixmap;
18 | }
19 |
--------------------------------------------------------------------------------
/src/imageprovider.h:
--------------------------------------------------------------------------------
1 | #ifndef IMAGEPROVIDER_H
2 | #define IMAGEPROVIDER_H
3 |
4 | #include
5 |
6 | class ImageProvider : public QQuickImageProvider
7 | {
8 | public:
9 | ImageProvider();
10 | void setPixmap(const QPixmap &pixmap) { if (!pixmap.isNull()) m_pixmap = pixmap; }
11 | QPixmap requestPixmap(const QString &id, QSize *size, const QSize &requestedSize) override;
12 |
13 | private:
14 | QPixmap m_pixmap;
15 | };
16 |
17 | #endif // IMAGEPROVIDER_H
18 |
--------------------------------------------------------------------------------
/src/main.cpp:
--------------------------------------------------------------------------------
1 | #include "networkapi.h"
2 | #include "controlled.h"
3 | #include "controller.h"
4 | #include "framelesswindow.h"
5 | #include "imageprovider.h"
6 | #include "protocol.h"
7 |
8 | #include
9 | #include
10 | #include
11 |
12 | int main(int argc, char *argv[])
13 | {
14 | QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
15 |
16 | QGuiApplication app(argc, argv);
17 |
18 | #ifdef USE_TCP
19 | qRegisterMetaType("DataBlock");
20 | #endif
21 | qRegisterMetaType("SocketState");
22 | qRegisterMetaType("QHostAddress");
23 | qRegisterMetaType("RemoteEvent");
24 |
25 | qmlRegisterType("an.window", 1, 0, "FramelessWindow");
26 |
27 | NetworkApi *api = new NetworkApi;
28 | Controlled *controlled = new Controlled;
29 | Controller *controller = new Controller;
30 |
31 | QQmlApplicationEngine engine;
32 | engine.rootContext()->setContextProperty("NetworkApi", api);
33 | engine.rootContext()->setContextProperty("controlled", controlled);
34 | engine.rootContext()->setContextProperty("controller", controller);
35 | engine.addImageProvider(QLatin1String("screen"), controller->getImageProvider());
36 | engine.load(QUrl(QStringLiteral("qrc:/qml/main.qml")));
37 | if (engine.rootObjects().isEmpty())
38 | return -1;
39 |
40 | return app.exec();
41 | }
42 |
--------------------------------------------------------------------------------
/src/networkapi.cpp:
--------------------------------------------------------------------------------
1 | #include "networkapi.h"
2 | #include
3 |
4 | NetworkApi::NetworkApi(QObject *parent)
5 | : QObject (parent)
6 | {
7 |
8 | }
9 |
10 | bool NetworkApi::isLocalAddress(const QHostAddress &address)
11 | {
12 | #ifndef Q_OS_ANDROID //安卓需要一些东西
13 | auto interfaces = QNetworkInterface::allInterfaces();
14 | QList entry;
15 | for (auto interface : interfaces) {
16 | if (interface.flags() & (QNetworkInterface::IsUp | QNetworkInterface::IsRunning)) {
17 | entry = interface.addressEntries();
18 | QHostAddress ip = entry.at(1).ip();
19 | if (ip.toIPv4Address() == address.toIPv4Address()) //应对IPv6->IPv4
20 | return true;
21 | entry.clear();
22 | }
23 | }
24 | #endif
25 | return false;
26 | }
27 |
28 | QString NetworkApi::getLocalIpAddress()
29 | {
30 | QString localIp = "0.0.0.0";
31 | #ifndef Q_OS_ANDROID
32 | auto interfaces = QNetworkInterface::allInterfaces();
33 | QList entries;
34 | for (auto interface : interfaces) {
35 | if (interface.flags() & QNetworkInterface::IsLoopBack) //过滤环回地址
36 | continue;
37 |
38 | if (interface.flags() & (QNetworkInterface::IsUp | QNetworkInterface::IsRunning)) {
39 | entries = interface.addressEntries();
40 | for (auto entry : entries) {
41 | if (entry.ip().protocol() == QAbstractSocket::IPv4Protocol) {
42 | if (interface.name().indexOf("wireless") != -1) {
43 | localIp = entry.ip().toString();
44 | qDebug() << interface.humanReadableName() << interface.name() << " 无线网IP: " << localIp;
45 | } else if (interface.name().indexOf("ethernet") != -1) {
46 | //以太网ip暂时不用
47 | qDebug() << interface.humanReadableName() << interface.name() << " 以太网IP: " << entry.ip().toString();
48 | }
49 | }
50 | }
51 | entries.clear();
52 | }
53 | }
54 | #endif
55 | return localIp;
56 | }
57 |
--------------------------------------------------------------------------------
/src/networkapi.h:
--------------------------------------------------------------------------------
1 | #ifndef NETWORKAPI_H
2 | #define NETWORKAPI_H
3 |
4 | #include
5 |
6 | class QHostAddress;
7 | class NetworkApi : public QObject
8 | {
9 | Q_OBJECT
10 |
11 | public:
12 | NetworkApi(QObject *parent = nullptr);
13 |
14 | static bool isLocalAddress(const QHostAddress &address);
15 | Q_INVOKABLE QString getLocalIpAddress();
16 | };
17 |
18 | #endif // NETWORKAPI_H
19 |
--------------------------------------------------------------------------------
/src/remoteevent.h:
--------------------------------------------------------------------------------
1 | #ifndef REMOTEEVENT_H
2 | #define REMOTEEVENT_H
3 |
4 | #include
5 | #include
6 |
7 | class RemoteEvent
8 | {
9 | public:
10 | enum class EventType
11 | {
12 | NoType = 0,
13 | Pressed,
14 | Released,
15 | Moved,
16 | KeyInput
17 | };
18 |
19 | RemoteEvent() { }
20 | RemoteEvent(EventType type, const QPointF &position)
21 | : m_type(type), m_position(position) { }
22 |
23 | EventType type() const { return m_type; }
24 | void setType(EventType type) { m_type = type; }
25 | QPointF position() const { return m_position; }
26 | void setPosition(const QPointF &position) { m_position = position; }
27 |
28 | bool isEmpty() { return m_type == EventType::NoType && m_position.isNull(); }
29 | int size() { return sizeof(m_type) + sizeof(m_position) + sizeof(int); }
30 |
31 | private:
32 | EventType m_type = EventType::NoType;
33 | QPointF m_position;
34 | };
35 |
36 | #endif // REMOTEEVENT_H
37 |
--------------------------------------------------------------------------------
/src/systemapi.cpp:
--------------------------------------------------------------------------------
1 | #include "systemapi.h"
2 |
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 |
10 | #ifdef Q_OS_WIN
11 | # include
12 | # include
13 | # if defined (USE_D3D)
14 | # include
15 | # include
16 | # include
17 | # elif defined (USE_DXGI) && defined (_WIN32_WINNT_WIN10)
18 | # include "dxgimanager.h"
19 | # endif
20 | #endif
21 |
22 | void SystemApi::mousePress(const QPointF &pos)
23 | {
24 | #ifdef Q_OS_WIN
25 | SetCursorPos(int(pos.x()), int(pos.y()));
26 | mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
27 | #endif
28 | }
29 |
30 | void SystemApi::mouseRelease(const QPointF &pos)
31 | {
32 | #ifdef Q_OS_WIN
33 | SetCursorPos(int(pos.x()), int(pos.y()));
34 | mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
35 | #endif
36 | }
37 |
38 | void SystemApi::mouseMove(const QPointF &pos)
39 | {
40 | #ifdef Q_OS_WIN
41 | SetCursorPos(int(pos.x()), int(pos.y()));
42 | mouse_event(MOUSEEVENTF_MOVE, 0, 0, 0, 0);
43 | #endif
44 | }
45 |
46 | QPixmap SystemApi::grabScreen()
47 | {
48 | QPixmap screen;
49 | #if defined (USE_D3D) && defined (Q_OS_WIN)
50 | static bool d3dCreated = false;
51 | static LPDIRECT3D9 lpD3D = nullptr;
52 | static LPDIRECT3DDEVICE9 lpDevice = nullptr;
53 | static D3DDISPLAYMODE ddm;
54 |
55 | if (!d3dCreated) {
56 | lpD3D = Direct3DCreate9(D3D_SDK_VERSION);
57 | lpD3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &ddm);
58 | D3DPRESENT_PARAMETERS d3dpp;
59 | ZeroMemory(&d3dpp, sizeof(D3DPRESENT_PARAMETERS));
60 | d3dpp.Windowed = true;
61 | d3dpp.Flags = D3DPRESENTFLAG_LOCKABLE_BACKBUFFER;
62 | d3dpp.BackBufferFormat = ddm.Format;
63 | d3dpp.BackBufferHeight = ddm.Height;
64 | d3dpp.BackBufferWidth = ddm.Width;
65 | d3dpp.MultiSampleType = D3DMULTISAMPLE_NONE;
66 | d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
67 | d3dpp.hDeviceWindow = GetDesktopWindow();
68 | d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT;
69 | d3dpp.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT;
70 |
71 | lpD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, GetDesktopWindow(),
72 | D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp, &lpDevice);
73 | d3dCreated = true;
74 | qDebug() << ddm.Width << ddm.Height << lpDevice;
75 | }
76 |
77 | if (lpDevice) {
78 | LPDIRECT3DSURFACE9 surface;
79 | lpDevice->CreateOffscreenPlainSurface(ddm.Width, ddm.Height, D3DFMT_A8R8G8B8, D3DPOOL_SCRATCH, &surface, nullptr);
80 | lpDevice->GetFrontBufferData(0, surface);
81 | LPD3DXBUFFER bufferedImage = nullptr;
82 | D3DXSaveSurfaceToFileInMemory(&bufferedImage, D3DXIFF_JPG, surface, nullptr, nullptr);
83 | screen.loadFromData(static_cast(bufferedImage->GetBufferPointer()), bufferedImage->GetBufferSize(), "JPG");
84 | bufferedImage->Release();
85 | surface->Release();
86 | }
87 | #elif defined (USE_GDI) && defined (Q_OS_WIN)
88 | int width = GetSystemMetrics(SM_CXSCREEN);
89 | int height = GetSystemMetrics(SM_CYSCREEN);
90 |
91 | HWND hwnd = GetDesktopWindow();
92 | HDC display_dc = GetDC(nullptr);
93 | HDC bitmap_dc = CreateCompatibleDC(display_dc);
94 | HBITMAP bitmap = CreateCompatibleBitmap(display_dc, width, height);
95 | HGDIOBJ null_bitmap = SelectObject(bitmap_dc, bitmap);
96 |
97 | // copy data
98 | HDC window_dc = GetDC(hwnd);
99 | BitBlt(bitmap_dc, 0, 0, width, height, window_dc, 0, 0, SRCCOPY | CAPTUREBLT);
100 |
101 | // clean up all but bitmap
102 | ReleaseDC(hwnd, window_dc);
103 | SelectObject(bitmap_dc, null_bitmap);
104 | DeleteDC(bitmap_dc);
105 |
106 | screen = QtWin::fromHBITMAP(bitmap);
107 | DeleteObject(bitmap);
108 | ReleaseDC(nullptr, display_dc);
109 | #elif defined (USE_DXGI) && defined (Q_OS_WIN) && defined (_WIN32_WINNT_WIN10)
110 | static DxgiManager dxgiManager;
111 | static bool dxgiInit = false;
112 | if (!dxgiInit) {
113 | if (!dxgiManager.init()) {
114 | qDebug() << "DXGI Init Error :" << dxgiManager.lastError();
115 | } else {
116 | dxgiInit = true;
117 | screen = dxgiManager.grabScreen();
118 | }
119 | } else {
120 | screen = dxgiManager.grabScreen();
121 | }
122 | #else
123 | screen = QGuiApplication::primaryScreen()->grabWindow(0);
124 | #endif
125 | if (!screen.isNull()) {
126 | QPainter painter(&screen);
127 | painter.drawPixmap(QCursor::pos(), grabCursor());
128 | }
129 |
130 | return screen;
131 | }
132 |
133 | QPixmap SystemApi::grabCursor()
134 | {
135 | QPixmap cursorPixmap;
136 | #ifdef Q_OS_WIN
137 | // Get Cursor Size
138 | int cursorWidth = GetSystemMetrics(SM_CXCURSOR);
139 | int cursorHeight = GetSystemMetrics(SM_CYCURSOR);
140 |
141 | // Get your device contexts.
142 | HDC hdcScreen = GetDC(nullptr);
143 | HDC hdcMem = CreateCompatibleDC(hdcScreen);
144 |
145 | // Create the bitmap to use as a canvas.
146 | HBITMAP hbmCanvas = CreateCompatibleBitmap(hdcScreen, cursorWidth, cursorHeight);
147 |
148 | // Select the bitmap into the device context.
149 | HGDIOBJ hbmOld = SelectObject(hdcMem, hbmCanvas);
150 |
151 | // Get information about the global cursor.
152 | CURSORINFO ci;
153 | ci.cbSize = sizeof(ci);
154 | GetCursorInfo(&ci);
155 |
156 | // Draw the cursor into the canvas.
157 | DrawIcon(hdcMem, 0, 0, ci.hCursor);
158 |
159 | // Convert to QPixmap
160 | cursorPixmap = QtWin::fromHBITMAP(hbmCanvas, QtWin::HBitmapAlpha);
161 |
162 | // Clean up after yourself.
163 | SelectObject(hdcMem, hbmOld);
164 | DeleteObject(hbmCanvas);
165 | DeleteDC(hdcMem);
166 | ReleaseDC(nullptr, hdcScreen);
167 | #endif
168 | return cursorPixmap;
169 | }
170 |
--------------------------------------------------------------------------------
/src/systemapi.h:
--------------------------------------------------------------------------------
1 | #ifndef SYSTEMAPI_H
2 | #define SYSTEMAPI_H
3 |
4 | #include
5 |
6 | class SystemApi
7 | {
8 | public:
9 | /** System Event */
10 | static void mousePress(const QPointF &pos);
11 | static void mouseRelease(const QPointF &pos);
12 | static void mouseMove(const QPointF &pos);
13 |
14 | /** System Tools */
15 | static QPixmap grabScreen();
16 | static QPixmap grabCursor();
17 | };
18 |
19 | #endif
20 |
--------------------------------------------------------------------------------
/src/tcp/controlled.cpp:
--------------------------------------------------------------------------------
1 | #include "controlled.h"
2 | #include "remoteevent.h"
3 | #include "systemapi.h"
4 | #include "socket.h"
5 |
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include
11 |
12 | Controlled::Controlled(QObject *parent)
13 | : QTcpServer (parent)
14 | {
15 | listen(QHostAddress::Any, 43800);
16 | }
17 |
18 | Controlled::~Controlled()
19 | {
20 |
21 | }
22 |
23 | void Controlled::finish()
24 | {
25 | if (m_controlled)
26 | QMetaObject::invokeMethod(m_controlled, "abort");
27 | }
28 |
29 | void Controlled::processEvent(const RemoteEvent &ev)
30 | {
31 | QRectF screenRect = qApp->primaryScreen()->geometry();
32 | QPointF localPos(ev.position().x() * screenRect.width(),
33 | ev.position().y() * screenRect.height());
34 |
35 | switch (ev.type())
36 | {
37 | case RemoteEvent::EventType::Pressed:
38 | SystemApi::mousePress(localPos);
39 | break;
40 | case RemoteEvent::EventType::Released:
41 | SystemApi::mouseRelease(localPos);
42 | break;
43 | case RemoteEvent::EventType::Moved:
44 | SystemApi::mouseMove(localPos);
45 | break;
46 | case RemoteEvent::EventType::KeyInput:
47 | break;
48 | default:
49 | break;
50 | }
51 | }
52 |
53 | void Controlled::timerEvent(QTimerEvent *event)
54 | {
55 | Q_UNUSED(event);
56 | if (m_controlled) {
57 | QBuffer buffer;
58 | buffer.open(QIODevice::WriteOnly);
59 | QTime time = QTime::currentTime();
60 | QPixmap pixmap = SystemApi::grabScreen();
61 | qDebug() << time.msecsTo(QTime::currentTime());
62 | pixmap.save(&buffer, "jpg", -1);
63 | BlockHeader header = { SCREEN_TYPE, qint32(buffer.size()) };
64 | DataBlock data = { header, buffer.data() };
65 | QMetaObject::invokeMethod(m_controlled, "writeToSocket", Q_ARG(DataBlock, data));
66 | }
67 | }
68 |
69 | void Controlled::incomingConnection(qintptr socketDescriptor)
70 | {
71 | if (!m_controlled) {
72 | QThread *thread = new QThread;
73 | connect(thread, &QThread::finished, thread, &QThread::deleteLater);
74 | m_controlled = new Socket;
75 | connect(m_controlled, &Socket::stateChanged, this, [this](QAbstractSocket::SocketState socketState) {
76 | switch (socketState)
77 | {
78 | case QAbstractSocket::ConnectedState:
79 | emit connected();
80 | break;
81 | default:
82 | break;
83 | }
84 | });
85 | connect(m_controlled, &Socket::disconnected, this, [this]() {
86 | Socket *socket = m_controlled;
87 | m_controlled = nullptr;
88 | socket->deleteLater();
89 | killTimer(m_timerId);
90 | m_timerId = 0;
91 | emit disconnected();
92 | });
93 | connect(m_controlled, &Socket::hasEventData, this, [this](const RemoteEvent &event) {
94 | processEvent(event);
95 | });
96 | m_controlled->setSocketDescriptor(socketDescriptor);
97 | m_controlled->moveToThread(thread);
98 | thread->start();
99 |
100 | if (!m_timerId)
101 | m_timerId = startTimer(std::chrono::milliseconds(30));
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/src/tcp/controlled.h:
--------------------------------------------------------------------------------
1 | #ifndef CONTROLLED_H
2 | #define CONTROLLED_H
3 |
4 | #include "protocol.h"
5 | #include
6 | #include
7 |
8 | class Socket;
9 | class RemoteEvent;
10 | class Controlled : public QTcpServer
11 | {
12 | Q_OBJECT
13 |
14 | public:
15 | explicit Controlled(QObject *parent = nullptr);
16 | ~Controlled();
17 |
18 | Q_INVOKABLE void finish();
19 |
20 | signals:
21 | void connected();
22 | void disconnected();
23 |
24 | public slots:
25 | void processEvent(const RemoteEvent &ev);
26 |
27 | protected:
28 | void timerEvent(QTimerEvent *event);
29 | void incomingConnection(qintptr socketDescriptor);
30 |
31 | private:
32 | Socket *m_controlled = nullptr;
33 | int m_timerId = 0;
34 | };
35 |
36 | #endif // CONTROLLED_H
37 |
--------------------------------------------------------------------------------
/src/tcp/controller.cpp:
--------------------------------------------------------------------------------
1 | #include "controller.h"
2 | #include "imageprovider.h"
3 | #include "networkapi.h"
4 | #include "socket.h"
5 |
6 | #include
7 | #include
8 | #include
9 |
10 | Controller::Controller(QObject *parent)
11 | : QObject(parent)
12 | {
13 | m_provider = new ImageProvider;
14 |
15 | m_socket = new Socket;
16 | QThread *thread = new QThread;
17 | connect(thread, &QThread::finished, thread, &QThread::deleteLater);
18 | connect(m_socket, &Socket::connected, this, &Controller::connected);
19 | connect(m_socket, &Socket::disconnected, this, &Controller::disconnected);
20 | connect(m_socket, &Socket::hasScreenData, this, [this](const QByteArray &screenData) {
21 | QPixmap pixmap;
22 | pixmap.loadFromData(screenData);
23 | m_provider->setPixmap(pixmap);
24 | emit needUpdate();
25 | });
26 | m_socket->moveToThread(thread);
27 | thread->start();
28 | }
29 |
30 | void Controller::finish()
31 | {
32 | QMetaObject::invokeMethod(m_socket, "abort");
33 | }
34 |
35 | void Controller::mousePressed(const QPointF &position)
36 | {
37 | sendRemoteEvent(RemoteEvent::EventType::Pressed, position);
38 | }
39 |
40 | void Controller::mouseReleased(const QPointF &position)
41 | {
42 | sendRemoteEvent(RemoteEvent::EventType::Released, position);
43 | }
44 |
45 | void Controller::mouseMoved(const QPointF &position)
46 | {
47 | sendRemoteEvent(RemoteEvent::EventType::Moved, position);
48 | }
49 |
50 | void Controller::requestNewConnection(const QString &address)
51 | {
52 | QHostAddress hostAddress(address);
53 | //有效且不为本机地址
54 | if (!hostAddress.isNull() && !NetworkApi::isLocalAddress(hostAddress)) {
55 | QMetaObject::invokeMethod(m_socket, "abort");
56 | QMetaObject::invokeMethod(m_socket, "connectHost", Q_ARG(QHostAddress, hostAddress), Q_ARG(quint16, 43800));
57 | }
58 | }
59 |
60 | void Controller::sendRemoteEvent(RemoteEvent::EventType type, const QPointF &position)
61 | {
62 | RemoteEvent event(type, position);
63 | QMetaObject::invokeMethod(m_socket, "writeToSocket", Q_ARG(RemoteEvent, event));
64 | }
65 |
--------------------------------------------------------------------------------
/src/tcp/controller.h:
--------------------------------------------------------------------------------
1 | #ifndef CONTROLLER_H
2 | #define CONTROLLER_H
3 |
4 | #include "remoteevent.h"
5 | #include
6 |
7 | class Socket;
8 | class ImageProvider;
9 | class Controller : public QObject
10 | {
11 | Q_OBJECT
12 | public:
13 | explicit Controller(QObject *parent = nullptr);
14 |
15 | ImageProvider* getImageProvider() { return m_provider; }
16 |
17 | Q_INVOKABLE void finish();
18 | Q_INVOKABLE void mousePressed(const QPointF &position);
19 | Q_INVOKABLE void mouseReleased(const QPointF &position);
20 | Q_INVOKABLE void mouseMoved(const QPointF &position);
21 | Q_INVOKABLE void requestNewConnection(const QString &address);
22 |
23 | signals:
24 | void connected();
25 | void disconnected();
26 | void needUpdate();
27 |
28 | private:
29 | inline void sendRemoteEvent(RemoteEvent::EventType type, const QPointF &position);
30 |
31 | Socket *m_socket;
32 | ImageProvider *m_provider;
33 | };
34 |
35 | #endif // CONTROLLER_H
36 |
--------------------------------------------------------------------------------
/src/tcp/protocol.cpp:
--------------------------------------------------------------------------------
1 | #include "protocol.h"
2 | #include "remoteevent.h"
3 |
4 | QDataStream& operator>>(QDataStream &in, BlockHeader &header)
5 | {
6 | in >> header.type
7 | >> header.dataSize;
8 |
9 | return in;
10 | }
11 |
12 | QDataStream& operator<<(QDataStream &out, const BlockHeader &header)
13 | {
14 | out << header.type
15 | << header.dataSize;
16 |
17 | return out;
18 | }
19 |
20 | QDataStream& operator>>(QDataStream &in, DataBlock &block)
21 | {
22 | in >> block.header
23 | >> block.data;
24 |
25 | return in;
26 | }
27 |
28 | QDataStream& operator<<(QDataStream &out, const DataBlock &block)
29 | {
30 | out << block.header
31 | << block.data;
32 |
33 | return out;
34 | }
35 |
36 | QDataStream &operator>>(QDataStream &in, RemoteEvent &block)
37 | {
38 | qint32 type;
39 | QPointF position;
40 | in >> type >> position;
41 | block.setType(RemoteEvent::EventType(type));
42 | block.setPosition(position);
43 |
44 | return in;
45 | }
46 |
47 | QDataStream &operator<<(QDataStream &out, const RemoteEvent &block)
48 | {
49 | out << qint32(block.type())
50 | << block.position();
51 |
52 | return out;
53 | }
54 |
55 | QDebug operator<<(QDebug debug, const BlockHeader &header)
56 | {
57 | debug << "[type]: " << header.type << endl
58 | << "[data size]: " << header.dataSize << endl;
59 |
60 | return debug;
61 | }
62 |
63 | QDebug operator<<(QDebug debug, const DataBlock &block)
64 | {
65 | debug << "[type]: " << block.header.type << endl
66 | << "[data size]: " << block.header.dataSize << endl;
67 |
68 | return debug;
69 | }
70 |
--------------------------------------------------------------------------------
/src/tcp/protocol.h:
--------------------------------------------------------------------------------
1 | #ifndef PROTOCOL_H
2 | #define PROTOCOL_H
3 |
4 | #include
5 | #include
6 | #include
7 |
8 | #define INFO_TYPE 0x01
9 | #define EVENT_TYPE 0x02
10 | #define SCREEN_TYPE 0x03
11 |
12 | struct BlockHeader
13 | {
14 | qint8 type;
15 | qint32 dataSize;
16 |
17 | BlockHeader() : type(0), dataSize(0) { }
18 | BlockHeader(qint8 t, qint32 s) : type(t), dataSize(s) { }
19 |
20 | bool isEmpty() const {
21 | return type == 0 && dataSize == 0;
22 | }
23 |
24 | void clear() {
25 | type = dataSize = 0;
26 | }
27 |
28 | int size() const {
29 | return sizeof(type) + sizeof(dataSize) + 4; //data为QByteArray会加4字节
30 | }
31 | };
32 |
33 | struct DataBlock
34 | {
35 | BlockHeader header;
36 | QByteArray data;
37 | };
38 |
39 | class RemoteEvent;
40 |
41 | extern QDataStream& operator>>(QDataStream &in, BlockHeader &header);
42 | extern QDataStream& operator<<(QDataStream &out, const BlockHeader &header);
43 | extern QDataStream& operator>>(QDataStream &in, DataBlock &block);
44 | extern QDataStream& operator<<(QDataStream &out, const DataBlock &block);
45 | extern QDataStream &operator>>(QDataStream &in, RemoteEvent &event);
46 | extern QDataStream &operator<<(QDataStream &out, const RemoteEvent &event);
47 | extern QDebug operator<<(QDebug debug, const BlockHeader &header);
48 | extern QDebug operator<<(QDebug debug, const DataBlock &block);
49 |
50 | #endif // PROTOCOL_H
51 |
--------------------------------------------------------------------------------
/src/tcp/socket.cpp:
--------------------------------------------------------------------------------
1 | #include "remoteevent.h"
2 | #include "socket.h"
3 |
4 | Socket::Socket(QObject *parent)
5 | : QTcpSocket (parent)
6 | {
7 | connect(this, &QTcpSocket::readyRead, this, [this]() {
8 | m_recvData += readAll();
9 | processRecvBlock();
10 | });
11 | }
12 |
13 | Socket::~Socket()
14 | {
15 |
16 | }
17 |
18 | void Socket::abort()
19 | {
20 | QTcpSocket::abort();
21 | m_recvData.clear();
22 | m_recvHeader.clear();
23 | }
24 |
25 | void Socket::writeToSocket(const DataBlock &block)
26 | {
27 | QByteArray data;
28 | QDataStream out(&data, QIODevice::WriteOnly);
29 | out.setVersion(QDataStream::Qt_5_12);
30 | out << block;
31 | write(data);
32 | }
33 |
34 | void Socket::writeToSocket(const RemoteEvent &event)
35 | {
36 | QByteArray data;
37 | QDataStream out(&data, QIODevice::WriteOnly);
38 | out.setVersion(QDataStream::Qt_5_12);
39 | out << event;
40 | BlockHeader header = { EVENT_TYPE, data.size() };
41 | DataBlock block = { header, data };
42 | out.device()->seek(0);
43 | out << block;
44 | write(data);
45 | flush();
46 | }
47 |
48 | void Socket::processRecvBlock()
49 | {
50 | if (m_recvHeader.isEmpty() && m_recvData.size() > 0) {
51 | BlockHeader header;
52 | QDataStream in(&m_recvData, QIODevice::ReadOnly);
53 | in.setVersion(QDataStream::Qt_5_12);
54 | in >> header;
55 |
56 | if (header.isEmpty())
57 | return;
58 |
59 | m_recvHeader = header;
60 | m_recvData.remove(0, header.size());
61 | }
62 |
63 | if (m_recvData.size() < m_recvHeader.dataSize)
64 | return;
65 |
66 | DataBlock block;
67 | block.header = m_recvHeader;
68 | block.data = m_recvData.left(m_recvHeader.dataSize);
69 | m_recvData = m_recvData.mid(m_recvHeader.dataSize);
70 | m_recvHeader.clear();
71 |
72 | if (block.header.type == SCREEN_TYPE) {
73 | emit hasScreenData(block.data);
74 | } else if (block.header.type == EVENT_TYPE) {
75 | RemoteEvent event;
76 | QDataStream in(block.data);
77 | in.setVersion(QDataStream::Qt_5_12);
78 | in >> event;
79 | emit hasEventData(event);
80 | }
81 |
82 | if (m_recvData.size() > 0) //如果还有则继续处理
83 | processRecvBlock();
84 | }
85 |
--------------------------------------------------------------------------------
/src/tcp/socket.h:
--------------------------------------------------------------------------------
1 | #ifndef SOCKET_H
2 | #define SOCKET_H
3 |
4 | #include "protocol.h"
5 | #include
6 |
7 | class RemoteEvent;
8 | class Socket : public QTcpSocket
9 | {
10 | Q_OBJECT
11 | public:
12 | explicit Socket(QObject *parent = nullptr);
13 | ~Socket();
14 |
15 | Q_INVOKABLE void abort();
16 | Q_INVOKABLE void connectHost(const QHostAddress &host, quint16 port) { QTcpSocket::connectToHost(host, port); }
17 | Q_INVOKABLE void writeToSocket(const QByteArray &block) { write(block); }
18 | Q_INVOKABLE void writeToSocket(const DataBlock &block);
19 | Q_INVOKABLE void writeToSocket(const RemoteEvent &event);
20 |
21 | signals:
22 | void hasScreenData(const QByteArray &screenData);
23 | void hasEventData(const RemoteEvent &event);
24 |
25 | private:
26 | void processRecvBlock();
27 |
28 | QByteArray m_recvData;
29 | BlockHeader m_recvHeader;
30 | };
31 |
32 | #endif // SOCKET_H
33 |
--------------------------------------------------------------------------------
/src/udp/connection.cpp:
--------------------------------------------------------------------------------
1 | #include "connection.h"
2 | #include "protocol.h"
3 | #include "remoteevent.h"
4 |
5 | Connection::Connection(QObject *parent)
6 | : QTcpSocket (parent)
7 | {
8 | connect(this, &QTcpSocket::readyRead, this, [this]() {
9 | m_recvData += readAll();
10 | processRecvData();
11 | });
12 | }
13 |
14 | void Connection::writeToSocket(const RemoteEvent &event)
15 | {
16 | QByteArray data;
17 | QDataStream out(&data, QIODevice::WriteOnly);
18 | out.setVersion(QDataStream::Qt_5_12);
19 | out << event;
20 | write(data);
21 | flush();
22 | }
23 |
24 | void Connection::processRecvData()
25 | {
26 | RemoteEvent event;
27 | QDataStream in(&m_recvData, QIODevice::ReadOnly);
28 | in.setVersion(QDataStream::Qt_5_12);
29 | in >> event;
30 |
31 | if (!event.isEmpty()) {
32 | m_recvData.remove(0, event.size());
33 | emit hasEventData(event);
34 | }
35 |
36 | if (m_recvData.size() > 0)
37 | processRecvData();
38 | }
39 |
--------------------------------------------------------------------------------
/src/udp/connection.h:
--------------------------------------------------------------------------------
1 | #ifndef CONNECTION_H
2 | #define CONNECTION_H
3 |
4 | #include
5 |
6 | class RemoteEvent;
7 | class Connection : public QTcpSocket
8 | {
9 | Q_OBJECT
10 |
11 | public:
12 | Connection(QObject *parent = nullptr);
13 |
14 | Q_INVOKABLE void abort() { QTcpSocket::abort(); }
15 | Q_INVOKABLE void writeToSocket(const RemoteEvent &event);
16 |
17 | signals:
18 | void hasEventData(const RemoteEvent &event);
19 |
20 | private:
21 | void processRecvData();
22 |
23 | QByteArray m_recvData;
24 | };
25 |
26 | #endif // CONNECTION_H
27 |
--------------------------------------------------------------------------------
/src/udp/controlled.cpp:
--------------------------------------------------------------------------------
1 | #include "controlled.h"
2 | #include "connection.h"
3 | #include "remoteevent.h"
4 | #include "systemapi.h"
5 | #include "socket.h"
6 |
7 | #include
8 | #include
9 | #include
10 | #include
11 |
12 | Controlled::Controlled(QObject *parent)
13 | : QTcpServer (parent)
14 | {
15 | listen(QHostAddress::Any, 43801);
16 | m_screenSocket = new Socket;
17 | QThread *thread = new QThread;
18 | connect(thread, &QThread::finished, thread, &QThread::deleteLater);
19 | m_screenSocket->moveToThread(thread);
20 | thread->start();
21 | }
22 |
23 | Controlled::~Controlled()
24 | {
25 |
26 | }
27 |
28 | void Controlled::finish()
29 | {
30 | if (m_connection)
31 | m_connection->disconnectFromHost();
32 | }
33 |
34 | void Controlled::processEvent(const RemoteEvent &ev)
35 | {
36 | QRectF screenRect = qApp->primaryScreen()->geometry();
37 | QPointF localPos(ev.position().x() * screenRect.width(),
38 | ev.position().y() * screenRect.height());
39 |
40 | switch (ev.type())
41 | {
42 | case RemoteEvent::EventType::Pressed:
43 | SystemApi::mousePress(localPos);
44 | break;
45 | case RemoteEvent::EventType::Released:
46 | SystemApi::mouseRelease(localPos);
47 | break;
48 | case RemoteEvent::EventType::Moved:
49 | SystemApi::mouseMove(localPos);
50 | break;
51 | case RemoteEvent::EventType::KeyInput:
52 | break;
53 | default:
54 | break;
55 | }
56 | }
57 |
58 | void Controlled::timerEvent(QTimerEvent *event)
59 | {
60 | Q_UNUSED(event);
61 | if (m_screenSocket) {
62 | QBuffer buffer;
63 | buffer.open(QIODevice::WriteOnly);
64 | QPixmap pixmap = SystemApi::grabScreen();
65 | pixmap.save(&buffer, "jpg", -1);
66 | QMetaObject::invokeMethod(m_screenSocket, "writeToSocket", Q_ARG(QByteArray, buffer.data()));
67 | }
68 | }
69 |
70 | void Controlled::incomingConnection(qintptr socketDescriptor)
71 | {
72 | if (!m_connection) {
73 | m_connection = new Connection(this);
74 | connect(m_connection, &Connection::stateChanged, this, [this](QAbstractSocket::SocketState socketState) {
75 | switch (socketState)
76 | {
77 | case QAbstractSocket::ConnectedState:
78 | emit connected();
79 | break;
80 | default:
81 | break;
82 | }
83 | });
84 | connect(m_connection, &Connection::disconnected, this, [this]() {
85 | m_connection->deleteLater();
86 | m_connection = nullptr;
87 | QMetaObject::invokeMethod(m_screenSocket, "finish");
88 | killTimer(m_timerId);
89 | m_timerId = 0;
90 | emit disconnected();
91 | });
92 | connect(m_connection, &Connection::hasEventData, this, [this](const RemoteEvent &event) {
93 | processEvent(event);
94 | });
95 | m_connection->setSocketDescriptor(socketDescriptor);
96 | QMetaObject::invokeMethod(m_screenSocket, "setDestAddr",
97 | Q_ARG(QHostAddress, QHostAddress(m_connection->peerAddress())));
98 |
99 | if (!m_timerId)
100 | m_timerId = startTimer(std::chrono::milliseconds(30));
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/src/udp/controlled.h:
--------------------------------------------------------------------------------
1 | #ifndef CONTROLLED_H
2 | #define CONTROLLED_H
3 |
4 | #include
5 | #include
6 |
7 | class Socket;
8 | class RemoteEvent;
9 | class Connection;
10 | class Controlled : public QTcpServer
11 | {
12 | Q_OBJECT
13 |
14 | public:
15 | explicit Controlled(QObject *parent = nullptr);
16 | ~Controlled();
17 |
18 | Q_INVOKABLE void finish();
19 |
20 | signals:
21 | void connected();
22 | void disconnected();
23 |
24 | public slots:
25 | void processEvent(const RemoteEvent &ev);
26 |
27 | protected:
28 | void timerEvent(QTimerEvent *event);
29 | void incomingConnection(qintptr socketDescriptor);
30 |
31 | private:
32 | int m_timerId = 0;
33 | Socket *m_screenSocket = nullptr;
34 | Connection *m_connection = nullptr;
35 | };
36 |
37 | #endif // CONTROLLED_H
38 |
--------------------------------------------------------------------------------
/src/udp/controller.cpp:
--------------------------------------------------------------------------------
1 | #include "controller.h"
2 | #include "connection.h"
3 | #include "imageprovider.h"
4 | #include "networkapi.h"
5 | #include "socket.h"
6 |
7 | #include
8 | #include
9 | #include
10 |
11 | Controller::Controller(QObject *parent)
12 | : QObject(parent)
13 | {
14 | m_provider = new ImageProvider;
15 | m_socket = new Socket;
16 | m_socket->bind(QHostAddress::Any, 43800);
17 | QThread *thread = new QThread;
18 | connect(thread, &QThread::finished, thread, &QThread::deleteLater);
19 | connect(m_socket, &Socket::hasScreenData, this, [this](const QByteArray &screenData) {
20 | QPixmap pixmap;
21 | pixmap.loadFromData(screenData);
22 | m_provider->setPixmap(pixmap);
23 | emit needUpdate();
24 | });
25 | m_socket->moveToThread(thread);
26 | thread->start();
27 |
28 | m_connection = new Connection(this);
29 | connect(m_connection, &Connection::connected, this, &Controller::connected);
30 | connect(m_connection, &Connection::disconnected, this, &Controller::disconnected);
31 | }
32 |
33 | void Controller::finish()
34 | {
35 | m_connection->abort();
36 | QMetaObject::invokeMethod(m_socket, "finish");
37 | }
38 |
39 | void Controller::mousePressed(const QPointF &position)
40 | {
41 | sendRemoteEvent(RemoteEvent::EventType::Pressed, position);
42 | }
43 |
44 | void Controller::mouseReleased(const QPointF &position)
45 | {
46 | sendRemoteEvent(RemoteEvent::EventType::Released, position);
47 | }
48 |
49 | void Controller::mouseMoved(const QPointF &position)
50 | {
51 | sendRemoteEvent(RemoteEvent::EventType::Moved, position);
52 | }
53 |
54 | void Controller::requestNewConnection(const QString &address)
55 | {
56 | m_connection->abort();
57 | QHostAddress hostAddress(address);
58 | //有效且不为本机地址
59 | if (!hostAddress.isNull() && !NetworkApi::isLocalAddress(hostAddress)){
60 | m_connection->connectToHost(address, 43801);
61 | QMetaObject::invokeMethod(m_socket, "setDestAddr", Q_ARG(QHostAddress, QHostAddress(address)));
62 | }
63 | }
64 |
65 | void Controller::sendRemoteEvent(RemoteEvent::EventType type, const QPointF &position)
66 | {
67 | RemoteEvent event(type, position);
68 | m_connection->writeToSocket(event);
69 | }
70 |
--------------------------------------------------------------------------------
/src/udp/controller.h:
--------------------------------------------------------------------------------
1 | #ifndef CONTROLLER_H
2 | #define CONTROLLER_H
3 |
4 | #include "remoteevent.h"
5 | #include
6 |
7 | class Socket;
8 | class Connection;
9 | class ImageProvider;
10 | class Controller : public QObject
11 | {
12 | Q_OBJECT
13 | public:
14 | explicit Controller(QObject *parent = nullptr);
15 |
16 | ImageProvider* getImageProvider() { return m_provider; }
17 |
18 | Q_INVOKABLE void finish();
19 | Q_INVOKABLE void mousePressed(const QPointF &position);
20 | Q_INVOKABLE void mouseReleased(const QPointF &position);
21 | Q_INVOKABLE void mouseMoved(const QPointF &position);
22 | Q_INVOKABLE void requestNewConnection(const QString &address);
23 |
24 | signals:
25 | void connected();
26 | void disconnected();
27 | void needUpdate();
28 |
29 | private:
30 | inline void sendRemoteEvent(RemoteEvent::EventType type, const QPointF &position);
31 |
32 | Socket *m_socket;
33 | Connection *m_connection;
34 | ImageProvider *m_provider;
35 | };
36 |
37 | #endif // CONTROLLER_H
38 |
--------------------------------------------------------------------------------
/src/udp/protocol.cpp:
--------------------------------------------------------------------------------
1 | #include "protocol.h"
2 | #include "remoteevent.h"
3 |
4 | QDataStream &operator>>(QDataStream &in, DataBlock &block)
5 | {
6 | in >> block.blockSize
7 | >> block.blockIndex
8 | >> block.blockNum
9 | >> block.data;
10 |
11 | return in;
12 | }
13 |
14 | QDataStream &operator<<(QDataStream &out, const DataBlock &block)
15 | {
16 | out << block.blockSize
17 | << block.blockIndex
18 | << block.blockNum
19 | << block.data;
20 |
21 | return out;
22 | }
23 |
24 | QDataStream &operator>>(QDataStream &in, RemoteEvent &block)
25 | {
26 | qint32 type;
27 | QPointF position;
28 | in >> type >> position;
29 | block.setType(RemoteEvent::EventType(type));
30 | block.setPosition(position);
31 |
32 | return in;
33 | }
34 |
35 | QDataStream &operator<<(QDataStream &out, const RemoteEvent &block)
36 | {
37 | out << qint32(block.type())
38 | << block.position();
39 |
40 | return out;
41 | }
42 |
43 | QDebug operator<<(QDebug debug, const DataBlock &block)
44 | {
45 | debug << "[blockSize]: " << block.blockSize << endl
46 | << "[blockIndex]: " << block.blockIndex << endl
47 | << "[blockNum]: " << block.blockNum << endl
48 | << "[data size]: " << block.data.size() << endl;
49 |
50 | return debug;
51 | }
52 |
--------------------------------------------------------------------------------
/src/udp/protocol.h:
--------------------------------------------------------------------------------
1 | #ifndef PROTOCOL_UDP_H
2 | #define PROTOCOL_UDP_H
3 |
4 | #include
5 | #include
6 | #include
7 |
8 | struct DataBlock
9 | {
10 | qint32 blockSize; //总大小
11 | qint32 blockIndex; //索引
12 | qint32 blockNum; //被分成的块数
13 | QByteArray data; //块数据
14 |
15 | DataBlock()
16 | : blockSize(0), blockIndex(0)
17 | , blockNum(0), data(QByteArray()) { }
18 |
19 | int size() const {
20 | return int(sizeof (blockIndex)) +
21 | int(sizeof (blockSize)) +
22 | int(sizeof (blockNum)) +
23 | data.size() + 4;
24 | }
25 | };
26 |
27 | class RemoteEvent;
28 |
29 | extern QDataStream &operator>>(QDataStream &in, DataBlock &block);
30 | extern QDataStream &operator<<(QDataStream &out, const DataBlock &block);
31 | extern QDataStream &operator>>(QDataStream &in, RemoteEvent &event);
32 | extern QDataStream &operator<<(QDataStream &out, const RemoteEvent &event);
33 | extern QDebug operator<<(QDebug debug, const DataBlock &block);
34 |
35 | #endif // PROTOCOL_UDP_H
36 |
--------------------------------------------------------------------------------
/src/udp/socket.cpp:
--------------------------------------------------------------------------------
1 | #include "remoteevent.h"
2 | #include "socket.h"
3 |
4 | #include
5 | #include
6 | #include
7 |
8 | static const int maxBlockSize = 4096;
9 |
10 | Socket::Socket(QObject *parent)
11 | : QUdpSocket (parent)
12 | {
13 | connect(this, &QUdpSocket::readyRead, this, &Socket::processRecvData);
14 | }
15 |
16 | Socket::~Socket()
17 | {
18 |
19 | }
20 |
21 | void Socket::finish()
22 | {
23 | m_destAddr = QHostAddress();
24 | }
25 |
26 | void Socket::writeToSocket(const QByteArray &d)
27 | {
28 | static QTime time = QTime::currentTime();
29 | static int frame = 0;
30 | if (time.msecsTo(QTime::currentTime()) > 1000) {
31 | qDebug().nospace() << "发送速率:" << frame << "/s";
32 | frame = 0;
33 | time = QTime::currentTime();
34 | }
35 |
36 | if (!m_destAddr.isNull()) {
37 | int currentIndex = 0;
38 | int blockOffset = 0;
39 | int blockSize = d.size();
40 | int blockNum = blockSize / maxBlockSize;
41 | int last = blockSize % maxBlockSize;
42 | if (last != 0) blockNum++;
43 |
44 | QByteArray data;
45 | QDataStream out(&data, QIODevice::WriteOnly);
46 | out.setVersion(QDataStream::Qt_5_12);
47 |
48 | while (currentIndex < blockNum) {
49 | DataBlock block;
50 | block.blockIndex = currentIndex + 1;
51 | block.blockNum = blockNum;
52 | block.blockSize = blockSize;
53 | block.data = d.mid(blockOffset, maxBlockSize);
54 |
55 | out.device()->seek(0);
56 | out << block;
57 | writeDatagram(data, m_destAddr, 43800);
58 | QThread::usleep(1); //避免发送过快
59 |
60 | data.clear();
61 | blockOffset += maxBlockSize;
62 | currentIndex++;
63 | }
64 | frame++;
65 | }
66 | }
67 |
68 | void Socket::processRecvData()
69 | {
70 | static int currentIndex = 1;
71 | static int blockSize = 0;
72 | static QByteArray recvData;
73 |
74 | while (hasPendingDatagrams()) {
75 | QNetworkDatagram datagram = receiveDatagram();
76 | QByteArray data = datagram.data();
77 |
78 | DataBlock block;
79 | QDataStream in(&data, QIODevice::ReadOnly);
80 | in >> block;
81 |
82 | if (currentIndex == 1)
83 | recvData.resize(block.blockSize);
84 |
85 | if (currentIndex == block.blockIndex) {
86 | currentIndex++;
87 | blockSize += block.data.size();
88 | recvData.insert((block.blockIndex - 1) * maxBlockSize, block.data);
89 |
90 | if (block.blockIndex == block.blockNum && blockSize == block.blockSize) {
91 | emit hasScreenData(recvData);
92 |
93 | currentIndex = 1;
94 | blockSize = 0;
95 | recvData.clear();
96 | }
97 | } else {
98 | currentIndex = 1;
99 | blockSize = 0;
100 | recvData.clear();
101 | }
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/src/udp/socket.h:
--------------------------------------------------------------------------------
1 | #ifndef SOCKET_UDP_H
2 | #define SOCKET_UDP_H
3 |
4 | #include "protocol.h"
5 | #include
6 |
7 | class RemoteEvent;
8 | class Socket : public QUdpSocket
9 | {
10 | Q_OBJECT
11 |
12 | public:
13 | Socket(QObject *parent = nullptr);
14 | ~Socket();
15 |
16 | QHostAddress destAddr() const { return m_destAddr; }
17 | Q_INVOKABLE void setDestAddr(const QHostAddress &addr) { m_destAddr = addr; }
18 |
19 | Q_INVOKABLE void finish();
20 | Q_INVOKABLE void writeToSocket(const QByteArray &d);
21 |
22 | signals:
23 | void hasScreenData(const QByteArray &screenData);
24 |
25 | private slots:
26 | void processRecvData();
27 |
28 | private:
29 | QHostAddress m_destAddr;
30 | };
31 |
32 | #endif // SOCKET_UDP_H
33 |
--------------------------------------------------------------------------------