78 Commits

Author SHA1 Message Date
dijunkun
95a014a601 Not allow clicking connect button if remote id is empty 2024-06-14 17:01:58 +08:00
dijunkun
34d6bac345 Update Windows platform layout style 2024-06-14 16:02:55 +08:00
dijunkun
399785409c Use element style header file to control layout 2024-06-14 15:37:16 +08:00
dijunkun
5f1d9b6912 Fix language localization error 2024-06-14 13:55:05 +08:00
dijunkun
d963a0cf38 1.The mouse control button allowed to be clicked Only when connection established; 2.Fix display resolution error after exit fullscreen 2024-06-14 10:48:35 +08:00
dijunkun
f9c1bc48b4 Add control/release mouse button 2024-06-13 17:36:02 +08:00
dijunkun
2906d05a4b Do not send mouse click event when cursor hovers over subwindows 2024-06-13 16:56:03 +08:00
dijunkun
053a0f86ad Add mouse control flag 2024-06-13 16:36:11 +08:00
dijunkun
6c2363b239 Clear render buffer when connection closed 2024-06-13 16:30:25 +08:00
dijunkun
167514fed8 Do not collapse menu window when connection established 2024-06-13 16:07:22 +08:00
dijunkun
342eb0c386 Reset connection_established_ flag when connection closed 2024-06-13 16:06:02 +08:00
dijunkun
52c7099dbe Fix crash when connecting to local desk 2024-06-13 15:49:26 +08:00
dijunkun
12faf7cd2d Do not reset is_create_connection_ when click disconnect button 2024-06-07 18:13:09 +08:00
dijunkun
6d921a3309 Fix server mode screen capture error 2024-06-07 16:30:18 +08:00
dijunkun
5a690ebbb6 Do not use 'S-' or 'C-' as the prefix for the user id of a peer 2024-06-07 16:27:05 +08:00
dijunkun
4b3839aa34 Only server can capture screen and control mouse 2024-06-07 14:07:22 +08:00
dijunkun
efb165b56f 1.Add CreateConnectionPeer method in order to recreate peer instance; 2.Fix settings OK/Cancel button position 2024-06-06 17:19:16 +08:00
dijunkun
0047b4ecc5 Recreate peer instance after settings changed 2024-06-06 15:11:02 +08:00
dijunkun
844710af7c Fix settings button value mismatch 2024-06-06 14:34:16 +08:00
dijunkun
562d54090a Use 'ImGuiWindowFlags_NoSavedSettings' for settings window 2024-06-06 09:57:37 +08:00
dijunkun
f7fd37651e Reset settings window position before it is opened 2024-06-06 09:55:08 +08:00
dijunkun
280f59f97d Enable movement of settings window 2024-06-05 17:33:42 +08:00
dijunkun
0683ad9d27 Use Combo instead of RadioButton for settings 2024-06-05 17:30:23 +08:00
dijunkun
e061e3b4d7 Support read configure params from input directly 2024-06-04 17:38:55 +08:00
dijunkun
eaedcb8d06 Fix crash caused by the release of screen capturer 2024-06-04 16:27:34 +08:00
dijunkun
e7e6380adc Start capturing screen when connection established 2024-06-03 23:48:53 +08:00
dijunkun
1f50483b50 Save settings into cache file 2024-06-03 17:02:20 +08:00
dijunkun
6f703c8267 Use OOP to refactor main function 2024-06-03 15:23:37 +08:00
dijunkun
d150c374b5 Reset main window width/height ratio automatically when width/height is changed 2024-06-03 11:30:55 +08:00
dijunkun
f29b2ee09d Test user date in peer instance and callback functions 2024-05-30 17:27:49 +08:00
dijunkun
0a934e8c01 Fix LNK1561 error on Winodws 2024-05-30 16:33:50 +08:00
dijunkun
2163aa87d4 The connection can use only one peer to realize server and client 2024-05-30 16:12:53 +08:00
dijunkun
5d8408d892 Use abstraction to refactor remote desk gui 2024-05-29 17:33:41 +08:00
dijunkun
93d0e3a5d0 Auto collapse menu bar when connection established 2024-05-28 17:37:06 +08:00
dijunkun
b4a5e91bc9 Support fullscreen 2024-05-28 16:27:04 +08:00
dijunkun
759078ef7f 1.Use PingFang.ttc as Chinese default fonts on MacOS;2.Fix link error for FFmpeg 2024-05-28 15:25:16 +08:00
dijunkun
905539a6eb 1.Use Windows font file 'simhei.ttf'; 2.Add test button for fullscreen 2024-05-27 17:06:09 +08:00
dijunkun
f1512812ad Support localization(Simplified Chinese[source-han-sans-regular]) 2024-05-24 17:15:44 +08:00
dijunkun
5f1cf89649 Fix crash during termination on windows 2024-05-24 15:48:37 +08:00
dijunkun
f291ad189a 1.Remove sdl2 from thirdparty since it is already required by imgui; 2.Update imgui to v1.90.6; 3.Update spdlog to v 1.14.1 2024-05-24 15:10:58 +08:00
dijunkun
8807636372 Fix crash caused by screen capturer or mouse controller init failed on Linux and MacOS 2024-05-23 15:48:10 +08:00
dijunkun
70be1d8afc Update submodule projectx 2024-05-22 16:39:27 +08:00
dijunkun
1f76aa427d Change submodule projectx url 2024-05-22 16:35:30 +08:00
dijunkun
134cbf8b75 Restore ffmpeg dependency for Linux and MacOS due to screen capture needs ffmpeg on these platforms 2024-05-22 14:06:12 +08:00
dijunkun
669b944cfd Remove resampling process during SDL2 capture audio stream 2024-05-21 17:01:08 +08:00
dijunkun
9962829885 Fix ARGB to NV12 error caused by uv stride 2024-05-20 10:47:44 +08:00
dijunkun
1393615f01 Remove dependency on FFmpeg 2024-05-17 17:55:57 +08:00
dijunkun
d58ae3a6b1 Fix AV1 codec on MacOS 2024-05-10 14:45:12 +08:00
dijunkun
a188729af6 Support AV1 stream transmitting over RTP 2024-05-09 17:05:38 +08:00
dijunkun
422478bd9a Support AV1 codec 2024-04-19 17:37:00 +08:00
dijunkun
d8980f0082 WGC screen capturer needs c++17 or above 2023-12-22 16:07:32 +08:00
dijunkun
e88bb017fa 1.Using c++14; 2.Using {} to initialize std::atomic 2023-12-22 15:46:54 +08:00
dijunkun
87466d6074 Update thirdparty library 2023-12-22 14:14:08 +08:00
Di Junkun
fbbbfc5e6a Update README_CN.md 2023-12-21 15:02:12 +08:00
Di Junkun
d2cefd1827 Update README.md 2023-12-21 14:59:44 +08:00
Di Junkun
a350e06529 Update README.md 2023-12-21 14:56:18 +08:00
Di Junkun
8c742ffa08 Update README.md 2023-12-21 14:34:11 +08:00
Di Junkun
475005b8a4 Update README.md 2023-12-20 15:54:54 +08:00
dijunkun
4da5188759 Update README.md 2023-12-20 15:19:44 +08:00
Di Junkun
d8df4df5ae Update README.md 2023-12-20 15:17:53 +08:00
dijunkun
fa05bbc8f8 Update README.md 2023-12-20 13:00:39 +08:00
Di Junkun
927f1a6d49 Update README.md 2023-12-20 09:34:43 +08:00
dijunkun
1d87f61d9f Update README.md 2023-12-19 17:33:16 +08:00
dijunkun
ce546b77f5 Hide cursor when remote peer connected 2023-12-18 16:52:49 +08:00
dijunkun
b9e69cde51 Remove unused variables 2023-12-18 16:17:55 +08:00
dijunkun
e3987b4a42 Fix remote action type undefined error 2023-12-18 16:06:53 +08:00
dijunkun
0034359431 Update submodule 2023-12-18 15:27:11 +08:00
dijunkun
cec275bbe0 Add 1 second time interval for retry join transmission 2023-12-18 15:10:41 +08:00
dijunkun
fe68464cd2 Fix remote id hint error 2023-12-18 13:43:08 +08:00
dijunkun
181c473625 Use SDL to get screen resolution 2023-12-18 11:15:55 +08:00
dijunkun
2fc89923ae Use factory method to create screen capturer 2023-12-15 17:29:17 +08:00
dijunkun
9276d5bfec Fix compile error 2023-12-14 17:14:52 +08:00
dijunkun
95ef8fe8b9 Use factory method to create mouse controller on MacOS 2023-12-14 17:02:43 +08:00
dijunkun
3ab0e0136e Use factory method to create mouse controller on Windows 2023-12-14 16:40:10 +08:00
dijunkun
daecc0d1e9 Use factory method to create mouse controller on Linux 2023-12-14 16:27:13 +08:00
dijunkun
bfecf47226 Fix cross platform compile error 2023-12-13 17:32:02 +08:00
dijunkun
09c42dd7ed 1.Add linux mouse control test;2.Fix unused variables warnning 2023-12-13 17:26:08 +08:00
dijunkun
a8a3b88514 Use uinput to control mouse and keyboard on Linux 2023-12-07 19:00:34 -08:00
48 changed files with 3694 additions and 1540 deletions

2
.gitmodules vendored
View File

@@ -1,3 +1,3 @@
[submodule "thirdparty/projectx"]
path = thirdparty/projectx
url = git@github.com:dijunkun/projectx.git
url = https://github.com/dijunkun/projectx.git

20
LICENSE Normal file
View File

@@ -0,0 +1,20 @@
The MIT License (MIT)
Copyright (c) 2023 The Continuous Desk Authors
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

67
README.md Normal file
View File

@@ -0,0 +1,67 @@
# Continuous Desk
#### More than remote desktop
----
[中文](README_CN.md) / [English](README.md)
![sup_example](https://github.com/dijunkun/continuous-desk/assets/29698109/46536bc8-3ddd-438d-bf52-dccf143f1c20)
# Intro
Continuous Desk is a lightweight cross-platform remote desktop. It allows multiple users to remotely control the same computer at the same time. In addition to desktop image transmission, it also supports end-to-end voice transmission, providing collaboration capabilities on the basis of remote desktop.
Continuous Desk is an experimental application of [Projectx](https://github.com/dijunkun/projectx) real-time communications library. Projectx is a lightweight cross-platform real-time communications library. It has basic capabilities such as network traversal ([RFC5245](https://datatracker.ietf.org/doc/html/rfc5245)), video software/hardware encoding/decoding (H264), audio encoding/decoding ([Opus](https://github.com/xiph/opus)), signaling interaction, and network congestion control ([TCP over UDP](https://libnice.freedesktop.org/)).
## Usage
Enter the remote desktop ID in the 'REMOTE ID' field on the menu bar, and click 'Connect' button to initiate the remote connection.
![usage1](https://github.com/dijunkun/continuous-desk/assets/29698109/2ad59e6d-bdba-46d0-90cf-cbc9c06c2278)
If the remote desktop is set with a connection password, the local end needs to enter the correct password to initiate the remote connection. If the password is incorrect, an "Incorrect password" alert will appear in the status bar.
![incorrect password](https://github.com/dijunkun/continuous-desk/assets/29698109/cb05501c-ec4e-4adf-952d-7a55ef770a97)
After connection successfully established, the status bar will display the message "ClientConnected."
![success](https://github.com/dijunkun/continuous-desk/assets/29698109/0cca21f7-48fe-44a5-b83d-eafeb8a81eb1)
## How to build
Requirements:
- [xmake](https://xmake.io/#/guide/installation)
- [cmake](https://cmake.org/download/)
- [vcpkg](https://vcpkg.io/en/getting-started)
Following packages need to be installed on Linux:
```
sudo apt-get install -y nvidia-cuda-toolkit libxcb-randr0-dev libxcb-xtest0-dev libxcb-xinerama0-dev libxcb-shape0-dev libxcb-xkb-dev libxcb-xfixes0-dev libxcb-shm0-dev libxv-dev libasound2-dev libsndio-dev libasound2-dev libpulse-dev
```
Commands:
```
git clone https://github.com/dijunkun/continuous-desk
cd continuous-desk
git submodule init
git submodule update
xmake b remote_desk
```
Run:
```
# Windows/MacOS
xmake r remote_desk
# root privileges are required on Linux
./remote_desk
```
## LICENSE
Continuous Desk is licenced under MIT, and some third-party libraries are distributed under their licenses.

67
README_CN.md Normal file
View File

@@ -0,0 +1,67 @@
# Continuous Desk
#### 不止远程桌面
----
[English](README.md) / [中文](README_CN.md)
![sup_example](https://github.com/dijunkun/continuous-desk/assets/29698109/46536bc8-3ddd-438d-bf52-dccf143f1c20)
## 简介
Continuous Desk 是一个轻量级的跨平台远程桌面软件。它允许多个用户在同一时间远程操控同一台电脑。除桌面图像传输外,它还支持端到端的语音传输,在远程桌面基础上提供额外的协作能力。
Continuous Desk 是 [Projectx](https://github.com/dijunkun/projectx) 实时音视频传输库的实验性应用。Projectx 是一个轻量级的跨平台实时音视频传输库。它具有网络透传([RFC5245](https://datatracker.ietf.org/doc/html/rfc5245)视频软硬编解码H264音频编解码[Opus](https://github.com/xiph/opus)),信令交互,网络拥塞控制([TCP over UDP](https://libnice.freedesktop.org/))等基础能力。
## 使用
在菜单栏“REMOTE ID”处输入远端桌面的ID点击“Connect”即可发起远程连接。
![usage1](https://github.com/dijunkun/continuous-desk/assets/29698109/2ad59e6d-bdba-46d0-90cf-cbc9c06c2278)
如果远端桌面设置了连接密码则本端需填写正确的连接密码才能成功发起远程连接。密码错误时状态栏会出现“Incorrect password”告警提示。
![incorrect password](https://github.com/dijunkun/continuous-desk/assets/29698109/cb05501c-ec4e-4adf-952d-7a55ef770a97)
连接成功建立后状态栏会有“ClientConnected”相关字样。
![success](https://github.com/dijunkun/continuous-desk/assets/29698109/0cca21f7-48fe-44a5-b83d-eafeb8a81eb1)
## 编译
依赖:
- [xmake](https://xmake.io/#/guide/installation)
- [cmake](https://cmake.org/download/)
- [vcpkg](https://vcpkg.io/en/getting-started)
Linux环境下需安装以下包
```
sudo apt-get install -y nvidia-cuda-toolkit libxcb-randr0-dev libxcb-xtest0-dev libxcb-xinerama0-dev libxcb-shape0-dev libxcb-xkb-dev libxcb-xfixes0-dev libxcb-shm0-dev libxv-dev libasound2-dev libsndio-dev libasound2-dev libpulse-dev
```
编译命令
```
git clone https://github.com/dijunkun/continuous-desk
cd continuous-desk
git submodule init
git submodule update
xmake b remote_desk
```
运行
```
# Windows/MacOS
xmake r remote_desk
# Linux下需使用root权限运行
./remote_desk
```
## 许可证
Continuous Desk 使用 MIT 许可证,其中使用到的第三方库根据自身许可证进行分发。

View File

@@ -1,16 +1,19 @@
[signal server]
ip = 120.77.216.215
ip = 150.158.81.30
port = 9099
[stun server]
ip = 120.77.216.215
ip = 150.158.81.30
port = 3478
[turn server]
ip = 120.77.216.215
ip = 150.158.81.30
port = 3478
username = dijunkun
password = dijunkunpw
[hardware acceleration]
turn_on = false
[av1 encoding]
turn_on = true

BIN
icon/捕获.PNG Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

BIN
icon/捕获.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

101
src/common/platform.cpp Normal file
View File

@@ -0,0 +1,101 @@
#include "platform.h"
#include "log.h"
#ifdef _WIN32
#include <Winsock2.h>
#include <iphlpapi.h>
#elif __APPLE__
#include <ifaddrs.h>
#include <net/if_dl.h>
#include <net/if_types.h>
#include <sys/socket.h>
#include <sys/types.h>
#elif __linux__
#include <fcntl.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <unistd.h>
#endif
std::string GetMac() {
char mac_addr[16];
int len = 0;
#ifdef _WIN32
IP_ADAPTER_INFO adapterInfo[16];
DWORD bufferSize = sizeof(adapterInfo);
DWORD result = GetAdaptersInfo(adapterInfo, &bufferSize);
if (result == ERROR_SUCCESS) {
PIP_ADAPTER_INFO adapter = adapterInfo;
while (adapter) {
for (UINT i = 0; i < adapter->AddressLength; i++) {
len += sprintf(mac_addr + len, "%.2X", adapter->Address[i]);
}
break;
}
}
#elif __APPLE__
std::string if_name = "en0";
struct ifaddrs *addrs;
struct ifaddrs *cursor;
const struct sockaddr_dl *dlAddr;
if (!getifaddrs(&addrs)) {
cursor = addrs;
while (cursor != 0) {
const struct sockaddr_dl *socAddr =
(const struct sockaddr_dl *)cursor->ifa_addr;
if ((cursor->ifa_addr->sa_family == AF_LINK) &&
(socAddr->sdl_type == IFT_ETHER) &&
strcmp(if_name.c_str(), cursor->ifa_name) == 0) {
dlAddr = (const struct sockaddr_dl *)cursor->ifa_addr;
const unsigned char *base =
(const unsigned char *)&dlAddr->sdl_data[dlAddr->sdl_nlen];
for (int i = 0; i < dlAddr->sdl_alen; i++) {
len += sprintf(mac_addr + len, "%.2X", base[i]);
}
}
cursor = cursor->ifa_next;
}
freeifaddrs(addrs);
}
#elif __linux__
int sock = socket(AF_INET, SOCK_DGRAM, 0);
if (sock < 0) {
return "";
}
struct ifreq ifr;
struct ifconf ifc;
char buf[1024];
ifc.ifc_len = sizeof(buf);
ifc.ifc_buf = buf;
if (ioctl(sock, SIOCGIFCONF, &ifc) < 0) {
close(sock);
return "";
}
struct ifreq *it = ifc.ifc_req;
const struct ifreq *const end = it + (ifc.ifc_len / sizeof(struct ifreq));
for (; it != end; ++it) {
std::strcpy(ifr.ifr_name, it->ifr_name);
if (ioctl(sock, SIOCGIFFLAGS, &ifr) < 0) {
continue;
}
if (ifr.ifr_flags & IFF_LOOPBACK) {
continue;
}
if (ioctl(sock, SIOCGIFHWADDR, &ifr) < 0) {
continue;
}
std::string mac_address;
for (int i = 0; i < 6; ++i) {
len += sprintf(mac_addr + len, "%.2X", ifr.ifr_hwaddr.sa_data[i] & 0xff);
}
break;
}
close(sock);
#endif
return mac_addr;
}

14
src/common/platform.h Normal file
View File

@@ -0,0 +1,14 @@
/*
* @Author: DI JUNKUN
* @Date: 2023-12-18
* Copyright (c) 2023 by DI JUNKUN, All Rights Reserved.
*/
#ifndef _PLATFORM_H_
#define _PLATFORM_H_
#include <iostream>
std::string GetMac();
#endif

View File

@@ -0,0 +1,38 @@
#include "config_center.h"
ConfigCenter::ConfigCenter() {}
ConfigCenter::~ConfigCenter() {}
int ConfigCenter::SetLanguage(LANGUAGE language) {
language_ = language;
return 0;
}
int ConfigCenter::SetVideoQuality(VIDEO_QUALITY video_quality) {
video_quality_ = video_quality;
return 0;
}
int ConfigCenter::SetVideoEncodeFormat(
VIDEO_ENCODE_FORMAT video_encode_format) {
video_encode_format_ = video_encode_format;
return 0;
}
int ConfigCenter::SetHardwareVideoCodec(bool hardware_video_codec) {
hardware_video_codec_ = hardware_video_codec;
return 0;
}
ConfigCenter::LANGUAGE ConfigCenter::GetLanguage() { return language_; }
ConfigCenter::VIDEO_QUALITY ConfigCenter::GetVideoQuality() {
return video_quality_;
}
ConfigCenter::VIDEO_ENCODE_FORMAT ConfigCenter::GetVideoEncodeFormat() {
return video_encode_format_;
}
bool ConfigCenter::IsHardwareVideoCodec() { return hardware_video_codec_; }

View File

@@ -0,0 +1,40 @@
/*
* @Author: DI JUNKUN
* @Date: 2024-05-29
* Copyright (c) 2024 by DI JUNKUN, All Rights Reserved.
*/
#ifndef _CONFIG_CENTER_H_
#define _CONFIG_CENTER_H_
class ConfigCenter {
public:
enum class LANGUAGE { CHINESE = 0, ENGLISH = 1 };
enum class VIDEO_QUALITY { LOW = 0, MEDIUM = 1, HIGH = 2 };
enum class VIDEO_ENCODE_FORMAT { AV1 = 0, H264 = 1 };
public:
ConfigCenter();
~ConfigCenter();
public:
int SetLanguage(LANGUAGE language);
int SetVideoQuality(VIDEO_QUALITY video_quality);
int SetVideoEncodeFormat(VIDEO_ENCODE_FORMAT video_encode_format);
int SetHardwareVideoCodec(bool hardware_video_codec);
public:
LANGUAGE GetLanguage();
VIDEO_QUALITY GetVideoQuality();
VIDEO_ENCODE_FORMAT GetVideoEncodeFormat();
bool IsHardwareVideoCodec();
private:
// Default value should be same with parameters in localization.h
LANGUAGE language_ = LANGUAGE::CHINESE;
VIDEO_QUALITY video_quality_ = VIDEO_QUALITY::MEDIUM;
VIDEO_ENCODE_FORMAT video_encode_format_ = VIDEO_ENCODE_FORMAT::AV1;
bool hardware_video_codec_ = false;
};
#endif

View File

@@ -0,0 +1,44 @@
/*
* @Author: DI JUNKUN
* @Date: 2023-12-14
* Copyright (c) 2023 by DI JUNKUN, All Rights Reserved.
*/
#ifndef _DEVICE_CONTROLLER_H_
#define _DEVICE_CONTROLLER_H_
#include <stdio.h>
typedef enum { mouse = 0, keyboard } ControlType;
typedef enum { move = 0, left_down, left_up, right_down, right_up } MouseFlag;
typedef enum { key_down = 0, key_up } KeyFlag;
typedef struct {
size_t x;
size_t y;
MouseFlag flag;
} Mouse;
typedef struct {
size_t key_value;
KeyFlag flag;
} Key;
typedef struct {
ControlType type;
union {
Mouse m;
Key k;
};
} RemoteAction;
class DeviceController {
public:
virtual ~DeviceController() {}
public:
virtual int Init(int screen_width, int screen_height) = 0;
virtual int Destroy() = 0;
virtual int SendCommand(RemoteAction remote_action) = 0;
};
#endif

View File

@@ -0,0 +1,33 @@
/*
* @Author: DI JUNKUN
* @Date: 2023-12-14
* Copyright (c) 2023 by DI JUNKUN, All Rights Reserved.
*/
#ifndef _DEVICE_CONTROLLER_FACTORY_H_
#define _DEVICE_CONTROLLER_FACTORY_H_
#include "device_controller.h"
#include "mouse_controller.h"
class DeviceControllerFactory {
public:
enum Device { Mouse = 0, Keyboard };
public:
virtual ~DeviceControllerFactory() {}
public:
DeviceController* Create(Device device) {
switch (device) {
case Mouse:
return new MouseController();
case Keyboard:
return nullptr;
default:
return nullptr;
}
}
};
#endif

View File

@@ -0,0 +1,127 @@
#include "mouse_controller.h"
#include "log.h"
MouseController::MouseController() {}
MouseController::~MouseController() {
if (uinput_fd_) {
ioctl(uinput_fd_, UI_DEV_DESTROY);
close(uinput_fd_);
}
}
int MouseController::Init(int screen_width, int screen_height) {
screen_width_ = screen_width;
screen_height_ = screen_height;
uinput_fd_ = open("/dev/uinput", O_WRONLY | O_NONBLOCK);
if (uinput_fd_ < 0) {
LOG_ERROR("Cannot open device: /dev/uinput");
return -1;
}
ioctl(uinput_fd_, UI_SET_EVBIT, EV_KEY);
ioctl(uinput_fd_, UI_SET_KEYBIT, BTN_RIGHT);
ioctl(uinput_fd_, UI_SET_KEYBIT, BTN_LEFT);
ioctl(uinput_fd_, UI_SET_EVBIT, EV_ABS);
ioctl(uinput_fd_, UI_SET_ABSBIT, ABS_X);
ioctl(uinput_fd_, UI_SET_ABSBIT, ABS_Y);
ioctl(uinput_fd_, UI_SET_EVBIT, EV_REL);
struct uinput_user_dev uidev;
memset(&uidev, 0, sizeof(uidev));
snprintf(uidev.name, UINPUT_MAX_NAME_SIZE, "VirtualMouse");
uidev.id.bustype = BUS_USB;
uidev.id.version = 1;
uidev.id.vendor = 0x1;
uidev.id.product = 0x1;
uidev.absmin[ABS_X] = 0;
uidev.absmax[ABS_X] = screen_width_;
uidev.absmin[ABS_Y] = 0;
uidev.absmax[ABS_Y] = screen_height_;
int res_uidev = write(uinput_fd_, &uidev, sizeof(uidev));
ioctl(uinput_fd_, UI_DEV_CREATE);
return 0;
}
int MouseController::Destroy() { return 0; }
int MouseController::SendCommand(RemoteAction remote_action) {
int mouse_pos_x = remote_action.m.x * screen_width_ / 1280;
int mouse_pos_y = remote_action.m.y * screen_height_ / 720;
if (remote_action.type == ControlType::mouse) {
struct input_event event;
memset(&event, 0, sizeof(event));
gettimeofday(&event.time, NULL);
if (remote_action.m.flag == MouseFlag::left_down) {
SimulateKeyDown(uinput_fd_, BTN_LEFT);
} else if (remote_action.m.flag == MouseFlag::left_up) {
SimulateKeyUp(uinput_fd_, BTN_LEFT);
} else if (remote_action.m.flag == MouseFlag::right_down) {
SimulateKeyDown(uinput_fd_, BTN_RIGHT);
} else if (remote_action.m.flag == MouseFlag::right_up) {
SimulateKeyUp(uinput_fd_, BTN_RIGHT);
} else {
SetMousePosition(uinput_fd_, mouse_pos_x, mouse_pos_y);
}
}
return 0;
}
void MouseController::SimulateKeyDown(int fd, int kval) {
int res_ev = 0;
struct input_event event;
memset(&event, 0, sizeof(event));
gettimeofday(&event.time, 0);
event.type = EV_KEY;
event.value = 1;
event.code = kval;
res_ev = write(fd, &event, sizeof(event));
event.type = EV_SYN;
event.value = 0;
event.code = SYN_REPORT;
res_ev = write(fd, &event, sizeof(event));
}
void MouseController::SimulateKeyUp(int fd, int kval) {
int res_ev = 0;
struct input_event event;
memset(&event, 0, sizeof(event));
gettimeofday(&event.time, 0);
event.type = EV_KEY;
event.value = 0;
event.code = kval;
res_ev = write(fd, &event, sizeof(event));
event.type = EV_SYN;
event.value = 0;
event.code = SYN_REPORT;
res_ev = write(fd, &event, sizeof(event));
}
void MouseController::SetMousePosition(int fd, int x, int y) {
struct input_event ev[2], ev_sync;
memset(ev, 0, sizeof(ev));
memset(&ev_sync, 0, sizeof(ev_sync));
ev[0].type = EV_ABS;
ev[0].code = ABS_X;
ev[0].value = x;
ev[1].type = EV_ABS;
ev[1].code = ABS_Y;
ev[1].value = y;
int res_w = write(fd, ev, sizeof(ev));
ev_sync.type = EV_SYN;
ev_sync.value = 0;
ev_sync.code = 0;
int res_ev_sync = write(fd, &ev_sync, sizeof(ev_sync));
}

View File

@@ -0,0 +1,41 @@
/*
* @Author: DI JUNKUN
* @Date: 2023-12-14
* Copyright (c) 2023 by DI JUNKUN, All Rights Reserved.
*/
#ifndef _MOUSE_CONTROLLER_H_
#define _MOUSE_CONTROLLER_H_
#include <fcntl.h>
#include <linux/uinput.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <unistd.h>
#include "device_controller.h"
class MouseController : public DeviceController {
public:
MouseController();
virtual ~MouseController();
public:
virtual int Init(int screen_width, int screen_height);
virtual int Destroy();
virtual int SendCommand(RemoteAction remote_action);
private:
void SimulateKeyDown(int fd, int kval);
void SimulateKeyUp(int fd, int kval);
void SetMousePosition(int fd, int x, int y);
private:
int uinput_fd_;
struct uinput_user_dev uinput_dev_;
int screen_width_ = 0;
int screen_height_ = 0;
};
#endif

View File

@@ -0,0 +1,49 @@
#include "mouse_controller.h"
#include <ApplicationServices/ApplicationServices.h>
#include "log.h"
MouseController::MouseController() {}
MouseController::~MouseController() {}
int MouseController::Init(int screen_width, int screen_height) {
screen_width_ = screen_width;
screen_height_ = screen_height;
return 0;
}
int MouseController::Destroy() { return 0; }
int MouseController::SendCommand(RemoteAction remote_action) {
int mouse_pos_x = remote_action.m.x * screen_width_ / 1280;
int mouse_pos_y = remote_action.m.y * screen_height_ / 720;
if (remote_action.type == ControlType::mouse) {
CGEventRef mouse_event;
CGEventType mouse_type;
if (remote_action.m.flag == MouseFlag::left_down) {
mouse_type = kCGEventLeftMouseDown;
} else if (remote_action.m.flag == MouseFlag::left_up) {
mouse_type = kCGEventLeftMouseUp;
} else if (remote_action.m.flag == MouseFlag::right_down) {
mouse_type = kCGEventRightMouseDown;
} else if (remote_action.m.flag == MouseFlag::right_up) {
mouse_type = kCGEventRightMouseUp;
} else {
mouse_type = kCGEventMouseMoved;
}
mouse_event = CGEventCreateMouseEvent(NULL, mouse_type,
CGPointMake(mouse_pos_x, mouse_pos_y),
kCGMouseButtonLeft);
CGEventPost(kCGHIDEventTap, mouse_event);
CFRelease(mouse_event);
}
return 0;
}

View File

@@ -0,0 +1,27 @@
/*
* @Author: DI JUNKUN
* @Date: 2023-12-14
* Copyright (c) 2023 by DI JUNKUN, All Rights Reserved.
*/
#ifndef _MOUSE_CONTROLLER_H_
#define _MOUSE_CONTROLLER_H_
#include "device_controller.h"
class MouseController : public DeviceController {
public:
MouseController();
virtual ~MouseController();
public:
virtual int Init(int screen_width, int screen_height);
virtual int Destroy();
virtual int SendCommand(RemoteAction remote_action);
private:
int screen_width_ = 0;
int screen_height_ = 0;
};
#endif

View File

@@ -0,0 +1,47 @@
#include "mouse_controller.h"
#include "log.h"
MouseController::MouseController() {}
MouseController::~MouseController() {}
int MouseController::Init(int screen_width, int screen_height) {
screen_width_ = screen_width;
screen_height_ = screen_height;
return 0;
}
int MouseController::Destroy() { return 0; }
int MouseController::SendCommand(RemoteAction remote_action) {
INPUT ip;
if (remote_action.type == ControlType::mouse) {
ip.type = INPUT_MOUSE;
ip.mi.dx = remote_action.m.x * screen_width_ / 1280;
ip.mi.dy = remote_action.m.y * screen_height_ / 720;
if (remote_action.m.flag == MouseFlag::left_down) {
ip.mi.dwFlags = MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_ABSOLUTE;
} else if (remote_action.m.flag == MouseFlag::left_up) {
ip.mi.dwFlags = MOUSEEVENTF_LEFTUP | MOUSEEVENTF_ABSOLUTE;
} else if (remote_action.m.flag == MouseFlag::right_down) {
ip.mi.dwFlags = MOUSEEVENTF_RIGHTDOWN | MOUSEEVENTF_ABSOLUTE;
} else if (remote_action.m.flag == MouseFlag::right_up) {
ip.mi.dwFlags = MOUSEEVENTF_RIGHTUP | MOUSEEVENTF_ABSOLUTE;
} else {
ip.mi.dwFlags = MOUSEEVENTF_MOVE;
}
ip.mi.mouseData = 0;
ip.mi.time = 0;
SetCursorPos(ip.mi.dx, ip.mi.dy);
if (ip.mi.dwFlags != MOUSEEVENTF_MOVE) {
SendInput(1, &ip, sizeof(INPUT));
}
}
return 0;
}

View File

@@ -0,0 +1,27 @@
/*
* @Author: DI JUNKUN
* @Date: 2023-12-14
* Copyright (c) 2023 by DI JUNKUN, All Rights Reserved.
*/
#ifndef _MOUSE_CONTROLLER_H_
#define _MOUSE_CONTROLLER_H_
#include "device_controller.h"
class MouseController : public DeviceController {
public:
MouseController();
virtual ~MouseController();
public:
virtual int Init(int screen_width, int screen_height);
virtual int Destroy();
virtual int SendCommand(RemoteAction remote_action);
private:
int screen_width_ = 0;
int screen_height_ = 0;
};
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,874 @@
#include <SDL.h>
#include <stdio.h>
#ifdef _WIN32
#ifdef REMOTE_DESK_DEBUG
#pragma comment(linker, "/subsystem:\"console\"")
#else
#pragma comment(linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"")
#endif
#endif
#include <stdio.h>
#include <atomic>
#include <chrono>
#include <cstring>
#include <fstream>
#include <iostream>
#include <string>
#include <thread>
#include "../../thirdparty/projectx/src/interface/x.h"
#include "config_center.h"
#include "device_controller_factory.h"
#include "imgui.h"
#include "imgui_impl_sdl2.h"
#include "imgui_impl_sdlrenderer2.h"
#include "log.h"
#include "platform.h"
#include "screen_capturer_factory.h"
#define NV12_BUFFER_SIZE 1280 * 720 * 3 / 2
#ifdef REMOTE_DESK_DEBUG
#define MOUSE_CONTROL 0
#else
#define MOUSE_CONTROL 1
#endif
#define CHINESE_FONT 1
int screen_w = 1280, screen_h = 720;
int window_w = 1280, window_h = 720;
const int pixel_w = 1280, pixel_h = 720;
unsigned char dst_buffer[pixel_w * pixel_h * 3 / 2];
unsigned char audio_buffer[960];
SDL_Texture *sdlTexture = nullptr;
SDL_Renderer *sdlRenderer = nullptr;
SDL_Rect sdlRect;
SDL_Window *window;
static SDL_AudioDeviceID input_dev;
static SDL_AudioDeviceID output_dev;
uint32_t start_time, end_time, elapsed_time;
uint32_t frame_count = 0;
int fps = 0;
static std::atomic<bool> audio_buffer_fresh{false};
static uint32_t last_ts = 0;
int dst_bufsize;
struct SwrContext *swr_ctx;
int ret;
int audio_len = 0;
std::string window_title = "Remote Desk Client";
std::string connection_status_str = "-";
std::string signal_status_str = "-";
std::atomic<SignalStatus> signal_status{SignalStatus::SignalClosed};
std::atomic<ConnectionStatus> connection_status{ConnectionStatus::Closed};
// Refresh Event
#define REFRESH_EVENT (SDL_USEREVENT + 1)
#define QUIT_EVENT (SDL_USEREVENT + 2)
typedef struct {
char password[7];
} CDCache;
int thread_exit = 0;
PeerPtr *peer_server = nullptr;
// PeerPtr *peer_server = nullptr;
bool joined = false;
bool received_frame = false;
bool menu_hovered = false;
static bool connect_button_pressed = false;
static bool fullscreen_button_pressed = false;
#if CHINESE_FONT
static const char *connect_label = u8"连接";
static const char *fullscreen_label = u8"全屏";
#else
static const char *connect_label = "Connect";
static const char *fullscreen_label = "FULLSCREEN";
#endif
static char input_password[7] = "";
static FILE *cd_cache_file = nullptr;
static CDCache cd_cache;
static bool is_create_connection = false;
static bool done = false;
ScreenCapturerFactory *screen_capturer_factory = nullptr;
ScreenCapturer *screen_capturer = nullptr;
DeviceControllerFactory *device_controller_factory = nullptr;
MouseController *mouse_controller = nullptr;
ConfigCenter config_center;
char *nv12_buffer = nullptr;
#ifdef __linux__
std::chrono::_V2::system_clock::time_point last_frame_time_;
#else
std::chrono::steady_clock::time_point last_frame_time_;
#endif
inline int ProcessMouseKeyEven(SDL_Event &ev) {
float ratio = (float)(1280.0 / window_w);
RemoteAction remote_action;
remote_action.m.x = (size_t)(ev.button.x * ratio);
remote_action.m.y = (size_t)(ev.button.y * ratio);
if (SDL_KEYDOWN == ev.type) // SDL_KEYUP
{
// printf("SDLK_DOWN: %d\n", SDL_KeyCode(ev.key.keysym.sym));
if (SDLK_DOWN == ev.key.keysym.sym) {
// printf("SDLK_DOWN \n");
} else if (SDLK_UP == ev.key.keysym.sym) {
// printf("SDLK_UP \n");
} else if (SDLK_LEFT == ev.key.keysym.sym) {
// printf("SDLK_LEFT \n");
} else if (SDLK_RIGHT == ev.key.keysym.sym) {
// printf("SDLK_RIGHT \n");
}
} else if (SDL_MOUSEBUTTONDOWN == ev.type) {
remote_action.type = ControlType::mouse;
if (SDL_BUTTON_LEFT == ev.button.button) {
remote_action.m.flag = MouseFlag::left_down;
} else if (SDL_BUTTON_RIGHT == ev.button.button) {
remote_action.m.flag = MouseFlag::right_down;
}
SendData(peer_server, DATA_TYPE::DATA, (const char *)&remote_action,
sizeof(remote_action));
} else if (SDL_MOUSEBUTTONUP == ev.type) {
remote_action.type = ControlType::mouse;
if (SDL_BUTTON_LEFT == ev.button.button) {
remote_action.m.flag = MouseFlag::left_up;
} else if (SDL_BUTTON_RIGHT == ev.button.button) {
remote_action.m.flag = MouseFlag::right_up;
}
SendData(peer_server, DATA_TYPE::DATA, (const char *)&remote_action,
sizeof(remote_action));
} else if (SDL_MOUSEMOTION == ev.type) {
remote_action.type = ControlType::mouse;
remote_action.m.flag = MouseFlag::move;
SendData(peer_server, DATA_TYPE::DATA, (const char *)&remote_action,
sizeof(remote_action));
} else if (SDL_QUIT == ev.type) {
SDL_Event event;
event.type = SDL_QUIT;
SDL_PushEvent(&event);
printf("SDL_QUIT\n");
return 0;
}
return 0;
}
void SdlCaptureAudioIn(void *userdata, Uint8 *stream, int len) {
if (1) {
if ("Connected" == connection_status_str) {
SendData(peer_server, DATA_TYPE::AUDIO, (const char *)stream, len);
}
} else {
memcpy(audio_buffer, stream, len);
audio_len = len;
SDL_Delay(10);
audio_buffer_fresh = true;
}
}
void SdlCaptureAudioOut(void *userdata, Uint8 *stream, int len) {
if (!audio_buffer_fresh) {
return;
}
SDL_memset(stream, 0, len);
if (audio_len == 0) {
return;
} else {
}
len = (len > audio_len ? audio_len : len);
SDL_MixAudioFormat(stream, audio_buffer, AUDIO_S16LSB, len,
SDL_MIX_MAXVOLUME);
audio_buffer_fresh = false;
}
void ServerReceiveVideoBuffer(const char *data, size_t size,
const char *user_id, size_t user_id_size) {
if (joined) {
memcpy(dst_buffer, data, size);
SDL_Event event;
event.type = REFRESH_EVENT;
SDL_PushEvent(&event);
received_frame = true;
}
}
void ClientReceiveVideoBuffer(const char *data, size_t size,
const char *user_id, size_t user_id_size) {
// std::cout << "Receive: [" << user_id << "] " << std::endl;
if (joined) {
memcpy(dst_buffer, data, size);
SDL_Event event;
event.type = REFRESH_EVENT;
SDL_PushEvent(&event);
received_frame = true;
}
}
void ServerReceiveAudioBuffer(const char *data, size_t size,
const char *user_id, size_t user_id_size) {
// memset(audio_buffer, 0, size);
// memcpy(audio_buffer, data, size);
// audio_len = size;
audio_buffer_fresh = true;
SDL_QueueAudio(output_dev, data, (Uint32)size);
// printf("queue audio\n");
}
void ClientReceiveAudioBuffer(const char *data, size_t size,
const char *user_id, size_t user_id_size) {
// std::cout << "Client receive audio, size " << size << ", user [" << user_id
// << "] " << std::endl;
SDL_QueueAudio(output_dev, data, (Uint32)size);
}
void ServerReceiveDataBuffer(const char *data, size_t size, const char *user_id,
size_t user_id_size) {
std::string user(user_id, user_id_size);
RemoteAction remote_action;
memcpy(&remote_action, data, sizeof(remote_action));
// std::cout << "remote_action: " << remote_action.type << " "
// << remote_action.m.flag << " " << remote_action.m.x << " "
// << remote_action.m.y << std::endl;
#if MOUSE_CONTROL
if (mouse_controller) {
mouse_controller->SendCommand(remote_action);
}
#endif
}
void ClientReceiveDataBuffer(const char *data, size_t size, const char *user_id,
size_t user_id_size) {}
void SignalStatus(SignalStatus status) {
signal_status = status;
if (SignalStatus::SignalConnecting == status) {
signal_status_str = "SignalConnecting";
} else if (SignalStatus::SignalConnected == status) {
signal_status_str = "SignalConnected";
} else if (SignalStatus::SignalFailed == status) {
signal_status_str = "SignalFailed";
} else if (SignalStatus::SignalClosed == status) {
signal_status_str = "SignalClosed";
} else if (SignalStatus::SignalReconnecting == status) {
signal_status_str = "SignalReconnecting";
}
}
void ConnectionStatus(ConnectionStatus status) {
connection_status = status;
if (ConnectionStatus::Connecting == status) {
connection_status_str = "Connecting";
} else if (ConnectionStatus::Connected == status) {
connection_status_str = "Connected";
joined = true;
} else if (ConnectionStatus::Disconnected == status) {
connection_status_str = "Disconnected";
} else if (ConnectionStatus::Failed == status) {
connection_status_str = "Failed";
} else if (ConnectionStatus::Closed == status) {
connection_status_str = "Closed";
} else if (ConnectionStatus::IncorrectPassword == status) {
connection_status_str = "Incorrect password";
if (connect_button_pressed) {
connect_button_pressed = false;
joined = false;
connect_label = connect_button_pressed ? "Disconnect" : "Connect";
}
} else if (ConnectionStatus::NoSuchTransmissionId == status) {
connection_status_str = "No such transmission id";
if (connect_button_pressed) {
connect_button_pressed = false;
joined = false;
connect_label = connect_button_pressed ? "Disconnect" : "Connect";
}
}
}
int main(int argc, char *argv[]) {
LOG_INFO("Remote desk");
last_ts = static_cast<uint32_t>(
std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::high_resolution_clock::now().time_since_epoch())
.count());
cd_cache_file = fopen("cache.cd", "r+");
if (cd_cache_file) {
fseek(cd_cache_file, 0, SEEK_SET);
fread(&cd_cache.password, sizeof(cd_cache.password), 1, cd_cache_file);
fclose(cd_cache_file);
strncpy(input_password, cd_cache.password, sizeof(cd_cache.password));
}
// Setup SDL
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER |
SDL_INIT_GAMECONTROLLER) != 0) {
printf("Error: %s\n", SDL_GetError());
return -1;
}
// From 2.0.18: Enable native IME.
#ifdef SDL_HINT_IME_SHOW_UI
SDL_SetHint(SDL_HINT_IME_SHOW_UI, "1");
#endif
// Create window with SDL_Renderer graphics context
SDL_WindowFlags window_flags =
(SDL_WindowFlags)(SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI);
window = SDL_CreateWindow("Remote Desk", SDL_WINDOWPOS_CENTERED,
SDL_WINDOWPOS_CENTERED, window_w, window_h,
window_flags);
SDL_DisplayMode DM;
SDL_GetCurrentDisplayMode(0, &DM);
screen_w = DM.w;
screen_h = DM.h;
sdlRenderer = SDL_CreateRenderer(
window, -1, SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_ACCELERATED);
if (sdlRenderer == nullptr) {
SDL_Log("Error creating SDL_Renderer!");
return 0;
}
Uint32 pixformat = 0;
pixformat = SDL_PIXELFORMAT_NV12;
sdlTexture = SDL_CreateTexture(sdlRenderer, pixformat,
SDL_TEXTUREACCESS_STREAMING, pixel_w, pixel_h);
// Audio
SDL_AudioSpec want_in, have_in, want_out, have_out;
SDL_zero(want_in);
want_in.freq = 48000;
want_in.format = AUDIO_S16LSB;
want_in.channels = 1;
want_in.samples = 480;
want_in.callback = SdlCaptureAudioIn;
input_dev = SDL_OpenAudioDevice(NULL, 1, &want_in, &have_in, 0);
if (input_dev == 0) {
SDL_Log("Failed to open input: %s", SDL_GetError());
// return 1;
}
SDL_zero(want_out);
want_out.freq = 48000;
want_out.format = AUDIO_S16LSB;
want_out.channels = 1;
// want_out.silence = 0;
want_out.samples = 480;
want_out.callback = NULL;
output_dev = SDL_OpenAudioDevice(NULL, 0, &want_out, &have_out, 0);
if (output_dev == 0) {
SDL_Log("Failed to open input: %s", SDL_GetError());
// return 1;
}
SDL_PauseAudioDevice(input_dev, 0);
SDL_PauseAudioDevice(output_dev, 0);
// Setup Dear ImGui context
IMGUI_CHECKVERSION();
ImGui::CreateContext();
ImGuiIO &io = ImGui::GetIO();
io.ConfigFlags |=
ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls
io.ConfigFlags |=
ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls
#if CHINESE_FONT
// Load Fonts
#ifdef _WIN32
std::string default_font_path = "c:/windows/fonts/simhei.ttf";
std::ifstream font_path_f(default_font_path.c_str());
std::string font_path =
font_path_f.good() ? "c:/windows/fonts/simhei.ttf" : "";
if (!font_path.empty()) {
io.Fonts->AddFontFromFileTTF(font_path.c_str(), 13.0f, NULL,
io.Fonts->GetGlyphRangesChineseFull());
}
#elif __APPLE__
std::string default_font_path = "/System/Library/Fonts/PingFang.ttc";
std::ifstream font_path_f(default_font_path.c_str());
std::string font_path =
font_path_f.good() ? "/System/Library/Fonts/PingFang.ttc" : "";
if (!font_path.empty()) {
io.Fonts->AddFontFromFileTTF(font_path.c_str(), 13.0f, NULL,
io.Fonts->GetGlyphRangesChineseFull());
}
#elif __linux__
io.Fonts->AddFontFromFileTTF("c:/windows/fonts/msyh.ttc", 13.0f, NULL,
io.Fonts->GetGlyphRangesChineseFull());
#endif
#endif
// Setup Dear ImGui style
// ImGui::StyleColorsDark();
ImGui::StyleColorsLight();
// Setup Platform/Renderer backends
ImGui_ImplSDL2_InitForSDLRenderer(window, sdlRenderer);
ImGui_ImplSDLRenderer2_Init(sdlRenderer);
// Our state
bool show_demo_window = true;
bool show_another_window = false;
ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f);
std::string mac_addr_str = GetMac();
std::thread rtc_thread(
[](int screen_width, int screen_height) {
std::string default_cfg_path = "../../../../config/config.ini";
std::ifstream f(default_cfg_path.c_str());
std::string mac_addr_str = GetMac();
Params server_params;
server_params.cfg_path =
f.good() ? "../../../../config/config.ini" : "config.ini";
server_params.on_receive_video_buffer = ServerReceiveVideoBuffer;
server_params.on_receive_audio_buffer = ServerReceiveAudioBuffer;
server_params.on_receive_data_buffer = ServerReceiveDataBuffer;
server_params.on_signal_status = SignalStatus;
server_params.on_connection_status = ConnectionStatus;
std::string transmission_id = "000001";
peer_server = CreatePeer(&server_params);
LOG_INFO("Create peer_server");
std::string server_user_id = "S-" + mac_addr_str;
Init(peer_server, server_user_id.c_str());
LOG_INFO("peer_server init finish");
{
while (SignalStatus::SignalConnected != signal_status && !done) {
std::this_thread::sleep_for(std::chrono::seconds(1));
}
if (done) {
return;
}
std::string user_id = "S-" + mac_addr_str;
is_create_connection =
CreateConnection(peer_server, mac_addr_str.c_str(),
input_password)
? false
: true;
nv12_buffer = new char[NV12_BUFFER_SIZE];
// Screen capture
screen_capturer_factory = new ScreenCapturerFactory();
screen_capturer = (ScreenCapturer *)screen_capturer_factory->Create();
last_frame_time_ = std::chrono::high_resolution_clock::now();
ScreenCapturer::RECORD_DESKTOP_RECT rect;
rect.left = 0;
rect.top = 0;
rect.right = screen_w;
rect.bottom = screen_h;
int screen_capturer_init_ret = screen_capturer->Init(
rect, 60,
[](unsigned char *data, int size, int width, int height) -> void {
auto now_time = std::chrono::high_resolution_clock::now();
std::chrono::duration<double> duration =
now_time - last_frame_time_;
auto tc = duration.count() * 1000;
if (tc >= 0) {
SendData(peer_server, DATA_TYPE::VIDEO, (const char *)data,
NV12_BUFFER_SIZE);
last_frame_time_ = now_time;
}
});
if (0 == screen_capturer_init_ret) {
screen_capturer->Start();
} else {
screen_capturer->Destroy();
screen_capturer = nullptr;
}
// Mouse control
device_controller_factory = new DeviceControllerFactory();
mouse_controller =
(MouseController *)device_controller_factory->Create(
DeviceControllerFactory::Device::Mouse);
int mouse_controller_init_ret =
mouse_controller->Init(screen_w, screen_h);
if (0 != mouse_controller_init_ret) {
mouse_controller->Destroy();
mouse_controller = nullptr;
}
}
},
screen_w, screen_h);
// Main loop
while (!done) {
// Start the Dear ImGui frame
ImGui_ImplSDLRenderer2_NewFrame();
ImGui_ImplSDL2_NewFrame();
ImGui::NewFrame();
if (joined && !menu_hovered) {
ImGui::SetMouseCursor(ImGuiMouseCursor_None);
}
{
static float f = 0.0f;
static int counter = 0;
const ImGuiViewport *main_viewport = ImGui::GetMainViewport();
ImGui::SetNextWindowPos(ImVec2(0, 0), ImGuiCond_Once);
// ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0.0f, 0.0f));
#if CHINESE_FONT
ImGui::SetNextWindowSize(ImVec2(160, 210));
#else
ImGui::SetNextWindowSize(ImVec2(180, 210));
#endif
#if CHINESE_FONT
if (!joined) {
ImGui::SetNextWindowPos(ImVec2(0, 0), ImGuiCond_Always);
ImGui::Begin(u8"菜单", nullptr,
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse |
ImGuiWindowFlags_NoMove);
} else {
ImGui::SetNextWindowCollapsed(true, ImGuiCond_Once);
ImGui::Begin(u8"菜单", nullptr, ImGuiWindowFlags_None);
}
#else
if (!joined) {
ImGui::SetNextWindowPos(ImVec2(0, 0), ImGuiCond_Always);
ImGui::Begin("Menu", nullptr,
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse |
ImGuiWindowFlags_NoMove);
} else {
ImGui::SetNextWindowCollapsed(true, ImGuiCond_Once);
ImGui::Begin("Menu", nullptr, ImGuiWindowFlags_None);
}
#endif
{
menu_hovered = ImGui::IsWindowHovered();
#if CHINESE_FONT
ImGui::Text(u8"本机ID:");
#else
ImGui::Text("LOCAL ID:");
#endif
ImGui::SameLine();
ImGui::SetNextItemWidth(90);
#if CHINESE_FONT
ImGui::SetCursorPosX(60.0f);
#else
ImGui::SetCursorPosX(80.0f);
#endif
ImGui::InputText(
"##local_id", (char *)mac_addr_str.c_str(),
mac_addr_str.length() + 1,
ImGuiInputTextFlags_CharsUppercase | ImGuiInputTextFlags_ReadOnly);
#if CHINESE_FONT
ImGui::Text(u8"密码:");
#else
ImGui::Text("PASSWORD:");
#endif
ImGui::SameLine();
char input_password_tmp[7] = "";
std::string input_password_str = "123456";
strncpy(input_password_tmp, input_password, sizeof(input_password));
ImGui::SetNextItemWidth(90);
#if CHINESE_FONT
ImGui::SetCursorPosX(60.0f);
ImGui::InputTextWithHint("##server_pwd", u8"最长6个字符",
input_password, IM_ARRAYSIZE(input_password),
ImGuiInputTextFlags_CharsNoBlank);
#else
ImGui::SetCursorPosX(80.0f);
ImGui::InputTextWithHint("##server_pwd", "max 6 chars", input_password,
IM_ARRAYSIZE(input_password),
ImGuiInputTextFlags_CharsNoBlank);
#endif
if (strcmp(input_password_tmp, input_password)) {
cd_cache_file = fopen("cache.cd", "w+");
if (cd_cache_file) {
fseek(cd_cache_file, 0, SEEK_SET);
strncpy(cd_cache.password, input_password, sizeof(input_password));
fwrite(&cd_cache.password, sizeof(cd_cache.password), 1,
cd_cache_file);
fclose(cd_cache_file);
}
LeaveConnection(peer_server);
CreateConnection(peer_server, mac_addr_str.c_str(), input_password);
}
ImGui::Spacing();
ImGui::Separator();
ImGui::Spacing();
{
{
static char remote_id[20] = "";
#if CHINESE_FONT
ImGui::Text(u8"远端ID:");
#else
ImGui::Text("REMOTE ID:");
#endif
ImGui::SameLine();
ImGui::SetNextItemWidth(90);
#if CHINESE_FONT
ImGui::SetCursorPosX(60.0f);
#else
ImGui::SetCursorPosX(80.0f);
#endif
ImGui::InputTextWithHint("##remote_id", mac_addr_str.c_str(),
remote_id, IM_ARRAYSIZE(remote_id),
ImGuiInputTextFlags_CharsUppercase |
ImGuiInputTextFlags_CharsNoBlank);
ImGui::Spacing();
#if CHINESE_FONT
ImGui::Text(u8"密码:");
#else
ImGui::Text("PASSWORD:");
#endif
ImGui::SameLine();
ImGui::SetNextItemWidth(90);
static char client_password[20] = "";
#if CHINESE_FONT
ImGui::SetCursorPosX(60.0f);
ImGui::InputTextWithHint("##client_pwd", u8"最长6个字符",
client_password,
IM_ARRAYSIZE(client_password),
ImGuiInputTextFlags_CharsNoBlank);
#else
ImGui::SetCursorPosX(80.0f);
ImGui::InputTextWithHint("##client_pwd", "max 6 chars",
client_password,
IM_ARRAYSIZE(client_password),
ImGuiInputTextFlags_CharsNoBlank);
#endif
ImGui::Spacing();
ImGui::Separator();
ImGui::Spacing();
if (ImGui::Button(connect_label)) {
int ret = -1;
if ("SignalConnected" == signal_status_str) {
#if CHINESE_FONT
if (strcmp(connect_label, u8"连接") == 0 && !joined) {
#else
if (strcmp(connect_label, "Connect") == 0 && !joined) {
#endif
std::string user_id = "C-" + mac_addr_str;
ret = JoinConnection(peer_server, remote_id, client_password);
if (0 == ret) {
// joined = true;
}
#if CHINESE_FONT
} else if (strcmp(connect_label, u8"断开连接") == 0 && joined) {
#else
} else if (strcmp(connect_label, "Disconnect") == 0 && joined) {
#endif
ret = LeaveConnection(peer_server);
CreateConnection(peer_server, mac_addr_str.c_str(),
input_password);
memset(audio_buffer, 0, 960);
if (0 == ret) {
joined = false;
received_frame = false;
}
}
if (0 == ret) {
connect_button_pressed = !connect_button_pressed;
#if CHINESE_FONT
connect_label =
connect_button_pressed ? u8"断开连接" : u8"连接";
#else
connect_label =
connect_button_pressed ? "Disconnect" : "Connect";
#endif
}
}
}
}
}
}
ImGui::Spacing();
ImGui::Separator();
ImGui::Spacing();
#if CHINESE_FONT
if (ImGui::Button(fullscreen_label)) {
if (strcmp(fullscreen_label, u8"全屏") == 0) {
#else
if (ImGui::Button(fullscreen_label)) {
if (strcmp(fullscreen_label, "FULLSCREEN") == 0) {
#endif
SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN_DESKTOP);
} else {
SDL_SetWindowFullscreen(window, SDL_FALSE);
}
fullscreen_button_pressed = !fullscreen_button_pressed;
#if CHINESE_FONT
fullscreen_label = fullscreen_button_pressed ? u8"退出全屏" : u8"全屏";
#else
fullscreen_label =
fullscreen_button_pressed ? "EXIT FULLSCREEN" : "FULLSCREEN";
#endif
}
ImGui::End();
}
// Rendering
ImGui::Render();
SDL_RenderSetScale(sdlRenderer, io.DisplayFramebufferScale.x,
io.DisplayFramebufferScale.y);
SDL_Event event;
while (SDL_PollEvent(&event)) {
ImGui_ImplSDL2_ProcessEvent(&event);
if (event.type == SDL_QUIT) {
done = true;
} else if (event.type == SDL_WINDOWEVENT &&
event.window.event == SDL_WINDOWEVENT_RESIZED) {
// SDL_GetWindowSize(window, &window_w, &window_h);
int window_w_last = window_w;
int window_h_last = window_h;
SDL_GetWindowSize(window, &window_w, &window_h);
int w_change_ratio = abs(window_w - window_w_last) / 16;
int h_change_ratio = abs(window_h - window_h_last) / 9;
if (w_change_ratio > h_change_ratio) {
window_h = window_w * 9 / 16;
} else {
window_w = window_h * 16 / 9;
}
SDL_SetWindowSize(window, window_w, window_h);
} else if (event.type == SDL_WINDOWEVENT &&
event.window.event == SDL_WINDOWEVENT_CLOSE &&
event.window.windowID == SDL_GetWindowID(window)) {
done = true;
} else if (event.type == REFRESH_EVENT) {
sdlRect.x = 0;
sdlRect.y = 0;
sdlRect.w = window_w;
sdlRect.h = window_h;
SDL_UpdateTexture(sdlTexture, NULL, dst_buffer, pixel_w);
} else {
if (joined) {
ProcessMouseKeyEven(event);
}
}
}
SDL_RenderClear(sdlRenderer);
SDL_RenderCopy(sdlRenderer, sdlTexture, NULL, &sdlRect);
if (!joined || !received_frame) {
SDL_RenderClear(sdlRenderer);
SDL_SetRenderDrawColor(
sdlRenderer, (Uint8)(clear_color.x * 0), (Uint8)(clear_color.y * 0),
(Uint8)(clear_color.z * 0), (Uint8)(clear_color.w * 0));
}
ImGui_ImplSDLRenderer2_RenderDrawData(ImGui::GetDrawData());
SDL_RenderPresent(sdlRenderer);
frame_count++;
end_time = SDL_GetTicks();
elapsed_time = end_time - start_time;
if (elapsed_time >= 1000) {
fps = frame_count / (elapsed_time / 1000);
frame_count = 0;
window_title = "Remote Desk Client FPS [" + std::to_string(fps) +
"] status [" + signal_status_str + "|" +
connection_status_str + "]";
// For MacOS, UI frameworks can only be called from the main thread
SDL_SetWindowTitle(window, window_title.c_str());
start_time = end_time;
}
}
// Cleanup
if (is_create_connection) {
LeaveConnection(peer_server);
}
rtc_thread.join();
SDL_CloseAudioDevice(output_dev);
SDL_CloseAudioDevice(input_dev);
if (screen_capturer) {
screen_capturer->Destroy();
}
if (mouse_controller) {
mouse_controller->Destroy();
}
ImGui_ImplSDLRenderer2_Shutdown();
ImGui_ImplSDL2_Shutdown();
ImGui::DestroyContext();
SDL_DestroyRenderer(sdlRenderer);
SDL_DestroyWindow(window);
SDL_CloseAudio();
SDL_Quit();
return 0;
}

View File

@@ -0,0 +1,47 @@
/*
* @Author: DI JUNKUN
* @Date: 2024-05-29
* Copyright (c) 2024 by DI JUNKUN, All Rights Reserved.
*/
#ifndef _LOCALIZATION_H_
#define _LOCALIZATION_H_
#include <string>
#include <vector>
namespace localization {
static std::vector<std::string> menu = {u8"菜单", "Menu"};
static std::vector<std::string> local_id = {u8"本机ID:", "Local ID:"};
static std::vector<std::string> password = {u8"密码:", "Password:"};
static std::vector<std::string> max_password_len = {u8"最大6个字符",
"Max 6 chars"};
static std::vector<std::string> remote_id = {u8"对端ID:", "Remote ID:"};
static std::vector<std::string> connect = {u8"连接", "Connect"};
static std::vector<std::string> disconnect = {u8"断开连接", "Disconnect"};
static std::vector<std::string> fullscreen = {u8"全屏", "Fullscreen"};
static std::vector<std::string> exit_fullscreen = {u8"退出全屏",
"Exit fullscreen"};
static std::vector<std::string> control_mouse = {u8"控制鼠标", "Mouse Control"};
static std::vector<std::string> release_mouse = {u8"释放鼠标", "Release Mouse"};
static std::vector<std::string> settings = {u8"设置", "Settings"};
static std::vector<std::string> language = {u8"语言:", "Language:"};
static std::vector<std::string> language_zh = {u8"中文", "Chinese"};
static std::vector<std::string> language_en = {u8"英文", "English"};
static std::vector<std::string> video_quality = {u8"视频质量:",
"Video Quality:"};
static std::vector<std::string> video_quality_high = {u8"", "High"};
static std::vector<std::string> video_quality_medium = {u8"", "Medium"};
static std::vector<std::string> video_quality_low = {u8"", "Low"};
static std::vector<std::string> video_encode_format = {u8"视频编码格式:",
"Video Encode Format:"};
static std::vector<std::string> av1 = {u8"AV1", "AV1"};
static std::vector<std::string> h264 = {u8"H.264", "H.264"};
static std::vector<std::string> enable_hardware_video_codec = {
u8"启用硬件编解码器:", "Enable Hardware Video Codec:"};
static std::vector<std::string> ok = {u8"确认", "OK"};
static std::vector<std::string> cancel = {u8"取消", "Cancel"};
} // namespace localization
#endif

View File

@@ -0,0 +1,81 @@
/*
* @Author: DI JUNKUN
* @Date: 2024-06-14
* Copyright (c) 2024 by DI JUNKUN, All Rights Reserved.
*/
#ifndef _LAYOUT_STYLE_H_
#define _LAYOUT_STYLE_H_
#ifdef _WIN32
#define MENU_WINDOW_WIDTH_CN 160
#define MENU_WINDOW_HEIGHT_CN 245
#define MENU_WINDOW_WIDTH_EN 190
#define MENU_WINDOW_HEIGHT_EN 245
#define IPUT_WINDOW_WIDTH 86
#define INPUT_WINDOW_PADDING_CN 66
#define INPUT_WINDOW_PADDING_EN 96
#define SETTINGS_WINDOW_WIDTH_CN 181
#define SETTINGS_WINDOW_WIDTH_EN 228
#define SETTINGS_WINDOW_HEIGHT_CN 190
#define SETTINGS_WINDOW_HEIGHT_EN 190
#define LANGUAGE_SELECT_WINDOW_PADDING_CN 100
#define LANGUAGE_SELECT_WINDOW_PADDING_EN 147
#define VIDEO_QUALITY_SELECT_WINDOW_PADDING_CN 100
#define VIDEO_QUALITY_SELECT_WINDOW_PADDING_EN 147
#define VIDEO_ENCODE_FORMAT_SELECT_WINDOW_PADDING_CN 100
#define VIDEO_ENCODE_FORMAT_SELECT_WINDOW_PADDING_EN 147
#define ENABLE_HARDWARE_VIDEO_CODEC_CHECKBOX_PADDING_CN 154
#define ENABLE_HARDWARE_VIDEO_CODEC_CHECKBOX_PADDING_EN 201
#define SETTINGS_SELECT_WINDOW_WIDTH 73
#define SETTINGS_OK_BUTTON_PADDING_CN 55
#define SETTINGS_OK_BUTTON_PADDING_EN 78
#elif __linux__
#define MENU_WINDOW_WIDTH_CN 160
#define MENU_WINDOW_HEIGHT_CN 245
#define MENU_WINDOW_WIDTH_EN 190
#define MENU_WINDOW_HEIGHT_EN 245
#define IPUT_WINDOW_WIDTH 90
#define INPUT_WINDOW_PADDING_CN 60
#define INPUT_WINDOW_PADDING_EN 80
#define SETTINGS_WINDOW_WIDTH_CN 188
#define SETTINGS_WINDOW_WIDTH_EN 228
#define SETTINGS_WINDOW_HEIGHT_CN 190
#define SETTINGS_WINDOW_HEIGHT_EN 190
#define LANGUAGE_SELECT_WINDOW_PADDING_CN 100
#define LANGUAGE_SELECT_WINDOW_PADDING_EN 140
#define VIDEO_QUALITY_SELECT_WINDOW_PADDING_CN 100
#define VIDEO_QUALITY_SELECT_WINDOW_PADDING_EN 140
#define VIDEO_ENCODE_FORMAT_SELECT_WINDOW_PADDING_CN 100
#define VIDEO_ENCODE_FORMAT_SELECT_WINDOW_PADDING_EN 140
#define ENABLE_HARDWARE_VIDEO_CODEC_CHECKBOX_PADDING_CN 161
#define ENABLE_HARDWARE_VIDEO_CODEC_CHECKBOX_PADDING_EN 201
#define SETTINGS_SELECT_WINDOW_WIDTH 60
#define SETTINGS_OK_BUTTON_PADDING_CN 60
#define SETTINGS_OK_BUTTON_PADDING_EN 80
#elif __APPLE__
#define MENU_WINDOW_WIDTH_CN 148
#define MENU_WINDOW_HEIGHT_CN 244
#define MENU_WINDOW_WIDTH_EN 148
#define MENU_WINDOW_HEIGHT_EN 244
#define IPUT_WINDOW_WIDTH 77
#define INPUT_WINDOW_PADDING_CN 63
#define INPUT_WINDOW_PADDING_EN 63
#define SETTINGS_WINDOW_WIDTH_CN 160
#define SETTINGS_WINDOW_WIDTH_EN 220
#define SETTINGS_WINDOW_HEIGHT_CN 190
#define SETTINGS_WINDOW_HEIGHT_EN 190
#define LANGUAGE_SELECT_WINDOW_PADDING_CN 90
#define LANGUAGE_SELECT_WINDOW_PADDING_EN 150
#define VIDEO_QUALITY_SELECT_WINDOW_PADDING_CN 90
#define VIDEO_QUALITY_SELECT_WINDOW_PADDING_EN 150
#define VIDEO_ENCODE_FORMAT_SELECT_WINDOW_PADDING_CN 90
#define VIDEO_ENCODE_FORMAT_SELECT_WINDOW_PADDING_EN 150
#define ENABLE_HARDWARE_VIDEO_CODEC_CHECK_WINDOW_PADDING_CN 133
#define ENABLE_HARDWARE_VIDEO_CODEC_CHECK_WINDOW_PADDING_EN 193
#define SETTINGS_SELECT_WINDOW_WIDTH 62
#define SETTINGS_OK_BUTTON_PADDING_CN 50
#define SETTINGS_OK_BUTTON_PADDING_EN 80
#endif
#endif

View File

@@ -0,0 +1,888 @@
#include "main_window.h"
#include <fstream>
#include <iostream>
#include <string>
#include "device_controller_factory.h"
#include "layout_style.h"
#include "localization.h"
#include "log.h"
#include "platform.h"
#include "screen_capturer_factory.h"
// Refresh Event
#define REFRESH_EVENT (SDL_USEREVENT + 1)
#define NV12_BUFFER_SIZE 1280 * 720 * 3 / 2
MainWindow::MainWindow() {}
MainWindow::~MainWindow() {}
int MainWindow::SaveSettingsIntoCacheFile() {
cd_cache_file_ = fopen("cache.cd", "w+");
if (!cd_cache_file_) {
return -1;
}
fseek(cd_cache_file_, 0, SEEK_SET);
strncpy(cd_cache_.password, input_password_, sizeof(input_password_));
memcpy(&cd_cache_.language, &language_button_value_,
sizeof(language_button_value_));
memcpy(&cd_cache_.video_quality, &video_quality_button_value_,
sizeof(video_quality_button_value_));
memcpy(&cd_cache_.video_encode_format, &video_encode_format_button_value_,
sizeof(video_encode_format_button_value_));
memcpy(&cd_cache_.enable_hardware_video_codec, &enable_hardware_video_codec_,
sizeof(enable_hardware_video_codec_));
fwrite(&cd_cache_, sizeof(cd_cache_), 1, cd_cache_file_);
fclose(cd_cache_file_);
return 0;
}
int MainWindow::LoadSettingsIntoCacheFile() {
cd_cache_file_ = fopen("cache.cd", "r+");
if (!cd_cache_file_) {
return -1;
}
fseek(cd_cache_file_, 0, SEEK_SET);
fread(&cd_cache_, sizeof(cd_cache_), 1, cd_cache_file_);
fclose(cd_cache_file_);
strncpy(input_password_, cd_cache_.password, sizeof(cd_cache_.password));
language_button_value_ = cd_cache_.language;
video_quality_button_value_ = cd_cache_.video_quality;
video_encode_format_button_value_ = cd_cache_.video_encode_format;
enable_hardware_video_codec_ = cd_cache_.enable_hardware_video_codec;
return 0;
}
int MainWindow::StartScreenCapture() {
screen_capturer_ = (ScreenCapturer *)screen_capturer_factory_->Create();
ScreenCapturer::RECORD_DESKTOP_RECT rect;
rect.left = 0;
rect.top = 0;
rect.right = screen_width_;
rect.bottom = screen_height_;
last_frame_time_ = std::chrono::high_resolution_clock::now();
int screen_capturer_init_ret = screen_capturer_->Init(
rect, 60,
[this](unsigned char *data, int size, int width, int height) -> void {
auto now_time = std::chrono::high_resolution_clock::now();
std::chrono::duration<double> duration = now_time - last_frame_time_;
auto tc = duration.count() * 1000;
if (tc >= 0) {
SendData(peer_, DATA_TYPE::VIDEO, (const char *)data,
NV12_BUFFER_SIZE);
last_frame_time_ = now_time;
}
});
if (0 == screen_capturer_init_ret) {
screen_capturer_->Start();
} else {
screen_capturer_->Destroy();
delete screen_capturer_;
screen_capturer_ = nullptr;
}
return 0;
}
int MainWindow::StopScreenCapture() {
if (screen_capturer_) {
LOG_INFO("Destroy screen capturer")
screen_capturer_->Destroy();
delete screen_capturer_;
screen_capturer_ = nullptr;
}
return 0;
}
int MainWindow::StartMouseControl() {
device_controller_factory_ = new DeviceControllerFactory();
mouse_controller_ = (MouseController *)device_controller_factory_->Create(
DeviceControllerFactory::Device::Mouse);
int mouse_controller_init_ret =
mouse_controller_->Init(screen_width_, screen_height_);
if (0 != mouse_controller_init_ret) {
LOG_INFO("Destroy mouse controller")
mouse_controller_->Destroy();
mouse_controller_ = nullptr;
}
return 0;
}
int MainWindow::StopMouseControl() {
if (mouse_controller_) {
mouse_controller_->Destroy();
delete mouse_controller_;
mouse_controller_ = nullptr;
}
return 0;
}
int MainWindow::CreateConnectionPeer() {
mac_addr_str_ = GetMac();
params_.use_cfg_file = false;
params_.signal_server_ip = "150.158.81.30";
params_.signal_server_port = 9099;
params_.stun_server_ip = "150.158.81.30";
params_.stun_server_port = 3478;
params_.turn_server_ip = "150.158.81.30";
params_.turn_server_port = 3478;
params_.turn_server_username = "dijunkun";
params_.turn_server_password = "dijunkunpw";
params_.hardware_acceleration = config_center_.IsHardwareVideoCodec();
params_.av1_encoding = config_center_.GetVideoEncodeFormat() ==
ConfigCenter::VIDEO_ENCODE_FORMAT::AV1
? true
: false;
params_.on_receive_video_buffer = OnReceiveVideoBufferCb;
params_.on_receive_audio_buffer = OnReceiveAudioBufferCb;
params_.on_receive_data_buffer = OnReceiveDataBufferCb;
params_.on_signal_status = OnSignalStatusCb;
params_.on_connection_status = OnConnectionStatusCb;
params_.user_data = this;
peer_ = CreatePeer(&params_);
if (peer_) {
LOG_INFO("Create peer instance successful");
local_id_ = mac_addr_str_;
Init(peer_, local_id_.c_str());
LOG_INFO("Peer init finish");
} else {
LOG_INFO("Create peer instance failed");
}
return 0;
}
int MainWindow::Run() {
LoadSettingsIntoCacheFile();
localization_language_ = (ConfigCenter::LANGUAGE)language_button_value_;
localization_language_index_ = language_button_value_;
// Setup SDL
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER |
SDL_INIT_GAMECONTROLLER) != 0) {
printf("Error: %s\n", SDL_GetError());
return -1;
}
// From 2.0.18: Enable native IME.
#ifdef SDL_HINT_IME_SHOW_UI
SDL_SetHint(SDL_HINT_IME_SHOW_UI, "1");
#endif
// Create main window with SDL_Renderer graphics context
SDL_WindowFlags window_flags =
(SDL_WindowFlags)(SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI);
main_window_ = SDL_CreateWindow("Remote Desk", SDL_WINDOWPOS_CENTERED,
SDL_WINDOWPOS_CENTERED, main_window_width_,
main_window_height_, window_flags);
SDL_DisplayMode DM;
SDL_GetCurrentDisplayMode(0, &DM);
screen_width_ = DM.w;
screen_height_ = DM.h;
sdl_renderer_ = SDL_CreateRenderer(
main_window_, -1, SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_ACCELERATED);
if (sdl_renderer_ == nullptr) {
SDL_Log("Error creating SDL_Renderer!");
return 0;
}
pixformat_ = SDL_PIXELFORMAT_NV12;
sdl_texture_ =
SDL_CreateTexture(sdl_renderer_, pixformat_, SDL_TEXTUREACCESS_STREAMING,
texture_width_, texture_height_);
// Setup Dear ImGui context
IMGUI_CHECKVERSION();
ImGui::CreateContext();
ImGuiIO &io = ImGui::GetIO();
io.ConfigFlags |=
ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls
io.ConfigFlags |=
ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls
if (config_center_.GetLanguage() == ConfigCenter::LANGUAGE::CHINESE) {
// Load Fonts
#ifdef _WIN32
std::string default_font_path = "c:/windows/fonts/simhei.ttf";
std::ifstream font_path_f(default_font_path.c_str());
std::string font_path =
font_path_f.good() ? "c:/windows/fonts/simhei.ttf" : "";
if (!font_path.empty()) {
io.Fonts->AddFontFromFileTTF(font_path.c_str(), 13.0f, NULL,
io.Fonts->GetGlyphRangesChineseFull());
}
#elif __APPLE__
std::string default_font_path = "/System/Library/Fonts/PingFang.ttc";
std::ifstream font_path_f(default_font_path.c_str());
std::string font_path =
font_path_f.good() ? "/System/Library/Fonts/PingFang.ttc" : "";
if (!font_path.empty()) {
io.Fonts->AddFontFromFileTTF(font_path.c_str(), 13.0f, NULL,
io.Fonts->GetGlyphRangesChineseFull());
}
#elif __linux__
io.Fonts->AddFontFromFileTTF("c:/windows/fonts/msyh.ttc", 13.0f, NULL,
io.Fonts->GetGlyphRangesChineseFull());
#endif
}
// Setup Dear ImGui style
// ImGui::StyleColorsDark();
ImGui::StyleColorsLight();
// Setup Platform/Renderer backends
ImGui_ImplSDL2_InitForSDLRenderer(main_window_, sdl_renderer_);
ImGui_ImplSDLRenderer2_Init(sdl_renderer_);
// Our state
ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f);
CreateConnectionPeer();
{
nv12_buffer_ = new char[NV12_BUFFER_SIZE];
// Screen capture
screen_capturer_factory_ = new ScreenCapturerFactory();
// Mouse control
device_controller_factory_ = new DeviceControllerFactory();
}
// Main loop
while (!exit_) {
if (SignalStatus::SignalConnected == signal_status_ &&
!is_create_connection_) {
is_create_connection_ =
CreateConnection(peer_, mac_addr_str_.c_str(), input_password_)
? false
: true;
LOG_INFO("Connected with signal server, create p2p connection");
}
if (!inited_ ||
localization_language_index_last_ != localization_language_index_) {
connect_button_label_ =
connect_button_pressed_
? localization::disconnect[localization_language_index_]
: localization::connect[localization_language_index_];
fullscreen_button_label_ =
fullscreen_button_pressed_
? localization::exit_fullscreen[localization_language_index_]
: localization::fullscreen[localization_language_index_];
mouse_control_button_label_ =
mouse_control_button_pressed_
? localization::release_mouse[localization_language_index_]
: localization::control_mouse[localization_language_index_];
settings_button_label_ =
localization::settings[localization_language_index_];
inited_ = true;
localization_language_index_last_ = localization_language_index_;
}
if (start_screen_capture_ && !screen_capture_is_started_) {
StartScreenCapture();
screen_capture_is_started_ = true;
} else if (!start_screen_capture_ && screen_capture_is_started_) {
StopScreenCapture();
screen_capture_is_started_ = false;
}
if (start_mouse_control_ && !mouse_control_is_started_) {
StartMouseControl();
mouse_control_is_started_ = true;
} else if (!start_mouse_control_ && mouse_control_is_started_) {
StopMouseControl();
mouse_control_is_started_ = false;
}
// Start the Dear ImGui frame
ImGui_ImplSDLRenderer2_NewFrame();
ImGui_ImplSDL2_NewFrame();
ImGui::NewFrame();
if (connection_established_ && !subwindow_hovered_ && control_mouse_) {
ImGui::SetMouseCursor(ImGuiMouseCursor_None);
}
// main window layout
{
ImGui::SetNextWindowPos(ImVec2(0, 0), ImGuiCond_Once);
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
ImGui::SetNextWindowSize(
ImVec2(MENU_WINDOW_WIDTH_CN, MENU_WINDOW_HEIGHT_CN));
} else {
ImGui::SetNextWindowSize(
ImVec2(MENU_WINDOW_WIDTH_EN, MENU_WINDOW_HEIGHT_EN));
}
if (!connection_established_) {
ImGui::SetNextWindowPos(ImVec2(0, 0), ImGuiCond_Always);
ImGui::Begin(localization::menu[localization_language_index_].c_str(),
nullptr,
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse |
ImGuiWindowFlags_NoMove);
} else {
// ImGui::SetNextWindowCollapsed(true, ImGuiCond_Once);
ImGui::Begin(localization::menu[localization_language_index_].c_str(),
nullptr, ImGuiWindowFlags_None);
}
{
subwindow_hovered_ = ImGui::IsWindowHovered();
// local
{
ImGui::Text(
localization::local_id[localization_language_index_].c_str());
ImGui::SameLine();
ImGui::SetNextItemWidth(IPUT_WINDOW_WIDTH);
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
ImGui::SetCursorPosX(INPUT_WINDOW_PADDING_CN);
} else {
ImGui::SetCursorPosX(INPUT_WINDOW_PADDING_EN);
}
ImGui::InputText("##local_id", (char *)mac_addr_str_.c_str(),
mac_addr_str_.length() + 1,
ImGuiInputTextFlags_CharsUppercase |
ImGuiInputTextFlags_ReadOnly);
ImGui::Text(
localization::password[localization_language_index_].c_str());
ImGui::SameLine();
strncpy(input_password_tmp_, input_password_,
sizeof(input_password_));
ImGui::SetNextItemWidth(IPUT_WINDOW_WIDTH);
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
ImGui::SetCursorPosX(INPUT_WINDOW_PADDING_CN);
} else {
ImGui::SetCursorPosX(INPUT_WINDOW_PADDING_EN);
}
ImGui::InputTextWithHint(
"##server_pwd",
localization::max_password_len[localization_language_index_]
.c_str(),
input_password_, IM_ARRAYSIZE(input_password_),
ImGuiInputTextFlags_CharsNoBlank);
if (strcmp(input_password_tmp_, input_password_)) {
SaveSettingsIntoCacheFile();
}
}
ImGui::Spacing();
ImGui::Separator();
ImGui::Spacing();
// remote
{
ImGui::Text(
localization::remote_id[localization_language_index_].c_str());
ImGui::SameLine();
ImGui::SetNextItemWidth(IPUT_WINDOW_WIDTH);
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
ImGui::SetCursorPosX(INPUT_WINDOW_PADDING_CN);
} else {
ImGui::SetCursorPosX(INPUT_WINDOW_PADDING_EN);
}
ImGui::InputTextWithHint("##remote_id_", mac_addr_str_.c_str(),
remote_id_, IM_ARRAYSIZE(remote_id_),
ImGuiInputTextFlags_CharsUppercase |
ImGuiInputTextFlags_CharsNoBlank);
ImGui::Spacing();
ImGui::Text(
localization::password[localization_language_index_].c_str());
ImGui::SameLine();
ImGui::SetNextItemWidth(IPUT_WINDOW_WIDTH);
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
ImGui::SetCursorPosX(INPUT_WINDOW_PADDING_CN);
} else {
ImGui::SetCursorPosX(INPUT_WINDOW_PADDING_EN);
}
ImGui::InputTextWithHint(
"##client_pwd",
localization::max_password_len[localization_language_index_]
.c_str(),
client_password_, IM_ARRAYSIZE(client_password_),
ImGuiInputTextFlags_CharsNoBlank);
ImGui::Spacing();
ImGui::Separator();
ImGui::Spacing();
if (ImGui::Button(connect_button_label_.c_str()) || rejoin_) {
int ret = -1;
if ("SignalConnected" == signal_status_str_) {
if (connect_button_label_ ==
localization::connect[localization_language_index_] &&
!connection_established_ && strlen(remote_id_)) {
if (remote_id_ == local_id_ && !peer_reserved_) {
peer_reserved_ = CreatePeer(&params_);
if (peer_reserved_) {
LOG_INFO("Create peer[reserved] instance successful");
std::string local_id = "C-" + mac_addr_str_;
Init(peer_reserved_, local_id.c_str());
LOG_INFO("Peer[reserved] init finish");
} else {
LOG_INFO("Create peer[reserved] instance failed");
}
}
ret = JoinConnection(peer_reserved_ ? peer_reserved_ : peer_,
remote_id_, client_password_);
if (0 == ret) {
if (!peer_reserved_) {
is_client_mode_ = true;
}
rejoin_ = false;
} else {
rejoin_ = true;
}
} else if (connect_button_label_ ==
localization::disconnect
[localization_language_index_] &&
connection_established_) {
ret = LeaveConnection(peer_reserved_ ? peer_reserved_ : peer_);
if (0 == ret) {
rejoin_ = false;
memset(audio_buffer_, 0, 960);
connection_established_ = false;
received_frame_ = false;
is_client_mode_ = false;
}
}
if (0 == ret) {
connect_button_pressed_ = !connect_button_pressed_;
connect_button_label_ =
connect_button_pressed_
? localization::disconnect[localization_language_index_]
: localization::connect[localization_language_index_];
}
}
}
}
}
ImGui::Spacing();
ImGui::Separator();
ImGui::Spacing();
// Mouse control
if (ImGui::Button(mouse_control_button_label_.c_str())) {
if (mouse_control_button_label_ ==
localization::control_mouse[localization_language_index_] &&
connection_established_) {
mouse_control_button_pressed_ = true;
control_mouse_ = true;
mouse_control_button_label_ =
localization::release_mouse[localization_language_index_];
} else {
control_mouse_ = false;
mouse_control_button_label_ =
localization::control_mouse[localization_language_index_];
}
mouse_control_button_pressed_ = !mouse_control_button_pressed_;
}
ImGui::SameLine();
// Fullscreen
if (ImGui::Button(fullscreen_button_label_.c_str())) {
if (fullscreen_button_label_ ==
localization::fullscreen[localization_language_index_]) {
main_window_width_before_fullscreen_ = main_window_width_;
main_window_height_before_fullscreen_ = main_window_height_;
SDL_SetWindowFullscreen(main_window_, SDL_WINDOW_FULLSCREEN_DESKTOP);
fullscreen_button_label_ =
localization::exit_fullscreen[localization_language_index_];
} else {
SDL_SetWindowFullscreen(main_window_, SDL_FALSE);
SDL_SetWindowSize(main_window_, main_window_width_before_fullscreen_,
main_window_height_before_fullscreen_);
main_window_width_ = main_window_width_before_fullscreen_;
main_window_height_ = main_window_height_before_fullscreen_;
fullscreen_button_label_ =
localization::fullscreen[localization_language_index_];
}
fullscreen_button_pressed_ = !fullscreen_button_pressed_;
}
ImGui::Spacing();
ImGui::Separator();
ImGui::Spacing();
if (ImGui::Button(settings_button_label_.c_str())) {
settings_button_pressed_ = !settings_button_pressed_;
settings_window_pos_reset_ = true;
}
if (settings_button_pressed_) {
if (settings_window_pos_reset_) {
const ImGuiViewport *viewport = ImGui::GetMainViewport();
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
ImGui::SetNextWindowPos(
ImVec2((viewport->WorkSize.x - viewport->WorkPos.x -
SETTINGS_WINDOW_WIDTH_CN) /
2,
(viewport->WorkSize.y - viewport->WorkPos.y -
SETTINGS_WINDOW_HEIGHT_CN) /
2));
ImGui::SetNextWindowSize(
ImVec2(SETTINGS_WINDOW_WIDTH_CN, SETTINGS_WINDOW_HEIGHT_CN));
} else {
ImGui::SetNextWindowPos(
ImVec2((viewport->WorkSize.x - viewport->WorkPos.x -
SETTINGS_WINDOW_WIDTH_EN) /
2,
(viewport->WorkSize.y - viewport->WorkPos.y -
SETTINGS_WINDOW_HEIGHT_EN) /
2));
ImGui::SetNextWindowSize(
ImVec2(SETTINGS_WINDOW_WIDTH_EN, SETTINGS_WINDOW_HEIGHT_EN));
}
settings_window_pos_reset_ = false;
}
// Settings
ImGui::Begin(
localization::settings[localization_language_index_].c_str(),
nullptr,
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse |
ImGuiWindowFlags_NoSavedSettings);
{
subwindow_hovered_ = ImGui::IsWindowHovered();
const char *language_items[] = {
localization::language_zh[localization_language_index_].c_str(),
localization::language_en[localization_language_index_].c_str()};
ImGui::SetCursorPosY(32);
ImGui::Text(
localization::language[localization_language_index_].c_str());
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
ImGui::SetCursorPosX(LANGUAGE_SELECT_WINDOW_PADDING_CN);
} else {
ImGui::SetCursorPosX(LANGUAGE_SELECT_WINDOW_PADDING_EN);
}
ImGui::SetCursorPosY(30);
ImGui::SetNextItemWidth(SETTINGS_SELECT_WINDOW_WIDTH);
ImGui::Combo("##language", &language_button_value_, language_items,
IM_ARRAYSIZE(language_items));
}
ImGui::Separator();
{
const char *video_quality_items[] = {
localization::video_quality_high[localization_language_index_]
.c_str(),
localization::video_quality_medium[localization_language_index_]
.c_str(),
localization::video_quality_low[localization_language_index_]
.c_str()};
ImGui::SetCursorPosY(62);
ImGui::Text(localization::video_quality[localization_language_index_]
.c_str());
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
ImGui::SetCursorPosX(VIDEO_QUALITY_SELECT_WINDOW_PADDING_CN);
} else {
ImGui::SetCursorPosX(VIDEO_QUALITY_SELECT_WINDOW_PADDING_EN);
}
ImGui::SetCursorPosY(60);
ImGui::SetNextItemWidth(SETTINGS_SELECT_WINDOW_WIDTH);
ImGui::Combo("##video_quality", &video_quality_button_value_,
video_quality_items, IM_ARRAYSIZE(video_quality_items));
}
ImGui::Separator();
{
const char *video_encode_format_items[] = {
localization::av1[localization_language_index_].c_str(),
localization::h264[localization_language_index_].c_str()};
ImGui::SetCursorPosY(92);
ImGui::Text(
localization::video_encode_format[localization_language_index_]
.c_str());
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
ImGui::SetCursorPosX(VIDEO_ENCODE_FORMAT_SELECT_WINDOW_PADDING_CN);
} else {
ImGui::SetCursorPosX(VIDEO_ENCODE_FORMAT_SELECT_WINDOW_PADDING_EN);
}
ImGui::SetCursorPosY(90);
ImGui::SetNextItemWidth(SETTINGS_SELECT_WINDOW_WIDTH);
ImGui::Combo("##video_encode_format",
&video_encode_format_button_value_,
video_encode_format_items,
IM_ARRAYSIZE(video_encode_format_items));
}
ImGui::Separator();
{
ImGui::SetCursorPosY(122);
ImGui::Text(localization::enable_hardware_video_codec
[localization_language_index_]
.c_str());
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
ImGui::SetCursorPosX(
ENABLE_HARDWARE_VIDEO_CODEC_CHECKBOX_PADDING_CN);
} else {
ImGui::SetCursorPosX(
ENABLE_HARDWARE_VIDEO_CODEC_CHECKBOX_PADDING_EN);
}
ImGui::SetCursorPosY(120);
ImGui::Checkbox("##enable_hardware_video_codec",
&enable_hardware_video_codec_);
}
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
ImGui::SetCursorPosX(SETTINGS_OK_BUTTON_PADDING_CN);
} else {
ImGui::SetCursorPosX(SETTINGS_OK_BUTTON_PADDING_EN);
}
ImGui::SetCursorPosY(160.0f);
// OK
if (ImGui::Button(
localization::ok[localization_language_index_].c_str())) {
settings_button_pressed_ = false;
// Language
if (language_button_value_ == 0) {
config_center_.SetLanguage(ConfigCenter::LANGUAGE::CHINESE);
} else {
config_center_.SetLanguage(ConfigCenter::LANGUAGE::ENGLISH);
}
language_button_value_last_ = language_button_value_;
localization_language_ =
(ConfigCenter::LANGUAGE)language_button_value_;
localization_language_index_ = language_button_value_;
LOG_INFO("Set localization language: {}",
localization_language_index_ == 0 ? "zh" : "en");
// Video quality
if (video_quality_button_value_ == 0) {
config_center_.SetVideoQuality(ConfigCenter::VIDEO_QUALITY::HIGH);
} else if (video_quality_button_value_ == 1) {
config_center_.SetVideoQuality(ConfigCenter::VIDEO_QUALITY::MEDIUM);
} else {
config_center_.SetVideoQuality(ConfigCenter::VIDEO_QUALITY::LOW);
}
video_quality_button_value_last_ = video_quality_button_value_;
// Video encode format
if (video_encode_format_button_value_ == 0) {
config_center_.SetVideoEncodeFormat(
ConfigCenter::VIDEO_ENCODE_FORMAT::AV1);
} else if (video_encode_format_button_value_ == 1) {
config_center_.SetVideoEncodeFormat(
ConfigCenter::VIDEO_ENCODE_FORMAT::H264);
}
video_encode_format_button_value_last_ =
video_encode_format_button_value_;
// Hardware video codec
if (enable_hardware_video_codec_) {
config_center_.SetHardwareVideoCodec(true);
} else {
config_center_.SetHardwareVideoCodec(false);
}
enable_hardware_video_codec_last_ = enable_hardware_video_codec_;
SaveSettingsIntoCacheFile();
settings_window_pos_reset_ = true;
// Recreate peer instance
LoadSettingsIntoCacheFile();
// Recreate peer instance
{
DestroyPeer(peer_);
CreateConnectionPeer();
LOG_INFO("Recreate peer instance successful");
}
}
ImGui::SameLine();
// Cancel
if (ImGui::Button(
localization::cancel[localization_language_index_].c_str())) {
settings_button_pressed_ = false;
if (language_button_value_ != language_button_value_last_) {
language_button_value_ = language_button_value_last_;
}
if (video_quality_button_value_ != video_quality_button_value_last_) {
video_quality_button_value_ = video_quality_button_value_last_;
}
if (video_encode_format_button_value_ !=
video_encode_format_button_value_last_) {
video_encode_format_button_value_ =
video_encode_format_button_value_last_;
}
if (enable_hardware_video_codec_ !=
enable_hardware_video_codec_last_) {
enable_hardware_video_codec_ = enable_hardware_video_codec_last_;
}
settings_window_pos_reset_ = true;
}
ImGui::End();
}
ImGui::End();
}
// Rendering
ImGui::Render();
SDL_RenderSetScale(sdl_renderer_, io.DisplayFramebufferScale.x,
io.DisplayFramebufferScale.y);
SDL_Event event;
while (SDL_PollEvent(&event)) {
ImGui_ImplSDL2_ProcessEvent(&event);
if (event.type == SDL_QUIT) {
exit_ = true;
} else if (event.type == SDL_WINDOWEVENT &&
event.window.event == SDL_WINDOWEVENT_RESIZED) {
int window_w_last = main_window_width_;
int window_h_last = main_window_height_;
SDL_GetWindowSize(main_window_, &main_window_width_,
&main_window_height_);
int w_change_ratio = abs(main_window_width_ - window_w_last) / 16;
int h_change_ratio = abs(main_window_height_ - window_h_last) / 9;
if (w_change_ratio > h_change_ratio) {
main_window_height_ = main_window_width_ * 9 / 16;
} else {
main_window_width_ = main_window_height_ * 16 / 9;
}
SDL_SetWindowSize(main_window_, main_window_width_,
main_window_height_);
} else if (event.type == SDL_WINDOWEVENT &&
event.window.event == SDL_WINDOWEVENT_CLOSE &&
event.window.windowID == SDL_GetWindowID(main_window_)) {
exit_ = true;
} else if (event.type == REFRESH_EVENT) {
sdl_rect_.x = 0;
sdl_rect_.y = 0;
sdl_rect_.w = main_window_width_;
sdl_rect_.h = main_window_height_;
SDL_UpdateTexture(sdl_texture_, NULL, dst_buffer_, 1280);
} else {
if (connection_established_) {
ProcessMouseKeyEven(event);
}
}
}
SDL_RenderClear(sdl_renderer_);
SDL_RenderCopy(sdl_renderer_, sdl_texture_, NULL, &sdl_rect_);
if (!connection_established_ || !received_frame_) {
SDL_RenderClear(sdl_renderer_);
SDL_SetRenderDrawColor(
sdl_renderer_, (Uint8)(clear_color.x * 0), (Uint8)(clear_color.y * 0),
(Uint8)(clear_color.z * 0), (Uint8)(clear_color.w * 0));
}
ImGui_ImplSDLRenderer2_RenderDrawData(ImGui::GetDrawData());
SDL_RenderPresent(sdl_renderer_);
frame_count_++;
end_time_ = SDL_GetTicks();
elapsed_time_ = end_time_ - start_time_;
if (elapsed_time_ >= 1000) {
fps_ = frame_count_ / (elapsed_time_ / 1000);
frame_count_ = 0;
window_title = "Remote Desk Client FPS [" + std::to_string(fps_) +
"] status [" + connection_status_str_ + "|" +
connection_status_str_ + "]";
// For MacOS, UI frameworks can only be called from the main thread
SDL_SetWindowTitle(main_window_, window_title.c_str());
start_time_ = end_time_;
}
}
// Cleanup
if (is_create_connection_) {
LeaveConnection(peer_);
is_client_mode_ = false;
}
if (peer_) {
DestroyPeer(peer_);
}
if (peer_reserved_) {
DestroyPeer(peer_reserved_);
}
SDL_CloseAudioDevice(output_dev_);
SDL_CloseAudioDevice(input_dev_);
ImGui_ImplSDLRenderer2_Shutdown();
ImGui_ImplSDL2_Shutdown();
ImGui::DestroyContext();
SDL_DestroyRenderer(sdl_renderer_);
SDL_DestroyWindow(main_window_);
SDL_CloseAudio();
SDL_Quit();
return 0;
}

View File

@@ -0,0 +1,190 @@
/*
* @Author: DI JUNKUN
* @Date: 2024-05-29
* Copyright (c) 2024 by DI JUNKUN, All Rights Reserved.
*/
#ifndef _MAIN_WINDOW_H_
#define _MAIN_WINDOW_H_
#include <SDL.h>
#include <atomic>
#include <chrono>
#include <string>
#include "../../thirdparty/projectx/src/interface/x.h"
#include "config_center.h"
#include "device_controller_factory.h"
#include "imgui.h"
#include "imgui_impl_sdl2.h"
#include "imgui_impl_sdlrenderer2.h"
#include "screen_capturer_factory.h"
class MainWindow {
public:
MainWindow();
~MainWindow();
public:
int Run();
public:
static void OnReceiveVideoBufferCb(const char *data, size_t size,
const char *user_id, size_t user_id_size,
void *user_data);
static void OnReceiveAudioBufferCb(const char *data, size_t size,
const char *user_id, size_t user_id_size,
void *user_data);
static void OnReceiveDataBufferCb(const char *data, size_t size,
const char *user_id, size_t user_id_size,
void *user_data);
static void OnSignalStatusCb(SignalStatus status, void *user_data);
static void OnConnectionStatusCb(ConnectionStatus status, void *user_data);
private:
int ProcessMouseKeyEven(SDL_Event &ev);
void SdlCaptureAudioIn(void *userdata, Uint8 *stream, int len);
void SdlCaptureAudioOut(void *userdata, Uint8 *stream, int len);
private:
int SaveSettingsIntoCacheFile();
int LoadSettingsIntoCacheFile();
int StartScreenCapture();
int StopScreenCapture();
int StartMouseControl();
int StopMouseControl();
int CreateConnectionPeer();
private:
typedef struct {
char password[7];
int language;
int video_quality;
int video_encode_format;
bool enable_hardware_video_codec;
} CDCache;
private:
FILE *cd_cache_file_ = nullptr;
CDCache cd_cache_;
ConfigCenter config_center_;
ConfigCenter::LANGUAGE localization_language_ =
ConfigCenter::LANGUAGE::CHINESE;
int localization_language_index_ = -1;
int localization_language_index_last_ = -1;
private:
std::string window_title = "Remote Desk Client";
std::string mac_addr_str_ = "";
std::string connect_button_label_ = "Connect";
std::string fullscreen_button_label_ = "Fullscreen";
std::string mouse_control_button_label_ = "Mouse Control";
std::string settings_button_label_ = "Setting";
char input_password_tmp_[7] = "";
char input_password_[7] = "";
std::string local_id_ = "";
char remote_id_[20] = "";
char client_password_[20] = "";
bool is_client_mode_ = false;
private:
int screen_width_ = 1280;
int screen_height_ = 720;
int main_window_width_ = 1280;
int main_window_height_ = 720;
int main_window_width_before_fullscreen_ = 1280;
int main_window_height_before_fullscreen_ = 720;
int texture_width_ = 1280;
int texture_height_ = 720;
SDL_Texture *sdl_texture_ = nullptr;
SDL_Renderer *sdl_renderer_ = nullptr;
SDL_Rect sdl_rect_;
SDL_Window *main_window_;
uint32_t pixformat_ = 0;
bool inited_ = false;
bool exit_ = false;
bool connection_established_ = false;
bool subwindow_hovered_ = false;
bool connect_button_pressed_ = false;
bool fullscreen_button_pressed_ = false;
bool mouse_control_button_pressed_ = false;
bool settings_button_pressed_ = false;
bool received_frame_ = false;
bool is_create_connection_ = false;
bool audio_buffer_fresh_ = false;
bool rejoin_ = false;
bool control_mouse_ = false;
int fps_ = 0;
uint32_t start_time_;
uint32_t end_time_;
uint32_t elapsed_time_;
uint32_t frame_count_ = 0;
private:
ConnectionStatus connection_status_ = ConnectionStatus::Closed;
SignalStatus signal_status_ = SignalStatus::SignalClosed;
std::string signal_status_str_ = "";
std::string connection_status_str_ = "";
private:
PeerPtr *peer_ = nullptr;
PeerPtr *peer_reserved_ = nullptr;
Params params_;
private:
SDL_AudioDeviceID input_dev_;
SDL_AudioDeviceID output_dev_;
unsigned char audio_buffer_[960];
int audio_len_ = 0;
char *nv12_buffer_ = nullptr;
unsigned char *dst_buffer_ = new unsigned char[1280 * 720 * 3];
private:
ScreenCapturerFactory *screen_capturer_factory_ = nullptr;
ScreenCapturer *screen_capturer_ = nullptr;
DeviceControllerFactory *device_controller_factory_ = nullptr;
MouseController *mouse_controller_ = nullptr;
#ifdef __linux__
std::chrono::_V2::system_clock::time_point last_frame_time_;
#else
std::chrono::steady_clock::time_point last_frame_time_;
#endif
private:
int language_button_value_ = 0;
int video_quality_button_value_ = 0;
int video_encode_format_button_value_ = 0;
bool enable_hardware_video_codec_ = false;
int language_button_value_last_ = 0;
int video_quality_button_value_last_ = 0;
int video_encode_format_button_value_last_ = 0;
bool enable_hardware_video_codec_last_ = false;
private:
std::atomic<bool> start_screen_capture_{false};
std::atomic<bool> start_mouse_control_{false};
std::atomic<bool> screen_capture_is_started_{false};
std::atomic<bool> mouse_control_is_started_{false};
private:
bool settings_window_pos_reset_ = true;
};
#endif

View File

@@ -0,0 +1,216 @@
#include "device_controller.h"
#include "localization.h"
#include "main_window.h"
// Refresh Event
#define REFRESH_EVENT (SDL_USEREVENT + 1)
#define NV12_BUFFER_SIZE 1280 * 720 * 3 / 2
#ifdef REMOTE_DESK_DEBUG
#else
#define MOUSE_CONTROL 1
#endif
int MainWindow::ProcessMouseKeyEven(SDL_Event &ev) {
if (!control_mouse_) {
return 0;
}
float ratio = (float)(1280.0 / main_window_width_);
RemoteAction remote_action;
remote_action.m.x = (size_t)(ev.button.x * ratio);
remote_action.m.y = (size_t)(ev.button.y * ratio);
if (SDL_KEYDOWN == ev.type) // SDL_KEYUP
{
// printf("SDLK_DOWN: %d\n", SDL_KeyCode(ev.key.keysym.sym));
if (SDLK_DOWN == ev.key.keysym.sym) {
// printf("SDLK_DOWN \n");
} else if (SDLK_UP == ev.key.keysym.sym) {
// printf("SDLK_UP \n");
} else if (SDLK_LEFT == ev.key.keysym.sym) {
// printf("SDLK_LEFT \n");
} else if (SDLK_RIGHT == ev.key.keysym.sym) {
// printf("SDLK_RIGHT \n");
}
} else if (SDL_MOUSEBUTTONDOWN == ev.type) {
remote_action.type = ControlType::mouse;
if (SDL_BUTTON_LEFT == ev.button.button) {
remote_action.m.flag = MouseFlag::left_down;
} else if (SDL_BUTTON_RIGHT == ev.button.button) {
remote_action.m.flag = MouseFlag::right_down;
}
if (subwindow_hovered_) {
remote_action.m.flag = MouseFlag::move;
}
SendData(peer_, DATA_TYPE::DATA, (const char *)&remote_action,
sizeof(remote_action));
} else if (SDL_MOUSEBUTTONUP == ev.type) {
remote_action.type = ControlType::mouse;
if (SDL_BUTTON_LEFT == ev.button.button) {
remote_action.m.flag = MouseFlag::left_up;
} else if (SDL_BUTTON_RIGHT == ev.button.button) {
remote_action.m.flag = MouseFlag::right_up;
}
if (subwindow_hovered_) {
remote_action.m.flag = MouseFlag::move;
}
SendData(peer_, DATA_TYPE::DATA, (const char *)&remote_action,
sizeof(remote_action));
} else if (SDL_MOUSEMOTION == ev.type) {
remote_action.type = ControlType::mouse;
remote_action.m.flag = MouseFlag::move;
SendData(peer_, DATA_TYPE::DATA, (const char *)&remote_action,
sizeof(remote_action));
} else if (SDL_QUIT == ev.type) {
SDL_Event event;
event.type = SDL_QUIT;
SDL_PushEvent(&event);
printf("SDL_QUIT\n");
return 0;
}
return 0;
}
void MainWindow::SdlCaptureAudioIn(void *userdata, Uint8 *stream, int len) {
if (1) {
if ("Connected" == connection_status_str_) {
SendData(peer_, DATA_TYPE::AUDIO, (const char *)stream, len);
}
} else {
memcpy(audio_buffer_, stream, len);
audio_len_ = len;
SDL_Delay(10);
audio_buffer_fresh_ = true;
}
}
void MainWindow::SdlCaptureAudioOut(void *userdata, Uint8 *stream, int len) {
if (!audio_buffer_fresh_) {
return;
}
SDL_memset(stream, 0, len);
if (audio_len_ == 0) {
return;
} else {
}
len = (len > audio_len_ ? audio_len_ : len);
SDL_MixAudioFormat(stream, audio_buffer_, AUDIO_S16LSB, len,
SDL_MIX_MAXVOLUME);
audio_buffer_fresh_ = false;
}
void MainWindow::OnReceiveVideoBufferCb(const char *data, size_t size,
const char *user_id,
size_t user_id_size, void *user_data) {
MainWindow *main_window = (MainWindow *)user_data;
if (main_window->connection_established_) {
memcpy(main_window->dst_buffer_, data, size);
SDL_Event event;
event.type = REFRESH_EVENT;
SDL_PushEvent(&event);
main_window->received_frame_ = true;
}
}
void MainWindow::OnReceiveAudioBufferCb(const char *data, size_t size,
const char *user_id,
size_t user_id_size, void *user_data) {
MainWindow *main_window = (MainWindow *)user_data;
main_window->audio_buffer_fresh_ = true;
SDL_QueueAudio(main_window->output_dev_, data, (uint32_t)size);
}
void MainWindow::OnReceiveDataBufferCb(const char *data, size_t size,
const char *user_id, size_t user_id_size,
void *user_data) {
MainWindow *main_window = (MainWindow *)user_data;
std::string user(user_id, user_id_size);
RemoteAction remote_action;
memcpy(&remote_action, data, sizeof(remote_action));
#if MOUSE_CONTROL
if (main_window->mouse_controller_) {
main_window->mouse_controller_->SendCommand(remote_action);
}
#endif
}
void MainWindow::OnSignalStatusCb(SignalStatus status, void *user_data) {
MainWindow *main_window = (MainWindow *)user_data;
main_window->signal_status_ = status;
if (SignalStatus::SignalConnecting == status) {
main_window->signal_status_str_ = "SignalConnecting";
} else if (SignalStatus::SignalConnected == status) {
main_window->signal_status_str_ = "SignalConnected";
} else if (SignalStatus::SignalFailed == status) {
main_window->signal_status_str_ = "SignalFailed";
} else if (SignalStatus::SignalClosed == status) {
main_window->signal_status_str_ = "SignalClosed";
} else if (SignalStatus::SignalReconnecting == status) {
main_window->signal_status_str_ = "SignalReconnecting";
}
}
void MainWindow::OnConnectionStatusCb(ConnectionStatus status,
void *user_data) {
MainWindow *main_window = (MainWindow *)user_data;
main_window->connection_status_ = status;
if (ConnectionStatus::Connecting == status) {
main_window->connection_status_str_ = "Connecting";
} else if (ConnectionStatus::Connected == status) {
main_window->connection_status_str_ = "Connected";
main_window->connection_established_ = true;
if (!main_window->is_client_mode_) {
main_window->start_screen_capture_ = true;
main_window->start_mouse_control_ = true;
}
} else if (ConnectionStatus::Disconnected == status) {
main_window->connection_status_str_ = "Disconnected";
} else if (ConnectionStatus::Failed == status) {
main_window->connection_status_str_ = "Failed";
} else if (ConnectionStatus::Closed == status) {
main_window->connection_status_str_ = "Closed";
main_window->start_screen_capture_ = false;
main_window->start_mouse_control_ = false;
main_window->connection_established_ = false;
main_window->control_mouse_ = false;
if (main_window->dst_buffer_) {
memset(main_window->dst_buffer_, 0, 1280 * 720 * 3);
SDL_UpdateTexture(main_window->sdl_texture_, NULL,
main_window->dst_buffer_, 1280);
}
} else if (ConnectionStatus::IncorrectPassword == status) {
main_window->connection_status_str_ = "Incorrect password";
if (main_window->connect_button_pressed_) {
main_window->connect_button_pressed_ = false;
main_window->connection_established_ = false;
main_window->connect_button_label_ =
main_window->connect_button_pressed_
? localization::disconnect[main_window
->localization_language_index_]
: localization::connect[main_window
->localization_language_index_];
}
} else if (ConnectionStatus::NoSuchTransmissionId == status) {
main_window->connection_status_str_ = "No such transmission id";
if (main_window->connect_button_pressed_) {
main_window->connect_button_pressed_ = false;
main_window->connection_established_ = false;
main_window->connect_button_label_ =
main_window->connect_button_pressed_
? localization::disconnect[main_window
->localization_language_index_]
: localization::connect[main_window
->localization_language_index_];
}
}
}

View File

@@ -1,138 +0,0 @@
#include "screen_capture_wgc.h"
#include <Windows.h>
#include <d3d11_4.h>
#include <winrt/Windows.Foundation.Metadata.h>
#include <winrt/Windows.Graphics.Capture.h>
#include <iostream>
BOOL WINAPI EnumMonitorProc(HMONITOR hmonitor, HDC hdc, LPRECT lprc,
LPARAM data) {
MONITORINFOEX info_ex;
info_ex.cbSize = sizeof(MONITORINFOEX);
GetMonitorInfo(hmonitor, &info_ex);
if (info_ex.dwFlags == DISPLAY_DEVICE_MIRRORING_DRIVER) return true;
if (info_ex.dwFlags & MONITORINFOF_PRIMARY) {
*(HMONITOR *)data = hmonitor;
}
return true;
}
HMONITOR GetPrimaryMonitor() {
HMONITOR hmonitor = nullptr;
::EnumDisplayMonitors(NULL, NULL, EnumMonitorProc, (LPARAM)&hmonitor);
return hmonitor;
}
ScreenCaptureWgc::ScreenCaptureWgc() {}
ScreenCaptureWgc::~ScreenCaptureWgc() {
Stop();
CleanUp();
}
bool ScreenCaptureWgc::IsWgcSupported() {
try {
/* no contract for IGraphicsCaptureItemInterop, verify 10.0.18362.0 */
return winrt::Windows::Foundation::Metadata::ApiInformation::
IsApiContractPresent(L"Windows.Foundation.UniversalApiContract", 8);
} catch (const winrt::hresult_error &) {
return false;
} catch (...) {
return false;
}
}
int ScreenCaptureWgc::Init(const RECORD_DESKTOP_RECT &rect, const int fps,
cb_desktop_data cb) {
int error = 0;
if (_inited == true) return error;
_fps = fps;
_on_data = cb;
do {
if (!IsWgcSupported()) {
std::cout << "AE_UNSUPPORT" << std::endl;
error = 2;
break;
}
session_ = new WgcSessionImpl();
if (!session_) {
error = -1;
std::cout << "AE_WGC_CREATE_CAPTURER_FAILED" << std::endl;
break;
}
session_->RegisterObserver(this);
error = session_->Initialize(GetPrimaryMonitor());
_inited = true;
} while (0);
if (error != 0) {
}
return error;
}
int ScreenCaptureWgc::Start() {
if (_running == true) {
std::cout << "record desktop duplication is already running" << std::endl;
return 0;
}
if (_inited == false) {
std::cout << "AE_NEED_INIT" << std::endl;
return 4;
}
_running = true;
session_->Start();
return 0;
}
int ScreenCaptureWgc::Pause() {
_paused = true;
if (session_) session_->Pause();
return 0;
}
int ScreenCaptureWgc::Resume() {
_paused = false;
if (session_) session_->Resume();
return 0;
}
int ScreenCaptureWgc::Stop() {
_running = false;
if (session_) session_->Stop();
return 0;
}
void ScreenCaptureWgc::OnFrame(const WgcSession::wgc_session_frame &frame) {
if (_on_data)
_on_data((unsigned char *)frame.data, frame.width * frame.height * 4,
frame.width, frame.height);
}
void ScreenCaptureWgc::CleanUp() {
_inited = false;
if (session_) session_->Release();
session_ = nullptr;
}

View File

@@ -1,61 +0,0 @@
#ifndef _SCREEN_CAPTURE_WGC_H_
#define _SCREEN_CAPTURE_WGC_H_
#include <atomic>
#include <functional>
#include <string>
#include <thread>
#include "wgc_session.h"
#include "wgc_session_impl.h"
typedef struct {
int left;
int top;
int right;
int bottom;
} RECORD_DESKTOP_RECT;
typedef std::function<void(unsigned char *, int, int, int)> cb_desktop_data;
typedef std::function<void(int)> cb_desktop_error;
class ScreenCaptureWgc : public WgcSession::wgc_session_observer {
public:
ScreenCaptureWgc();
~ScreenCaptureWgc();
public:
bool IsWgcSupported();
int Init(const RECORD_DESKTOP_RECT &rect, const int fps, cb_desktop_data cb);
int Start();
int Pause();
int Resume();
int Stop();
void OnFrame(const WgcSession::wgc_session_frame &frame);
protected:
void CleanUp();
private:
WgcSession *session_ = nullptr;
std::atomic_bool _running;
std::atomic_bool _paused;
std::atomic_bool _inited;
std::thread _thread;
std::string _device_name;
RECORD_DESKTOP_RECT _rect;
int _fps;
cb_desktop_data _on_data;
cb_desktop_error _on_error;
};
#endif

View File

@@ -1,4 +1,4 @@
#include "screen_capture_x11.h"
#include "screen_capturer_x11.h"
#include <iostream>
@@ -7,20 +7,23 @@
#define NV12_BUFFER_SIZE 1280 * 720 * 3 / 2
unsigned char nv12_buffer_[NV12_BUFFER_SIZE];
ScreenCaptureX11::ScreenCaptureX11() {}
ScreenCapturerX11::ScreenCapturerX11() {}
ScreenCaptureX11::~ScreenCaptureX11() {
if (capture_thread_->joinable()) {
ScreenCapturerX11::~ScreenCapturerX11() {
if (inited_ && capture_thread_->joinable()) {
capture_thread_->join();
inited_ = false;
}
}
int ScreenCaptureX11::Init(const RECORD_DESKTOP_RECT &rect, const int fps,
cb_desktop_data cb) {
int ScreenCapturerX11::Init(const RECORD_DESKTOP_RECT &rect, const int fps,
cb_desktop_data cb) {
if (cb) {
_on_data = cb;
}
fps_ = fps;
av_log_set_level(AV_LOG_QUIET);
pFormatCtx_ = avformat_alloc_context();
@@ -33,9 +36,10 @@ int ScreenCaptureX11::Init(const RECORD_DESKTOP_RECT &rect, const int fps,
// av_dict_set(&options_, "follow_mouse", "centered", 0);
// Video frame size. The default is to capture the full screen
// av_dict_set(&options_, "video_size", "1280x720", 0);
ifmt_ = (AVInputFormat *)av_find_input_format("x11grab");
std::string capture_method = "x11grab";
ifmt_ = (AVInputFormat *)av_find_input_format(capture_method.c_str());
if (!ifmt_) {
printf("Couldn't find_input_format\n");
LOG_ERROR("Couldn't find_input_format [{}]", capture_method.c_str());
}
// Grab at position 10,20
@@ -92,10 +96,14 @@ int ScreenCaptureX11::Init(const RECORD_DESKTOP_RECT &rect, const int fps,
pFrame_->width, pFrame_->height, pCodecCtx_->pix_fmt, pFrameNv12_->width,
pFrameNv12_->height, AV_PIX_FMT_NV12, SWS_BICUBIC, NULL, NULL, NULL);
inited_ = true;
return 0;
}
int ScreenCaptureX11::Start() {
int ScreenCapturerX11::Destroy() { return 0; }
int ScreenCapturerX11::Start() {
capture_thread_.reset(new std::thread([this]() {
while (1) {
if (av_read_frame(pFormatCtx_, packet_) >= 0) {
@@ -125,12 +133,12 @@ int ScreenCaptureX11::Start() {
return 0;
}
int ScreenCaptureX11::Pause() { return 0; }
int ScreenCapturerX11::Stop() { return 0; }
int ScreenCaptureX11::Resume() { return 0; }
int ScreenCapturerX11::Pause() { return 0; }
int ScreenCaptureX11::Stop() { return 0; }
int ScreenCapturerX11::Resume() { return 0; }
void ScreenCaptureX11::OnFrame() {}
void ScreenCapturerX11::OnFrame() {}
void ScreenCaptureX11::CleanUp() {}
void ScreenCapturerX11::CleanUp() {}

View File

@@ -1,11 +1,12 @@
#ifndef _SCREEN_CAPTURE_X11_H_
#define _SCREEN_CAPTURE_X11_H_
#ifndef _SCREEN_CAPTURER_X11_H_
#define _SCREEN_CAPTURER_X11_H_
#include <atomic>
#include <functional>
#include <string>
#include <thread>
#include "screen_capturer.h"
#ifdef __cplusplus
extern "C" {
#endif
@@ -18,28 +19,23 @@ extern "C" {
};
#endif
typedef struct {
int left;
int top;
int right;
int bottom;
} RECORD_DESKTOP_RECT;
typedef std::function<void(unsigned char *, int, int, int)> cb_desktop_data;
typedef std::function<void(int)> cb_desktop_error;
class ScreenCaptureX11 {
class ScreenCapturerX11 : public ScreenCapturer {
public:
ScreenCaptureX11();
~ScreenCaptureX11();
ScreenCapturerX11();
~ScreenCapturerX11();
public:
int Init(const RECORD_DESKTOP_RECT &rect, const int fps, cb_desktop_data cb);
virtual int Init(const RECORD_DESKTOP_RECT &rect, const int fps,
cb_desktop_data cb);
virtual int Destroy();
virtual int Start();
virtual int Stop();
int Start();
int Pause();
int Resume();
int Stop();
void OnFrame();
@@ -60,12 +56,14 @@ class ScreenCaptureX11 {
int _fps;
cb_desktop_data _on_data;
cb_desktop_error _on_error;
private:
int i_ = 0;
int videoindex_ = 0;
int got_picture_ = 0;
int fps_ = 0;
bool inited_ = false;
// ffmpeg
AVFormatContext *pFormatCtx_ = nullptr;
AVCodecContext *pCodecCtx_ = nullptr;

View File

@@ -1,4 +1,4 @@
#include "screen_capture_avf.h"
#include "screen_capturer_avf.h"
#include <iostream>
@@ -7,16 +7,17 @@
#define NV12_BUFFER_SIZE 1280 * 720 * 3 / 2
unsigned char nv12_buffer_[NV12_BUFFER_SIZE];
ScreenCaptureAvf::ScreenCaptureAvf() {}
ScreenCapturerAvf::ScreenCapturerAvf() {}
ScreenCaptureAvf::~ScreenCaptureAvf() {
if (capture_thread_->joinable()) {
ScreenCapturerAvf::~ScreenCapturerAvf() {
if (inited_ && capture_thread_->joinable()) {
capture_thread_->join();
inited_ = false;
}
}
int ScreenCaptureAvf::Init(const RECORD_DESKTOP_RECT &rect, const int fps,
cb_desktop_data cb) {
int ScreenCapturerAvf::Init(const RECORD_DESKTOP_RECT &rect, const int fps,
cb_desktop_data cb) {
if (cb) {
_on_data = cb;
}
@@ -96,10 +97,14 @@ int ScreenCaptureAvf::Init(const RECORD_DESKTOP_RECT &rect, const int fps,
pFrame_->width, pFrame_->height, pCodecCtx_->pix_fmt, pFrameNv12_->width,
pFrameNv12_->height, AV_PIX_FMT_NV12, SWS_BICUBIC, NULL, NULL, NULL);
inited_ = true;
return 0;
}
int ScreenCaptureAvf::Start() {
int ScreenCapturerAvf::Destroy() { return 0; }
int ScreenCapturerAvf::Start() {
capture_thread_.reset(new std::thread([this]() {
while (1) {
if (av_read_frame(pFormatCtx_, packet_) >= 0) {
@@ -129,12 +134,12 @@ int ScreenCaptureAvf::Start() {
return 0;
}
int ScreenCaptureAvf::Pause() { return 0; }
int ScreenCapturerAvf::Stop() { return 0; }
int ScreenCaptureAvf::Resume() { return 0; }
int ScreenCapturerAvf::Pause() { return 0; }
int ScreenCaptureAvf::Stop() { return 0; }
int ScreenCapturerAvf::Resume() { return 0; }
void ScreenCaptureAvf::OnFrame() {}
void ScreenCapturerAvf::OnFrame() {}
void ScreenCaptureAvf::CleanUp() {}
void ScreenCapturerAvf::CleanUp() {}

View File

@@ -4,14 +4,16 @@
* Copyright (c) 2023 by DI JUNKUN, All Rights Reserved.
*/
#ifndef _SCREEN_CAPTURE_AVF_H_
#define _SCREEN_CAPTURE_AVF_H_
#ifndef _SCREEN_CAPTURER_AVF_H_
#define _SCREEN_CAPTURER_AVF_H_
#include <atomic>
#include <functional>
#include <string>
#include <thread>
#include "screen_capturer.h"
#ifdef __cplusplus
extern "C" {
#endif
@@ -24,28 +26,22 @@ extern "C" {
};
#endif
typedef struct {
int left;
int top;
int right;
int bottom;
} RECORD_DESKTOP_RECT;
typedef std::function<void(unsigned char *, int, int, int)> cb_desktop_data;
typedef std::function<void(int)> cb_desktop_error;
class ScreenCaptureAvf {
class ScreenCapturerAvf : public ScreenCapturer {
public:
ScreenCaptureAvf();
~ScreenCaptureAvf();
ScreenCapturerAvf();
~ScreenCapturerAvf();
public:
int Init(const RECORD_DESKTOP_RECT &rect, const int fps, cb_desktop_data cb);
virtual int Init(const RECORD_DESKTOP_RECT &rect, const int fps,
cb_desktop_data cb);
virtual int Destroy();
virtual int Start();
virtual int Stop();
int Start();
int Pause();
int Resume();
int Stop();
void OnFrame();
@@ -66,12 +62,13 @@ class ScreenCaptureAvf {
int _fps;
cb_desktop_data _on_data;
cb_desktop_error _on_error;
private:
int i_ = 0;
int videoindex_ = 0;
int got_picture_ = 0;
bool inited_ = false;
// ffmpeg
AVFormatContext *pFormatCtx_ = nullptr;
AVCodecContext *pCodecCtx_ = nullptr;

View File

@@ -0,0 +1,35 @@
/*
* @Author: DI JUNKUN
* @Date: 2023-12-15
* Copyright (c) 2023 by DI JUNKUN, All Rights Reserved.
*/
#ifndef _SCREEN_CAPTURER_H_
#define _SCREEN_CAPTURER_H_
#include <functional>
class ScreenCapturer {
public:
typedef struct {
int left;
int top;
int right;
int bottom;
} RECORD_DESKTOP_RECT;
typedef std::function<void(unsigned char *, int, int, int)> cb_desktop_data;
public:
virtual ~ScreenCapturer() {}
public:
virtual int Init(const RECORD_DESKTOP_RECT &rect, const int fps,
cb_desktop_data cb) = 0;
virtual int Destroy() = 0;
virtual int Start() = 0;
virtual int Stop() = 0;
};
#endif

View File

@@ -0,0 +1,37 @@
/*
* @Author: DI JUNKUN
* @Date: 2023-12-15
* Copyright (c) 2023 by DI JUNKUN, All Rights Reserved.
*/
#ifndef _SCREEN_CAPTURER_FACTORY_H_
#define _SCREEN_CAPTURER_FACTORY_H_
#ifdef _WIN32
#include "screen_capturer_wgc.h"
#elif __linux__
#include "screen_capturer_x11.h"
#elif __APPLE__
#include "screen_capturer_avf.h"
#endif
class ScreenCapturerFactory {
public:
virtual ~ScreenCapturerFactory() {}
public:
ScreenCapturer* Create() {
#ifdef _WIN32
return new ScreenCapturerWgc();
#elif __linux__
return new ScreenCapturerX11();
#elif __APPLE__
return new ScreenCapturerAvf();
#else
return nullptr;
#endif
}
};
#endif

View File

@@ -0,0 +1,207 @@
#include "screen_capturer_wgc.h"
#include <Windows.h>
#include <d3d11_4.h>
#include <winrt/Windows.Foundation.Metadata.h>
#include <winrt/Windows.Graphics.Capture.h>
#include <iostream>
#include "libyuv.h"
BOOL WINAPI EnumMonitorProc(HMONITOR hmonitor, HDC hdc, LPRECT lprc,
LPARAM data) {
MONITORINFOEX info_ex;
info_ex.cbSize = sizeof(MONITORINFOEX);
GetMonitorInfo(hmonitor, &info_ex);
if (info_ex.dwFlags == DISPLAY_DEVICE_MIRRORING_DRIVER) return true;
if (info_ex.dwFlags & MONITORINFOF_PRIMARY) {
*(HMONITOR *)data = hmonitor;
}
return true;
}
HMONITOR GetPrimaryMonitor() {
HMONITOR hmonitor = nullptr;
::EnumDisplayMonitors(NULL, NULL, EnumMonitorProc, (LPARAM)&hmonitor);
return hmonitor;
}
ScreenCapturerWgc::ScreenCapturerWgc() {}
ScreenCapturerWgc::~ScreenCapturerWgc() {
Stop();
CleanUp();
if (nv12_frame_) {
delete nv12_frame_;
nv12_frame_ = nullptr;
}
if (nv12_frame_scaled_) {
delete nv12_frame_scaled_;
nv12_frame_scaled_ = nullptr;
}
}
bool ScreenCapturerWgc::IsWgcSupported() {
try {
/* no contract for IGraphicsCaptureItemInterop, verify 10.0.18362.0 */
return winrt::Windows::Foundation::Metadata::ApiInformation::
IsApiContractPresent(L"Windows.Foundation.UniversalApiContract", 8);
} catch (const winrt::hresult_error &) {
return false;
} catch (...) {
return false;
}
}
int ScreenCapturerWgc::Init(const RECORD_DESKTOP_RECT &rect, const int fps,
cb_desktop_data cb) {
int error = 0;
if (_inited == true) return error;
int r = rect.right;
int b = rect.bottom;
nv12_frame_ = new unsigned char[rect.right * rect.bottom * 3 / 2];
nv12_frame_scaled_ = new unsigned char[1280 * 720 * 3 / 2];
_fps = fps;
_on_data = cb;
do {
if (!IsWgcSupported()) {
std::cout << "AE_UNSUPPORT" << std::endl;
error = 2;
break;
}
session_ = new WgcSessionImpl();
if (!session_) {
error = -1;
std::cout << "AE_WGC_CREATE_CAPTURER_FAILED" << std::endl;
break;
}
session_->RegisterObserver(this);
error = session_->Initialize(GetPrimaryMonitor());
_inited = true;
} while (0);
if (error != 0) {
}
return error;
}
int ScreenCapturerWgc::Destroy() { return 0; }
int ScreenCapturerWgc::Start() {
if (_running == true) {
std::cout << "record desktop duplication is already running" << std::endl;
return 0;
}
if (_inited == false) {
std::cout << "AE_NEED_INIT" << std::endl;
return 4;
}
_running = true;
session_->Start();
return 0;
}
int ScreenCapturerWgc::Pause() {
_paused = true;
if (session_) session_->Pause();
return 0;
}
int ScreenCapturerWgc::Resume() {
_paused = false;
if (session_) session_->Resume();
return 0;
}
int ScreenCapturerWgc::Stop() {
_running = false;
if (session_) session_->Stop();
return 0;
}
void ConvertABGRtoBGRA(const uint8_t *abgr_data, uint8_t *bgra_data, int width,
int height, int abgr_stride, int bgra_stride) {
for (int i = 0; i < height; ++i) {
for (int j = 0; j < width; ++j) {
// ABGR<47><52>BGRA<52><41><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ӳ<EFBFBD><D3B3>
int abgr_index = (i * abgr_stride + j) * 4;
int bgra_index = (i * bgra_stride + j) * 4;
// ֱ<>ӽ<EFBFBD><D3BD><EFBFBD><EFBFBD><EFBFBD>ɫ<EFBFBD>ͺ<EFBFBD>ɫ<EFBFBD><C9AB><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ͬʱ<CDAC><CAB1><EFBFBD><EFBFBD>Alphaͨ<61><CDA8><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
bgra_data[bgra_index + 0] = abgr_data[abgr_index + 2]; // <20><>ɫ
bgra_data[bgra_index + 1] = abgr_data[abgr_index + 1]; // <20><>ɫ
bgra_data[bgra_index + 2] = abgr_data[abgr_index + 0]; // <20><>ɫ
bgra_data[bgra_index + 3] = abgr_data[abgr_index + 3]; // Alpha
}
}
}
void ConvertBGRAtoABGR(const uint8_t *bgra_data, uint8_t *abgr_data, int width,
int height, int bgra_stride, int abgr_stride) {
for (int i = 0; i < height; ++i) {
for (int j = 0; j < width; ++j) {
// BGRA<52><41>ABGR<47><52><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ӳ<EFBFBD><D3B3>
int bgra_index = (i * bgra_stride + j) * 4;
int abgr_index = (i * abgr_stride + j) * 4;
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ɫ<EFBFBD><C9AB><EFBFBD><EFBFBD>ɫ<EFBFBD><C9AB><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ͬʱ<CDAC><CAB1><EFBFBD><EFBFBD>Alphaͨ<61><CDA8><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ǰ<EFBFBD><C7B0>
abgr_data[abgr_index + 0] = bgra_data[bgra_index + 3]; // Alpha
abgr_data[abgr_index + 1] = bgra_data[bgra_index + 0]; // Blue
abgr_data[abgr_index + 2] = bgra_data[bgra_index + 1]; // Green
abgr_data[abgr_index + 3] = bgra_data[bgra_index + 2]; // Red
}
}
}
void ScreenCapturerWgc::OnFrame(const WgcSession::wgc_session_frame &frame) {
if (_on_data) {
int width = 1280;
int height = 720;
libyuv::ARGBToNV12((const uint8_t *)frame.data, frame.width * 4,
(uint8_t *)nv12_frame_, frame.width,
(uint8_t *)(nv12_frame_ + frame.width * frame.height),
frame.width, frame.width, frame.height);
libyuv::NV12Scale(
(const uint8_t *)nv12_frame_, frame.width,
(const uint8_t *)(nv12_frame_ + frame.width * frame.height),
frame.width, frame.width, frame.height, (uint8_t *)nv12_frame_scaled_,
width, (uint8_t *)(nv12_frame_scaled_ + width * height), width, width,
height, libyuv::FilterMode::kFilterLinear);
_on_data(nv12_frame_scaled_, width * height * 3 / 2, width, height);
}
}
void ScreenCapturerWgc::CleanUp() {
_inited = false;
if (session_) session_->Release();
session_ = nullptr;
}

View File

@@ -0,0 +1,58 @@
#ifndef _SCREEN_CAPTURER_WGC_H_
#define _SCREEN_CAPTURER_WGC_H_
#include <atomic>
#include <functional>
#include <string>
#include <thread>
#include "screen_capturer.h"
#include "wgc_session.h"
#include "wgc_session_impl.h"
class ScreenCapturerWgc : public ScreenCapturer,
public WgcSession::wgc_session_observer {
public:
ScreenCapturerWgc();
~ScreenCapturerWgc();
public:
bool IsWgcSupported();
virtual int Init(const RECORD_DESKTOP_RECT &rect, const int fps,
cb_desktop_data cb);
virtual int Destroy();
virtual int Start();
int Pause();
int Resume();
virtual int Stop();
void OnFrame(const WgcSession::wgc_session_frame &frame);
protected:
void CleanUp();
private:
WgcSession *session_ = nullptr;
std::atomic_bool _running;
std::atomic_bool _paused;
std::atomic_bool _inited;
std::thread _thread;
std::string _device_name;
RECORD_DESKTOP_RECT _rect;
int _fps;
cb_desktop_data _on_data = nullptr;
unsigned char *nv12_frame_ = nullptr;
unsigned char *nv12_frame_scaled_ = nullptr;
};
#endif

View File

@@ -0,0 +1,150 @@
#include <X11/Xlib.h>
#include <fcntl.h>
#include <linux/uinput.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <cstring>
#include <iostream>
#include <sstream>
using namespace std;
int fd;
Display *dpy;
void initMouse();
void destroyMouse();
void mouseLeftClick();
void mouseRightClick();
void mouseGetPosition(int &x, int &y);
void mouseMove(int xdelta, int ydelta);
void initMouse() {
fd = open("/dev/uinput", O_WRONLY | O_NONBLOCK);
ioctl(fd, UI_SET_EVBIT, EV_KEY);
ioctl(fd, UI_SET_KEYBIT, BTN_RIGHT);
ioctl(fd, UI_SET_KEYBIT, BTN_LEFT);
ioctl(fd, UI_SET_EVBIT, EV_ABS);
ioctl(fd, UI_SET_ABSBIT, ABS_X);
ioctl(fd, UI_SET_ABSBIT, ABS_Y);
ioctl(fd, UI_SET_EVBIT, EV_REL);
struct uinput_user_dev uidev;
memset(&uidev, 0, sizeof(uidev));
snprintf(uidev.name, UINPUT_MAX_NAME_SIZE, "VirtualMouse");
uidev.id.bustype = BUS_USB;
uidev.id.version = 1;
uidev.id.vendor = 0x1;
uidev.id.product = 0x1;
uidev.absmin[ABS_X] = 0;
uidev.absmax[ABS_X] = 3200;
uidev.absmin[ABS_Y] = 0;
uidev.absmax[ABS_Y] = 900;
write(fd, &uidev, sizeof(uidev));
ioctl(fd, UI_DEV_CREATE);
sleep(2);
}
void mouseLeftClick() {
struct input_event ev_click, ev_sync;
memset(&ev_click, 0, sizeof(ev_click));
memset(&ev_sync, 0, sizeof(ev_sync));
ev_click.type = EV_KEY;
ev_click.code = BTN_LEFT;
ev_click.value = 1;
// write left click event
write(fd, &ev_click, sizeof(ev_click));
// sync left click event
ev_sync.type = EV_SYN;
write(fd, &ev_sync, sizeof(ev_sync));
}
void mouseRightClick() {
struct input_event ev_click, ev_sync;
memset(&ev_click, 0, sizeof(ev_click));
memset(&ev_sync, 0, sizeof(ev_sync));
ev_click.type = EV_KEY;
ev_click.code = BTN_RIGHT;
ev_click.value = 1;
// write right click event
write(fd, &ev_click, sizeof(ev_click));
// sync right click event
ev_sync.type = EV_SYN;
write(fd, &ev_sync, sizeof(ev_sync));
}
void mouseSetPosition(int x, int y) {
struct input_event ev[2], ev_sync;
memset(ev, 0, sizeof(ev));
memset(&ev_sync, 0, sizeof(ev_sync));
ev[0].type = EV_ABS;
ev[0].code = ABS_X;
ev[0].value = x;
ev[1].type = EV_ABS;
ev[1].code = ABS_Y;
ev[1].value = y;
int res_w = write(fd, ev, sizeof(ev));
std::cout << "res w : " << res_w << "\n";
ev_sync.type = EV_SYN;
ev_sync.value = 0;
ev_sync.code = 0;
int res_ev_sync = write(fd, &ev_sync, sizeof(ev_sync));
std::cout << "res syn : " << res_ev_sync << "\n";
}
void initDisplay() { dpy = XOpenDisplay(NULL); }
void destroyMouse() { ioctl(fd, UI_DEV_DESTROY); }
void mouseMove(int xdelta, int ydelta) {
int xx, yy;
mouseGetPosition(xx, yy);
mouseSetPosition(xx + xdelta, yy + ydelta);
}
void mouseGetPosition(int &x, int &y) {
Window root, child;
int rootX, rootY, winX, winY;
unsigned int mask;
XQueryPointer(dpy, DefaultRootWindow(dpy), &root, &child, &rootX, &rootY,
&winX, &winY, &mask);
std::cout << "root x : " << rootX << "\n";
std::cout << "root y : " << rootY << "\n";
x = rootX;
y = rootY;
}
int main() {
initMouse();
initDisplay();
int tempx, tempy;
for (int i = 0; i < 5; ++i) {
mouseMove(100, 100);
sleep(1);
std::cout << "i : " << i << "\n";
}
destroyMouse();
return 0;
}

View File

@@ -43,6 +43,7 @@ package("ffmpeg")
add_configs("hardcoded-tables", {description = "Enable hardcoded tables.", default = true, type = "boolean"})
add_configs("asm", {description = "Enable asm", default = false, type = "boolean"})
add_configs("libopenh264", {description = "Enable libopenh264", default = false, type = "boolean"})
add_configs("libxcb", {description = "Enable libxcb", default = true, type = "boolean"})
end
add_links("avfilter", "avdevice", "avformat", "avcodec", "swscale", "swresample", "avutil", "postproc")

View File

@@ -1,11 +0,0 @@
package("sdl2")
add_urls("https://github.com/libsdl-org/SDL/archive/refs/tags/release-2.28.3.tar.gz", {alias = "github"})
add_versions("github:2.28.3", "c17455d6e0c484bfe634b8de6af4c608e86ee449c28e40af04064aa6643fe382")
add_deps("cmake")
on_install(function (package)
local configs = {}
table.insert(configs, "-DSDL_SHARED_ENABLED_BY_DEFAULT=OFF -DSDL_TEST_ENABLED_BY_DEFAULT=OFF")
import("package.tools.cmake").install(package, configs)
end)
package_end()

View File

@@ -1,4 +1,4 @@
includes("sdl2", "projectx")
includes("projectx")
if is_plat("windows") then
elseif is_plat("linux") then
includes("ffmpeg")

111
xmake.lua
View File

@@ -5,41 +5,42 @@ set_license("LGPL-3.0")
add_rules("mode.release", "mode.debug")
set_languages("c++17")
-- set_policy("build.warning", true)
-- set_warnings("all", "extra")
add_defines("UNICODE")
if is_mode("debug") then
add_defines("REMOTE_DESK_DEBUG")
end
add_requires("sdl2", {system = false})
add_requires("spdlog 1.11.0", {system = false})
add_requires("imgui 1.89.9", {configs = {sdl2 = true, sdl2_renderer = true}})
add_requires("spdlog 1.14.1", {system = false})
add_requires("imgui 1.90.6", {configs = {sdl2 = true, sdl2_renderer = true}})
add_requires("libyuv")
if is_os("windows") then
add_links("Shell32", "windowsapp", "dwmapi", "User32", "kernel32",
"SDL2-static", "SDL2main", "gdi32", "winmm", "setupapi", "version",
"Imm32", "iphlpapi")
add_requires("vcpkg::ffmpeg 5.1.2", {configs = {shared = false}})
add_packages("vcpkg::ffmpeg")
elseif is_os("linux") then
add_requires("ffmpeg 5.1.2", {system = false})
add_packages("ffmpeg")
add_syslinks("pthread", "dl")
add_links("SDL2")
add_linkdirs("thirdparty/projectx/thirdparty/nvcodec/Lib/x64")
add_links("SDL2", "cuda", "nvidia-encode", "nvcuvid")
add_ldflags("-lavformat", "-lavdevice", "-lavfilter", "-lavcodec",
"-lswscale", "-lavutil", "-lswresample",
"-lasound", "-lxcb-shape", "-lxcb-xfixes", "-lsndio", "-lxcb",
"-lxcb-shm", "-lXext", "-lX11", "-lXv", "-ldl", "-lpthread",
"-lxcb-shm", "-lXext", "-lX11", "-lXv", "-ldl", "-lpthread",
{force = true})
elseif is_os("macosx") then
add_requires("ffmpeg 5.1.2", {system = false})
add_requires("libxcb", {system = false})
add_packages("ffmpeg", "libxcb")
add_packages("libxcb")
add_links("SDL2", "SDL2main")
add_ldflags("-Wl,-ld_classic")
add_frameworks("OpenGL")
end
add_packages("spdlog", "sdl2", "imgui")
add_packages("spdlog", "imgui")
includes("thirdparty")
@@ -49,47 +50,90 @@ target("log")
add_headerfiles("src/log/log.h")
add_includedirs("src/log", {public = true})
target("screen_capture")
target("common")
set_kind("object")
add_deps("log")
add_files("src/common/*.cpp")
add_includedirs("src/common", {public = true})
target("screen_capturer")
set_kind("object")
add_deps("log")
add_packages("libyuv")
add_includedirs("src/screen_capturer", {public = true})
if is_os("windows") then
add_files("src/screen_capture/windows/*.cpp")
add_includedirs("src/screen_capture/windows", {public = true})
add_files("src/screen_capturer/windows/*.cpp")
add_includedirs("src/screen_capturer/windows", {public = true})
elseif is_os("macosx") then
add_files("src/screen_capture/macosx/*.cpp")
add_includedirs("src/screen_capture/macosx", {public = true})
add_packages("ffmpeg")
add_files("src/screen_capturer/macosx/*.cpp")
add_includedirs("src/screen_capturer/macosx", {public = true})
elseif is_os("linux") then
add_files("src/screen_capture/linux/*.cpp")
add_includedirs("src/screen_capture/linux", {public = true})
add_packages("ffmpeg")
add_files("src/screen_capturer/linux/*.cpp")
add_includedirs("src/screen_capturer/linux", {public = true})
end
target("device_controller")
set_kind("object")
add_deps("log")
add_includedirs("src/device_controller", {public = true})
if is_os("windows") then
add_files("src/device_controller/mouse/windows/*.cpp")
add_includedirs("src/device_controller/mouse/windows", {public = true})
elseif is_os("macosx") then
add_files("src/device_controller/mouse/mac/*.cpp")
add_includedirs("src/device_controller/mouse/mac", {public = true})
elseif is_os("linux") then
add_files("src/device_controller/mouse/linux/*.cpp")
add_includedirs("src/device_controller/mouse/linux", {public = true})
end
target("config_center")
set_kind("object")
add_deps("log")
add_files("src/config_center/*.cpp")
add_includedirs("src/config_center", {public = true})
target("localization")
set_kind("headeronly")
add_includedirs("src/localization", {public = true})
target("main_window")
set_kind("object")
add_deps("log", "common", "localization", "config_center", "projectx", "screen_capturer", "device_controller")
if is_os("macosx") then
add_packages("ffmpeg")
elseif is_os("linux") then
add_packages("ffmpeg")
end
add_files("src/main_window/*.cpp")
add_includedirs("src/main_window", {public = true})
target("remote_desk")
set_kind("binary")
add_deps("log", "screen_capture", "projectx")
add_deps("log", "common", "main_window")
if is_os("macosx") then
add_packages("ffmpeg")
elseif is_os("linux") then
add_packages("ffmpeg")
end
add_files("src/gui/main.cpp")
-- after_install(function (target)
-- os.cp("$(projectdir)/thirdparty/nvcodec/Lib/x64/*.so", "$(projectdir)/out/bin")
-- os.cp("$(projectdir)/thirdparty/nvcodec/Lib/x64/*.so.1", "$(projectdir)/out/bin")
-- os.cp("$(projectdir)/out/lib/*.so", "$(projectdir)/out/bin")
-- os.rm("$(projectdir)/out/include")
-- os.rm("$(projectdir)/out/lib")
-- end)
-- target("screen_capture")
-- target("screen_capturer")
-- set_kind("binary")
-- add_packages("sdl2", "imgui", "ffmpeg", "openh264")
-- add_files("test/screen_capture/linux_capture.cpp")
-- add_files("test/screen_capturer/linux_capture.cpp")
-- add_ldflags("-lavformat", "-lavdevice", "-lavfilter", "-lavcodec",
-- "-lswscale", "-lavutil", "-lswresample",
-- "-lasound", "-lxcb-shape", "-lxcb-xfixes", "-lsndio", "-lxcb",
-- "-lxcb-shm", "-lXext", "-lX11", "-lXv", "-lpthread", "-lSDL2", "-lopenh264",
-- "-ldl", {force = true})
-- target("screen_capture")
-- target("screen_capturer")
-- set_kind("binary")
-- add_packages("sdl2", "imgui", "ffmpeg", "openh264")
-- add_files("test/screen_capture/mac_capture.cpp")
-- add_files("test/screen_capturer/mac_capture.cpp")
-- add_ldflags("-lavformat", "-lavdevice", "-lavfilter", "-lavcodec",
-- "-lswscale", "-lavutil", "-lswresample",
-- "-lasound", "-lxcb-shape", "-lxcb-xfixes", "-lsndio", "-lxcb",
@@ -129,4 +173,9 @@ target("remote_desk")
-- "-lswscale", "-lavutil", "-lswresample",
-- "-lasound", "-lxcb-shape", "-lxcb-xfixes", "-lsndio", "-lxcb",
-- "-lxcb-shm", "-lXext", "-lX11", "-lXv", "-lpthread", "-lSDL2", "-lopenh264",
-- "-ldl", {force = true})
-- "-ldl", {force = true})
-- target("mouse_control")
-- set_kind("binary")
-- add_files("test/linux_mouse_control/mouse_control.cpp")
-- add_includedirs("test/linux_mouse_control")