28 Commits

Author SHA1 Message Date
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
35 changed files with 1230 additions and 651 deletions

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 softwar/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 许可证,其中使用到的第三方库根据自身许可证进行分发。

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,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,122 @@
#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;
uinput_fd_ = open("/dev/uinput", O_WRONLY | O_NONBLOCK);
if (uinput_fd_ < 0) {
LOG_ERROR("Cannot open device: /dev/uinput");
}
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_;
write(uinput_fd_, &uidev, sizeof(uidev));
ioctl(uinput_fd_, UI_DEV_CREATE);
return 0;
}
int MouseController::Destroy() {
ioctl(uinput_fd_, UI_DEV_DESTROY);
close(uinput_fd_);
}
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) {
struct input_event event;
memset(&event, 0, sizeof(event));
gettimeofday(&event.time, 0);
event.type = EV_KEY;
event.value = 1;
event.code = kval;
write(fd, &event, sizeof(event));
event.type = EV_SYN;
event.value = 0;
event.code = SYN_REPORT;
write(fd, &event, sizeof(event));
}
void MouseController::SimulateKeyUp(int fd, int kval) {
struct input_event event;
memset(&event, 0, sizeof(event));
gettimeofday(&event.time, 0);
event.type = EV_KEY;
event.value = 0;
event.code = kval;
write(fd, &event, sizeof(event));
event.type = EV_SYN;
event.value = 0;
event.code = SYN_REPORT;
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

View File

@@ -1,22 +1,11 @@
#include <SDL.h>
#include <stdio.h>
#ifdef _WIN32
// #pragma comment(linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"")
#ifdef REMOTE_DESK_DEBUG
#pragma comment(linker, "/subsystem:\"console\"")
#include <Winsock2.h>
#include <iphlpapi.h>
#elif __APPLE__
#include <ApplicationServices/ApplicationServices.h>
#include <ifaddrs.h>
#include <net/if_dl.h>
#include <net/if_types.h>
#include <sys/socket.h>
#include <sys/types.h>
#elif __linux__
#include <net/if.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <unistd.h>
#else
#pragma comment(linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"")
#endif
#endif
#include <atomic>
@@ -27,33 +16,23 @@
#include <string>
#include <thread>
#include "platform.h"
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavdevice/avdevice.h>
#include <libavfilter/avfilter.h>
#include <libavformat/avformat.h>
#include <libavutil/channel_layout.h>
#include <libavutil/imgutils.h>
#include <libavutil/opt.h>
#include <libavutil/samplefmt.h>
#include <libswresample/swresample.h>
#include <libswscale/swscale.h>
};
#include <stdio.h>
#include "../../thirdparty/projectx/src/interface/x.h"
#include "device_controller_factory.h"
#include "imgui.h"
#include "imgui_impl_sdl2.h"
#include "imgui_impl_sdlrenderer2.h"
#include "log.h"
#ifdef _WIN32
#include "screen_capture_wgc.h"
#elif __linux__
#include "screen_capture_x11.h"
#elif __APPLE__
#include "screen_capture_avf.h"
#endif
#include "../../thirdparty/projectx/src/interface/x.h"
#include "screen_capturer_factory.h"
#define NV12_BUFFER_SIZE 1280 * 720 * 3 / 2
@@ -80,72 +59,52 @@ uint32_t start_time, end_time, elapsed_time;
uint32_t frame_count = 0;
int fps = 0;
#if 1
static Uint8 *buffer = 0;
static int in_pos = 0;
static int out_pos = 0;
static std::atomic<bool> audio_buffer_fresh = false;
static std::atomic<bool> audio_buffer_fresh{false};
static uint32_t last_ts = 0;
char *out = "audio_old.pcm";
FILE *outfile = fopen(out, "wb+");
int64_t src_ch_layout = AV_CH_LAYOUT_MONO;
int src_rate = 48000;
enum AVSampleFormat src_sample_fmt = AV_SAMPLE_FMT_S16;
int src_nb_channels = 0;
uint8_t **src_data = NULL; // <20><><EFBFBD><EFBFBD>ָ<EFBFBD><D6B8>
uint8_t **src_data = NULL;
int src_linesize;
int src_nb_samples = 480;
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
int64_t dst_ch_layout = AV_CH_LAYOUT_MONO;
int dst_rate = 48000;
enum AVSampleFormat dst_sample_fmt = AV_SAMPLE_FMT_S16;
int dst_nb_channels = 0;
uint8_t **dst_data = NULL; // <20><><EFBFBD><EFBFBD>ָ<EFBFBD><D6B8>
uint8_t **dst_data = NULL;
int dst_linesize;
int dst_nb_samples;
int max_dst_nb_samples;
// <20><><EFBFBD><EFBFBD><EFBFBD>ļ<EFBFBD>
const char *dst_filename = NULL; // <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>pcm<63><6D><EFBFBD><EFBFBD><EFBFBD>أ<EFBFBD>Ȼ<EFBFBD>󲥷<EFBFBD><F3B2A5B7><EFBFBD>֤
FILE *dst_file;
int dst_bufsize;
// <20>ز<EFBFBD><D8B2><EFBFBD>ʵ<EFBFBD><CAB5>
struct SwrContext *swr_ctx;
double t;
int ret;
#endif
int audio_len = 0;
#define MAX_FRAME_SIZE 6 * 960
#define CHANNELS 2
unsigned char pcm_bytes[MAX_FRAME_SIZE * CHANNELS * 2];
std::string window_title = "Remote Desk Client";
std::string server_connection_status_str = "-";
std::string client_connection_status_str = "-";
std::string server_signal_status_str = "-";
std::string client_signal_status_str = "-";
std::atomic<ConnectionStatus> server_connection_status =
ConnectionStatus::Closed;
std::atomic<ConnectionStatus> client_connection_status =
ConnectionStatus::Closed;
std::atomic<SignalStatus> server_signal_status = SignalStatus::SignalClosed;
std::atomic<SignalStatus> client_signal_status = SignalStatus::SignalClosed;
std::atomic<ConnectionStatus> server_connection_status{
ConnectionStatus::Closed};
std::atomic<ConnectionStatus> client_connection_status{
ConnectionStatus::Closed};
std::atomic<SignalStatus> server_signal_status{SignalStatus::SignalClosed};
std::atomic<SignalStatus> client_signal_status{SignalStatus::SignalClosed};
// Refresh Event
#define REFRESH_EVENT (SDL_USEREVENT + 1)
#define QUIT_EVENT (SDL_USEREVENT + 2)
typedef struct {
char password[16];
char password[7];
} CDCache;
int thread_exit = 0;
@@ -153,23 +112,22 @@ PeerPtr *peer_server = nullptr;
PeerPtr *peer_client = nullptr;
bool joined = false;
bool received_frame = false;
bool menu_hovered = false;
static bool connect_button_pressed = false;
static const char *connect_label = "Connect";
static char input_password[7] = "";
static FILE *cd_cache_file = nullptr;
static CDCache cd_cache;
static char mac_addr[16];
static bool is_create_connection = false;
static bool done = false;
#ifdef _WIN32
ScreenCaptureWgc *screen_capture = nullptr;
#elif __linux__
ScreenCaptureX11 *screen_capture = nullptr;
#elif __APPLE__
ScreenCaptureAvf *screen_capture = nullptr;
#endif
ScreenCapturerFactory *screen_capturer_factory = nullptr;
ScreenCapturer *screen_capturer = nullptr;
DeviceControllerFactory *device_controller_factory = nullptr;
MouseController *mouse_controller = nullptr;
char *nv12_buffer = nullptr;
@@ -179,32 +137,12 @@ std::chrono::_V2::system_clock::time_point last_frame_time_;
std::chrono::steady_clock::time_point last_frame_time_;
#endif
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;
inline int ProcessMouseKeyEven(SDL_Event &ev) {
float ratio = 1280.0 / window_w;
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
{
@@ -222,70 +160,26 @@ inline int ProcessMouseKeyEven(SDL_Event &ev) {
// printf("SDLK_RIGHT \n");
}
} else if (SDL_MOUSEBUTTONDOWN == ev.type) {
remote_action.type = ControlType::mouse;
if (SDL_BUTTON_LEFT == ev.button.button) {
int px = ev.button.x;
int py = ev.button.y;
// printf("SDL_MOUSEBUTTONDOWN x, y %d %d \n", px, py);
remote_action.type = ControlType::mouse;
remote_action.m.flag = MouseFlag::left_down;
remote_action.m.x = ev.button.x * ratio;
remote_action.m.y = ev.button.y * ratio;
SendData(peer_client, DATA_TYPE::DATA, (const char *)&remote_action,
sizeof(remote_action));
} else if (SDL_BUTTON_RIGHT == ev.button.button) {
int px = ev.button.x;
int py = ev.button.y;
// printf("SDL_BUTTON_RIGHT x, y %d %d \n", px, py);
remote_action.type = ControlType::mouse;
remote_action.m.flag = MouseFlag::right_down;
remote_action.m.x = ev.button.x * ratio;
remote_action.m.y = ev.button.y * ratio;
SendData(peer_client, DATA_TYPE::DATA, (const char *)&remote_action,
sizeof(remote_action));
}
SendData(peer_client, 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) {
int px = ev.button.x;
int py = ev.button.y;
// printf("SDL_MOUSEBUTTONUP x, y %d %d \n", px, py);
remote_action.type = ControlType::mouse;
remote_action.m.flag = MouseFlag::left_up;
remote_action.m.x = ev.button.x * ratio;
remote_action.m.y = ev.button.y * ratio;
SendData(peer_client, DATA_TYPE::DATA, (const char *)&remote_action,
sizeof(remote_action));
} else if (SDL_BUTTON_RIGHT == ev.button.button) {
int px = ev.button.x;
int py = ev.button.y;
// printf("SDL_MOUSEBUTTONUP x, y %d %d \n", px, py);
remote_action.type = ControlType::mouse;
remote_action.m.flag = MouseFlag::right_up;
remote_action.m.x = ev.button.x * ratio;
remote_action.m.y = ev.button.y * ratio;
SendData(peer_client, DATA_TYPE::DATA, (const char *)&remote_action,
sizeof(remote_action));
}
SendData(peer_client, DATA_TYPE::DATA, (const char *)&remote_action,
sizeof(remote_action));
} else if (SDL_MOUSEMOTION == ev.type) {
int px = ev.motion.x;
int py = ev.motion.y;
// printf("SDL_MOUSEMOTION x, y %d %d \n", px, py);
remote_action.type = ControlType::mouse;
remote_action.m.flag = MouseFlag::move;
remote_action.m.x = ev.button.x * ratio;
remote_action.m.y = ev.button.y * ratio;
SendData(peer_client, DATA_TYPE::DATA, (const char *)&remote_action,
sizeof(remote_action));
} else if (SDL_QUIT == ev.type) {
@@ -301,8 +195,8 @@ inline int ProcessMouseKeyEven(SDL_Event &ev) {
void SdlCaptureAudioIn(void *userdata, Uint8 *stream, int len) {
int64_t delay = swr_get_delay(swr_ctx, src_rate);
dst_nb_samples =
av_rescale_rnd(delay + src_nb_samples, dst_rate, src_rate, AV_ROUND_UP);
dst_nb_samples = (int)av_rescale_rnd(delay + src_nb_samples, dst_rate,
src_rate, AV_ROUND_UP);
if (dst_nb_samples > max_dst_nb_samples) {
av_freep(&dst_data[0]);
ret = av_samples_alloc(dst_data, &dst_linesize, dst_nb_channels,
@@ -387,7 +281,7 @@ void ServerReceiveAudioBuffer(const char *data, size_t size,
// audio_len = size;
audio_buffer_fresh = true;
SDL_QueueAudio(output_dev, data, size);
SDL_QueueAudio(output_dev, data, (Uint32)size);
// printf("queue audio\n");
}
@@ -395,7 +289,7 @@ 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, size);
SDL_QueueAudio(output_dev, data, (Uint32)size);
}
void ServerReceiveDataBuffer(const char *data, size_t size, const char *user_id,
@@ -408,129 +302,13 @@ void ServerReceiveDataBuffer(const char *data, size_t size, const char *user_id,
// std::cout << "remote_action: " << remote_action.type << " "
// << remote_action.m.flag << " " << remote_action.m.x << " "
// << remote_action.m.y << std::endl;
int mouse_pos_x = remote_action.m.x * screen_w / 1280;
int mouse_pos_y = remote_action.m.y * screen_h / 720;
#if 1
#ifdef _WIN32
INPUT ip;
if (remote_action.type == ControlType::mouse) {
ip.type = INPUT_MOUSE;
ip.mi.dx = remote_action.m.x * screen_w / 1280;
ip.mi.dy = remote_action.m.y * screen_h / 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;
// Set cursor pos
SetCursorPos(ip.mi.dx, ip.mi.dy);
// Send the press
if (ip.mi.dwFlags != MOUSEEVENTF_MOVE) {
SendInput(1, &ip, sizeof(INPUT));
}
// std::cout << "Receive data from [" << user << "], " << ip.type << " "
// << ip.mi.dwFlags << " " << ip.mi.dx << " " << ip.mi.dy
// << std::endl;
}
#elif __APPLE__
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);
}
#endif
#if MOUSE_CONTROL
mouse_controller->SendCommand(remote_action);
#endif
}
void ClientReceiveDataBuffer(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;
int mouse_pos_x = remote_action.m.x * screen_w / 1280;
int mouse_pos_y = remote_action.m.y * screen_h / 720;
#ifdef _WIN32
INPUT ip;
if (remote_action.type == ControlType::mouse) {
ip.type = INPUT_MOUSE;
ip.mi.dx = mouse_pos_x;
ip.mi.dy = mouse_pos_y;
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;
#if MOUSE_CONTROL
// Set cursor pos
SetCursorPos(ip.mi.dx, ip.mi.dy);
// Send the press
if (ip.mi.dwFlags != MOUSEEVENTF_MOVE) {
SendInput(1, &ip, sizeof(INPUT));
}
#endif
// std::cout << "Receive data from [" << user << "], " << ip.type << " "
// << ip.mi.dwFlags << " " << ip.mi.dx << " " << ip.mi.dy
// << std::endl;
}
#elif __APPLE__
CGEventRef mouse_event;
mouse_event = CGEventCreateMouseEvent(NULL, kCGEventMouseMoved,
CGPointMake(mouse_pos_x, mouse_pos_y),
kCGMouseButtonLeft);
CGEventPost(kCGHIDEventTap, mouse_event);
CFRelease(mouse_event);
#endif
}
size_t user_id_size) {}
void ServerSignalStatus(SignalStatus status) {
server_signal_status = status;
@@ -590,6 +368,7 @@ void ClientConnectionStatus(ConnectionStatus status) {
client_connection_status_str = "ClientConnecting";
} else if (ConnectionStatus::Connected == status) {
client_connection_status_str = "ClientConnected";
joined = true;
} else if (ConnectionStatus::Disconnected == status) {
client_connection_status_str = "ClientDisconnected";
} else if (ConnectionStatus::Failed == status) {
@@ -614,15 +393,6 @@ void ClientConnectionStatus(ConnectionStatus status) {
}
int initResampler() {
// dst_filename = "res.pcm";
// dst_file = fopen(dst_filename, "wb");
// if (!dst_file) {
// fprintf(stderr, "Could not open destination file %s\n", dst_filename);
// exit(1);
// }
// <20><><EFBFBD><EFBFBD><EFBFBD>ز<EFBFBD><D8B2><EFBFBD><EFBFBD><EFBFBD>
/* create resampler context */
swr_ctx = swr_alloc();
if (!swr_ctx) {
@@ -631,18 +401,15 @@ int initResampler() {
return -1;
}
// <20><><EFBFBD><EFBFBD><EFBFBD>ز<EFBFBD><D8B2><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
/* set options */
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
av_opt_set_int(swr_ctx, "in_channel_layout", src_ch_layout, 0);
av_opt_set_int(swr_ctx, "in_sample_rate", src_rate, 0);
av_opt_set_sample_fmt(swr_ctx, "in_sample_fmt", src_sample_fmt, 0);
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
av_opt_set_int(swr_ctx, "out_channel_layout", dst_ch_layout, 0);
av_opt_set_int(swr_ctx, "out_sample_rate", dst_rate, 0);
av_opt_set_sample_fmt(swr_ctx, "out_sample_fmt", dst_sample_fmt, 0);
// <20><>ʼ<EFBFBD><CABC><EFBFBD>ز<EFBFBD><D8B2><EFBFBD>
/* initialize the resampling context */
if ((ret = swr_init(swr_ctx)) < 0) {
fprintf(stderr, "Failed to initialize the resampling context\n");
@@ -650,9 +417,8 @@ int initResampler() {
}
/* allocate source and destination samples buffers */
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Դ<EFBFBD><D4B4>ͨ<EFBFBD><CDA8><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
src_nb_channels = av_get_channel_layout_nb_channels(src_ch_layout);
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Դ<EFBFBD><D4B4><EFBFBD><EFBFBD><EFBFBD>ڴ<EFBFBD><DAB4>ռ<EFBFBD>
ret = av_samples_alloc_array_and_samples(&src_data, &src_linesize,
src_nb_channels, src_nb_samples,
src_sample_fmt, 0);
@@ -661,13 +427,12 @@ int initResampler() {
return -1;
}
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
max_dst_nb_samples = dst_nb_samples =
av_rescale_rnd(src_nb_samples, dst_rate, src_rate, AV_ROUND_UP);
/* buffer is going to be directly written to a rawaudio file, no alignment */
dst_nb_channels = av_get_channel_layout_nb_channels(dst_ch_layout);
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ڴ<EFBFBD>
ret = av_samples_alloc_array_and_samples(&dst_data, &dst_linesize,
dst_nb_channels, dst_nb_samples,
dst_sample_fmt, 0);
@@ -677,110 +442,6 @@ int initResampler() {
}
}
std::string GetMac(char *mac_addr) {
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;
}
int BGRAToNV12FFmpeg(unsigned char *src_buffer, int width, int height,
unsigned char *dst_buffer) {
AVFrame *Input_pFrame = av_frame_alloc();
AVFrame *Output_pFrame = av_frame_alloc();
struct SwsContext *img_convert_ctx =
sws_getContext(width, height, AV_PIX_FMT_BGRA, 1280, 720, AV_PIX_FMT_NV12,
SWS_FAST_BILINEAR, nullptr, nullptr, nullptr);
av_image_fill_arrays(Input_pFrame->data, Input_pFrame->linesize, src_buffer,
AV_PIX_FMT_BGRA, width, height, 1);
av_image_fill_arrays(Output_pFrame->data, Output_pFrame->linesize, dst_buffer,
AV_PIX_FMT_NV12, 1280, 720, 1);
sws_scale(img_convert_ctx, (uint8_t const **)Input_pFrame->data,
Input_pFrame->linesize, 0, height, Output_pFrame->data,
Output_pFrame->linesize);
if (Input_pFrame) av_free(Input_pFrame);
if (Output_pFrame) av_free(Output_pFrame);
if (img_convert_ctx) sws_freeContext(img_convert_ctx);
return 0;
}
int main() {
LOG_INFO("Remote desk");
@@ -799,141 +460,6 @@ int main() {
strncpy(input_password, cd_cache.password, sizeof(cd_cache.password));
}
std::thread rtc_thread([] {
std::string default_cfg_path = "../../../../config/config.ini";
std::ifstream f(default_cfg_path.c_str());
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 = ServerSignalStatus;
server_params.on_connection_status = ServerConnectionStatus;
Params client_params;
client_params.cfg_path =
f.good() ? "../../../../config/config.ini" : "config.ini";
client_params.on_receive_video_buffer = ClientReceiveVideoBuffer;
client_params.on_receive_audio_buffer = ClientReceiveAudioBuffer;
client_params.on_receive_data_buffer = ClientReceiveDataBuffer;
client_params.on_signal_status = ClientSignalStatus;
client_params.on_connection_status = ClientConnectionStatus;
std::string transmission_id = "000001";
GetMac(mac_addr);
peer_server = CreatePeer(&server_params);
LOG_INFO("Create peer_server");
std::string server_user_id = "S-" + std::string(GetMac(mac_addr));
Init(peer_server, server_user_id.c_str());
LOG_INFO("peer_server init finish");
peer_client = CreatePeer(&client_params);
LOG_INFO("Create peer_client");
std::string client_user_id = "C-" + std::string(GetMac(mac_addr));
Init(peer_client, client_user_id.c_str());
LOG_INFO("peer_client init finish");
{
while (SignalStatus::SignalConnected != server_signal_status && !done) {
}
if (done) {
return;
}
std::string user_id = "S-" + std::string(GetMac(mac_addr));
is_create_connection =
CreateConnection(peer_server, mac_addr, input_password) ? false
: true;
nv12_buffer = new char[NV12_BUFFER_SIZE];
#ifdef _WIN32
screen_capture = new ScreenCaptureWgc();
RECORD_DESKTOP_RECT rect;
rect.left = 0;
rect.top = 0;
rect.right = GetSystemMetrics(SM_CXSCREEN);
rect.bottom = GetSystemMetrics(SM_CYSCREEN);
last_frame_time_ = std::chrono::high_resolution_clock::now();
screen_capture->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) {
BGRAToNV12FFmpeg(data, width, height,
(unsigned char *)nv12_buffer);
SendData(peer_server, DATA_TYPE::VIDEO, (const char *)nv12_buffer,
NV12_BUFFER_SIZE);
// std::cout << "Send" << std::endl;
last_frame_time_ = now_time;
}
});
screen_capture->Start();
#elif __linux__
screen_capture = new ScreenCaptureX11();
RECORD_DESKTOP_RECT rect;
rect.left = 0;
rect.top = 0;
rect.right = 0;
rect.bottom = 0;
last_frame_time_ = std::chrono::high_resolution_clock::now();
screen_capture->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;
}
});
screen_capture->Start();
#elif __APPLE__
screen_capture = new ScreenCaptureAvf();
RECORD_DESKTOP_RECT rect;
rect.left = 0;
rect.top = 0;
rect.right = 0;
rect.bottom = 0;
last_frame_time_ = std::chrono::high_resolution_clock::now();
screen_capture->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;
}
});
screen_capture->Start();
#endif
}
});
// Setup SDL
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER |
SDL_INIT_GAMECONTROLLER) != 0) {
@@ -1026,6 +552,104 @@ int main() {
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 = ServerSignalStatus;
server_params.on_connection_status = ServerConnectionStatus;
Params client_params;
client_params.cfg_path =
f.good() ? "../../../../config/config.ini" : "config.ini";
client_params.on_receive_video_buffer = ClientReceiveVideoBuffer;
client_params.on_receive_audio_buffer = ClientReceiveAudioBuffer;
client_params.on_receive_data_buffer = ClientReceiveDataBuffer;
client_params.on_signal_status = ClientSignalStatus;
client_params.on_connection_status = ClientConnectionStatus;
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");
peer_client = CreatePeer(&client_params);
LOG_INFO("Create peer_client");
std::string client_user_id = "C-" + mac_addr_str;
Init(peer_client, client_user_id.c_str());
LOG_INFO("peer_client init finish");
{
while (SignalStatus::SignalConnected != server_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;
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;
}
});
screen_capturer->Start();
// Mouse control
device_controller_factory = new DeviceControllerFactory();
mouse_controller =
(MouseController *)device_controller_factory->Create(
DeviceControllerFactory::Device::Mouse);
mouse_controller->Init(screen_w, screen_h);
}
},
screen_w, screen_h);
// Main loop
while (!done) {
// Start the Dear ImGui frame
@@ -1033,6 +657,10 @@ int main() {
ImGui_ImplSDL2_NewFrame();
ImGui::NewFrame();
if (joined && !menu_hovered) {
ImGui::SetMouseCursor(ImGuiMouseCursor_None);
}
{
static float f = 0.0f;
static int counter = 0;
@@ -1044,11 +672,14 @@ int main() {
ImGui::Begin("Menu", nullptr, ImGuiWindowFlags_NoResize);
{
menu_hovered = ImGui::IsWindowHovered();
ImGui::Text(" LOCAL ID:");
ImGui::SameLine();
ImGui::SetNextItemWidth(95);
ImGui::InputText("##local_id", mac_addr, IM_ARRAYSIZE(mac_addr),
ImGuiInputTextFlags_CharsUppercase);
ImGui::InputText(
"##local_id", (char *)mac_addr_str.c_str(),
mac_addr_str.length() + 1,
ImGuiInputTextFlags_CharsUppercase | ImGuiInputTextFlags_ReadOnly);
ImGui::Text(" PASSWORD:");
ImGui::SameLine();
@@ -1070,7 +701,7 @@ int main() {
fclose(cd_cache_file);
}
LeaveConnection(peer_server);
CreateConnection(peer_server, mac_addr, input_password);
CreateConnection(peer_server, mac_addr_str.c_str(), input_password);
}
ImGui::Spacing();
@@ -1081,15 +712,13 @@ int main() {
{
{
static char remote_id[20] = "";
// if (strcmp(remote_id, "") == 0) {
// strcpy(remote_id, GetMac(mac_addr).c_str());
// }
ImGui::Text("REMOTE ID:");
ImGui::SameLine();
ImGui::SetNextItemWidth(95);
ImGui::InputTextWithHint("##remote_id", mac_addr, remote_id,
IM_ARRAYSIZE(remote_id),
ImGuiInputTextFlags_CharsNoBlank);
ImGui::InputTextWithHint("##remote_id", mac_addr_str.c_str(),
remote_id, IM_ARRAYSIZE(remote_id),
ImGuiInputTextFlags_CharsUppercase |
ImGuiInputTextFlags_CharsNoBlank);
ImGui::Spacing();
@@ -1106,10 +735,10 @@ int main() {
int ret = -1;
if ("ClientSignalConnected" == client_signal_status_str) {
if (strcmp(connect_label, "Connect") == 0 && !joined) {
std::string user_id = "C-" + std::string(GetMac(mac_addr));
std::string user_id = "C-" + mac_addr_str;
ret = JoinConnection(peer_client, remote_id, client_password);
if (0 == ret) {
joined = true;
// joined = true;
}
} else if (strcmp(connect_label, "Disconnect") == 0 && joined) {
ret = LeaveConnection(peer_client);
@@ -1227,6 +856,8 @@ int main() {
SDL_CloseAudioDevice(output_dev);
SDL_CloseAudioDevice(input_dev);
mouse_controller->Destroy();
ImGui_ImplSDLRenderer2_Shutdown();
ImGui_ImplSDL2_Shutdown();
ImGui::DestroyContext();

View File

@@ -1,4 +1,4 @@
#include "screen_capture_x11.h"
#include "screen_capturer_x11.h"
#include <iostream>
@@ -7,20 +7,18 @@
#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()) {
capture_thread_->join();
}
}
ScreenCapturerX11::~ScreenCapturerX11() {}
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 +31,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
@@ -95,7 +94,15 @@ int ScreenCaptureX11::Init(const RECORD_DESKTOP_RECT &rect, const int fps,
return 0;
}
int ScreenCaptureX11::Start() {
int ScreenCapturerX11::Destroy() {
if (capture_thread_->joinable()) {
capture_thread_->join();
}
return 0;
}
int ScreenCapturerX11::Start() {
capture_thread_.reset(new std::thread([this]() {
while (1) {
if (av_read_frame(pFormatCtx_, packet_) >= 0) {
@@ -125,12 +132,12 @@ int ScreenCaptureX11::Start() {
return 0;
}
int ScreenCaptureX11::Pause() { return 0; }
int ScreenCapturerX11::Pause() { return 0; }
int ScreenCaptureX11::Resume() { return 0; }
int ScreenCapturerX11::Resume() { return 0; }
int ScreenCaptureX11::Stop() { return 0; }
int ScreenCapturerX11::Stop() { 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,25 +19,19 @@ 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();
int Start();
int Pause();
int Resume();
int Stop();
@@ -60,12 +55,12 @@ 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;
// 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,12 @@
#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()) {
capture_thread_->join();
}
}
ScreenCapturerAvf::~ScreenCapturerAvf() {}
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;
}
@@ -99,7 +95,15 @@ int ScreenCaptureAvf::Init(const RECORD_DESKTOP_RECT &rect, const int fps,
return 0;
}
int ScreenCaptureAvf::Start() {
int ScreenCapturerAvf::Destroy() {
if (capture_thread_->joinable()) {
capture_thread_->join();
}
return 0;
}
int ScreenCapturerAvf::Start() {
capture_thread_.reset(new std::thread([this]() {
while (1) {
if (av_read_frame(pFormatCtx_, packet_) >= 0) {
@@ -129,12 +133,12 @@ int ScreenCaptureAvf::Start() {
return 0;
}
int ScreenCaptureAvf::Pause() { return 0; }
int ScreenCapturerAvf::Pause() { return 0; }
int ScreenCaptureAvf::Resume() { return 0; }
int ScreenCapturerAvf::Resume() { return 0; }
int ScreenCaptureAvf::Stop() { return 0; }
int ScreenCapturerAvf::Stop() { 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,25 +26,18 @@ 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();
int Start();
int Pause();
int Resume();
int Stop();
@@ -66,7 +61,6 @@ class ScreenCaptureAvf {
int _fps;
cb_desktop_data _on_data;
cb_desktop_error _on_error;
private:
int i_ = 0;

View File

@@ -0,0 +1,33 @@
/*
* @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;
};
#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

@@ -1,12 +1,41 @@
#include "screen_capture_wgc.h"
#include "screen_capturer_wgc.h"
#include <Windows.h>
#include <d3d11_4.h>
#include <winrt/Windows.Foundation.Metadata.h>
#include <winrt/Windows.Graphics.Capture.h>
extern "C" {
#include <libavutil/imgutils.h>
#include <libswscale/swscale.h>
};
#include <iostream>
int BGRAToNV12FFmpeg(unsigned char *src_buffer, int width, int height,
unsigned char *dst_buffer) {
AVFrame *Input_pFrame = av_frame_alloc();
AVFrame *Output_pFrame = av_frame_alloc();
struct SwsContext *img_convert_ctx =
sws_getContext(width, height, AV_PIX_FMT_BGRA, 1280, 720, AV_PIX_FMT_NV12,
SWS_FAST_BILINEAR, nullptr, nullptr, nullptr);
av_image_fill_arrays(Input_pFrame->data, Input_pFrame->linesize, src_buffer,
AV_PIX_FMT_BGRA, width, height, 1);
av_image_fill_arrays(Output_pFrame->data, Output_pFrame->linesize, dst_buffer,
AV_PIX_FMT_NV12, 1280, 720, 1);
sws_scale(img_convert_ctx, (uint8_t const **)Input_pFrame->data,
Input_pFrame->linesize, 0, height, Output_pFrame->data,
Output_pFrame->linesize);
if (Input_pFrame) av_free(Input_pFrame);
if (Output_pFrame) av_free(Output_pFrame);
if (img_convert_ctx) sws_freeContext(img_convert_ctx);
return 0;
}
BOOL WINAPI EnumMonitorProc(HMONITOR hmonitor, HDC hdc, LPRECT lprc,
LPARAM data) {
MONITORINFOEX info_ex;
@@ -31,14 +60,11 @@ HMONITOR GetPrimaryMonitor() {
return hmonitor;
}
ScreenCaptureWgc::ScreenCaptureWgc() {}
ScreenCapturerWgc::ScreenCapturerWgc() {}
ScreenCaptureWgc::~ScreenCaptureWgc() {
Stop();
CleanUp();
}
ScreenCapturerWgc::~ScreenCapturerWgc() {}
bool ScreenCaptureWgc::IsWgcSupported() {
bool ScreenCapturerWgc::IsWgcSupported() {
try {
/* no contract for IGraphicsCaptureItemInterop, verify 10.0.18362.0 */
return winrt::Windows::Foundation::Metadata::ApiInformation::
@@ -50,11 +76,13 @@ bool ScreenCaptureWgc::IsWgcSupported() {
}
}
int ScreenCaptureWgc::Init(const RECORD_DESKTOP_RECT &rect, const int fps,
cb_desktop_data cb) {
int ScreenCapturerWgc::Init(const RECORD_DESKTOP_RECT &rect, const int fps,
cb_desktop_data cb) {
int error = 0;
if (_inited == true) return error;
nv12_frame_ = new unsigned char[rect.right * rect.bottom * 4];
_fps = fps;
_on_data = cb;
@@ -86,7 +114,19 @@ int ScreenCaptureWgc::Init(const RECORD_DESKTOP_RECT &rect, const int fps,
return error;
}
int ScreenCaptureWgc::Start() {
int ScreenCapturerWgc::Destroy() {
if (nv12_frame_) {
delete nv12_frame_;
nv12_frame_ = nullptr;
}
Stop();
CleanUp();
return 0;
}
int ScreenCapturerWgc::Start() {
if (_running == true) {
std::cout << "record desktop duplication is already running" << std::endl;
return 0;
@@ -103,19 +143,19 @@ int ScreenCaptureWgc::Start() {
return 0;
}
int ScreenCaptureWgc::Pause() {
int ScreenCapturerWgc::Pause() {
_paused = true;
if (session_) session_->Pause();
return 0;
}
int ScreenCaptureWgc::Resume() {
int ScreenCapturerWgc::Resume() {
_paused = false;
if (session_) session_->Resume();
return 0;
}
int ScreenCaptureWgc::Stop() {
int ScreenCapturerWgc::Stop() {
_running = false;
if (session_) session_->Stop();
@@ -123,13 +163,15 @@ int ScreenCaptureWgc::Stop() {
return 0;
}
void ScreenCaptureWgc::OnFrame(const WgcSession::wgc_session_frame &frame) {
void ScreenCapturerWgc::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);
BGRAToNV12FFmpeg((unsigned char *)frame.data, frame.width, frame.height,
nv12_frame_);
_on_data(nv12_frame_, frame.width * frame.height * 4, frame.width,
frame.height);
}
void ScreenCaptureWgc::CleanUp() {
void ScreenCapturerWgc::CleanUp() {
_inited = false;
if (session_) session_->Release();

View File

@@ -1,35 +1,30 @@
#ifndef _SCREEN_CAPTURE_WGC_H_
#define _SCREEN_CAPTURE_WGC_H_
#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"
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 {
class ScreenCapturerWgc : public ScreenCapturer,
public WgcSession::wgc_session_observer {
public:
ScreenCaptureWgc();
~ScreenCaptureWgc();
ScreenCapturerWgc();
~ScreenCapturerWgc();
public:
bool IsWgcSupported();
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();
int Start();
int Pause();
int Resume();
int Stop();
@@ -55,7 +50,8 @@ class ScreenCaptureWgc : public WgcSession::wgc_session_observer {
int _fps;
cb_desktop_data _on_data;
cb_desktop_error _on_error;
unsigned char *nv12_frame_ = 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

@@ -2,7 +2,6 @@ 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")

View File

@@ -5,6 +5,9 @@ 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")
@@ -24,11 +27,12 @@ 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})
@@ -49,23 +53,45 @@ 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_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_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_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("remote_desk")
set_kind("binary")
add_deps("log", "screen_capture", "projectx")
add_deps("log", "common", "screen_capturer", "device_controller", "projectx")
add_files("src/gui/main.cpp")
-- after_install(function (target)
@@ -76,20 +102,20 @@ target("remote_desk")
-- 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 +155,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")