mirror of
https://github.com/kunkundi/crossdesk.git
synced 2025-10-29 04:10:17 +08:00
Compare commits
225 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
418ab7a1d2 | ||
|
|
6a2c9af316 | ||
|
|
eaabf478cc | ||
|
|
ffe3ca76af | ||
|
|
c5a6302220 | ||
|
|
9b2f81690f | ||
|
|
9621e6b570 | ||
|
|
ce3ae03bef | ||
|
|
e0457213ea | ||
|
|
10cdc440a0 | ||
|
|
ddc62c90bb | ||
|
|
9e70d0e8fc | ||
|
|
4533d53ba8 | ||
|
|
0caa243006 | ||
|
|
1e58abdfdd | ||
|
|
370ac08d09 | ||
|
|
31b6b2736c | ||
|
|
abd22ab7f1 | ||
|
|
5ab68988aa | ||
|
|
ba3edcc02a | ||
|
|
8414a57a5b | ||
|
|
3f777e4662 | ||
|
|
52828183a1 | ||
|
|
df7489f8e2 | ||
|
|
4fea7d86e1 | ||
|
|
cb17b7c8db | ||
|
|
cf7ef89bf2 | ||
|
|
2d2a578800 | ||
|
|
0ba12f3ccf | ||
|
|
5ac603977d | ||
|
|
25d5a80bee | ||
|
|
c9d452a025 | ||
|
|
8132d62c02 | ||
|
|
ca32ebeefe | ||
|
|
5bf5e9ee25 | ||
|
|
bf097008e7 | ||
|
|
59b1208321 | ||
|
|
84194188f8 | ||
|
|
a067441fb9 | ||
|
|
6eac8380b6 | ||
|
|
5aed8317ca | ||
|
|
9aed7a19cf | ||
|
|
c0154be1aa | ||
|
|
f9d024e971 | ||
|
|
526eb4bb31 | ||
|
|
f9347cbd49 | ||
|
|
b6671bdbe7 | ||
|
|
edcf5d408c | ||
|
|
8c8731909e | ||
|
|
de721ac6e3 | ||
|
|
963f1da1d8 | ||
|
|
4c6159e4d4 | ||
|
|
e3c2e9ec6d | ||
|
|
02022bdcdf | ||
|
|
19ea426efc | ||
|
|
863070a8a7 | ||
|
|
44f9e6a8c9 | ||
|
|
087d5d7e52 | ||
|
|
26fa53f867 | ||
|
|
d18af6cbc6 | ||
|
|
b5bb62bd22 | ||
|
|
9ed3ab9929 | ||
|
|
dca18762e0 | ||
|
|
fed7c3b103 | ||
|
|
d246b7a04d | ||
|
|
a49ca813e0 | ||
|
|
0c688efaee | ||
|
|
be3561d46f | ||
|
|
c3af40a3f0 | ||
|
|
d493b9a131 | ||
|
|
4e4e84ae4d | ||
|
|
fea545e5e7 | ||
|
|
9096769a85 | ||
|
|
04ab157ecb | ||
|
|
2331f08283 | ||
|
|
9f8f99f21b | ||
|
|
56dadb6a49 | ||
|
|
59c9ca8d53 | ||
|
|
f16a4e8aa2 | ||
|
|
890615e13a | ||
|
|
2f72e3957e | ||
|
|
1292018f51 | ||
|
|
8ae9513104 | ||
|
|
c1efe2f4ac | ||
|
|
1210a0b631 | ||
|
|
39863c597e | ||
|
|
8a964f0030 | ||
|
|
74e29f25bf | ||
|
|
1e5bea2b1e | ||
|
|
d8297ebb74 | ||
|
|
93d7f71cf2 | ||
|
|
887a217828 | ||
|
|
89b12136e4 | ||
|
|
def7025abf | ||
|
|
35af5aab43 | ||
|
|
9ea67df0fd | ||
|
|
72fda8a728 | ||
|
|
070b48d7a7 | ||
|
|
6168009cef | ||
|
|
06a7243ac1 | ||
|
|
c8602b0d89 | ||
|
|
e3c730fd5f | ||
|
|
b252cb6ddd | ||
|
|
8ca1e8e5a1 | ||
|
|
a7d45b78c8 | ||
|
|
018231eee4 | ||
|
|
4704d494ec | ||
|
|
65927c2091 | ||
|
|
574b9d10ab | ||
|
|
ff510a3b44 | ||
|
|
4d3c864950 | ||
|
|
2cde54cf30 | ||
|
|
d1f3d11318 | ||
|
|
436228946b | ||
|
|
2b4083ee10 | ||
|
|
e3abb4e3de | ||
|
|
4b7cd1005b | ||
|
|
03ea96096d | ||
|
|
0ea8916426 | ||
|
|
43b36eb893 | ||
|
|
03b6a187b3 | ||
|
|
664412dd4e | ||
|
|
b37e08a202 | ||
|
|
a05d72ec67 | ||
|
|
f77e9fe6a8 | ||
|
|
1f9614e060 | ||
|
|
50d92a763a | ||
|
|
ec23656334 | ||
|
|
880c2949c3 | ||
|
|
07f5fe81c8 | ||
|
|
5a992b6589 | ||
|
|
8e03e8e79b | ||
|
|
ceb3d9fe20 | ||
|
|
0dc0b87bc4 | ||
|
|
3a4284fece | ||
|
|
502a90f121 | ||
|
|
88cd4aca4a | ||
|
|
3395004f93 | ||
|
|
e4c05e1f4d | ||
|
|
d17c70c2c8 | ||
|
|
7b42923418 | ||
|
|
5b6bdee25a | ||
|
|
05deb73c29 | ||
|
|
3685acc549 | ||
|
|
8f5a53937a | ||
|
|
b9c5db41ab | ||
|
|
a99a4230af | ||
|
|
f446154747 | ||
|
|
5a1e2c5ed9 | ||
|
|
ff6f295fac | ||
|
|
3111b3a641 | ||
|
|
20bb13ce85 | ||
|
|
5aa05f3a13 | ||
|
|
c911aa2eb1 | ||
|
|
d0cd2fe9ab | ||
|
|
9702805331 | ||
|
|
872152f1be | ||
|
|
b822221d7f | ||
|
|
95ad605b36 | ||
|
|
af32e25149 | ||
|
|
e63b384d1e | ||
|
|
7f25f7426c | ||
|
|
eed93ea953 | ||
|
|
b5f8e92526 | ||
|
|
af04b0571e | ||
|
|
75452a3e76 | ||
|
|
3f717f1df2 | ||
|
|
ad6f2c2c70 | ||
|
|
8076e7f662 | ||
|
|
be78496992 | ||
|
|
a3f745d441 | ||
|
|
e693d920d3 | ||
|
|
0f1b89eda9 | ||
|
|
172b8836fd | ||
|
|
71178ffa33 | ||
|
|
95a014a601 | ||
|
|
34d6bac345 | ||
|
|
399785409c | ||
|
|
5f1d9b6912 | ||
|
|
d963a0cf38 | ||
|
|
f9c1bc48b4 | ||
|
|
2906d05a4b | ||
|
|
053a0f86ad | ||
|
|
6c2363b239 | ||
|
|
167514fed8 | ||
|
|
342eb0c386 | ||
|
|
52c7099dbe | ||
|
|
12faf7cd2d | ||
|
|
6d921a3309 | ||
|
|
5a690ebbb6 | ||
|
|
4b3839aa34 | ||
|
|
efb165b56f | ||
|
|
0047b4ecc5 | ||
|
|
844710af7c | ||
|
|
562d54090a | ||
|
|
f7fd37651e | ||
|
|
280f59f97d | ||
|
|
0683ad9d27 | ||
|
|
e061e3b4d7 | ||
|
|
eaedcb8d06 | ||
|
|
e7e6380adc | ||
|
|
1f50483b50 | ||
|
|
6f703c8267 | ||
|
|
d150c374b5 | ||
|
|
f29b2ee09d | ||
|
|
0a934e8c01 | ||
|
|
2163aa87d4 | ||
|
|
5d8408d892 | ||
|
|
93d0e3a5d0 | ||
|
|
b4a5e91bc9 | ||
|
|
759078ef7f | ||
|
|
905539a6eb | ||
|
|
f1512812ad | ||
|
|
5f1cf89649 | ||
|
|
f291ad189a | ||
|
|
8807636372 | ||
|
|
70be1d8afc | ||
|
|
1f76aa427d | ||
|
|
134cbf8b75 | ||
|
|
669b944cfd | ||
|
|
9962829885 | ||
|
|
1393615f01 | ||
|
|
d58ae3a6b1 | ||
|
|
a188729af6 | ||
|
|
422478bd9a |
2
.gitmodules
vendored
2
.gitmodules
vendored
@@ -1,3 +1,3 @@
|
||||
[submodule "thirdparty/projectx"]
|
||||
path = thirdparty/projectx
|
||||
url = git@github.com:dijunkun/projectx.git
|
||||
url = https://github.com/dijunkun/projectx.git
|
||||
|
||||
39
Info.plist
Normal file
39
Info.plist
Normal file
@@ -0,0 +1,39 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<!-- 应用的Bundle identifier,通常使用反向域名标记 -->
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>com.yourcompany.yourappname</string>
|
||||
|
||||
<!-- 应用的显示名称 -->
|
||||
<key>CFBundleName</key>
|
||||
<string>Your App Name</string>
|
||||
|
||||
<!-- 应用的版本号 -->
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0.0</string>
|
||||
|
||||
<!-- 应用的构建版本号 -->
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
|
||||
<!-- 请求麦克风访问权限 -->
|
||||
<key>NSMicrophoneUsageDescription</key>
|
||||
<string>App requires access to the microphone for audio recording.</string>
|
||||
|
||||
<!-- 请求相机访问权限 -->
|
||||
<key>NSCameraUsageDescription</key>
|
||||
<string>App requires access to the camera for video recording.</string>
|
||||
|
||||
<!-- 请求使用连续相机设备 -->
|
||||
<key>NSCameraUseContinuityCameraDeviceType</key>
|
||||
<string>Your usage description here</string>
|
||||
|
||||
<!-- High DPI -->>
|
||||
<key>NSHighResolutionCapable</key>
|
||||
<true/>
|
||||
|
||||
<!-- 其他权限和配置可以在这里添加 -->
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
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/)).
|
||||
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
|
||||
|
||||
|
||||
@@ -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
|
||||
2348
fonts/OPPOSans_Regular.h
Normal file
2348
fonts/OPPOSans_Regular.h
Normal file
File diff suppressed because it is too large
Load Diff
5668
fonts/fa_regular_400.h
Normal file
5668
fonts/fa_regular_400.h
Normal file
File diff suppressed because it is too large
Load Diff
35041
fonts/fa_solid_900.h
Normal file
35041
fonts/fa_solid_900.h
Normal file
File diff suppressed because it is too large
Load Diff
1
icon/app.rc
Normal file
1
icon/app.rc
Normal file
@@ -0,0 +1 @@
|
||||
IDI_ICON1 ICON "app_icon.ico"
|
||||
BIN
icon/app_icon.ico
Normal file
BIN
icon/app_icon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 687 B |
BIN
icon/捕获.PNG
BIN
icon/捕获.PNG
Binary file not shown.
|
Before Width: | Height: | Size: 30 KiB |
BIN
icon/捕获.ico
BIN
icon/捕获.ico
Binary file not shown.
|
Before Width: | Height: | Size: 84 KiB |
@@ -1,6 +1,6 @@
|
||||
#include "platform.h"
|
||||
|
||||
#include "log.h"
|
||||
#include "rd_log.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <Winsock2.h>
|
||||
@@ -25,13 +25,13 @@ std::string GetMac() {
|
||||
#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]);
|
||||
len += sprintf_s(mac_addr + len, sizeof(mac_addr) - len, "%.2X",
|
||||
adapter->Address[i]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -55,7 +55,8 @@ std::string GetMac() {
|
||||
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]);
|
||||
len +=
|
||||
snprintf(mac_addr + len, sizeof(mac_addr) - len, "%.2X", base[i]);
|
||||
}
|
||||
}
|
||||
cursor = cursor->ifa_next;
|
||||
@@ -99,3 +100,26 @@ std::string GetMac() {
|
||||
#endif
|
||||
return mac_addr;
|
||||
}
|
||||
|
||||
std::string GetHostName() {
|
||||
char hostname[256];
|
||||
#ifdef _WIN32
|
||||
WSADATA wsaData;
|
||||
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
|
||||
std::cerr << "WSAStartup failed." << std::endl;
|
||||
return "";
|
||||
}
|
||||
if (gethostname(hostname, sizeof(hostname)) == SOCKET_ERROR) {
|
||||
LOG_ERROR("gethostname failed: {}", WSAGetLastError());
|
||||
WSACleanup();
|
||||
return "";
|
||||
}
|
||||
WSACleanup();
|
||||
#else
|
||||
if (gethostname(hostname, sizeof(hostname)) == -1) {
|
||||
LOG_ERROR("gethostname failed");
|
||||
return "";
|
||||
}
|
||||
#endif
|
||||
return hostname;
|
||||
}
|
||||
@@ -10,5 +10,6 @@
|
||||
#include <iostream>
|
||||
|
||||
std::string GetMac();
|
||||
std::string GetHostName();
|
||||
|
||||
#endif
|
||||
45
src/config_center/config_center.cpp
Normal file
45
src/config_center/config_center.cpp
Normal file
@@ -0,0 +1,45 @@
|
||||
#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;
|
||||
}
|
||||
|
||||
int ConfigCenter::SetTurn(bool enable_turn) {
|
||||
enable_turn_ = enable_turn;
|
||||
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_; }
|
||||
|
||||
bool ConfigCenter::IsEnableTurn() { return enable_turn_; }
|
||||
43
src/config_center/config_center.h
Normal file
43
src/config_center/config_center.h
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* @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);
|
||||
int SetTurn(bool enable_turn);
|
||||
|
||||
public:
|
||||
LANGUAGE GetLanguage();
|
||||
VIDEO_QUALITY GetVideoQuality();
|
||||
VIDEO_ENCODE_FORMAT GetVideoEncodeFormat();
|
||||
bool IsHardwareVideoCodec();
|
||||
bool IsEnableTurn();
|
||||
|
||||
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;
|
||||
bool enable_turn_ = false;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -9,7 +9,12 @@
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
typedef enum { mouse = 0, keyboard } ControlType;
|
||||
typedef enum {
|
||||
mouse = 0,
|
||||
keyboard,
|
||||
audio_capture,
|
||||
host_infomation
|
||||
} ControlType;
|
||||
typedef enum { move = 0, left_down, left_up, right_down, right_up } MouseFlag;
|
||||
typedef enum { key_down = 0, key_up } KeyFlag;
|
||||
typedef struct {
|
||||
@@ -23,22 +28,35 @@ typedef struct {
|
||||
KeyFlag flag;
|
||||
} Key;
|
||||
|
||||
typedef struct {
|
||||
char host_name[64];
|
||||
size_t host_name_size;
|
||||
} HostInfo;
|
||||
|
||||
typedef struct {
|
||||
ControlType type;
|
||||
union {
|
||||
Mouse m;
|
||||
Key k;
|
||||
HostInfo i;
|
||||
bool a;
|
||||
};
|
||||
} RemoteAction;
|
||||
|
||||
// int key_code, bool is_down
|
||||
typedef void (*OnKeyAction)(int, bool, void*);
|
||||
|
||||
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;
|
||||
// virtual int Init(int screen_width, int screen_height);
|
||||
// virtual int Destroy();
|
||||
// virtual int SendCommand(RemoteAction remote_action);
|
||||
|
||||
// virtual int Hook();
|
||||
// virtual int Unhook();
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -8,6 +8,7 @@
|
||||
#define _DEVICE_CONTROLLER_FACTORY_H_
|
||||
|
||||
#include "device_controller.h"
|
||||
#include "keyboard_capturer.h"
|
||||
#include "mouse_controller.h"
|
||||
|
||||
class DeviceControllerFactory {
|
||||
@@ -23,7 +24,7 @@ class DeviceControllerFactory {
|
||||
case Mouse:
|
||||
return new MouseController();
|
||||
case Keyboard:
|
||||
return nullptr;
|
||||
return new KeyboardCapturer();
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
15
src/device_controller/keyboard/linux/keyboard_capturer.cpp
Normal file
15
src/device_controller/keyboard/linux/keyboard_capturer.cpp
Normal file
@@ -0,0 +1,15 @@
|
||||
#include "keyboard_capturer.h"
|
||||
|
||||
KeyboardCapturer::KeyboardCapturer() {}
|
||||
|
||||
KeyboardCapturer::~KeyboardCapturer() {}
|
||||
|
||||
int KeyboardCapturer::Hook(OnKeyAction on_key_action, void *user_ptr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int KeyboardCapturer::Unhook() { return 0; }
|
||||
|
||||
int KeyboardCapturer::SendKeyboardCommand(int key_code, bool is_down) {
|
||||
return 0;
|
||||
}
|
||||
25
src/device_controller/keyboard/linux/keyboard_capturer.h
Normal file
25
src/device_controller/keyboard/linux/keyboard_capturer.h
Normal file
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* @Author: DI JUNKUN
|
||||
* @Date: 2024-11-22
|
||||
* Copyright (c) 2024 by DI JUNKUN, All Rights Reserved.
|
||||
*/
|
||||
|
||||
#ifndef _KEYBOARD_CAPTURER_H_
|
||||
#define _KEYBOARD_CAPTURER_H_
|
||||
|
||||
#include "device_controller.h"
|
||||
|
||||
class KeyboardCapturer : public DeviceController {
|
||||
public:
|
||||
KeyboardCapturer();
|
||||
virtual ~KeyboardCapturer();
|
||||
|
||||
public:
|
||||
virtual int Hook(OnKeyAction on_key_action, void *user_ptr);
|
||||
virtual int Unhook();
|
||||
virtual int SendKeyboardCommand(int key_code, bool is_down);
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
#endif
|
||||
135
src/device_controller/keyboard/mac/keyboard_capturer.cpp
Normal file
135
src/device_controller/keyboard/mac/keyboard_capturer.cpp
Normal file
@@ -0,0 +1,135 @@
|
||||
#include "keyboard_capturer.h"
|
||||
|
||||
#include "keyboard_converter.h"
|
||||
#include "rd_log.h"
|
||||
|
||||
static OnKeyAction g_on_key_action = nullptr;
|
||||
static void *g_user_ptr = nullptr;
|
||||
|
||||
CGEventRef eventCallback(CGEventTapProxy proxy, CGEventType type,
|
||||
CGEventRef event, void *userInfo) {
|
||||
KeyboardCapturer *keyboard_capturer = (KeyboardCapturer *)userInfo;
|
||||
if (!keyboard_capturer) {
|
||||
LOG_ERROR("keyboard_capturer is nullptr");
|
||||
return event;
|
||||
}
|
||||
|
||||
int vk_code = 0;
|
||||
|
||||
if (type == kCGEventKeyDown || type == kCGEventKeyUp) {
|
||||
CGKeyCode key_code = static_cast<CGKeyCode>(
|
||||
CGEventGetIntegerValueField(event, kCGKeyboardEventKeycode));
|
||||
if (CGKeyCodeToVkCode.find(key_code) != CGKeyCodeToVkCode.end()) {
|
||||
g_on_key_action(CGKeyCodeToVkCode[key_code], type == kCGEventKeyDown,
|
||||
g_user_ptr);
|
||||
}
|
||||
} else if (type == kCGEventFlagsChanged) {
|
||||
CGEventFlags current_flags = CGEventGetFlags(event);
|
||||
CGKeyCode key_code = static_cast<CGKeyCode>(
|
||||
CGEventGetIntegerValueField(event, kCGKeyboardEventKeycode));
|
||||
|
||||
// caps lock
|
||||
bool caps_lock_state = (current_flags & kCGEventFlagMaskAlphaShift) != 0;
|
||||
if (caps_lock_state != keyboard_capturer->caps_lock_flag_) {
|
||||
keyboard_capturer->caps_lock_flag_ = caps_lock_state;
|
||||
if (keyboard_capturer->caps_lock_flag_) {
|
||||
g_on_key_action(CGKeyCodeToVkCode[key_code], true, g_user_ptr);
|
||||
} else {
|
||||
g_on_key_action(CGKeyCodeToVkCode[key_code], false, g_user_ptr);
|
||||
}
|
||||
}
|
||||
|
||||
// shift
|
||||
bool shift_state = (current_flags & kCGEventFlagMaskShift) != 0;
|
||||
if (shift_state != keyboard_capturer->shift_flag_) {
|
||||
keyboard_capturer->shift_flag_ = shift_state;
|
||||
if (keyboard_capturer->shift_flag_) {
|
||||
g_on_key_action(CGKeyCodeToVkCode[key_code], true, g_user_ptr);
|
||||
} else {
|
||||
g_on_key_action(CGKeyCodeToVkCode[key_code], false, g_user_ptr);
|
||||
}
|
||||
}
|
||||
|
||||
// control
|
||||
bool control_state = (current_flags & kCGEventFlagMaskControl) != 0;
|
||||
if (control_state != keyboard_capturer->control_flag_) {
|
||||
keyboard_capturer->control_flag_ = control_state;
|
||||
if (keyboard_capturer->control_flag_) {
|
||||
g_on_key_action(CGKeyCodeToVkCode[key_code], true, g_user_ptr);
|
||||
} else {
|
||||
g_on_key_action(CGKeyCodeToVkCode[key_code], false, g_user_ptr);
|
||||
}
|
||||
}
|
||||
|
||||
// option
|
||||
bool option_state = (current_flags & kCGEventFlagMaskAlternate) != 0;
|
||||
if (option_state != keyboard_capturer->option_flag_) {
|
||||
keyboard_capturer->option_flag_ = option_state;
|
||||
if (keyboard_capturer->option_flag_) {
|
||||
g_on_key_action(CGKeyCodeToVkCode[key_code], true, g_user_ptr);
|
||||
} else {
|
||||
g_on_key_action(CGKeyCodeToVkCode[key_code], false, g_user_ptr);
|
||||
}
|
||||
}
|
||||
|
||||
// command
|
||||
bool command_state = (current_flags & kCGEventFlagMaskCommand) != 0;
|
||||
if (command_state != keyboard_capturer->command_flag_) {
|
||||
keyboard_capturer->command_flag_ = command_state;
|
||||
if (keyboard_capturer->command_flag_) {
|
||||
g_on_key_action(CGKeyCodeToVkCode[key_code], true, g_user_ptr);
|
||||
} else {
|
||||
g_on_key_action(CGKeyCodeToVkCode[key_code], false, g_user_ptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
KeyboardCapturer::KeyboardCapturer() {}
|
||||
|
||||
KeyboardCapturer::~KeyboardCapturer() {}
|
||||
|
||||
int KeyboardCapturer::Hook(OnKeyAction on_key_action, void *user_ptr) {
|
||||
g_on_key_action = on_key_action;
|
||||
g_user_ptr = user_ptr;
|
||||
|
||||
CGEventMask eventMask = (1 << kCGEventKeyDown) | (1 << kCGEventKeyUp) |
|
||||
(1 << kCGEventFlagsChanged);
|
||||
|
||||
event_tap_ = CGEventTapCreate(kCGSessionEventTap, kCGHeadInsertEventTap,
|
||||
kCGEventTapOptionDefault, eventMask,
|
||||
eventCallback, this);
|
||||
|
||||
if (!event_tap_) {
|
||||
LOG_ERROR("CGEventTapCreate failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
run_loop_source_ =
|
||||
CFMachPortCreateRunLoopSource(kCFAllocatorDefault, event_tap_, 0);
|
||||
|
||||
CFRunLoopAddSource(CFRunLoopGetCurrent(), run_loop_source_,
|
||||
kCFRunLoopCommonModes);
|
||||
|
||||
CGEventTapEnable(event_tap_, true);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int KeyboardCapturer::Unhook() {
|
||||
CFRelease(run_loop_source_);
|
||||
CFRelease(event_tap_);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int KeyboardCapturer::SendKeyboardCommand(int key_code, bool is_down) {
|
||||
if (vkCodeToCGKeyCode.find(key_code) != vkCodeToCGKeyCode.end()) {
|
||||
CGKeyCode cg_key_code = vkCodeToCGKeyCode[key_code];
|
||||
CGEventRef event = CGEventCreateKeyboardEvent(NULL, cg_key_code, is_down);
|
||||
CGEventPost(kCGHIDEventTap, event);
|
||||
CFRelease(event);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
36
src/device_controller/keyboard/mac/keyboard_capturer.h
Normal file
36
src/device_controller/keyboard/mac/keyboard_capturer.h
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* @Author: DI JUNKUN
|
||||
* @Date: 2024-11-22
|
||||
* Copyright (c) 2024 by DI JUNKUN, All Rights Reserved.
|
||||
*/
|
||||
|
||||
#ifndef _KEYBOARD_CAPTURER_H_
|
||||
#define _KEYBOARD_CAPTURER_H_
|
||||
|
||||
#include <ApplicationServices/ApplicationServices.h>
|
||||
|
||||
#include "device_controller.h"
|
||||
|
||||
class KeyboardCapturer : public DeviceController {
|
||||
public:
|
||||
KeyboardCapturer();
|
||||
virtual ~KeyboardCapturer();
|
||||
|
||||
public:
|
||||
virtual int Hook(OnKeyAction on_key_action, void *user_ptr);
|
||||
virtual int Unhook();
|
||||
virtual int SendKeyboardCommand(int key_code, bool is_down);
|
||||
|
||||
private:
|
||||
CFMachPortRef event_tap_;
|
||||
CFRunLoopSourceRef run_loop_source_;
|
||||
|
||||
public:
|
||||
bool caps_lock_flag_ = false;
|
||||
bool shift_flag_ = false;
|
||||
bool control_flag_ = false;
|
||||
bool option_flag_ = false;
|
||||
bool command_flag_ = false;
|
||||
};
|
||||
|
||||
#endif
|
||||
248
src/device_controller/keyboard/mac/keyboard_converter.h
Normal file
248
src/device_controller/keyboard/mac/keyboard_converter.h
Normal file
@@ -0,0 +1,248 @@
|
||||
/*
|
||||
* @Author: DI JUNKUN
|
||||
* @Date: 2024-11-25
|
||||
* Copyright (c) 2024 by DI JUNKUN, All Rights Reserved.
|
||||
*/
|
||||
|
||||
#ifndef _KEYBOARD_CONVERTER_H_
|
||||
#define _KEYBOARD_CONVERTER_H_
|
||||
|
||||
#include <map>
|
||||
|
||||
// Windows vkCode to macOS CGKeyCode (104 keys)
|
||||
std::map<int, int> vkCodeToCGKeyCode = {
|
||||
// A-Z
|
||||
{0x41, 0x00}, // A
|
||||
{0x42, 0x0B}, // B
|
||||
{0x43, 0x08}, // C
|
||||
{0x44, 0x02}, // D
|
||||
{0x45, 0x0E}, // E
|
||||
{0x46, 0x03}, // F
|
||||
{0x47, 0x05}, // G
|
||||
{0x48, 0x04}, // H
|
||||
{0x49, 0x22}, // I
|
||||
{0x4A, 0x26}, // J
|
||||
{0x4B, 0x28}, // K
|
||||
{0x4C, 0x25}, // L
|
||||
{0x4D, 0x2E}, // M
|
||||
{0x4E, 0x2D}, // N
|
||||
{0x4F, 0x1F}, // O
|
||||
{0x50, 0x23}, // P
|
||||
{0x51, 0x0C}, // Q
|
||||
{0x52, 0x0F}, // R
|
||||
{0x53, 0x01}, // S
|
||||
{0x54, 0x11}, // T
|
||||
{0x55, 0x20}, // U
|
||||
{0x56, 0x09}, // V
|
||||
{0x57, 0x0D}, // W
|
||||
{0x58, 0x07}, // X
|
||||
{0x59, 0x10}, // Y
|
||||
{0x5A, 0x06}, // Z
|
||||
|
||||
// 0-9
|
||||
{0x30, 0x1D}, // 0
|
||||
{0x31, 0x12}, // 1
|
||||
{0x32, 0x13}, // 2
|
||||
{0x33, 0x14}, // 3
|
||||
{0x34, 0x15}, // 4
|
||||
{0x35, 0x17}, // 5
|
||||
{0x36, 0x16}, // 6
|
||||
{0x37, 0x1A}, // 7
|
||||
{0x38, 0x1C}, // 8
|
||||
{0x39, 0x19}, // 9
|
||||
|
||||
// F1-F12
|
||||
{0x70, 0x7A}, // F1
|
||||
{0x71, 0x78}, // F2
|
||||
{0x72, 0x63}, // F3
|
||||
{0x73, 0x76}, // F4
|
||||
{0x74, 0x60}, // F5
|
||||
{0x75, 0x61}, // F6
|
||||
{0x76, 0x62}, // F7
|
||||
{0x77, 0x64}, // F8
|
||||
{0x78, 0x65}, // F9
|
||||
{0x79, 0x6D}, // F10
|
||||
{0x7A, 0x67}, // F11
|
||||
{0x7B, 0x6F}, // F12
|
||||
|
||||
// control keys
|
||||
{0x1B, 0x35}, // Escape
|
||||
{0x0D, 0x24}, // Enter
|
||||
{0x20, 0x31}, // Space
|
||||
{0x08, 0x33}, // Backspace
|
||||
{0x09, 0x30}, // Tab
|
||||
{0x2C, 0x74}, // Print Screen
|
||||
{0x2D, 0x72}, // Insert
|
||||
{0x2E, 0x75}, // Delete
|
||||
{0x24, 0x73}, // Home
|
||||
{0x23, 0x77}, // End
|
||||
{0x21, 0x79}, // Page Up
|
||||
{0x22, 0x7A}, // Page Down
|
||||
|
||||
// arrow keys
|
||||
{0x25, 0x7B}, // Left Arrow
|
||||
{0x27, 0x7C}, // Right Arrow
|
||||
{0x26, 0x7E}, // Up Arrow
|
||||
{0x28, 0x7D}, // Down Arrow
|
||||
|
||||
// numpad
|
||||
{0x60, 0x52}, // Numpad 0
|
||||
{0x61, 0x53}, // Numpad 1
|
||||
{0x62, 0x54}, // Numpad 2
|
||||
{0x63, 0x55}, // Numpad 3
|
||||
{0x64, 0x56}, // Numpad 4
|
||||
{0x65, 0x57}, // Numpad 5
|
||||
{0x66, 0x58}, // Numpad 6
|
||||
{0x67, 0x59}, // Numpad 7
|
||||
{0x68, 0x5B}, // Numpad 8
|
||||
{0x69, 0x5C}, // Numpad 9
|
||||
{0x6E, 0x41}, // Numpad .
|
||||
{0x6F, 0x4B}, // Numpad /
|
||||
{0x6A, 0x43}, // Numpad *
|
||||
{0x6D, 0x4E}, // Numpad -
|
||||
{0x6B, 0x45}, // Numpad +
|
||||
|
||||
// symbol keys
|
||||
{0xBA, 0x29}, // ; (Semicolon)
|
||||
{0xDE, 0x27}, // ' (Quote)
|
||||
{0xC0, 0x32}, // ` (Backtick)
|
||||
{0xBC, 0x2B}, // , (Comma)
|
||||
{0xBE, 0x2F}, // . (Period)
|
||||
{0xBF, 0x2C}, // / (Slash)
|
||||
{0xDC, 0x2A}, // \ (Backslash)
|
||||
{0xDB, 0x21}, // [ (Left Bracket)
|
||||
{0xDD, 0x1E}, // ] (Right Bracket)
|
||||
{0xBD, 0x1B}, // - (Minus)
|
||||
{0xBB, 0x18}, // = (Equals)
|
||||
|
||||
// modifier keys
|
||||
{0x14, 0x39}, // Caps Lock
|
||||
{0xA0, 0x38}, // Shift (Left)
|
||||
{0xA1, 0x3C}, // Shift (Right)
|
||||
{0xA2, 0x3B}, // Ctrl (Left)
|
||||
{0xA3, 0x3E}, // Ctrl (Right)
|
||||
{0xA4, 0x3A}, // Alt (Left)
|
||||
{0xA5, 0x3D}, // Alt (Right)
|
||||
{0x5B, 0x37}, // Left Command (Windows key)
|
||||
{0x5C, 0x36}, // Right Command
|
||||
};
|
||||
|
||||
// macOS CGKeyCode to Windows vkCode
|
||||
std::map<int, int> CGKeyCodeToVkCode = {
|
||||
// A-Z
|
||||
{0x00, 0x41}, // A
|
||||
{0x0B, 0x42}, // B
|
||||
{0x08, 0x43}, // C
|
||||
{0x02, 0x44}, // D
|
||||
{0x0E, 0x45}, // E
|
||||
{0x03, 0x46}, // F
|
||||
{0x05, 0x47}, // G
|
||||
{0x04, 0x48}, // H
|
||||
{0x22, 0x49}, // I
|
||||
{0x26, 0x4A}, // J
|
||||
{0x28, 0x4B}, // K
|
||||
{0x25, 0x4C}, // L
|
||||
{0x2E, 0x4D}, // M
|
||||
{0x2D, 0x4E}, // N
|
||||
{0x1F, 0x4F}, // O
|
||||
{0x23, 0x50}, // P
|
||||
{0x0C, 0x51}, // Q
|
||||
{0x0F, 0x52}, // R
|
||||
{0x01, 0x53}, // S
|
||||
{0x11, 0x54}, // T
|
||||
{0x20, 0x55}, // U
|
||||
{0x09, 0x56}, // V
|
||||
{0x0D, 0x57}, // W
|
||||
{0x07, 0x58}, // X
|
||||
{0x10, 0x59}, // Y
|
||||
{0x06, 0x5A}, // Z
|
||||
|
||||
// 0-9
|
||||
{0x1D, 0x30}, // 0
|
||||
{0x12, 0x31}, // 1
|
||||
{0x13, 0x32}, // 2
|
||||
{0x14, 0x33}, // 3
|
||||
{0x15, 0x34}, // 4
|
||||
{0x17, 0x35}, // 5
|
||||
{0x16, 0x36}, // 6
|
||||
{0x1A, 0x37}, // 7
|
||||
{0x1C, 0x38}, // 8
|
||||
{0x19, 0x39}, // 9
|
||||
|
||||
// F1-F12
|
||||
{0x7A, 0x70}, // F1
|
||||
{0x78, 0x71}, // F2
|
||||
{0x63, 0x72}, // F3
|
||||
{0x76, 0x73}, // F4
|
||||
{0x60, 0x74}, // F5
|
||||
{0x61, 0x75}, // F6
|
||||
{0x62, 0x76}, // F7
|
||||
{0x64, 0x77}, // F8
|
||||
{0x65, 0x78}, // F9
|
||||
{0x6D, 0x79}, // F10
|
||||
{0x67, 0x7A}, // F11
|
||||
{0x6F, 0x7B}, // F12
|
||||
|
||||
// control keys
|
||||
{0x35, 0x1B}, // Escape
|
||||
{0x24, 0x0D}, // Enter
|
||||
{0x31, 0x20}, // Space
|
||||
{0x33, 0x08}, // Backspace
|
||||
{0x30, 0x09}, // Tab
|
||||
{0x74, 0x2C}, // Print Screen
|
||||
{0x72, 0x2D}, // Insert
|
||||
{0x75, 0x2E}, // Delete
|
||||
{0x73, 0x24}, // Home
|
||||
{0x77, 0x23}, // End
|
||||
{0x79, 0x21}, // Page Up
|
||||
{0x7A, 0x22}, // Page Down
|
||||
|
||||
// arrow keys
|
||||
{0x7B, 0x25}, // Left Arrow
|
||||
{0x7C, 0x27}, // Right Arrow
|
||||
{0x7E, 0x26}, // Up Arrow
|
||||
{0x7D, 0x28}, // Down Arrow
|
||||
|
||||
// numpad
|
||||
{0x52, 0x60}, // Numpad 0
|
||||
{0x53, 0x61}, // Numpad 1
|
||||
{0x54, 0x62}, // Numpad 2
|
||||
{0x55, 0x63}, // Numpad 3
|
||||
{0x56, 0x64}, // Numpad 4
|
||||
{0x57, 0x65}, // Numpad 5
|
||||
{0x58, 0x66}, // Numpad 6
|
||||
{0x59, 0x67}, // Numpad 7
|
||||
{0x5B, 0x68}, // Numpad 8
|
||||
{0x5C, 0x69}, // Numpad 9
|
||||
{0x41, 0x6E}, // Numpad .
|
||||
{0x4B, 0x6F}, // Numpad /
|
||||
{0x43, 0x6A}, // Numpad *
|
||||
{0x4E, 0x6D}, // Numpad -
|
||||
{0x45, 0x6B}, // Numpad +
|
||||
|
||||
// symbol keys
|
||||
{0x29, 0xBA}, // ; (Semicolon)
|
||||
{0x27, 0xDE}, // ' (Quote)
|
||||
{0x32, 0xC0}, // ` (Backtick)
|
||||
{0x2B, 0xBC}, // , (Comma)
|
||||
{0x2F, 0xBE}, // . (Period)
|
||||
{0x2C, 0xBF}, // / (Slash)
|
||||
{0x2A, 0xDC}, // \ (Backslash)
|
||||
{0x21, 0xDB}, // [ (Left Bracket)
|
||||
{0x1E, 0xDD}, // ] (Right Bracket)
|
||||
{0x1B, 0xBD}, // - (Minus)
|
||||
{0x18, 0xBB}, // = (Equals)
|
||||
|
||||
// modifier keys
|
||||
{0x39, 0x14}, // Caps Lock
|
||||
{0x38, 0xA0}, // Shift (Left)
|
||||
{0x3C, 0xA1}, // Shift (Right)
|
||||
{0x3B, 0xA2}, // Control (Left)
|
||||
{0x3E, 0xA3}, // Control (Right)
|
||||
{0x3A, 0xA4}, // Alt (Left)
|
||||
{0x3D, 0xA5}, // Alt (Right)
|
||||
{0x37, 0x5B}, // Left Command (Windows key)
|
||||
{0x36, 0x5C}, // Right Command
|
||||
};
|
||||
|
||||
#endif
|
||||
56
src/device_controller/keyboard/windows/keyboard_capturer.cpp
Normal file
56
src/device_controller/keyboard/windows/keyboard_capturer.cpp
Normal file
@@ -0,0 +1,56 @@
|
||||
#include "keyboard_capturer.h"
|
||||
|
||||
#include "rd_log.h"
|
||||
|
||||
static OnKeyAction g_on_key_action = nullptr;
|
||||
static void* g_user_ptr = nullptr;
|
||||
|
||||
LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam) {
|
||||
if (nCode == HC_ACTION && g_on_key_action) {
|
||||
KBDLLHOOKSTRUCT* kbData = reinterpret_cast<KBDLLHOOKSTRUCT*>(lParam);
|
||||
|
||||
if (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN) {
|
||||
g_on_key_action(kbData->vkCode, true, g_user_ptr);
|
||||
} else if (wParam == WM_KEYUP || wParam == WM_SYSKEYUP) {
|
||||
g_on_key_action(kbData->vkCode, false, g_user_ptr);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
return CallNextHookEx(NULL, nCode, wParam, lParam);
|
||||
}
|
||||
|
||||
KeyboardCapturer::KeyboardCapturer() {}
|
||||
|
||||
KeyboardCapturer::~KeyboardCapturer() {}
|
||||
|
||||
int KeyboardCapturer::Hook(OnKeyAction on_key_action, void* user_ptr) {
|
||||
g_on_key_action = on_key_action;
|
||||
g_user_ptr = user_ptr;
|
||||
|
||||
keyboard_hook_ = SetWindowsHookEx(WH_KEYBOARD_LL, KeyboardProc, NULL, 0);
|
||||
if (!keyboard_hook_) {
|
||||
LOG_ERROR("Failed to install keyboard hook!")
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int KeyboardCapturer::Unhook() {
|
||||
UnhookWindowsHookEx(keyboard_hook_);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// apply remote keyboard commands to the local machine
|
||||
int KeyboardCapturer::SendKeyboardCommand(int key_code, bool is_down) {
|
||||
INPUT input = {0};
|
||||
input.type = INPUT_KEYBOARD;
|
||||
input.ki.wVk = (WORD)key_code;
|
||||
|
||||
if (!is_down) {
|
||||
input.ki.dwFlags = KEYEVENTF_KEYUP;
|
||||
}
|
||||
SendInput(1, &input, sizeof(INPUT));
|
||||
|
||||
return 0;
|
||||
}
|
||||
28
src/device_controller/keyboard/windows/keyboard_capturer.h
Normal file
28
src/device_controller/keyboard/windows/keyboard_capturer.h
Normal file
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* @Author: DI JUNKUN
|
||||
* @Date: 2024-11-22
|
||||
* Copyright (c) 2024 by DI JUNKUN, All Rights Reserved.
|
||||
*/
|
||||
|
||||
#ifndef _KEYBOARD_CAPTURER_H_
|
||||
#define _KEYBOARD_CAPTURER_H_
|
||||
|
||||
#include <Windows.h>
|
||||
|
||||
#include "device_controller.h"
|
||||
|
||||
class KeyboardCapturer : public DeviceController {
|
||||
public:
|
||||
KeyboardCapturer();
|
||||
virtual ~KeyboardCapturer();
|
||||
|
||||
public:
|
||||
virtual int Hook(OnKeyAction on_key_action, void *user_ptr);
|
||||
virtual int Unhook();
|
||||
virtual int SendKeyboardCommand(int key_code, bool is_down);
|
||||
|
||||
private:
|
||||
HHOOK keyboard_hook_ = nullptr;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -4,7 +4,12 @@
|
||||
|
||||
MouseController::MouseController() {}
|
||||
|
||||
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;
|
||||
@@ -13,6 +18,7 @@ int MouseController::Init(int screen_width, int 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);
|
||||
@@ -35,19 +41,16 @@ int MouseController::Init(int screen_width, int screen_height) {
|
||||
uidev.absmin[ABS_Y] = 0;
|
||||
uidev.absmax[ABS_Y] = screen_height_;
|
||||
|
||||
write(uinput_fd_, &uidev, sizeof(uidev));
|
||||
int res_uidev = 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::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;
|
||||
int mouse_pos_x = remote_action.m.x;
|
||||
int mouse_pos_y = remote_action.m.y;
|
||||
|
||||
if (remote_action.type == ControlType::mouse) {
|
||||
struct input_event event;
|
||||
@@ -71,6 +74,7 @@ int MouseController::SendCommand(RemoteAction remote_action) {
|
||||
}
|
||||
|
||||
void MouseController::SimulateKeyDown(int fd, int kval) {
|
||||
int res_ev = 0;
|
||||
struct input_event event;
|
||||
memset(&event, 0, sizeof(event));
|
||||
gettimeofday(&event.time, 0);
|
||||
@@ -78,15 +82,16 @@ void MouseController::SimulateKeyDown(int fd, int kval) {
|
||||
event.type = EV_KEY;
|
||||
event.value = 1;
|
||||
event.code = kval;
|
||||
write(fd, &event, sizeof(event));
|
||||
res_ev = write(fd, &event, sizeof(event));
|
||||
|
||||
event.type = EV_SYN;
|
||||
event.value = 0;
|
||||
event.code = SYN_REPORT;
|
||||
write(fd, &event, sizeof(event));
|
||||
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);
|
||||
@@ -94,12 +99,12 @@ void MouseController::SimulateKeyUp(int fd, int kval) {
|
||||
event.type = EV_KEY;
|
||||
event.value = 0;
|
||||
event.code = kval;
|
||||
write(fd, &event, sizeof(event));
|
||||
res_ev = write(fd, &event, sizeof(event));
|
||||
|
||||
event.type = EV_SYN;
|
||||
event.value = 0;
|
||||
event.code = SYN_REPORT;
|
||||
write(fd, &event, sizeof(event));
|
||||
res_ev = write(fd, &event, sizeof(event));
|
||||
}
|
||||
|
||||
void MouseController::SetMousePosition(int fd, int x, int y) {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
#include <ApplicationServices/ApplicationServices.h>
|
||||
|
||||
#include "log.h"
|
||||
#include "rd_log.h"
|
||||
|
||||
MouseController::MouseController() {}
|
||||
|
||||
@@ -12,14 +12,19 @@ int MouseController::Init(int screen_width, int screen_height) {
|
||||
screen_width_ = screen_width;
|
||||
screen_height_ = screen_height;
|
||||
|
||||
pixel_width_ =
|
||||
CGDisplayModeGetPixelWidth(CGDisplayCopyDisplayMode(CGMainDisplayID()));
|
||||
pixel_height_ =
|
||||
CGDisplayModeGetPixelHeight(CGDisplayCopyDisplayMode(CGMainDisplayID()));
|
||||
|
||||
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;
|
||||
int mouse_pos_x = remote_action.m.x * screen_width_ / pixel_width_;
|
||||
int mouse_pos_y = remote_action.m.y * screen_height_ / pixel_height_;
|
||||
|
||||
if (remote_action.type == ControlType::mouse) {
|
||||
CGEventRef mouse_event;
|
||||
|
||||
@@ -22,6 +22,9 @@ class MouseController : public DeviceController {
|
||||
private:
|
||||
int screen_width_ = 0;
|
||||
int screen_height_ = 0;
|
||||
|
||||
int pixel_width_ = 0;
|
||||
int pixel_height_ = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,6 +1,6 @@
|
||||
#include "mouse_controller.h"
|
||||
|
||||
#include "log.h"
|
||||
#include "rd_log.h"
|
||||
|
||||
MouseController::MouseController() {}
|
||||
|
||||
@@ -20,8 +20,8 @@ int MouseController::SendCommand(RemoteAction remote_action) {
|
||||
|
||||
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;
|
||||
ip.mi.dx = (LONG)remote_action.m.x;
|
||||
ip.mi.dy = (LONG)remote_action.m.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) {
|
||||
|
||||
863
src/gui/main.cpp
863
src/gui/main.cpp
@@ -1,5 +1,3 @@
|
||||
#include <SDL.h>
|
||||
#include <stdio.h>
|
||||
#ifdef _WIN32
|
||||
#ifdef REMOTE_DESK_DEBUG
|
||||
#pragma comment(linker, "/subsystem:\"console\"")
|
||||
@@ -8,865 +6,14 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include "rd_log.h"
|
||||
#include "render.h"
|
||||
|
||||
#include "platform.h"
|
||||
|
||||
extern "C" {
|
||||
#include <libavdevice/avdevice.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"
|
||||
#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
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
int src_linesize;
|
||||
int src_nb_samples = 480;
|
||||
|
||||
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;
|
||||
int dst_linesize;
|
||||
int dst_nb_samples;
|
||||
int max_dst_nb_samples;
|
||||
|
||||
int dst_bufsize;
|
||||
struct SwrContext *swr_ctx;
|
||||
|
||||
int ret;
|
||||
|
||||
int audio_len = 0;
|
||||
|
||||
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};
|
||||
|
||||
// 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_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 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;
|
||||
|
||||
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_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) {
|
||||
remote_action.m.flag = MouseFlag::left_up;
|
||||
} else if (SDL_BUTTON_RIGHT == ev.button.button) {
|
||||
remote_action.m.flag = MouseFlag::right_up;
|
||||
}
|
||||
SendData(peer_client, 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_client, 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) {
|
||||
int64_t delay = swr_get_delay(swr_ctx, src_rate);
|
||||
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,
|
||||
dst_nb_samples, dst_sample_fmt, 1);
|
||||
if (ret < 0) return;
|
||||
max_dst_nb_samples = dst_nb_samples;
|
||||
}
|
||||
|
||||
ret = swr_convert(swr_ctx, dst_data, dst_nb_samples,
|
||||
(const uint8_t **)&stream, src_nb_samples);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "Error while converting\n");
|
||||
return;
|
||||
}
|
||||
dst_bufsize = av_samples_get_buffer_size(&dst_linesize, dst_nb_channels, ret,
|
||||
dst_sample_fmt, 1);
|
||||
if (dst_bufsize < 0) {
|
||||
fprintf(stderr, "Could not get sample buffer size\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (1) {
|
||||
if ("ClientConnected" == client_connection_status_str) {
|
||||
SendData(peer_client, DATA_TYPE::AUDIO, (const char *)dst_data[0],
|
||||
dst_bufsize);
|
||||
}
|
||||
|
||||
if ("ServerConnected" == server_connection_status_str) {
|
||||
SendData(peer_server, DATA_TYPE::AUDIO, (const char *)dst_data[0],
|
||||
dst_bufsize);
|
||||
}
|
||||
} else {
|
||||
memcpy(audio_buffer, dst_data[0], dst_bufsize);
|
||||
audio_len = dst_bufsize;
|
||||
SDL_Delay(10);
|
||||
audio_buffer_fresh = true;
|
||||
}
|
||||
}
|
||||
|
||||
void SdlCaptureAudioOut(void *userdata, Uint8 *stream, int len) {
|
||||
// if ("ClientConnected" != client_connection_status_str) {
|
||||
// return;
|
||||
// }
|
||||
|
||||
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) {}
|
||||
|
||||
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
|
||||
mouse_controller->SendCommand(remote_action);
|
||||
#endif
|
||||
}
|
||||
|
||||
void ClientReceiveDataBuffer(const char *data, size_t size, const char *user_id,
|
||||
size_t user_id_size) {}
|
||||
|
||||
void ServerSignalStatus(SignalStatus status) {
|
||||
server_signal_status = status;
|
||||
if (SignalStatus::SignalConnecting == status) {
|
||||
server_signal_status_str = "ServerSignalConnecting";
|
||||
} else if (SignalStatus::SignalConnected == status) {
|
||||
server_signal_status_str = "ServerSignalConnected";
|
||||
} else if (SignalStatus::SignalFailed == status) {
|
||||
server_signal_status_str = "ServerSignalFailed";
|
||||
} else if (SignalStatus::SignalClosed == status) {
|
||||
server_signal_status_str = "ServerSignalClosed";
|
||||
} else if (SignalStatus::SignalReconnecting == status) {
|
||||
server_signal_status_str = "ServerSignalReconnecting";
|
||||
}
|
||||
}
|
||||
|
||||
void ClientSignalStatus(SignalStatus status) {
|
||||
client_signal_status = status;
|
||||
if (SignalStatus::SignalConnecting == status) {
|
||||
client_signal_status_str = "ClientSignalConnecting";
|
||||
} else if (SignalStatus::SignalConnected == status) {
|
||||
client_signal_status_str = "ClientSignalConnected";
|
||||
} else if (SignalStatus::SignalFailed == status) {
|
||||
client_signal_status_str = "ClientSignalFailed";
|
||||
} else if (SignalStatus::SignalClosed == status) {
|
||||
client_signal_status_str = "ClientSignalClosed";
|
||||
} else if (SignalStatus::SignalReconnecting == status) {
|
||||
client_signal_status_str = "ClientSignalReconnecting";
|
||||
}
|
||||
}
|
||||
|
||||
void ServerConnectionStatus(ConnectionStatus status) {
|
||||
server_connection_status = status;
|
||||
if (ConnectionStatus::Connecting == status) {
|
||||
server_connection_status_str = "ServerConnecting";
|
||||
} else if (ConnectionStatus::Connected == status) {
|
||||
server_connection_status_str = "ServerConnected";
|
||||
} else if (ConnectionStatus::Disconnected == status) {
|
||||
server_connection_status_str = "ServerDisconnected";
|
||||
} else if (ConnectionStatus::Failed == status) {
|
||||
server_connection_status_str = "ServerFailed";
|
||||
} else if (ConnectionStatus::Closed == status) {
|
||||
server_connection_status_str = "ServerClosed";
|
||||
} else if (ConnectionStatus::IncorrectPassword == status) {
|
||||
server_connection_status_str = "Incorrect password";
|
||||
if (connect_button_pressed) {
|
||||
connect_button_pressed = false;
|
||||
joined = false;
|
||||
connect_label = connect_button_pressed ? "Disconnect" : "Connect";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ClientConnectionStatus(ConnectionStatus status) {
|
||||
client_connection_status = status;
|
||||
if (ConnectionStatus::Connecting == 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) {
|
||||
client_connection_status_str = "ClientFailed";
|
||||
} else if (ConnectionStatus::Closed == status) {
|
||||
client_connection_status_str = "ClientClosed";
|
||||
} else if (ConnectionStatus::IncorrectPassword == status) {
|
||||
client_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) {
|
||||
client_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 initResampler() {
|
||||
/* create resampler context */
|
||||
swr_ctx = swr_alloc();
|
||||
if (!swr_ctx) {
|
||||
fprintf(stderr, "Could not allocate resampler context\n");
|
||||
ret = AVERROR(ENOMEM);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* set options */
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
/* initialize the resampling context */
|
||||
if ((ret = swr_init(swr_ctx)) < 0) {
|
||||
fprintf(stderr, "Failed to initialize the resampling context\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* allocate source and destination samples buffers */
|
||||
src_nb_channels = av_get_channel_layout_nb_channels(src_ch_layout);
|
||||
|
||||
ret = av_samples_alloc_array_and_samples(&src_data, &src_linesize,
|
||||
src_nb_channels, src_nb_samples,
|
||||
src_sample_fmt, 0);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "Could not allocate source samples\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
ret = av_samples_alloc_array_and_samples(&dst_data, &dst_linesize,
|
||||
dst_nb_channels, dst_nb_samples,
|
||||
dst_sample_fmt, 0);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "Could not allocate destination samples\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
int main([[maybe_unused]] int argc, [[maybe_unused]] char *argv[]) {
|
||||
LOG_INFO("Remote desk");
|
||||
Render render;
|
||||
|
||||
last_ts = static_cast<uint32_t>(
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::high_resolution_clock::now().time_since_epoch())
|
||||
.count());
|
||||
|
||||
initResampler();
|
||||
|
||||
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
|
||||
|
||||
// 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 = 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
|
||||
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::SetNextWindowSize(ImVec2(190, 200));
|
||||
|
||||
ImGui::Begin("Menu", nullptr, ImGuiWindowFlags_NoResize);
|
||||
|
||||
{
|
||||
menu_hovered = ImGui::IsWindowHovered();
|
||||
ImGui::Text(" LOCAL ID:");
|
||||
ImGui::SameLine();
|
||||
ImGui::SetNextItemWidth(95);
|
||||
ImGui::InputText(
|
||||
"##local_id", (char *)mac_addr_str.c_str(),
|
||||
mac_addr_str.length() + 1,
|
||||
ImGuiInputTextFlags_CharsUppercase | ImGuiInputTextFlags_ReadOnly);
|
||||
|
||||
ImGui::Text(" PASSWORD:");
|
||||
ImGui::SameLine();
|
||||
ImGui::SetNextItemWidth(95);
|
||||
|
||||
char input_password_tmp[7] = "";
|
||||
strncpy(input_password_tmp, input_password, sizeof(input_password));
|
||||
|
||||
ImGui::InputTextWithHint("##server_pwd", "max 6 chars", input_password,
|
||||
IM_ARRAYSIZE(input_password),
|
||||
ImGuiInputTextFlags_CharsNoBlank);
|
||||
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] = "";
|
||||
ImGui::Text("REMOTE ID:");
|
||||
ImGui::SameLine();
|
||||
ImGui::SetNextItemWidth(95);
|
||||
ImGui::InputTextWithHint("##remote_id", mac_addr_str.c_str(),
|
||||
remote_id, IM_ARRAYSIZE(remote_id),
|
||||
ImGuiInputTextFlags_CharsUppercase |
|
||||
ImGuiInputTextFlags_CharsNoBlank);
|
||||
|
||||
ImGui::Spacing();
|
||||
|
||||
ImGui::Text(" PASSWORD:");
|
||||
ImGui::SameLine();
|
||||
ImGui::SetNextItemWidth(95);
|
||||
static char client_password[20] = "";
|
||||
ImGui::InputTextWithHint("##client_pwd", "max 6 chars",
|
||||
client_password,
|
||||
IM_ARRAYSIZE(client_password),
|
||||
ImGuiInputTextFlags_CharsNoBlank);
|
||||
|
||||
if (ImGui::Button(connect_label)) {
|
||||
int ret = -1;
|
||||
if ("ClientSignalConnected" == client_signal_status_str) {
|
||||
if (strcmp(connect_label, "Connect") == 0 && !joined) {
|
||||
std::string user_id = "C-" + mac_addr_str;
|
||||
ret = JoinConnection(peer_client, remote_id, client_password);
|
||||
if (0 == ret) {
|
||||
// joined = true;
|
||||
}
|
||||
} else if (strcmp(connect_label, "Disconnect") == 0 && joined) {
|
||||
ret = LeaveConnection(peer_client);
|
||||
memset(audio_buffer, 0, 960);
|
||||
if (0 == ret) {
|
||||
joined = false;
|
||||
received_frame = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (0 == ret) {
|
||||
connect_button_pressed = !connect_button_pressed;
|
||||
connect_label =
|
||||
connect_button_pressed ? "Disconnect" : "Connect";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::Spacing();
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
ImGui::Spacing();
|
||||
|
||||
{
|
||||
if (ImGui::Button("Resize Window")) {
|
||||
SDL_GetWindowSize(window, &window_w, &window_h);
|
||||
|
||||
if (window_h != window_w * 9 / 16) {
|
||||
window_w = window_h * 16 / 9;
|
||||
}
|
||||
|
||||
SDL_SetWindowSize(window, window_w, window_h);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
} 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 [" + server_signal_status_str + "|" +
|
||||
client_signal_status_str + "|" +
|
||||
server_connection_status_str + "|" +
|
||||
client_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);
|
||||
}
|
||||
|
||||
if (joined) {
|
||||
LeaveConnection(peer_client);
|
||||
}
|
||||
|
||||
rtc_thread.join();
|
||||
SDL_CloseAudioDevice(output_dev);
|
||||
SDL_CloseAudioDevice(input_dev);
|
||||
|
||||
mouse_controller->Destroy();
|
||||
|
||||
ImGui_ImplSDLRenderer2_Shutdown();
|
||||
ImGui_ImplSDL2_Shutdown();
|
||||
ImGui::DestroyContext();
|
||||
|
||||
SDL_DestroyRenderer(sdlRenderer);
|
||||
SDL_DestroyWindow(window);
|
||||
|
||||
SDL_CloseAudio();
|
||||
SDL_Quit();
|
||||
render.Run();
|
||||
|
||||
return 0;
|
||||
}
|
||||
103
src/localization/localization.h
Normal file
103
src/localization/localization.h
Normal file
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* @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> local_desktop = {u8"本桌面", "Local Desktop"};
|
||||
static std::vector<std::string> local_id = {u8"本机ID", "Local ID"};
|
||||
static std::vector<std::string> local_id_copied_to_clipboard = {
|
||||
u8"已复制到剪贴板", "Copied to clipboard"};
|
||||
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_desktop = {u8"控制远程桌面",
|
||||
"Control Remote Desktop"};
|
||||
static std::vector<std::string> remote_id = {u8"对端ID", "Remote ID"};
|
||||
static std::vector<std::string> connect = {u8"连接", "Connect"};
|
||||
static std::vector<std::string> recent_connections = {u8"近期连接",
|
||||
"Recent Connections"};
|
||||
static std::vector<std::string> disconnect = {u8"断开连接", "Disconnect"};
|
||||
static std::vector<std::string> fullscreen = {u8"全屏", " Fullscreen"};
|
||||
static std::vector<std::string> show_net_traffic_stats = {
|
||||
u8"显示流量统计", "Show Net Traffic Stats"};
|
||||
static std::vector<std::string> hide_net_traffic_stats = {
|
||||
u8"隐藏流量统计", "Hide Net Traffic Stats"};
|
||||
static std::vector<std::string> video = {u8"视频", "Video"};
|
||||
static std::vector<std::string> audio = {u8"音频", "Audio"};
|
||||
static std::vector<std::string> data = {u8"数据", "Data"};
|
||||
static std::vector<std::string> total = {u8"总计", "Total"};
|
||||
static std::vector<std::string> in = {u8"输入", "In"};
|
||||
static std::vector<std::string> out = {u8"输出", "Out"};
|
||||
static std::vector<std::string> loss_rate = {u8"丢包率", "Loss Rate"};
|
||||
static std::vector<std::string> exit_fullscreen = {u8"退出全屏",
|
||||
"Exit fullscreen"};
|
||||
static std::vector<std::string> control_mouse = {u8"控制", "Control"};
|
||||
static std::vector<std::string> release_mouse = {u8"释放", "Release"};
|
||||
static std::vector<std::string> audio_capture = {u8"声音", "Audio"};
|
||||
static std::vector<std::string> mute = {u8" 静音", " Mute"};
|
||||
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> enable_turn = {u8"启用中继服务:",
|
||||
"Enable TURN Service:"};
|
||||
|
||||
static std::vector<std::string> ok = {u8"确认", "OK"};
|
||||
static std::vector<std::string> cancel = {u8"取消", "Cancel"};
|
||||
|
||||
static std::vector<std::string> new_password = {
|
||||
u8"请输入六位密码:", "Please input a six-char password:"};
|
||||
|
||||
static std::vector<std::string> input_password = {u8"请输入密码:",
|
||||
"Please input password:"};
|
||||
static std::vector<std::string> validate_password = {u8"验证密码中...",
|
||||
"Validate password ..."};
|
||||
static std::vector<std::string> reinput_password = {
|
||||
u8"请重新输入密码", "Please input password again"};
|
||||
|
||||
static std::vector<std::string> remember_password = {u8"记住密码",
|
||||
"Remember password"};
|
||||
|
||||
static std::vector<std::string> signal_connected = {u8"已连接服务器",
|
||||
"Connected"};
|
||||
static std::vector<std::string> signal_disconnected = {u8"未连接服务器",
|
||||
"Disconnected"};
|
||||
|
||||
static std::vector<std::string> p2p_connected = {u8"对等连接已建立",
|
||||
"P2P Connected"};
|
||||
static std::vector<std::string> p2p_disconnected = {u8"对等连接已断开",
|
||||
"P2P Disconnected"};
|
||||
static std::vector<std::string> p2p_connecting = {u8"正在建立对等连接...",
|
||||
"P2P Connecting ..."};
|
||||
static std::vector<std::string> p2p_failed = {u8"对等连接失败", "P2P Failed"};
|
||||
static std::vector<std::string> p2p_closed = {u8"对等连接已关闭", "P2P closed"};
|
||||
|
||||
static std::vector<std::string> no_such_id = {u8"无此ID", "No such ID"};
|
||||
|
||||
static std::vector<std::string> about = {u8"关于", "About"};
|
||||
static std::vector<std::string> version = {u8"版本", "Version"};
|
||||
|
||||
static std::vector<std::string> confirm_delete_connection = {
|
||||
u8"确认删除此连接", "Confirm to delete this connection"};
|
||||
} // namespace localization
|
||||
|
||||
#endif
|
||||
127
src/log/log.h
127
src/log/log.h
@@ -1,127 +0,0 @@
|
||||
#ifndef _LOG_H_
|
||||
#define _LOG_H_
|
||||
|
||||
#include <chrono>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#include "spdlog/common.h"
|
||||
#include "spdlog/logger.h"
|
||||
#include "spdlog/sinks/base_sink.h"
|
||||
#include "spdlog/sinks/rotating_file_sink.h"
|
||||
#include "spdlog/sinks/stdout_color_sinks.h"
|
||||
#include "spdlog/spdlog.h"
|
||||
|
||||
using namespace std::chrono;
|
||||
|
||||
#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_INFO
|
||||
|
||||
// SPDLOG_TRACE(...)
|
||||
// SPDLOG_DEBUG(...)
|
||||
// SPDLOG_INFO(...)
|
||||
// SPDLOG_WARN(...)
|
||||
// SPDLOG_ERROR(...)
|
||||
// SPDLOG_CRITICAL(...)
|
||||
|
||||
#ifdef SIGNAL_LOGGER
|
||||
constexpr auto LOGGER_NAME = "siganl_server";
|
||||
#else
|
||||
constexpr auto LOGGER_NAME = "remote_desk";
|
||||
#endif
|
||||
|
||||
#define LOG_INFO(...) \
|
||||
if (nullptr == spdlog::get(LOGGER_NAME)) { \
|
||||
auto now = std::chrono::system_clock::now() + std::chrono::hours(8); \
|
||||
auto timet = std::chrono::system_clock::to_time_t(now); \
|
||||
auto localTime = *std::gmtime(&timet); \
|
||||
std::stringstream ss; \
|
||||
std::string filename; \
|
||||
ss << LOGGER_NAME; \
|
||||
ss << std::put_time(&localTime, "-%Y%m%d-%H%M%S.log"); \
|
||||
ss >> filename; \
|
||||
std::string path = "logs/" + filename; \
|
||||
std::vector<spdlog::sink_ptr> sinks; \
|
||||
sinks.push_back(std::make_shared<spdlog::sinks::stdout_color_sink_mt>()); \
|
||||
sinks.push_back(std::make_shared<spdlog::sinks::rotating_file_sink_mt>( \
|
||||
path, 1048576 * 5, 3)); \
|
||||
auto combined_logger = std::make_shared<spdlog::logger>( \
|
||||
LOGGER_NAME, begin(sinks), end(sinks)); \
|
||||
combined_logger->flush_on(spdlog::level::info); \
|
||||
spdlog::register_logger(combined_logger); \
|
||||
SPDLOG_LOGGER_INFO(combined_logger, __VA_ARGS__); \
|
||||
} else { \
|
||||
SPDLOG_LOGGER_INFO(spdlog::get(LOGGER_NAME), __VA_ARGS__); \
|
||||
}
|
||||
|
||||
#define LOG_WARN(...) \
|
||||
if (nullptr == spdlog::get(LOGGER_NAME)) { \
|
||||
auto now = std::chrono::system_clock::now() + std::chrono::hours(8); \
|
||||
auto timet = std::chrono::system_clock::to_time_t(now); \
|
||||
auto localTime = *std::gmtime(&timet); \
|
||||
std::stringstream ss; \
|
||||
std::string filename; \
|
||||
ss << LOGGER_NAME; \
|
||||
ss << std::put_time(&localTime, "-%Y%m%d-%H%M%S.log"); \
|
||||
ss >> filename; \
|
||||
std::string path = "logs/" + filename; \
|
||||
std::vector<spdlog::sink_ptr> sinks; \
|
||||
sinks.push_back(std::make_shared<spdlog::sinks::stdout_color_sink_mt>()); \
|
||||
sinks.push_back(std::make_shared<spdlog::sinks::rotating_file_sink_mt>( \
|
||||
path, 1048576 * 5, 3)); \
|
||||
auto combined_logger = std::make_shared<spdlog::logger>( \
|
||||
LOGGER_NAME, begin(sinks), end(sinks)); \
|
||||
spdlog::register_logger(combined_logger); \
|
||||
SPDLOG_LOGGER_WARN(combined_logger, __VA_ARGS__); \
|
||||
} else { \
|
||||
SPDLOG_LOGGER_WARN(spdlog::get(LOGGER_NAME), __VA_ARGS__); \
|
||||
}
|
||||
|
||||
#define LOG_ERROR(...) \
|
||||
if (nullptr == spdlog::get(LOGGER_NAME)) { \
|
||||
auto now = std::chrono::system_clock::now() + std::chrono::hours(8); \
|
||||
auto timet = std::chrono::system_clock::to_time_t(now); \
|
||||
auto localTime = *std::gmtime(&timet); \
|
||||
std::stringstream ss; \
|
||||
std::string filename; \
|
||||
ss << LOGGER_NAME; \
|
||||
ss << std::put_time(&localTime, "-%Y%m%d-%H%M%S.log"); \
|
||||
ss >> filename; \
|
||||
std::string path = "logs/" + filename; \
|
||||
std::vector<spdlog::sink_ptr> sinks; \
|
||||
sinks.push_back(std::make_shared<spdlog::sinks::stdout_color_sink_mt>()); \
|
||||
sinks.push_back(std::make_shared<spdlog::sinks::rotating_file_sink_mt>( \
|
||||
path, 1048576 * 5, 3)); \
|
||||
auto combined_logger = std::make_shared<spdlog::logger>( \
|
||||
LOGGER_NAME, begin(sinks), end(sinks)); \
|
||||
spdlog::register_logger(combined_logger); \
|
||||
SPDLOG_LOGGER_ERROR(combined_logger, __VA_ARGS__); \
|
||||
} else { \
|
||||
SPDLOG_LOGGER_ERROR(spdlog::get(LOGGER_NAME), __VA_ARGS__); \
|
||||
}
|
||||
|
||||
#define LOG_FATAL(...) \
|
||||
if (nullptr == spdlog::get(LOGGER_NAME)) { \
|
||||
auto now = std::chrono::system_clock::now() + std::chrono::hours(8); \
|
||||
auto timet = std::chrono::system_clock::to_time_t(now); \
|
||||
auto localTime = *std::gmtime(&timet); \
|
||||
std::stringstream ss; \
|
||||
std::string filename; \
|
||||
ss << LOGGER_NAME; \
|
||||
ss << std::put_time(&localTime, "-%Y%m%d-%H%M%S.log"); \
|
||||
ss >> filename; \
|
||||
std::string path = "logs/" + filename; \
|
||||
std::vector<spdlog::sink_ptr> sinks; \
|
||||
sinks.push_back(std::make_shared<spdlog::sinks::stdout_color_sink_mt>()); \
|
||||
sinks.push_back(std::make_shared<spdlog::sinks::rotating_file_sink_mt>( \
|
||||
path, 1048576 * 5, 3)); \
|
||||
auto combined_logger = std::make_shared<spdlog::logger>( \
|
||||
LOGGER_NAME, begin(sinks), end(sinks)); \
|
||||
spdlog::register_logger(combined_logger); \
|
||||
SPDLOG_LOGGER_CRITICAL(combined_logger, __VA_ARGS__); \
|
||||
} else { \
|
||||
SPDLOG_LOGGER_CRITICAL(spdlog::get(LOGGER_NAME), __VA_ARGS__); \
|
||||
}
|
||||
|
||||
#endif
|
||||
37
src/log/rd_log.cpp
Normal file
37
src/log/rd_log.cpp
Normal file
@@ -0,0 +1,37 @@
|
||||
#include "rd_log.h"
|
||||
|
||||
std::shared_ptr<spdlog::logger> get_logger() {
|
||||
if (auto logger = spdlog::get(LOGGER_NAME)) {
|
||||
return logger;
|
||||
}
|
||||
|
||||
auto now = std::chrono::system_clock::now() + std::chrono::hours(8);
|
||||
auto now_time = std::chrono::system_clock::to_time_t(now);
|
||||
|
||||
std::tm tm_info;
|
||||
|
||||
#ifdef _WIN32
|
||||
gmtime_s(&tm_info, &now_time);
|
||||
#else
|
||||
gmtime_r(&now_time, &tm_info);
|
||||
#endif
|
||||
|
||||
std::stringstream ss;
|
||||
std::string filename;
|
||||
ss << LOGGER_NAME;
|
||||
ss << std::put_time(&tm_info, "-%Y%m%d-%H%M%S.log");
|
||||
ss >> filename;
|
||||
|
||||
std::string path = "logs/" + filename;
|
||||
std::vector<spdlog::sink_ptr> sinks;
|
||||
sinks.push_back(std::make_shared<spdlog::sinks::stdout_color_sink_mt>());
|
||||
sinks.push_back(std::make_shared<spdlog::sinks::rotating_file_sink_mt>(
|
||||
path, 1048576 * 5, 3));
|
||||
|
||||
auto combined_logger =
|
||||
std::make_shared<spdlog::logger>(LOGGER_NAME, begin(sinks), end(sinks));
|
||||
combined_logger->flush_on(spdlog::level::info);
|
||||
spdlog::register_logger(combined_logger);
|
||||
|
||||
return combined_logger;
|
||||
}
|
||||
39
src/log/rd_log.h
Normal file
39
src/log/rd_log.h
Normal file
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* @Author: DI JUNKUN
|
||||
* @Date: 2024-11-26
|
||||
* Copyright (c) 2024 by DI JUNKUN, All Rights Reserved.
|
||||
*/
|
||||
|
||||
#ifndef _RD_LOG_H_
|
||||
#define _RD_LOG_H_
|
||||
|
||||
#include <chrono>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#include "spdlog/common.h"
|
||||
#include "spdlog/logger.h"
|
||||
#include "spdlog/sinks/base_sink.h"
|
||||
#include "spdlog/sinks/rotating_file_sink.h"
|
||||
#include "spdlog/sinks/stdout_color_sinks.h"
|
||||
#include "spdlog/spdlog.h"
|
||||
|
||||
using namespace std::chrono;
|
||||
|
||||
#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_INFO
|
||||
|
||||
constexpr auto LOGGER_NAME = "rd";
|
||||
|
||||
std::shared_ptr<spdlog::logger> get_logger();
|
||||
|
||||
#define LOG_INFO(...) SPDLOG_LOGGER_INFO(get_logger(), __VA_ARGS__);
|
||||
|
||||
#define LOG_WARN(...) SPDLOG_LOGGER_WARN(get_logger(), __VA_ARGS__);
|
||||
|
||||
#define LOG_ERROR(...) SPDLOG_LOGGER_ERROR(get_logger(), __VA_ARGS__);
|
||||
|
||||
#define LOG_FATAL(...) SPDLOG_LOGGER_CRITICAL(get_logger(), __VA_ARGS__);
|
||||
|
||||
#endif
|
||||
@@ -9,7 +9,12 @@ unsigned char nv12_buffer_[NV12_BUFFER_SIZE];
|
||||
|
||||
ScreenCapturerX11::ScreenCapturerX11() {}
|
||||
|
||||
ScreenCapturerX11::~ScreenCapturerX11() {}
|
||||
ScreenCapturerX11::~ScreenCapturerX11() {
|
||||
if (inited_ && capture_thread_.joinable()) {
|
||||
capture_thread_.join();
|
||||
inited_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
int ScreenCapturerX11::Init(const RECORD_DESKTOP_RECT &rect, const int fps,
|
||||
cb_desktop_data cb) {
|
||||
@@ -27,6 +32,8 @@ int ScreenCapturerX11::Init(const RECORD_DESKTOP_RECT &rect, const int fps,
|
||||
|
||||
// grabbing frame rate
|
||||
av_dict_set(&options_, "framerate", "30", 0);
|
||||
// show remote cursor
|
||||
av_dict_set(&options_, "capture_cursor", "0", 0);
|
||||
// Make the grabbed area follow the mouse
|
||||
// av_dict_set(&options_, "follow_mouse", "centered", 0);
|
||||
// Video frame size. The default is to capture the full screen
|
||||
@@ -91,20 +98,19 @@ int ScreenCapturerX11::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 ScreenCapturerX11::Destroy() {
|
||||
if (capture_thread_->joinable()) {
|
||||
capture_thread_->join();
|
||||
}
|
||||
|
||||
running_ = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ScreenCapturerX11::Start() {
|
||||
capture_thread_.reset(new std::thread([this]() {
|
||||
while (1) {
|
||||
capture_thread_ = std::thread([this]() {
|
||||
while (running_) {
|
||||
if (av_read_frame(pFormatCtx_, packet_) >= 0) {
|
||||
if (packet_->stream_index == videoindex_) {
|
||||
avcodec_send_packet(pCodecCtx_, packet_);
|
||||
@@ -127,17 +133,20 @@ int ScreenCapturerX11::Start() {
|
||||
}
|
||||
}
|
||||
}
|
||||
}));
|
||||
});
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ScreenCapturerX11::Stop() {
|
||||
running_ = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ScreenCapturerX11::Pause() { return 0; }
|
||||
|
||||
int ScreenCapturerX11::Resume() { return 0; }
|
||||
|
||||
int ScreenCapturerX11::Stop() { return 0; }
|
||||
|
||||
void ScreenCapturerX11::OnFrame() {}
|
||||
|
||||
void ScreenCapturerX11::CleanUp() {}
|
||||
|
||||
@@ -32,9 +32,10 @@ class ScreenCapturerX11 : public ScreenCapturer {
|
||||
|
||||
virtual int Start();
|
||||
|
||||
virtual int Stop();
|
||||
|
||||
int Pause();
|
||||
int Resume();
|
||||
int Stop();
|
||||
|
||||
void OnFrame();
|
||||
|
||||
@@ -61,6 +62,8 @@ class ScreenCapturerX11 : public ScreenCapturer {
|
||||
int videoindex_ = 0;
|
||||
int got_picture_ = 0;
|
||||
int fps_ = 0;
|
||||
bool inited_ = false;
|
||||
|
||||
// ffmpeg
|
||||
AVFormatContext *pFormatCtx_ = nullptr;
|
||||
AVCodecContext *pCodecCtx_ = nullptr;
|
||||
@@ -74,7 +77,8 @@ class ScreenCapturerX11 : public ScreenCapturer {
|
||||
struct SwsContext *img_convert_ctx_ = nullptr;
|
||||
|
||||
// thread
|
||||
std::unique_ptr<std::thread> capture_thread_ = nullptr;
|
||||
std::thread capture_thread_;
|
||||
std::atomic_bool running_;
|
||||
};
|
||||
|
||||
#endif
|
||||
212
src/screen_capturer/macosx/avfoundation/screen_capturer_avf.cpp
Normal file
212
src/screen_capturer/macosx/avfoundation/screen_capturer_avf.cpp
Normal file
@@ -0,0 +1,212 @@
|
||||
#include "screen_capturer_avf.h"
|
||||
|
||||
#include <ApplicationServices/ApplicationServices.h>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "rd_log.h"
|
||||
|
||||
#define USE_SCALE_FACTOR 0
|
||||
|
||||
ScreenCapturerAvf::ScreenCapturerAvf() {}
|
||||
|
||||
ScreenCapturerAvf::~ScreenCapturerAvf() {
|
||||
if (inited_ && capture_thread_.joinable()) {
|
||||
capture_thread_.join();
|
||||
inited_ = false;
|
||||
}
|
||||
|
||||
if (nv12_frame_) {
|
||||
delete[] nv12_frame_;
|
||||
nv12_frame_ = nullptr;
|
||||
}
|
||||
|
||||
if (pFormatCtx_) {
|
||||
avformat_close_input(&pFormatCtx_);
|
||||
pFormatCtx_ = nullptr;
|
||||
}
|
||||
|
||||
if (pCodecCtx_) {
|
||||
avcodec_free_context(&pCodecCtx_);
|
||||
pCodecCtx_ = nullptr;
|
||||
}
|
||||
|
||||
if (options_) {
|
||||
av_dict_free(&options_);
|
||||
options_ = nullptr;
|
||||
}
|
||||
|
||||
if (pFrame_) {
|
||||
av_frame_free(&pFrame_);
|
||||
pFrame_ = nullptr;
|
||||
}
|
||||
|
||||
if (packet_) {
|
||||
av_packet_free(&packet_);
|
||||
packet_ = nullptr;
|
||||
}
|
||||
|
||||
#if USE_SCALE_FACTOR
|
||||
if (img_convert_ctx_) {
|
||||
sws_freeContext(img_convert_ctx_);
|
||||
img_convert_ctx_ = nullptr;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
int ScreenCapturerAvf::Init(const int fps, cb_desktop_data cb) {
|
||||
if (cb) {
|
||||
_on_data = cb;
|
||||
}
|
||||
|
||||
av_log_set_level(AV_LOG_QUIET);
|
||||
|
||||
pFormatCtx_ = avformat_alloc_context();
|
||||
|
||||
avdevice_register_all();
|
||||
|
||||
// grabbing frame rate
|
||||
av_dict_set(&options_, "framerate", "60", 0);
|
||||
av_dict_set(&options_, "pixel_format", "nv12", 0);
|
||||
// show remote cursor
|
||||
av_dict_set(&options_, "capture_cursor", "0", 0);
|
||||
// Make the grabbed area follow the mouse
|
||||
// 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", "1440x900", 0);
|
||||
ifmt_ = (AVInputFormat *)av_find_input_format("avfoundation");
|
||||
if (!ifmt_) {
|
||||
printf("Couldn't find_input_format\n");
|
||||
}
|
||||
|
||||
// Grab at position 10,20
|
||||
if (avformat_open_input(&pFormatCtx_, "Capture screen 0", ifmt_, &options_) !=
|
||||
0) {
|
||||
printf("Couldn't open input stream.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (avformat_find_stream_info(pFormatCtx_, NULL) < 0) {
|
||||
printf("Couldn't find stream information.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
videoindex_ = -1;
|
||||
for (i_ = 0; i_ < pFormatCtx_->nb_streams; i_++)
|
||||
if (pFormatCtx_->streams[i_]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
|
||||
videoindex_ = i_;
|
||||
break;
|
||||
}
|
||||
if (videoindex_ == -1) {
|
||||
printf("Didn't find a video stream.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
pCodecParam_ = pFormatCtx_->streams[videoindex_]->codecpar;
|
||||
|
||||
pCodecCtx_ = avcodec_alloc_context3(NULL);
|
||||
avcodec_parameters_to_context(pCodecCtx_, pCodecParam_);
|
||||
|
||||
pCodec_ = const_cast<AVCodec *>(avcodec_find_decoder(pCodecCtx_->codec_id));
|
||||
if (pCodec_ == NULL) {
|
||||
printf("Codec not found.\n");
|
||||
return -1;
|
||||
}
|
||||
if (avcodec_open2(pCodecCtx_, pCodec_, NULL) < 0) {
|
||||
printf("Could not open codec.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
const int screen_w = pFormatCtx_->streams[videoindex_]->codecpar->width;
|
||||
const int screen_h = pFormatCtx_->streams[videoindex_]->codecpar->height;
|
||||
|
||||
pFrame_ = av_frame_alloc();
|
||||
pFrame_->width = screen_w;
|
||||
pFrame_->height = screen_h;
|
||||
|
||||
#if USE_SCALE_FACTOR
|
||||
pFrame_resized_ = av_frame_alloc();
|
||||
pFrame_resized_->width = CGDisplayPixelsWide(CGMainDisplayID());
|
||||
pFrame_resized_->height = CGDisplayPixelsHigh(CGMainDisplayID());
|
||||
|
||||
img_convert_ctx_ =
|
||||
sws_getContext(pFrame_->width, pFrame_->height, pCodecCtx_->pix_fmt,
|
||||
pFrame_resized_->width, pFrame_resized_->height,
|
||||
AV_PIX_FMT_NV12, SWS_BICUBIC, NULL, NULL, NULL);
|
||||
#endif
|
||||
|
||||
if (!nv12_frame_) {
|
||||
nv12_frame_ = new unsigned char[screen_w * screen_h * 3 / 2];
|
||||
}
|
||||
|
||||
packet_ = (AVPacket *)av_malloc(sizeof(AVPacket));
|
||||
|
||||
inited_ = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ScreenCapturerAvf::Destroy() {
|
||||
running_ = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ScreenCapturerAvf::Start() {
|
||||
if (running_) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
running_ = true;
|
||||
capture_thread_ = std::thread([this]() {
|
||||
while (running_) {
|
||||
if (av_read_frame(pFormatCtx_, packet_) >= 0) {
|
||||
if (packet_->stream_index == videoindex_) {
|
||||
avcodec_send_packet(pCodecCtx_, packet_);
|
||||
av_packet_unref(packet_);
|
||||
got_picture_ = avcodec_receive_frame(pCodecCtx_, pFrame_);
|
||||
|
||||
if (!got_picture_) {
|
||||
#if USE_SCALE_FACTOR
|
||||
av_image_fill_arrays(pFrame_resized_->data,
|
||||
pFrame_resized_->linesize, nv12_frame_,
|
||||
AV_PIX_FMT_NV12, pFrame_resized_->width,
|
||||
pFrame_resized_->height, 1);
|
||||
|
||||
sws_scale(img_convert_ctx_, pFrame_->data, pFrame_->linesize, 0,
|
||||
pFrame_->height, pFrame_resized_->data,
|
||||
pFrame_resized_->linesize);
|
||||
|
||||
_on_data((unsigned char *)nv12_frame_,
|
||||
pFrame_resized_->width * pFrame_resized_->height * 3 / 2,
|
||||
pFrame_resized_->width, pFrame_resized_->height);
|
||||
#else
|
||||
memcpy(nv12_frame_, pFrame_->data[0],
|
||||
pFrame_->linesize[0] * pFrame_->height);
|
||||
memcpy(nv12_frame_ + pFrame_->linesize[0] * pFrame_->height,
|
||||
pFrame_->data[1],
|
||||
pFrame_->linesize[1] * pFrame_->height / 2);
|
||||
_on_data((unsigned char *)nv12_frame_,
|
||||
pFrame_->width * pFrame_->height * 3 / 2, pFrame_->width,
|
||||
pFrame_->height);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ScreenCapturerAvf::Stop() {
|
||||
running_ = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ScreenCapturerAvf::Pause() { return 0; }
|
||||
|
||||
int ScreenCapturerAvf::Resume() { return 0; }
|
||||
|
||||
void ScreenCapturerAvf::OnFrame() {}
|
||||
|
||||
void ScreenCapturerAvf::CleanUp() {}
|
||||
@@ -32,15 +32,16 @@ class ScreenCapturerAvf : public ScreenCapturer {
|
||||
~ScreenCapturerAvf();
|
||||
|
||||
public:
|
||||
virtual int Init(const RECORD_DESKTOP_RECT &rect, const int fps,
|
||||
cb_desktop_data cb);
|
||||
virtual int Init(const int fps, cb_desktop_data cb);
|
||||
|
||||
virtual int Destroy();
|
||||
|
||||
virtual int Start();
|
||||
|
||||
virtual int Stop();
|
||||
|
||||
int Pause();
|
||||
int Resume();
|
||||
int Stop();
|
||||
|
||||
void OnFrame();
|
||||
|
||||
@@ -48,7 +49,6 @@ class ScreenCapturerAvf : public ScreenCapturer {
|
||||
void CleanUp();
|
||||
|
||||
private:
|
||||
std::atomic_bool _running;
|
||||
std::atomic_bool _paused;
|
||||
std::atomic_bool _inited;
|
||||
|
||||
@@ -56,8 +56,6 @@ class ScreenCapturerAvf : public ScreenCapturer {
|
||||
|
||||
std::string _device_name;
|
||||
|
||||
RECORD_DESKTOP_RECT _rect;
|
||||
|
||||
int _fps;
|
||||
|
||||
cb_desktop_data _on_data;
|
||||
@@ -66,6 +64,8 @@ class ScreenCapturerAvf : public ScreenCapturer {
|
||||
int i_ = 0;
|
||||
int videoindex_ = 0;
|
||||
int got_picture_ = 0;
|
||||
bool inited_ = false;
|
||||
|
||||
// ffmpeg
|
||||
AVFormatContext *pFormatCtx_ = nullptr;
|
||||
AVCodecContext *pCodecCtx_ = nullptr;
|
||||
@@ -74,12 +74,14 @@ class ScreenCapturerAvf : public ScreenCapturer {
|
||||
AVDictionary *options_ = nullptr;
|
||||
AVInputFormat *ifmt_ = nullptr;
|
||||
AVFrame *pFrame_ = nullptr;
|
||||
AVFrame *pFrameNv12_ = nullptr;
|
||||
AVFrame *pFrame_resized_ = nullptr;
|
||||
AVPacket *packet_ = nullptr;
|
||||
struct SwsContext *img_convert_ctx_ = nullptr;
|
||||
unsigned char *nv12_frame_ = nullptr;
|
||||
|
||||
// thread
|
||||
std::unique_ptr<std::thread> capture_thread_ = nullptr;
|
||||
std::thread capture_thread_;
|
||||
std::atomic_bool running_;
|
||||
};
|
||||
|
||||
#endif
|
||||
104
src/screen_capturer/macosx/core_graphics/screen_capturer_cg.cpp
Normal file
104
src/screen_capturer/macosx/core_graphics/screen_capturer_cg.cpp
Normal file
@@ -0,0 +1,104 @@
|
||||
#include <IOSurface/IOSurface.h>
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "rd_log.h"
|
||||
#include "screen_capturer_cgd.h"
|
||||
|
||||
ScreenCapturerCg::ScreenCapturerCg() {}
|
||||
|
||||
ScreenCapturerCg::~ScreenCapturerCg() {}
|
||||
|
||||
int ScreenCapturerCg::Init(const int fps, cb_desktop_data cb) {
|
||||
if (cb) {
|
||||
_on_data = cb;
|
||||
}
|
||||
|
||||
size_t pixel_width = 1280;
|
||||
size_t pixel_height = 720;
|
||||
CGDirectDisplayID display_id = 0;
|
||||
|
||||
CGDisplayStreamFrameAvailableHandler handler =
|
||||
^(CGDisplayStreamFrameStatus status, uint64_t display_time,
|
||||
IOSurfaceRef frame_surface, CGDisplayStreamUpdateRef updateRef) {
|
||||
if (status == kCGDisplayStreamFrameStatusStopped) return;
|
||||
// Only pay attention to frame updates.
|
||||
if (status != kCGDisplayStreamFrameStatusFrameComplete) return;
|
||||
|
||||
// size_t count = 0;
|
||||
// const CGRect* rects = CGDisplayStreamUpdateGetRects(
|
||||
// updateRef, kCGDisplayStreamUpdateDirtyRects, &count);
|
||||
|
||||
// 获取帧数据
|
||||
void* frameData = IOSurfaceGetBaseAddressOfPlane(frame_surface, 0);
|
||||
size_t width = IOSurfaceGetWidthOfPlane(frame_surface, 0);
|
||||
size_t height = IOSurfaceGetHeightOfPlane(frame_surface, 0);
|
||||
};
|
||||
|
||||
CFDictionaryRef properties_dictionary = CFDictionaryCreate(
|
||||
kCFAllocatorDefault, (const void*[]){kCGDisplayStreamShowCursor},
|
||||
(const void*[]){kCFBooleanFalse}, 1, &kCFTypeDictionaryKeyCallBacks,
|
||||
&kCFTypeDictionaryValueCallBacks);
|
||||
|
||||
CGDisplayStreamRef display_stream =
|
||||
CGDisplayStreamCreate(display_id, pixel_width, pixel_height, 'BGRA',
|
||||
properties_dictionary, handler);
|
||||
|
||||
if (display_stream) {
|
||||
CGError error = CGDisplayStreamStart(display_stream);
|
||||
if (error != kCGErrorSuccess) return -1;
|
||||
|
||||
CFRunLoopSourceRef source = CGDisplayStreamGetRunLoopSource(display_stream);
|
||||
CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopCommonModes);
|
||||
display_streams_.push_back(display_stream);
|
||||
}
|
||||
|
||||
CFRelease(properties_dictionary);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ScreenCapturerCg::Destroy() {
|
||||
running_ = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ScreenCapturerCg::Start() {
|
||||
if (_running) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
running_ = true;
|
||||
capture_thread_ = std::thread([this]() {
|
||||
while (running_) {
|
||||
CFRunLoopRun();
|
||||
}
|
||||
});
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ScreenCapturerCg::Stop() {
|
||||
running_ = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ScreenCapturerCg::Pause() { return 0; }
|
||||
|
||||
int ScreenCapturerCg::Resume() { return 0; }
|
||||
|
||||
void ScreenCapturerCg::OnFrame() {}
|
||||
|
||||
void ScreenCapturerCg::CleanUp() {}
|
||||
|
||||
//
|
||||
|
||||
void ScreenCapturerCg::UnregisterRefreshAndMoveHandlers() {
|
||||
for (CGDisplayStreamRef stream : display_streams_) {
|
||||
CFRunLoopSourceRef source = CGDisplayStreamGetRunLoopSource(stream);
|
||||
CFRunLoopRemoveSource(CFRunLoopGetCurrent(), source, kCFRunLoopCommonModes);
|
||||
CGDisplayStreamStop(stream);
|
||||
CFRelease(stream);
|
||||
}
|
||||
display_streams_.clear();
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
|
||||
/*
|
||||
* @Author: DI JUNKUN
|
||||
* @Date: 2024-10-16
|
||||
* Copyright (c) 2024 by DI JUNKUN, All Rights Reserved.
|
||||
*/
|
||||
|
||||
#ifndef _SCREEN_CAPTURER_CGD_H_
|
||||
#define _SCREEN_CAPTURER_CGD_H_
|
||||
|
||||
#include <CoreGraphics/CoreGraphics.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
#include "screen_capturer.h"
|
||||
|
||||
class ScreenCapturerCg : public ScreenCapturer {
|
||||
public:
|
||||
ScreenCapturerCg();
|
||||
~ScreenCapturerCg();
|
||||
|
||||
public:
|
||||
virtual int Init(const int fps, cb_desktop_data cb);
|
||||
|
||||
virtual int Destroy();
|
||||
|
||||
virtual int Start();
|
||||
|
||||
virtual int Stop();
|
||||
|
||||
int Pause();
|
||||
|
||||
int Resume();
|
||||
|
||||
void OnFrame();
|
||||
|
||||
protected:
|
||||
void CleanUp();
|
||||
|
||||
private:
|
||||
int _fps;
|
||||
cb_desktop_data _on_data;
|
||||
|
||||
// thread
|
||||
std::thread capture_thread_;
|
||||
std::atomic_bool running_;
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,144 +0,0 @@
|
||||
#include "screen_capturer_avf.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "log.h"
|
||||
|
||||
#define NV12_BUFFER_SIZE 1280 * 720 * 3 / 2
|
||||
unsigned char nv12_buffer_[NV12_BUFFER_SIZE];
|
||||
|
||||
ScreenCapturerAvf::ScreenCapturerAvf() {}
|
||||
|
||||
ScreenCapturerAvf::~ScreenCapturerAvf() {}
|
||||
|
||||
int ScreenCapturerAvf::Init(const RECORD_DESKTOP_RECT &rect, const int fps,
|
||||
cb_desktop_data cb) {
|
||||
if (cb) {
|
||||
_on_data = cb;
|
||||
}
|
||||
|
||||
av_log_set_level(AV_LOG_QUIET);
|
||||
|
||||
pFormatCtx_ = avformat_alloc_context();
|
||||
|
||||
avdevice_register_all();
|
||||
|
||||
// grabbing frame rate
|
||||
av_dict_set(&options_, "framerate", "60", 0);
|
||||
av_dict_set(&options_, "pixel_format", "nv12", 0);
|
||||
// show remote cursor
|
||||
av_dict_set(&options_, "capture_cursor", "1", 0);
|
||||
// Make the grabbed area follow the mouse
|
||||
// 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("avfoundation");
|
||||
if (!ifmt_) {
|
||||
printf("Couldn't find_input_format\n");
|
||||
}
|
||||
|
||||
// Grab at position 10,20
|
||||
if (avformat_open_input(&pFormatCtx_, "Capture screen 0", ifmt_, &options_) !=
|
||||
0) {
|
||||
printf("Couldn't open input stream.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (avformat_find_stream_info(pFormatCtx_, NULL) < 0) {
|
||||
printf("Couldn't find stream information.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
videoindex_ = -1;
|
||||
for (i_ = 0; i_ < pFormatCtx_->nb_streams; i_++)
|
||||
if (pFormatCtx_->streams[i_]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
|
||||
videoindex_ = i_;
|
||||
break;
|
||||
}
|
||||
if (videoindex_ == -1) {
|
||||
printf("Didn't find a video stream.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
pCodecParam_ = pFormatCtx_->streams[videoindex_]->codecpar;
|
||||
|
||||
pCodecCtx_ = avcodec_alloc_context3(NULL);
|
||||
avcodec_parameters_to_context(pCodecCtx_, pCodecParam_);
|
||||
|
||||
pCodec_ = const_cast<AVCodec *>(avcodec_find_decoder(pCodecCtx_->codec_id));
|
||||
if (pCodec_ == NULL) {
|
||||
printf("Codec not found.\n");
|
||||
return -1;
|
||||
}
|
||||
if (avcodec_open2(pCodecCtx_, pCodec_, NULL) < 0) {
|
||||
printf("Could not open codec.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
const int screen_w = pFormatCtx_->streams[videoindex_]->codecpar->width;
|
||||
const int screen_h = pFormatCtx_->streams[videoindex_]->codecpar->height;
|
||||
|
||||
pFrame_ = av_frame_alloc();
|
||||
pFrameNv12_ = av_frame_alloc();
|
||||
|
||||
pFrame_->width = screen_w;
|
||||
pFrame_->height = screen_h;
|
||||
pFrameNv12_->width = 1280;
|
||||
pFrameNv12_->height = 720;
|
||||
|
||||
packet_ = (AVPacket *)av_malloc(sizeof(AVPacket));
|
||||
|
||||
img_convert_ctx_ = sws_getContext(
|
||||
pFrame_->width, pFrame_->height, pCodecCtx_->pix_fmt, pFrameNv12_->width,
|
||||
pFrameNv12_->height, AV_PIX_FMT_NV12, SWS_BICUBIC, NULL, NULL, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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) {
|
||||
if (packet_->stream_index == videoindex_) {
|
||||
avcodec_send_packet(pCodecCtx_, packet_);
|
||||
av_packet_unref(packet_);
|
||||
got_picture_ = avcodec_receive_frame(pCodecCtx_, pFrame_);
|
||||
|
||||
if (!got_picture_) {
|
||||
av_image_fill_arrays(pFrameNv12_->data, pFrameNv12_->linesize,
|
||||
nv12_buffer_, AV_PIX_FMT_NV12,
|
||||
pFrameNv12_->width, pFrameNv12_->height, 1);
|
||||
|
||||
sws_scale(img_convert_ctx_, pFrame_->data, pFrame_->linesize, 0,
|
||||
pFrame_->height, pFrameNv12_->data,
|
||||
pFrameNv12_->linesize);
|
||||
|
||||
_on_data((unsigned char *)nv12_buffer_,
|
||||
pFrameNv12_->width * pFrameNv12_->height * 3 / 2,
|
||||
pFrameNv12_->width, pFrameNv12_->height);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ScreenCapturerAvf::Pause() { return 0; }
|
||||
|
||||
int ScreenCapturerAvf::Resume() { return 0; }
|
||||
|
||||
int ScreenCapturerAvf::Stop() { return 0; }
|
||||
|
||||
void ScreenCapturerAvf::OnFrame() {}
|
||||
|
||||
void ScreenCapturerAvf::CleanUp() {}
|
||||
@@ -0,0 +1,51 @@
|
||||
#include "screen_capturer_sck.h"
|
||||
|
||||
#include "rd_log.h"
|
||||
|
||||
ScreenCapturerSck::ScreenCapturerSck() {}
|
||||
ScreenCapturerSck::~ScreenCapturerSck() {
|
||||
// if (inited_ && capture_thread_.joinable()) {
|
||||
// capture_thread_.join();
|
||||
// inited_ = false;
|
||||
// }
|
||||
}
|
||||
|
||||
int ScreenCapturerSck::Init(const int fps, cb_desktop_data cb) {
|
||||
if (cb) {
|
||||
on_data_ = cb;
|
||||
} else {
|
||||
LOG_ERROR("cb is null");
|
||||
return -1;
|
||||
}
|
||||
|
||||
screen_capturer_sck_impl_ = CreateScreenCapturerSck();
|
||||
screen_capturer_sck_impl_->Init(fps, on_data_);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ScreenCapturerSck::Destroy() { return 0; }
|
||||
|
||||
int ScreenCapturerSck::Start() {
|
||||
// if (running_) {
|
||||
// return 0;
|
||||
// }
|
||||
|
||||
// running_ = true;
|
||||
// capture_thread_ = std::thread([this]() {
|
||||
// while (running_) {
|
||||
// }
|
||||
// });
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ScreenCapturerSck::Stop() { return 0; }
|
||||
|
||||
int ScreenCapturerSck::Pause() { return 0; }
|
||||
|
||||
int ScreenCapturerSck::Resume() { return 0; }
|
||||
|
||||
void ScreenCapturerSck::OnFrame() {}
|
||||
|
||||
void ScreenCapturerSck::CleanUp() {}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* @Author: DI JUNKUN
|
||||
* @Date: 2024-10-17
|
||||
* Copyright (c) 2024 by DI JUNKUN, All Rights Reserved.
|
||||
*/
|
||||
|
||||
#ifndef _SCREEN_CAPTURER_SCK_H_
|
||||
#define _SCREEN_CAPTURER_SCK_H_
|
||||
|
||||
#include <atomic>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
#include "screen_capturer.h"
|
||||
|
||||
class ScreenCapturerSck : public ScreenCapturer {
|
||||
public:
|
||||
ScreenCapturerSck();
|
||||
~ScreenCapturerSck();
|
||||
|
||||
public:
|
||||
virtual int Init(const int fps, cb_desktop_data cb);
|
||||
|
||||
virtual int Destroy();
|
||||
|
||||
virtual int Start();
|
||||
|
||||
virtual int Stop();
|
||||
|
||||
int Pause();
|
||||
|
||||
int Resume();
|
||||
|
||||
void OnFrame();
|
||||
|
||||
protected:
|
||||
void CleanUp();
|
||||
|
||||
private:
|
||||
std::unique_ptr<ScreenCapturer> CreateScreenCapturerSck();
|
||||
|
||||
private:
|
||||
int _fps;
|
||||
cb_desktop_data on_data_;
|
||||
unsigned char* nv12_frame_ = nullptr;
|
||||
bool inited_ = false;
|
||||
|
||||
// thread
|
||||
std::thread capture_thread_;
|
||||
std::atomic_bool running_;
|
||||
|
||||
private:
|
||||
std::unique_ptr<ScreenCapturer> screen_capturer_sck_impl_;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,256 @@
|
||||
/*
|
||||
* Copyright (c) 2024 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "screen_capturer_sck.h"
|
||||
|
||||
#include "rd_log.h"
|
||||
#include <CoreGraphics/CoreGraphics.h>
|
||||
#include <IOSurface/IOSurface.h>
|
||||
#include <ScreenCaptureKit/ScreenCaptureKit.h>
|
||||
#include <atomic>
|
||||
#include <mutex>
|
||||
|
||||
class ScreenCapturerSckImpl;
|
||||
|
||||
// The ScreenCaptureKit API was available in macOS 12.3, but full-screen capture
|
||||
// was reported to be broken before macOS 13 - see http://crbug.com/40234870.
|
||||
// Also, the `SCContentFilter` fields `contentRect` and `pointPixelScale` were
|
||||
// introduced in macOS 14.
|
||||
API_AVAILABLE(macos(14.0))
|
||||
@interface SckHelper : NSObject <SCStreamDelegate, SCStreamOutput>
|
||||
|
||||
- (instancetype)initWithCapturer:(ScreenCapturerSckImpl *)capturer;
|
||||
|
||||
- (void)onShareableContentCreated:(SCShareableContent *)content;
|
||||
|
||||
// Called just before the capturer is destroyed. This avoids a dangling pointer,
|
||||
// and prevents any new calls into a deleted capturer. If any method-call on the
|
||||
// capturer is currently running on a different thread, this blocks until it
|
||||
// completes.
|
||||
- (void)releaseCapturer;
|
||||
@end
|
||||
|
||||
class API_AVAILABLE(macos(14.0)) ScreenCapturerSckImpl : public ScreenCapturer {
|
||||
public:
|
||||
explicit ScreenCapturerSckImpl();
|
||||
|
||||
ScreenCapturerSckImpl(const ScreenCapturerSckImpl &) = delete;
|
||||
ScreenCapturerSckImpl &operator=(const ScreenCapturerSckImpl &) = delete;
|
||||
~ScreenCapturerSckImpl();
|
||||
|
||||
public:
|
||||
int Init(const int fps, cb_desktop_data cb);
|
||||
void OnReceiveContent(SCShareableContent *content);
|
||||
void OnNewIOSurface(IOSurfaceRef io_surface, CFDictionaryRef attachment);
|
||||
|
||||
virtual int Destroy() { return 0; }
|
||||
|
||||
virtual int Start() { return 0; }
|
||||
|
||||
virtual int Stop() { return 0; }
|
||||
|
||||
private:
|
||||
SckHelper *__strong helper_;
|
||||
SCStream *__strong stream_;
|
||||
|
||||
cb_desktop_data _on_data;
|
||||
unsigned char *nv12_frame_ = nullptr;
|
||||
bool permanent_error_ = false;
|
||||
CGDirectDisplayID current_display_ = -1;
|
||||
std::mutex mtx_;
|
||||
};
|
||||
|
||||
@implementation SckHelper {
|
||||
// This lock is to prevent the capturer being destroyed while an instance
|
||||
// method is still running on another thread.
|
||||
std::mutex helper_mtx_;
|
||||
ScreenCapturerSckImpl *_capturer;
|
||||
}
|
||||
|
||||
- (instancetype)initWithCapturer:(ScreenCapturerSckImpl *)capturer {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_capturer = capturer;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)onShareableContentCreated:(SCShareableContent *)content {
|
||||
std::lock_guard<std::mutex> lock(helper_mtx_);
|
||||
if (_capturer) {
|
||||
_capturer->OnReceiveContent(content);
|
||||
} else {
|
||||
LOG_ERROR("Invalid capturer");
|
||||
}
|
||||
}
|
||||
|
||||
- (void)stream:(SCStream *)stream
|
||||
didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
|
||||
ofType:(SCStreamOutputType)type {
|
||||
CVPixelBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
|
||||
if (!pixelBuffer) {
|
||||
return;
|
||||
}
|
||||
|
||||
IOSurfaceRef ioSurface = CVPixelBufferGetIOSurface(pixelBuffer);
|
||||
if (!ioSurface) {
|
||||
return;
|
||||
}
|
||||
|
||||
CFArrayRef attachmentsArray = CMSampleBufferGetSampleAttachmentsArray(
|
||||
sampleBuffer, /*createIfNecessary=*/false);
|
||||
if (!attachmentsArray || CFArrayGetCount(attachmentsArray) <= 0) {
|
||||
LOG_ERROR("Discarding frame with no attachments");
|
||||
return;
|
||||
}
|
||||
|
||||
CFDictionaryRef attachment =
|
||||
static_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(attachmentsArray, 0));
|
||||
|
||||
std::lock_guard<std::mutex> lock(helper_mtx_);
|
||||
if (_capturer) {
|
||||
_capturer->OnNewIOSurface(ioSurface, attachment);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)releaseCapturer {
|
||||
std::lock_guard<std::mutex> lock(helper_mtx_);
|
||||
_capturer = nullptr;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
ScreenCapturerSckImpl::ScreenCapturerSckImpl() {
|
||||
helper_ = [[SckHelper alloc] initWithCapturer:this];
|
||||
}
|
||||
|
||||
ScreenCapturerSckImpl::~ScreenCapturerSckImpl() {
|
||||
[stream_ stopCaptureWithCompletionHandler:nil];
|
||||
[helper_ releaseCapturer];
|
||||
}
|
||||
|
||||
int ScreenCapturerSckImpl::Init(const int fps, cb_desktop_data cb) {
|
||||
_on_data = cb;
|
||||
|
||||
SckHelper *local_helper = helper_;
|
||||
auto handler = ^(SCShareableContent *content, NSError *error) {
|
||||
[local_helper onShareableContentCreated:content];
|
||||
};
|
||||
|
||||
[SCShareableContent getShareableContentWithCompletionHandler:handler];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ScreenCapturerSckImpl::OnReceiveContent(SCShareableContent *content) {
|
||||
if (!content) {
|
||||
LOG_ERROR("getShareableContent failed");
|
||||
permanent_error_ = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!content.displays.count) {
|
||||
LOG_ERROR("getShareableContent returned no displays");
|
||||
permanent_error_ = true;
|
||||
return;
|
||||
}
|
||||
|
||||
SCDisplay *captured_display;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mtx_);
|
||||
for (SCDisplay *display in content.displays) {
|
||||
if (current_display_ == display.displayID) {
|
||||
captured_display = display;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!captured_display) {
|
||||
if (-1 == current_display_) {
|
||||
LOG_ERROR("Full screen capture is not supported, falling back to first "
|
||||
"display");
|
||||
} else {
|
||||
LOG_ERROR("Display [{}] not found, falling back to first display",
|
||||
current_display_);
|
||||
}
|
||||
captured_display = content.displays.firstObject;
|
||||
}
|
||||
}
|
||||
|
||||
SCContentFilter *filter =
|
||||
[[SCContentFilter alloc] initWithDisplay:captured_display
|
||||
excludingWindows:@[]];
|
||||
SCStreamConfiguration *config = [[SCStreamConfiguration alloc] init];
|
||||
config.pixelFormat = kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange;
|
||||
config.showsCursor = false;
|
||||
config.width = filter.contentRect.size.width * filter.pointPixelScale;
|
||||
config.height = filter.contentRect.size.height * filter.pointPixelScale;
|
||||
config.captureResolution = SCCaptureResolutionNominal;
|
||||
|
||||
std::lock_guard<std::mutex> lock(mtx_);
|
||||
|
||||
if (stream_) {
|
||||
LOG_INFO("Updating stream configuration");
|
||||
[stream_ updateContentFilter:filter completionHandler:nil];
|
||||
[stream_ updateConfiguration:config completionHandler:nil];
|
||||
} else {
|
||||
stream_ = [[SCStream alloc] initWithFilter:filter
|
||||
configuration:config
|
||||
delegate:helper_];
|
||||
|
||||
// TODO: crbug.com/327458809 - Choose an appropriate sampleHandlerQueue for
|
||||
// best performance.
|
||||
NSError *add_stream_output_error;
|
||||
bool add_stream_output_result =
|
||||
[stream_ addStreamOutput:helper_
|
||||
type:SCStreamOutputTypeScreen
|
||||
sampleHandlerQueue:nil
|
||||
error:&add_stream_output_error];
|
||||
if (!add_stream_output_result) {
|
||||
stream_ = nil;
|
||||
LOG_ERROR("addStreamOutput failed");
|
||||
permanent_error_ = true;
|
||||
return;
|
||||
}
|
||||
|
||||
auto handler = ^(NSError *error) {
|
||||
if (error) {
|
||||
// It should be safe to access `this` here, because the C++ destructor
|
||||
// calls stopCaptureWithCompletionHandler on the stream, which cancels
|
||||
// this handler.
|
||||
permanent_error_ = true;
|
||||
LOG_ERROR("startCaptureWithCompletionHandler failed");
|
||||
} else {
|
||||
LOG_INFO("Capture started");
|
||||
}
|
||||
};
|
||||
|
||||
[stream_ startCaptureWithCompletionHandler:handler];
|
||||
}
|
||||
}
|
||||
|
||||
void ScreenCapturerSckImpl::OnNewIOSurface(IOSurfaceRef io_surface,
|
||||
CFDictionaryRef attachment) {
|
||||
size_t width = IOSurfaceGetWidth(io_surface);
|
||||
size_t height = IOSurfaceGetHeight(io_surface);
|
||||
|
||||
uint32_t aseed;
|
||||
IOSurfaceLock(io_surface, kIOSurfaceLockReadOnly, &aseed);
|
||||
|
||||
nv12_frame_ =
|
||||
static_cast<unsigned char *>(IOSurfaceGetBaseAddress(io_surface));
|
||||
|
||||
_on_data(nv12_frame_, width * height * 3 / 2, width, height);
|
||||
|
||||
IOSurfaceUnlock(io_surface, kIOSurfaceLockReadOnly, &aseed);
|
||||
}
|
||||
|
||||
std::unique_ptr<ScreenCapturer> ScreenCapturerSck::CreateScreenCapturerSck() {
|
||||
return std::make_unique<ScreenCapturerSckImpl>();
|
||||
}
|
||||
@@ -11,23 +11,18 @@
|
||||
|
||||
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 Init(const int fps, cb_desktop_data cb) = 0;
|
||||
virtual int Destroy() = 0;
|
||||
|
||||
virtual int Start() = 0;
|
||||
|
||||
virtual int Stop() = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -8,12 +8,12 @@
|
||||
#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"
|
||||
// #include "screen_capturer_avf.h"
|
||||
#include "screen_capturer_sck.h"
|
||||
#endif
|
||||
|
||||
class ScreenCapturerFactory {
|
||||
@@ -27,7 +27,8 @@ class ScreenCapturerFactory {
|
||||
#elif __linux__
|
||||
return new ScreenCapturerX11();
|
||||
#elif __APPLE__
|
||||
return new ScreenCapturerAvf();
|
||||
// return new ScreenCapturerAvf();
|
||||
return new ScreenCapturerSck();
|
||||
#else
|
||||
return nullptr;
|
||||
#endif
|
||||
|
||||
@@ -5,39 +5,12 @@
|
||||
#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);
|
||||
#include "libyuv.h"
|
||||
|
||||
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) {
|
||||
BOOL WINAPI EnumMonitorProc(HMONITOR hmonitor, [[maybe_unused]] HDC hdc,
|
||||
[[maybe_unused]] LPRECT lprc, LPARAM data) {
|
||||
MONITORINFOEX info_ex;
|
||||
info_ex.cbSize = sizeof(MONITORINFOEX);
|
||||
|
||||
@@ -62,7 +35,20 @@ HMONITOR GetPrimaryMonitor() {
|
||||
|
||||
ScreenCapturerWgc::ScreenCapturerWgc() {}
|
||||
|
||||
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 {
|
||||
@@ -76,12 +62,12 @@ bool ScreenCapturerWgc::IsWgcSupported() {
|
||||
}
|
||||
}
|
||||
|
||||
int ScreenCapturerWgc::Init(const RECORD_DESKTOP_RECT &rect, const int fps,
|
||||
cb_desktop_data cb) {
|
||||
int ScreenCapturerWgc::Init(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];
|
||||
// nv12_frame_ = new unsigned char[rect.right * rect.bottom * 3 / 2];
|
||||
// nv12_frame_scaled_ = new unsigned char[1280 * 720 * 3 / 2];
|
||||
|
||||
_fps = fps;
|
||||
|
||||
@@ -114,17 +100,7 @@ int ScreenCapturerWgc::Init(const RECORD_DESKTOP_RECT &rect, const int fps,
|
||||
return error;
|
||||
}
|
||||
|
||||
int ScreenCapturerWgc::Destroy() {
|
||||
if (nv12_frame_) {
|
||||
delete nv12_frame_;
|
||||
nv12_frame_ = nullptr;
|
||||
}
|
||||
|
||||
Stop();
|
||||
CleanUp();
|
||||
|
||||
return 0;
|
||||
}
|
||||
int ScreenCapturerWgc::Destroy() { return 0; }
|
||||
|
||||
int ScreenCapturerWgc::Start() {
|
||||
if (_running == true) {
|
||||
@@ -163,12 +139,53 @@ int ScreenCapturerWgc::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) {
|
||||
int abgr_index = (i * abgr_stride + j) * 4;
|
||||
int bgra_index = (i * bgra_stride + j) * 4;
|
||||
|
||||
bgra_data[bgra_index + 0] = abgr_data[abgr_index + 2]; // 蓝色
|
||||
bgra_data[bgra_index + 1] = abgr_data[abgr_index + 1]; // 绿色
|
||||
bgra_data[bgra_index + 2] = abgr_data[abgr_index + 0]; // 红色
|
||||
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) {
|
||||
int bgra_index = (i * bgra_stride + j) * 4;
|
||||
int abgr_index = (i * abgr_stride + j) * 4;
|
||||
|
||||
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)
|
||||
BGRAToNV12FFmpeg((unsigned char *)frame.data, frame.width, frame.height,
|
||||
nv12_frame_);
|
||||
_on_data(nv12_frame_, frame.width * frame.height * 4, frame.width,
|
||||
frame.height);
|
||||
if (_on_data) {
|
||||
// int width = 1280;
|
||||
// int height = 720;
|
||||
|
||||
if (!nv12_frame_) {
|
||||
nv12_frame_ = new unsigned char[frame.width * frame.height * 3 / 2];
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
_on_data(nv12_frame_, frame.width * frame.height * 3 / 2, frame.width,
|
||||
frame.height);
|
||||
}
|
||||
}
|
||||
|
||||
void ScreenCapturerWgc::CleanUp() {
|
||||
|
||||
@@ -19,15 +19,14 @@ class ScreenCapturerWgc : public ScreenCapturer,
|
||||
public:
|
||||
bool IsWgcSupported();
|
||||
|
||||
virtual int Init(const RECORD_DESKTOP_RECT &rect, const int fps,
|
||||
cb_desktop_data cb);
|
||||
virtual int Init(const int fps, cb_desktop_data cb);
|
||||
virtual int Destroy();
|
||||
|
||||
virtual int Start();
|
||||
|
||||
int Pause();
|
||||
int Resume();
|
||||
int Stop();
|
||||
virtual int Stop();
|
||||
|
||||
void OnFrame(const WgcSession::wgc_session_frame &frame);
|
||||
|
||||
@@ -45,13 +44,12 @@ class ScreenCapturerWgc : public ScreenCapturer,
|
||||
|
||||
std::string _device_name;
|
||||
|
||||
RECORD_DESKTOP_RECT _rect;
|
||||
|
||||
int _fps;
|
||||
|
||||
cb_desktop_data _on_data;
|
||||
cb_desktop_data _on_data = nullptr;
|
||||
|
||||
unsigned char *nv12_frame_ = nullptr;
|
||||
unsigned char *nv12_frame_scaled_ = nullptr;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -89,6 +89,8 @@ int WgcSessionImpl::Start() {
|
||||
|
||||
capture_session_.StartCapture();
|
||||
|
||||
capture_session_.IsCursorCaptureEnabled(false);
|
||||
|
||||
error = 0;
|
||||
} catch (winrt::hresult_error) {
|
||||
std::cout << "AE_WGC_CREATE_CAPTURER_FAILED" << std::endl;
|
||||
@@ -146,7 +148,7 @@ auto WgcSessionImpl::CreateD3D11Device() {
|
||||
|
||||
if (DXGI_ERROR_UNSUPPORTED == hr) {
|
||||
// change D3D_DRIVER_TYPE
|
||||
D3D_DRIVER_TYPE type = D3D_DRIVER_TYPE_WARP;
|
||||
type = D3D_DRIVER_TYPE_WARP;
|
||||
hr = D3D11CreateDevice(nullptr, type, nullptr, flags, nullptr, 0,
|
||||
D3D11_SDK_VERSION, d3d_device.put(), nullptr,
|
||||
nullptr);
|
||||
@@ -211,7 +213,7 @@ HRESULT WgcSessionImpl::CreateMappedTexture(
|
||||
|
||||
void WgcSessionImpl::OnFrame(
|
||||
winrt::Windows::Graphics::Capture::Direct3D11CaptureFramePool const &sender,
|
||||
winrt::Windows::Foundation::IInspectable const &args) {
|
||||
[[maybe_unused]] winrt::Windows::Foundation::IInspectable const &args) {
|
||||
std::lock_guard locker(lock_);
|
||||
|
||||
auto is_new_size = false;
|
||||
|
||||
1408
src/single_window/IconsFontAwesome6.h
Normal file
1408
src/single_window/IconsFontAwesome6.h
Normal file
File diff suppressed because it is too large
Load Diff
55
src/single_window/about_window.cpp
Normal file
55
src/single_window/about_window.cpp
Normal file
@@ -0,0 +1,55 @@
|
||||
#include "layout_style.h"
|
||||
#include "localization.h"
|
||||
#include "rd_log.h"
|
||||
#include "render.h"
|
||||
|
||||
int Render::AboutWindow() {
|
||||
if (show_about_window_) {
|
||||
const ImGuiViewport *viewport = ImGui::GetMainViewport();
|
||||
|
||||
ImGui::SetNextWindowPos(ImVec2(
|
||||
(viewport->WorkSize.x - viewport->WorkPos.x - about_window_width_) / 2,
|
||||
(viewport->WorkSize.y - viewport->WorkPos.y - about_window_height_) /
|
||||
2));
|
||||
|
||||
ImGui::SetNextWindowSize(ImVec2(about_window_width_, about_window_height_));
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 1.0f);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 5.0f);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 3.0f);
|
||||
ImGui::SetWindowFontScale(0.5f);
|
||||
ImGui::Begin(
|
||||
localization::about[localization_language_index_].c_str(), nullptr,
|
||||
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse |
|
||||
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoSavedSettings);
|
||||
ImGui::SetWindowFontScale(1.0f);
|
||||
ImGui::SetWindowFontScale(0.5f);
|
||||
|
||||
std::string version;
|
||||
#ifdef RD_VERSION
|
||||
version = RD_VERSION;
|
||||
#else
|
||||
version = "Unknown";
|
||||
#endif
|
||||
|
||||
std::string text =
|
||||
localization::version[localization_language_index_] + ": " + version;
|
||||
ImGui::Text("%s", text.c_str());
|
||||
|
||||
ImGui::SetCursorPosX(about_window_width_ * 0.42f);
|
||||
ImGui::SetCursorPosY(about_window_height_ * 0.75f);
|
||||
// OK
|
||||
if (ImGui::Button(localization::ok[localization_language_index_].c_str())) {
|
||||
show_about_window_ = false;
|
||||
}
|
||||
|
||||
ImGui::SetWindowFontScale(1.0f);
|
||||
ImGui::SetWindowFontScale(0.5f);
|
||||
ImGui::End();
|
||||
ImGui::SetWindowFontScale(1.0f);
|
||||
ImGui::PopStyleVar(3);
|
||||
ImGui::PopStyleColor();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
173
src/single_window/connection_status_window.cpp
Normal file
173
src/single_window/connection_status_window.cpp
Normal file
@@ -0,0 +1,173 @@
|
||||
#include "layout_style.h"
|
||||
#include "localization.h"
|
||||
#include "rd_log.h"
|
||||
#include "render.h"
|
||||
|
||||
int Render::ConnectionStatusWindow() {
|
||||
if (show_connection_status_window_) {
|
||||
const ImGuiViewport *viewport = ImGui::GetMainViewport();
|
||||
|
||||
ImGui::SetNextWindowPos(ImVec2((viewport->WorkSize.x - viewport->WorkPos.x -
|
||||
connection_status_window_width_) /
|
||||
2,
|
||||
(viewport->WorkSize.y - viewport->WorkPos.y -
|
||||
connection_status_window_height_) /
|
||||
2));
|
||||
|
||||
ImGui::SetNextWindowSize(ImVec2(connection_status_window_width_,
|
||||
connection_status_window_height_));
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 3.0f);
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(1.0, 1.0, 1.0, 1.0));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 1.0f);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 5.0f);
|
||||
|
||||
ImGui::Begin("ConnectionStatusWindow", nullptr,
|
||||
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse |
|
||||
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar |
|
||||
ImGuiWindowFlags_NoSavedSettings);
|
||||
ImGui::PopStyleVar(2);
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
ImGui::SetWindowFontScale(0.5f);
|
||||
std::string text;
|
||||
|
||||
if (ConnectionStatus::Connecting == connection_status_) {
|
||||
text = localization::p2p_connecting[localization_language_index_];
|
||||
ImGui::SetCursorPosX(connection_status_window_width_ * 3 / 7);
|
||||
ImGui::SetCursorPosY(connection_status_window_height_ * 2 / 3);
|
||||
} else if (ConnectionStatus::Connected == connection_status_) {
|
||||
text = localization::p2p_connected[localization_language_index_];
|
||||
ImGui::SetCursorPosX(connection_status_window_width_ * 3 / 7);
|
||||
ImGui::SetCursorPosY(connection_status_window_height_ * 2 / 3);
|
||||
// ok
|
||||
if (ImGui::Button(
|
||||
localization::ok[localization_language_index_].c_str()) ||
|
||||
ImGui::IsKeyPressed(ImGuiKey_Enter) ||
|
||||
ImGui::IsKeyPressed(ImGuiKey_Escape)) {
|
||||
show_connection_status_window_ = false;
|
||||
}
|
||||
} else if (ConnectionStatus::Disconnected == connection_status_) {
|
||||
text = localization::p2p_disconnected[localization_language_index_];
|
||||
ImGui::SetCursorPosX(connection_status_window_width_ * 3 / 7);
|
||||
ImGui::SetCursorPosY(connection_status_window_height_ * 2 / 3);
|
||||
// ok
|
||||
if (ImGui::Button(
|
||||
localization::ok[localization_language_index_].c_str()) ||
|
||||
ImGui::IsKeyPressed(ImGuiKey_Enter) ||
|
||||
ImGui::IsKeyPressed(ImGuiKey_Escape)) {
|
||||
show_connection_status_window_ = false;
|
||||
}
|
||||
} else if (ConnectionStatus::Failed == connection_status_) {
|
||||
text = localization::p2p_failed[localization_language_index_];
|
||||
ImGui::SetCursorPosX(connection_status_window_width_ * 3 / 7);
|
||||
ImGui::SetCursorPosY(connection_status_window_height_ * 2 / 3);
|
||||
// ok
|
||||
if (ImGui::Button(
|
||||
localization::ok[localization_language_index_].c_str()) ||
|
||||
ImGui::IsKeyPressed(ImGuiKey_Enter) ||
|
||||
ImGui::IsKeyPressed(ImGuiKey_Escape)) {
|
||||
show_connection_status_window_ = false;
|
||||
}
|
||||
} else if (ConnectionStatus::Closed == connection_status_) {
|
||||
text = localization::p2p_closed[localization_language_index_];
|
||||
ImGui::SetCursorPosX(connection_status_window_width_ * 3 / 7);
|
||||
ImGui::SetCursorPosY(connection_status_window_height_ * 2 / 3);
|
||||
// ok
|
||||
if (ImGui::Button(
|
||||
localization::ok[localization_language_index_].c_str()) ||
|
||||
ImGui::IsKeyPressed(ImGuiKey_Enter) ||
|
||||
ImGui::IsKeyPressed(ImGuiKey_Escape)) {
|
||||
show_connection_status_window_ = false;
|
||||
}
|
||||
} else if (ConnectionStatus::IncorrectPassword == connection_status_) {
|
||||
if (!password_validating_) {
|
||||
if (password_validating_time_ == 1) {
|
||||
text = localization::input_password[localization_language_index_];
|
||||
} else {
|
||||
text = localization::reinput_password[localization_language_index_];
|
||||
}
|
||||
|
||||
auto window_width = ImGui::GetWindowSize().x;
|
||||
auto window_height = ImGui::GetWindowSize().y;
|
||||
ImGui::SetCursorPosX((window_width - IPUT_WINDOW_WIDTH / 2) * 0.5f);
|
||||
ImGui::SetCursorPosY(window_height * 0.4f);
|
||||
ImGui::SetNextItemWidth(IPUT_WINDOW_WIDTH / 2);
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f);
|
||||
|
||||
if (focus_on_input_widget_) {
|
||||
ImGui::SetKeyboardFocusHere();
|
||||
focus_on_input_widget_ = false;
|
||||
}
|
||||
|
||||
ImGui::InputText("##password", remote_password_,
|
||||
IM_ARRAYSIZE(remote_password_),
|
||||
ImGuiInputTextFlags_CharsNoBlank);
|
||||
|
||||
ImGui::SetWindowFontScale(0.4f);
|
||||
|
||||
ImVec2 text_size = ImGui::CalcTextSize(
|
||||
localization::remember_password[localization_language_index_]
|
||||
.c_str());
|
||||
ImGui::SetCursorPosX((window_width - text_size.x) * 0.5f - 13.0f);
|
||||
ImGui::Checkbox(
|
||||
localization::remember_password[localization_language_index_]
|
||||
.c_str(),
|
||||
&remember_password_);
|
||||
ImGui::SetWindowFontScale(0.5f);
|
||||
ImGui::PopStyleVar();
|
||||
|
||||
ImGui::SetCursorPosX(window_width * 0.315f);
|
||||
ImGui::SetCursorPosY(window_height * 0.75f);
|
||||
// ok
|
||||
if (ImGui::Button(
|
||||
localization::ok[localization_language_index_].c_str()) ||
|
||||
ImGui::IsKeyPressed(ImGuiKey_Enter)) {
|
||||
show_connection_status_window_ = true;
|
||||
password_validating_ = true;
|
||||
rejoin_ = true;
|
||||
focus_on_input_widget_ = true;
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
// cancel
|
||||
if (ImGui::Button(
|
||||
localization::cancel[localization_language_index_].c_str()) ||
|
||||
ImGui::IsKeyPressed(ImGuiKey_Escape)) {
|
||||
memset(remote_password_, 0, sizeof(remote_password_));
|
||||
show_connection_status_window_ = false;
|
||||
focus_on_input_widget_ = true;
|
||||
}
|
||||
} else if (password_validating_) {
|
||||
text = localization::validate_password[localization_language_index_];
|
||||
ImGui::SetCursorPosX(connection_status_window_width_ * 3 / 7);
|
||||
ImGui::SetCursorPosY(connection_status_window_height_ * 2 / 3);
|
||||
}
|
||||
} else if (ConnectionStatus::NoSuchTransmissionId == connection_status_) {
|
||||
text = localization::no_such_id[localization_language_index_];
|
||||
ImGui::SetCursorPosX(connection_status_window_width_ * 3 / 7);
|
||||
ImGui::SetCursorPosY(connection_status_window_height_ * 2 / 3);
|
||||
// ok
|
||||
if (ImGui::Button(
|
||||
localization::ok[localization_language_index_].c_str()) ||
|
||||
ImGui::IsKeyPressed(ImGuiKey_Enter)) {
|
||||
show_connection_status_window_ = false;
|
||||
re_enter_remote_id_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
auto window_width = ImGui::GetWindowSize().x;
|
||||
auto window_height = ImGui::GetWindowSize().y;
|
||||
auto text_width = ImGui::CalcTextSize(text.c_str()).x;
|
||||
ImGui::SetCursorPosX((window_width - text_width) * 0.5f);
|
||||
ImGui::SetCursorPosY(window_height * 0.2f);
|
||||
ImGui::Text("%s", text.c_str());
|
||||
ImGui::SetWindowFontScale(1.0f);
|
||||
|
||||
ImGui::End();
|
||||
ImGui::PopStyleVar();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
281
src/single_window/control_bar.cpp
Normal file
281
src/single_window/control_bar.cpp
Normal file
@@ -0,0 +1,281 @@
|
||||
#include "layout_style.h"
|
||||
#include "localization.h"
|
||||
#include "rd_log.h"
|
||||
#include "render.h"
|
||||
|
||||
int CountDigits(int number) {
|
||||
if (number == 0) return 1;
|
||||
return (int)std::floor(std::log10(std::abs(number))) + 1;
|
||||
}
|
||||
|
||||
int BitrateDisplay(int bitrate) {
|
||||
int num_of_digits = CountDigits(bitrate);
|
||||
if (num_of_digits <= 3) {
|
||||
ImGui::Text("%d bps", bitrate);
|
||||
} else if (num_of_digits > 3 && num_of_digits <= 6) {
|
||||
ImGui::Text("%d kbps", bitrate / 1000);
|
||||
} else {
|
||||
ImGui::Text("%.1f mbps", bitrate / 1000000.0f);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int LossRateDisplay(float loss_rate) {
|
||||
if (loss_rate < 0.01f) {
|
||||
ImGui::Text("0%%");
|
||||
} else {
|
||||
ImGui::Text("%.0f%%", loss_rate * 100);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Render::ControlBar() {
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 3.0f);
|
||||
|
||||
ImVec2 mouse_button_pos = ImVec2(0, 0);
|
||||
if (control_bar_expand_) {
|
||||
ImGui::SetCursorPosX(
|
||||
is_control_bar_in_left_ ? (control_window_width_ + 5.0f) : 38.0f);
|
||||
// mouse control button
|
||||
ImDrawList* draw_list = ImGui::GetWindowDrawList();
|
||||
|
||||
if (is_control_bar_in_left_) {
|
||||
draw_list->AddLine(
|
||||
ImVec2(ImGui::GetCursorScreenPos().x - 5.0f,
|
||||
ImGui::GetCursorScreenPos().y - 7.0f),
|
||||
ImVec2(ImGui::GetCursorScreenPos().x - 5.0f,
|
||||
ImGui::GetCursorScreenPos().y - 7.0f + control_window_height_),
|
||||
IM_COL32(178, 178, 178, 255), 1.0f);
|
||||
}
|
||||
|
||||
mouse_button_pos = ImGui::GetCursorScreenPos();
|
||||
float disable_mouse_x = ImGui::GetCursorScreenPos().x + 4.0f;
|
||||
float disable_mouse_y = ImGui::GetCursorScreenPos().y + 4.0f;
|
||||
std::string mouse = mouse_control_button_pressed_ ? ICON_FA_COMPUTER_MOUSE
|
||||
: ICON_FA_COMPUTER_MOUSE;
|
||||
if (ImGui::Button(mouse.c_str(), ImVec2(25, 25))) {
|
||||
if (connection_established_) {
|
||||
control_mouse_ = !control_mouse_;
|
||||
start_keyboard_capturer_ = !start_keyboard_capturer_;
|
||||
mouse_control_button_pressed_ = !mouse_control_button_pressed_;
|
||||
mouse_control_button_label_ =
|
||||
mouse_control_button_pressed_
|
||||
? localization::release_mouse[localization_language_index_]
|
||||
: localization::control_mouse[localization_language_index_];
|
||||
}
|
||||
}
|
||||
if (!mouse_control_button_pressed_) {
|
||||
draw_list->AddLine(
|
||||
ImVec2(disable_mouse_x, disable_mouse_y),
|
||||
ImVec2(disable_mouse_x + 16.0f, disable_mouse_y + 14.2f),
|
||||
IM_COL32(0, 0, 0, 255), 2.0f);
|
||||
draw_list->AddLine(
|
||||
ImVec2(disable_mouse_x - 1.2f, disable_mouse_y + 1.2f),
|
||||
ImVec2(disable_mouse_x + 15.3f, disable_mouse_y + 15.4f),
|
||||
ImGui::IsItemHovered() ? IM_COL32(66, 150, 250, 255)
|
||||
: IM_COL32(179, 213, 253, 255),
|
||||
2.0f);
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
// audio capture button
|
||||
float disable_audio_x = ImGui::GetCursorScreenPos().x + 4;
|
||||
float disable_audio_y = ImGui::GetCursorScreenPos().y + 4.0f;
|
||||
// std::string audio = audio_capture_button_pressed_ ? ICON_FA_VOLUME_HIGH
|
||||
// : ICON_FA_VOLUME_XMARK;
|
||||
std::string audio = audio_capture_button_pressed_ ? ICON_FA_VOLUME_HIGH
|
||||
: ICON_FA_VOLUME_HIGH;
|
||||
if (ImGui::Button(audio.c_str(), ImVec2(25, 25))) {
|
||||
if (connection_established_) {
|
||||
audio_capture_ = !audio_capture_;
|
||||
audio_capture_button_pressed_ = !audio_capture_button_pressed_;
|
||||
audio_capture_button_label_ =
|
||||
audio_capture_button_pressed_
|
||||
? localization::audio_capture[localization_language_index_]
|
||||
: localization::mute[localization_language_index_];
|
||||
|
||||
RemoteAction remote_action;
|
||||
remote_action.type = ControlType::audio_capture;
|
||||
remote_action.a = audio_capture_button_pressed_;
|
||||
SendDataFrame(peer_, (const char*)&remote_action,
|
||||
sizeof(remote_action));
|
||||
}
|
||||
}
|
||||
if (!audio_capture_button_pressed_) {
|
||||
draw_list->AddLine(
|
||||
ImVec2(disable_audio_x, disable_audio_y),
|
||||
ImVec2(disable_audio_x + 16.0f, disable_audio_y + 14.2f),
|
||||
IM_COL32(0, 0, 0, 255), 2.0f);
|
||||
draw_list->AddLine(
|
||||
ImVec2(disable_audio_x - 1.2f, disable_audio_y + 1.2f),
|
||||
ImVec2(disable_audio_x + 15.3f, disable_audio_y + 15.4f),
|
||||
ImGui::IsItemHovered() ? IM_COL32(66, 150, 250, 255)
|
||||
: IM_COL32(179, 213, 253, 255),
|
||||
2.0f);
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
// net traffic stats button
|
||||
bool button_color_style_pushed = false;
|
||||
if (net_traffic_stats_button_pressed_) {
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(66 / 255.0f, 150 / 255.0f,
|
||||
250 / 255.0f, 1.0f));
|
||||
button_color_style_pushed = true;
|
||||
}
|
||||
std::string net_traffic_stats = ICON_FA_SIGNAL;
|
||||
if (ImGui::Button(net_traffic_stats.c_str(), ImVec2(25, 25))) {
|
||||
net_traffic_stats_button_pressed_ = !net_traffic_stats_button_pressed_;
|
||||
control_window_height_is_changing_ = true;
|
||||
net_traffic_stats_button_pressed_time_ = ImGui::GetTime();
|
||||
net_traffic_stats_button_label_ =
|
||||
net_traffic_stats_button_pressed_
|
||||
? localization::hide_net_traffic_stats
|
||||
[localization_language_index_]
|
||||
: localization::show_net_traffic_stats
|
||||
[localization_language_index_];
|
||||
}
|
||||
if (button_color_style_pushed) {
|
||||
ImGui::PopStyleColor();
|
||||
button_color_style_pushed = false;
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
// fullscreen button
|
||||
std::string fullscreen =
|
||||
fullscreen_button_pressed_ ? ICON_FA_COMPRESS : ICON_FA_EXPAND;
|
||||
if (ImGui::Button(fullscreen.c_str(), ImVec2(25, 25))) {
|
||||
fullscreen_button_pressed_ = !fullscreen_button_pressed_;
|
||||
fullscreen_button_label_ =
|
||||
fullscreen_button_pressed_
|
||||
? localization::exit_fullscreen[localization_language_index_]
|
||||
: localization::fullscreen[localization_language_index_];
|
||||
// save stream window last size
|
||||
SDL_GetWindowSize(stream_window_, &stream_window_width_last_,
|
||||
&stream_window_height_last_);
|
||||
if (fullscreen_button_pressed_) {
|
||||
SDL_SetWindowFullscreen(stream_window_, SDL_WINDOW_FULLSCREEN_DESKTOP);
|
||||
} else {
|
||||
SDL_SetWindowFullscreen(stream_window_, SDL_FALSE);
|
||||
}
|
||||
reset_control_bar_pos_ = true;
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
// close button
|
||||
std::string close_button = ICON_FA_XMARK;
|
||||
if (ImGui::Button(close_button.c_str(), ImVec2(25, 25))) {
|
||||
SDL_Event event;
|
||||
event.type = SDL_QUIT;
|
||||
SDL_PushEvent(&event);
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
if (!is_control_bar_in_left_) {
|
||||
draw_list->AddLine(
|
||||
ImVec2(ImGui::GetCursorScreenPos().x - 3.0f,
|
||||
ImGui::GetCursorScreenPos().y - 7.0f),
|
||||
ImVec2(ImGui::GetCursorScreenPos().x - 3.0f,
|
||||
ImGui::GetCursorScreenPos().y - 7.0f + control_window_height_),
|
||||
IM_COL32(178, 178, 178, 255), 1.0f);
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::SetCursorPosX(
|
||||
is_control_bar_in_left_ ? (control_window_width_ * 2 - 20.0f) : 5.0f);
|
||||
|
||||
std::string control_bar =
|
||||
control_bar_expand_
|
||||
? (is_control_bar_in_left_ ? ICON_FA_ANGLE_LEFT : ICON_FA_ANGLE_RIGHT)
|
||||
: (is_control_bar_in_left_ ? ICON_FA_ANGLE_RIGHT
|
||||
: ICON_FA_ANGLE_LEFT);
|
||||
if (ImGui::Button(control_bar.c_str(), ImVec2(15, 25))) {
|
||||
control_bar_expand_ = !control_bar_expand_;
|
||||
control_bar_button_pressed_time_ = ImGui::GetTime();
|
||||
control_window_width_is_changing_ = true;
|
||||
|
||||
if (!control_bar_expand_) {
|
||||
control_window_height_ = 40;
|
||||
net_traffic_stats_button_pressed_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (net_traffic_stats_button_pressed_ && control_bar_expand_) {
|
||||
NetTrafficStats();
|
||||
}
|
||||
|
||||
ImGui::PopStyleVar();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Render::NetTrafficStats() {
|
||||
ImGui::SetCursorPos(ImVec2(
|
||||
is_control_bar_in_left_ ? (control_window_width_ + 5.0f) : 5.0f, 40.0f));
|
||||
|
||||
if (ImGui::BeginTable("NetTrafficStats", 4, ImGuiTableFlags_BordersH,
|
||||
ImVec2(control_window_max_width_ - 10.0f,
|
||||
control_window_max_height_ - 40.0f))) {
|
||||
ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed);
|
||||
ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch);
|
||||
ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch);
|
||||
ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed);
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text(" ");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s", localization::in[localization_language_index_].c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s", localization::out[localization_language_index_].c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s",
|
||||
localization::loss_rate[localization_language_index_].c_str());
|
||||
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s",
|
||||
localization::video[localization_language_index_].c_str());
|
||||
ImGui::TableNextColumn();
|
||||
BitrateDisplay((int)net_traffic_stats_.video_inbound_stats.bitrate);
|
||||
ImGui::TableNextColumn();
|
||||
BitrateDisplay((int)net_traffic_stats_.video_outbound_stats.bitrate);
|
||||
ImGui::TableNextColumn();
|
||||
LossRateDisplay(net_traffic_stats_.video_inbound_stats.loss_rate);
|
||||
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s",
|
||||
localization::audio[localization_language_index_].c_str());
|
||||
ImGui::TableNextColumn();
|
||||
BitrateDisplay((int)net_traffic_stats_.audio_inbound_stats.bitrate);
|
||||
ImGui::TableNextColumn();
|
||||
BitrateDisplay((int)net_traffic_stats_.audio_outbound_stats.bitrate);
|
||||
ImGui::TableNextColumn();
|
||||
LossRateDisplay(net_traffic_stats_.audio_inbound_stats.loss_rate);
|
||||
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s", localization::data[localization_language_index_].c_str());
|
||||
ImGui::TableNextColumn();
|
||||
BitrateDisplay((int)net_traffic_stats_.data_inbound_stats.bitrate);
|
||||
ImGui::TableNextColumn();
|
||||
BitrateDisplay((int)net_traffic_stats_.data_outbound_stats.bitrate);
|
||||
ImGui::TableNextColumn();
|
||||
LossRateDisplay(net_traffic_stats_.data_inbound_stats.loss_rate);
|
||||
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s",
|
||||
localization::total[localization_language_index_].c_str());
|
||||
ImGui::TableNextColumn();
|
||||
BitrateDisplay((int)net_traffic_stats_.total_inbound_stats.bitrate);
|
||||
ImGui::TableNextColumn();
|
||||
BitrateDisplay((int)net_traffic_stats_.total_outbound_stats.bitrate);
|
||||
ImGui::TableNextColumn();
|
||||
LossRateDisplay(net_traffic_stats_.total_inbound_stats.loss_rate);
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
230
src/single_window/control_window.cpp
Normal file
230
src/single_window/control_window.cpp
Normal file
@@ -0,0 +1,230 @@
|
||||
#include "rd_log.h"
|
||||
#include "render.h"
|
||||
|
||||
int Render::ControlWindow() {
|
||||
double time_duration = ImGui::GetTime() - control_bar_button_pressed_time_;
|
||||
if (control_window_width_is_changing_) {
|
||||
if (control_bar_expand_) {
|
||||
control_window_width_ =
|
||||
(float)(control_window_min_width_ +
|
||||
(control_window_max_width_ - control_window_min_width_) * 4 *
|
||||
time_duration);
|
||||
} else {
|
||||
control_window_width_ =
|
||||
(float)(control_window_max_width_ -
|
||||
(control_window_max_width_ - control_window_min_width_) * 4 *
|
||||
time_duration);
|
||||
}
|
||||
}
|
||||
|
||||
time_duration = ImGui::GetTime() - net_traffic_stats_button_pressed_time_;
|
||||
if (control_window_height_is_changing_) {
|
||||
if (control_bar_expand_ && net_traffic_stats_button_pressed_) {
|
||||
control_window_height_ =
|
||||
(float)(control_window_min_height_ +
|
||||
(control_window_max_height_ - control_window_min_height_) *
|
||||
4 * time_duration);
|
||||
} else if (control_bar_expand_ && !net_traffic_stats_button_pressed_) {
|
||||
control_window_height_ =
|
||||
(float)(control_window_max_height_ -
|
||||
(control_window_max_height_ - control_window_min_height_) *
|
||||
4 * time_duration);
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(1, 1, 1, 1));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowMinSize, ImVec2(0, 0));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 10.0f);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, 10.0f);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0));
|
||||
|
||||
ImGui::SetNextWindowSize(
|
||||
ImVec2(control_window_width_, control_window_height_), ImGuiCond_Always);
|
||||
|
||||
if (0 == control_winodw_pos_.x && 0 == control_winodw_pos_.y) {
|
||||
ImGui::SetNextWindowPos(ImVec2(0, title_bar_height_ + 1), ImGuiCond_Once);
|
||||
}
|
||||
|
||||
if (reset_control_bar_pos_) {
|
||||
float new_control_window_pos_x, new_control_window_pos_y, new_cursor_pos_x,
|
||||
new_cursor_pos_y;
|
||||
|
||||
// set control window pos
|
||||
new_control_window_pos_x = control_winodw_pos_.x;
|
||||
if (control_winodw_pos_.y < stream_render_rect_last_.y) {
|
||||
new_control_window_pos_y =
|
||||
stream_render_rect_.y -
|
||||
(stream_render_rect_last_.y - control_winodw_pos_.y);
|
||||
if (fullscreen_button_pressed_ && new_control_window_pos_y < 0) {
|
||||
new_control_window_pos_y = 0;
|
||||
} else if (!fullscreen_button_pressed_ &&
|
||||
new_control_window_pos_y < (title_bar_height_ + 1)) {
|
||||
new_control_window_pos_y = title_bar_height_ + 1;
|
||||
}
|
||||
} else if (control_winodw_pos_.y + control_window_height_ >
|
||||
stream_render_rect_last_.y + stream_render_rect_last_.h) {
|
||||
new_control_window_pos_y =
|
||||
stream_render_rect_.y + stream_render_rect_.h +
|
||||
(control_winodw_pos_.y - stream_render_rect_last_.y -
|
||||
stream_render_rect_last_.h);
|
||||
if (new_control_window_pos_y >
|
||||
stream_window_height_ - control_window_height_) {
|
||||
new_control_window_pos_y =
|
||||
stream_window_height_ - control_window_height_;
|
||||
}
|
||||
} else if (control_winodw_pos_.y + control_window_height_ ==
|
||||
stream_render_rect_last_.y + stream_render_rect_last_.h) {
|
||||
new_control_window_pos_y = stream_render_rect_.y + stream_render_rect_.h -
|
||||
control_window_height_;
|
||||
} else {
|
||||
new_control_window_pos_y =
|
||||
(control_winodw_pos_.y - stream_render_rect_last_.y) /
|
||||
(float)(stream_render_rect_last_.h) * stream_render_rect_.h +
|
||||
stream_render_rect_.y;
|
||||
}
|
||||
|
||||
ImGui::SetNextWindowPos(
|
||||
ImVec2(new_control_window_pos_x, new_control_window_pos_y),
|
||||
ImGuiCond_Always);
|
||||
|
||||
if (0 != mouse_diff_control_bar_pos_x_ &&
|
||||
0 != mouse_diff_control_bar_pos_y_) {
|
||||
// set cursor pos
|
||||
new_cursor_pos_x =
|
||||
new_control_window_pos_x + mouse_diff_control_bar_pos_x_;
|
||||
new_cursor_pos_y =
|
||||
new_control_window_pos_y + mouse_diff_control_bar_pos_y_;
|
||||
|
||||
SDL_WarpMouseInWindow(stream_window_, (int)new_cursor_pos_x,
|
||||
(int)new_cursor_pos_y);
|
||||
}
|
||||
reset_control_bar_pos_ = false;
|
||||
} else if (!reset_control_bar_pos_ &&
|
||||
ImGui::IsMouseReleased(ImGuiPopupFlags_MouseButtonLeft) ||
|
||||
control_window_width_is_changing_) {
|
||||
if (control_winodw_pos_.x <= stream_window_width_ / 2) {
|
||||
float pos_x = 0;
|
||||
float pos_y =
|
||||
(control_winodw_pos_.y >=
|
||||
(fullscreen_button_pressed_ ? 0 : (title_bar_height_ + 1)) &&
|
||||
control_winodw_pos_.y <=
|
||||
stream_window_height_ - control_window_height_)
|
||||
? control_winodw_pos_.y
|
||||
: (control_winodw_pos_.y < (fullscreen_button_pressed_
|
||||
? 0
|
||||
: (title_bar_height_ + 1))
|
||||
? (fullscreen_button_pressed_ ? 0
|
||||
: (title_bar_height_ + 1))
|
||||
: (stream_window_height_ - control_window_height_));
|
||||
|
||||
if (control_bar_expand_) {
|
||||
if (control_window_width_ >= control_window_max_width_) {
|
||||
control_window_width_ = control_window_max_width_;
|
||||
control_window_width_is_changing_ = false;
|
||||
} else {
|
||||
control_window_width_is_changing_ = true;
|
||||
}
|
||||
} else {
|
||||
if (control_window_width_ <= control_window_min_width_) {
|
||||
control_window_width_ = control_window_min_width_;
|
||||
control_window_width_is_changing_ = false;
|
||||
} else {
|
||||
control_window_width_is_changing_ = true;
|
||||
}
|
||||
}
|
||||
ImGui::SetNextWindowPos(ImVec2(pos_x, pos_y), ImGuiCond_Always);
|
||||
is_control_bar_in_left_ = true;
|
||||
} else if (control_winodw_pos_.x > stream_window_width_ / 2) {
|
||||
float pos_x = 0;
|
||||
float pos_y =
|
||||
(control_winodw_pos_.y >=
|
||||
(fullscreen_button_pressed_ ? 0 : (title_bar_height_ + 1)) &&
|
||||
control_winodw_pos_.y <=
|
||||
stream_window_height_ - control_window_height_)
|
||||
? control_winodw_pos_.y
|
||||
: (control_winodw_pos_.y < (fullscreen_button_pressed_
|
||||
? 0
|
||||
: (title_bar_height_ + 1))
|
||||
? (fullscreen_button_pressed_ ? 0
|
||||
: (title_bar_height_ + 1))
|
||||
: (stream_window_height_ - control_window_height_));
|
||||
|
||||
if (control_bar_expand_) {
|
||||
if (control_window_width_ >= control_window_max_width_) {
|
||||
control_window_width_ = control_window_max_width_;
|
||||
control_window_width_is_changing_ = false;
|
||||
pos_x = stream_window_width_ - control_window_max_width_;
|
||||
} else {
|
||||
control_window_width_is_changing_ = true;
|
||||
pos_x = stream_window_width_ - control_window_width_;
|
||||
}
|
||||
} else {
|
||||
if (control_window_width_ <= control_window_min_width_) {
|
||||
control_window_width_ = control_window_min_width_;
|
||||
control_window_width_is_changing_ = false;
|
||||
pos_x = stream_window_width_ - control_window_min_width_;
|
||||
} else {
|
||||
control_window_width_is_changing_ = true;
|
||||
pos_x = stream_window_width_ - control_window_width_;
|
||||
}
|
||||
}
|
||||
ImGui::SetNextWindowPos(ImVec2(pos_x, pos_y), ImGuiCond_Always);
|
||||
is_control_bar_in_left_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (control_bar_expand_ && control_window_height_is_changing_) {
|
||||
if (net_traffic_stats_button_pressed_) {
|
||||
if (control_window_height_ >= control_window_max_height_) {
|
||||
control_window_height_ = control_window_max_height_;
|
||||
control_window_height_is_changing_ = false;
|
||||
} else {
|
||||
control_window_height_is_changing_ = true;
|
||||
}
|
||||
} else {
|
||||
if (control_window_height_ <= control_window_min_height_) {
|
||||
control_window_height_ = control_window_min_height_;
|
||||
control_window_height_is_changing_ = false;
|
||||
} else {
|
||||
control_window_height_is_changing_ = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::Begin("ControlWindow", nullptr,
|
||||
ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize |
|
||||
ImGuiWindowFlags_NoScrollbar);
|
||||
ImGui::PopStyleVar();
|
||||
|
||||
control_winodw_pos_ = ImGui::GetWindowPos();
|
||||
SDL_GetMouseState(&mouse_pos_x_, &mouse_pos_y_);
|
||||
mouse_diff_control_bar_pos_x_ = mouse_pos_x_ - control_winodw_pos_.x;
|
||||
mouse_diff_control_bar_pos_y_ = mouse_pos_y_ - control_winodw_pos_.y;
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
|
||||
static bool a, b, c, d, e;
|
||||
ImGui::SetNextWindowPos(
|
||||
ImVec2(is_control_bar_in_left_
|
||||
? control_winodw_pos_.x - control_window_width_
|
||||
: control_winodw_pos_.x,
|
||||
control_winodw_pos_.y),
|
||||
ImGuiCond_Always);
|
||||
ImGui::SetWindowFontScale(0.5f);
|
||||
|
||||
ImGui::BeginChild("ControlBar",
|
||||
ImVec2(control_window_width_ * 2, control_window_height_),
|
||||
ImGuiChildFlags_Border, ImGuiWindowFlags_NoDecoration);
|
||||
ImGui::SetWindowFontScale(1.0f);
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
ControlBar();
|
||||
control_bar_hovered_ = ImGui::IsWindowHovered();
|
||||
|
||||
ImGui::EndChild();
|
||||
ImGui::End();
|
||||
ImGui::PopStyleVar(4);
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
return 0;
|
||||
}
|
||||
39
src/single_window/layout_style.h
Normal file
39
src/single_window/layout_style.h
Normal file
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* @Author: DI JUNKUN
|
||||
* @Date: 2024-06-14
|
||||
* Copyright (c) 2024 by DI JUNKUN, All Rights Reserved.
|
||||
*/
|
||||
|
||||
#ifndef _LAYOUT_STYLE_H_
|
||||
#define _LAYOUT_STYLE_H_
|
||||
|
||||
#define MENU_WINDOW_WIDTH_CN 300
|
||||
#define MENU_WINDOW_HEIGHT_CN 280
|
||||
#define LOCAL_WINDOW_WIDTH_CN 300
|
||||
#define LOCAL_WINDOW_HEIGHT_CN 280
|
||||
#define REMOTE_WINDOW_WIDTH_CN 300
|
||||
#define REMOTE_WINDOW_HEIGHT_CN 280
|
||||
#define MENU_WINDOW_WIDTH_EN 190
|
||||
#define MENU_WINDOW_HEIGHT_EN 245
|
||||
#define IPUT_WINDOW_WIDTH 160
|
||||
#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 220
|
||||
#define SETTINGS_WINDOW_HEIGHT_EN 220
|
||||
#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 151
|
||||
#define ENABLE_HARDWARE_VIDEO_CODEC_CHECKBOX_PADDING_EN 198
|
||||
#define ENABLE_TURN_CHECKBOX_PADDING_CN 151
|
||||
#define ENABLE_TURN_CHECKBOX_PADDING_EN 198
|
||||
#define SETTINGS_SELECT_WINDOW_WIDTH 73
|
||||
#define SETTINGS_OK_BUTTON_PADDING_CN 55
|
||||
#define SETTINGS_OK_BUTTON_PADDING_EN 78
|
||||
|
||||
#endif
|
||||
317
src/single_window/local_peer_window.cpp
Normal file
317
src/single_window/local_peer_window.cpp
Normal file
@@ -0,0 +1,317 @@
|
||||
#include <random>
|
||||
|
||||
#include "layout_style.h"
|
||||
#include "localization.h"
|
||||
#include "rd_log.h"
|
||||
#include "render.h"
|
||||
|
||||
int Render::LocalWindow() {
|
||||
ImGui::SetNextWindowPos(ImVec2(-1.0f, title_bar_height_), ImGuiCond_Always);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 3.0f);
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
|
||||
ImGui::BeginChild("LocalDesktopWindow",
|
||||
ImVec2(local_window_width_, local_window_height_),
|
||||
ImGuiChildFlags_None,
|
||||
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse |
|
||||
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar |
|
||||
ImGuiWindowFlags_NoBringToFrontOnFocus);
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + main_window_text_y_padding_);
|
||||
ImGui::Indent(main_child_window_x_padding_);
|
||||
|
||||
ImGui::TextColored(
|
||||
ImVec4(0.0f, 0.0f, 0.0f, 0.5f), "%s",
|
||||
localization::local_desktop[localization_language_index_].c_str());
|
||||
|
||||
ImGui::Spacing();
|
||||
{
|
||||
ImGui::SetNextWindowPos(
|
||||
ImVec2(main_child_window_x_padding_,
|
||||
title_bar_height_ + main_child_window_y_padding_),
|
||||
ImGuiCond_Always);
|
||||
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(239.0f / 255, 240.0f / 255,
|
||||
242.0f / 255, 1.0f));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, 10.0f);
|
||||
ImGui::BeginChild(
|
||||
"LocalDesktopWindow_1",
|
||||
ImVec2(local_child_window_width_, local_child_window_height_),
|
||||
ImGuiChildFlags_Border,
|
||||
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse |
|
||||
ImGuiWindowFlags_NoTitleBar |
|
||||
ImGuiWindowFlags_NoBringToFrontOnFocus);
|
||||
ImGui::PopStyleVar();
|
||||
ImGui::PopStyleColor();
|
||||
{
|
||||
ImGui::SetWindowFontScale(0.8f);
|
||||
ImGui::Text("%s",
|
||||
localization::local_id[localization_language_index_].c_str());
|
||||
|
||||
ImGui::Spacing();
|
||||
|
||||
ImGui::SetNextItemWidth(IPUT_WINDOW_WIDTH);
|
||||
ImGui::SetWindowFontScale(1.0f);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f);
|
||||
|
||||
if (strcmp(client_id_display_, client_id_)) {
|
||||
for (int i = 0, j = 0; i < sizeof(client_id_); i++, j++) {
|
||||
client_id_display_[j] = client_id_[i];
|
||||
if (i == 2 || i == 5) {
|
||||
client_id_display_[++j] = ' ';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::InputText(
|
||||
"##local_id", client_id_display_, IM_ARRAYSIZE(client_id_display_),
|
||||
ImGuiInputTextFlags_CharsUppercase | ImGuiInputTextFlags_ReadOnly);
|
||||
ImGui::PopStyleVar();
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0, 0, 0, 0));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0, 0, 0, 0));
|
||||
ImGui::SetWindowFontScale(0.5f);
|
||||
if (ImGui::Button(ICON_FA_COPY, ImVec2(35, 38))) {
|
||||
local_id_copied_ = true;
|
||||
ImGui::SetClipboardText(client_id_);
|
||||
copy_start_time_ = ImGui::GetTime();
|
||||
}
|
||||
ImGui::SetWindowFontScale(1.0f);
|
||||
ImGui::PopStyleColor(3);
|
||||
|
||||
double time_duration = ImGui::GetTime() - copy_start_time_;
|
||||
if (local_id_copied_ && time_duration < 1.0f) {
|
||||
const ImGuiViewport *viewport = ImGui::GetMainViewport();
|
||||
ImGui::SetNextWindowPos(
|
||||
ImVec2((viewport->WorkSize.x - viewport->WorkPos.x -
|
||||
notification_window_width_) /
|
||||
2,
|
||||
(viewport->WorkSize.y - viewport->WorkPos.y -
|
||||
notification_window_height_) /
|
||||
2));
|
||||
|
||||
ImGui::SetNextWindowSize(
|
||||
ImVec2(notification_window_width_, notification_window_height_));
|
||||
ImGui::PushStyleColor(
|
||||
ImGuiCol_WindowBg,
|
||||
ImVec4(1.0f, 1.0f, 1.0f, 1.0f - (float)time_duration));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 5.0f);
|
||||
ImGui::Begin("ConnectionStatusWindow", nullptr,
|
||||
ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove |
|
||||
ImGuiWindowFlags_NoSavedSettings);
|
||||
ImGui::PopStyleVar(2);
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
auto window_width = ImGui::GetWindowSize().x;
|
||||
auto window_height = ImGui::GetWindowSize().y;
|
||||
ImGui::SetWindowFontScale(0.8f);
|
||||
std::string text = localization::local_id_copied_to_clipboard
|
||||
[localization_language_index_];
|
||||
auto text_width = ImGui::CalcTextSize(text.c_str()).x;
|
||||
ImGui::SetCursorPosX((window_width - text_width) * 0.5f);
|
||||
ImGui::SetCursorPosY(window_height * 0.5f);
|
||||
ImGui::PushStyleColor(ImGuiCol_Text,
|
||||
ImVec4(0, 0, 0, 1.0f - (float)time_duration));
|
||||
ImGui::Text("%s", text.c_str());
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::SetWindowFontScale(1.0f);
|
||||
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
ImGui::Spacing();
|
||||
ImGui::Separator();
|
||||
ImGui::Spacing();
|
||||
|
||||
ImGui::SetWindowFontScale(0.8f);
|
||||
ImGui::Text("%s",
|
||||
localization::password[localization_language_index_].c_str());
|
||||
ImGui::SetWindowFontScale(1.0f);
|
||||
|
||||
ImGui::SetNextItemWidth(IPUT_WINDOW_WIDTH);
|
||||
ImGui::Spacing();
|
||||
|
||||
if (!password_inited_) {
|
||||
char a[] = {
|
||||
"123456789QWERTYUPASDFGHJKLZXCVBNMqwertyupasdfghijkzxcvbnm"};
|
||||
std::mt19937 generator((unsigned int)std::chrono::system_clock::now()
|
||||
.time_since_epoch()
|
||||
.count());
|
||||
std::uniform_int_distribution<int> distribution(0,
|
||||
(int)(strlen(a) - 1));
|
||||
|
||||
random_password_.clear();
|
||||
for (int i = 0; i < 6; i++) {
|
||||
random_password_ += a[distribution(generator)];
|
||||
}
|
||||
password_inited_ = true;
|
||||
if (0 != strcmp(random_password_.c_str(), password_saved_)) {
|
||||
memcpy(password_saved_, random_password_.c_str(),
|
||||
sizeof(password_saved_));
|
||||
LOG_INFO("Generate new password and save into cache file");
|
||||
SaveSettingsIntoCacheFile();
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f);
|
||||
ImGui::InputTextWithHint(
|
||||
"##server_pwd",
|
||||
localization::max_password_len[localization_language_index_].c_str(),
|
||||
password_saved_, IM_ARRAYSIZE(password_saved_),
|
||||
show_password_
|
||||
? ImGuiInputTextFlags_CharsNoBlank | ImGuiInputTextFlags_ReadOnly
|
||||
: ImGuiInputTextFlags_CharsNoBlank |
|
||||
ImGuiInputTextFlags_Password |
|
||||
ImGuiInputTextFlags_ReadOnly);
|
||||
ImGui::PopStyleVar();
|
||||
|
||||
ImGui::SameLine();
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0, 0, 0, 0));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0, 0, 0, 0));
|
||||
ImGui::SetWindowFontScale(0.5f);
|
||||
auto l_x = ImGui::GetCursorScreenPos().x;
|
||||
auto l_y = ImGui::GetCursorScreenPos().y;
|
||||
if (ImGui::Button(ICON_FA_EYE, ImVec2(22, 38))) {
|
||||
show_password_ = !show_password_;
|
||||
}
|
||||
|
||||
if (!show_password_) {
|
||||
ImDrawList *draw_list = ImGui::GetWindowDrawList();
|
||||
draw_list->AddLine(ImVec2(l_x + 3.0f, l_y + 12.5f),
|
||||
ImVec2(l_x + 20.3f, l_y + 26.5f),
|
||||
IM_COL32(239, 240, 242, 255), 2.0f);
|
||||
draw_list->AddLine(ImVec2(l_x + 3.0f, l_y + 11.0f),
|
||||
ImVec2(l_x + 20.3f, l_y + 25.0f),
|
||||
IM_COL32(0, 0, 0, 255), 1.5f);
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
if (ImGui::Button(
|
||||
regenerate_password_ ? ICON_FA_SPINNER : ICON_FA_ARROWS_ROTATE,
|
||||
ImVec2(22, 38))) {
|
||||
regenerate_password_ = true;
|
||||
password_inited_ = false;
|
||||
regenerate_password_start_time_ = ImGui::GetTime();
|
||||
LeaveConnection(peer_, client_id_);
|
||||
is_create_connection_ = false;
|
||||
}
|
||||
if (ImGui::GetTime() - regenerate_password_start_time_ > 0.3f) {
|
||||
regenerate_password_ = false;
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
if (ImGui::Button(ICON_FA_PEN, ImVec2(22, 38))) {
|
||||
show_reset_password_window_ = true;
|
||||
}
|
||||
ImGui::SetWindowFontScale(1.0f);
|
||||
ImGui::PopStyleColor(3);
|
||||
|
||||
if (show_reset_password_window_) {
|
||||
const ImGuiViewport *viewport = ImGui::GetMainViewport();
|
||||
|
||||
ImGui::SetNextWindowPos(
|
||||
ImVec2((viewport->WorkSize.x - viewport->WorkPos.x -
|
||||
connection_status_window_width_) /
|
||||
2,
|
||||
(viewport->WorkSize.y - viewport->WorkPos.y -
|
||||
connection_status_window_height_) /
|
||||
2));
|
||||
|
||||
ImGui::SetNextWindowSize(ImVec2(connection_status_window_width_,
|
||||
connection_status_window_height_));
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 3.0f);
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(1.0, 1.0, 1.0, 1.0));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 1.0f);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 5.0f);
|
||||
|
||||
ImGui::Begin("ResetPasswordWindow", nullptr,
|
||||
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse |
|
||||
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar |
|
||||
ImGuiWindowFlags_NoSavedSettings);
|
||||
ImGui::PopStyleVar(2);
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
auto window_width = ImGui::GetWindowSize().x;
|
||||
auto window_height = ImGui::GetWindowSize().y;
|
||||
std::string text =
|
||||
localization::new_password[localization_language_index_];
|
||||
ImGui::SetWindowFontScale(0.5f);
|
||||
auto text_width = ImGui::CalcTextSize(text.c_str()).x;
|
||||
ImGui::SetCursorPosX((window_width - text_width) * 0.5f);
|
||||
ImGui::SetCursorPosY(window_height * 0.2f);
|
||||
ImGui::Text("%s", text.c_str());
|
||||
|
||||
ImGui::SetCursorPosX((window_width - IPUT_WINDOW_WIDTH / 2) * 0.5f);
|
||||
ImGui::SetCursorPosY(window_height * 0.4f);
|
||||
ImGui::SetNextItemWidth(IPUT_WINDOW_WIDTH / 2);
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f);
|
||||
|
||||
if (focus_on_input_widget_) {
|
||||
ImGui::SetKeyboardFocusHere();
|
||||
focus_on_input_widget_ = false;
|
||||
}
|
||||
|
||||
bool enter_pressed = ImGui::InputText(
|
||||
"##new_password", new_password_, IM_ARRAYSIZE(new_password_),
|
||||
ImGuiInputTextFlags_CharsNoBlank |
|
||||
ImGuiInputTextFlags_EnterReturnsTrue);
|
||||
|
||||
ImGui::PopStyleVar();
|
||||
|
||||
ImGui::SetCursorPosX(window_width * 0.315f);
|
||||
ImGui::SetCursorPosY(window_height * 0.75f);
|
||||
|
||||
// OK
|
||||
if (ImGui::Button(
|
||||
localization::ok[localization_language_index_].c_str()) ||
|
||||
enter_pressed) {
|
||||
if (6 != strlen(new_password_)) {
|
||||
LOG_ERROR("Invalid password length");
|
||||
show_reset_password_window_ = true;
|
||||
focus_on_input_widget_ = true;
|
||||
} else {
|
||||
show_reset_password_window_ = false;
|
||||
LOG_INFO("Generate new password and save into cache file");
|
||||
memcpy(password_saved_, new_password_, sizeof(password_saved_));
|
||||
memset(new_password_, 0, sizeof(new_password_));
|
||||
SaveSettingsIntoCacheFile();
|
||||
LeaveConnection(peer_, client_id_);
|
||||
is_create_connection_ = false;
|
||||
focus_on_input_widget_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
if (ImGui::Button(
|
||||
localization::cancel[localization_language_index_].c_str())) {
|
||||
show_reset_password_window_ = false;
|
||||
focus_on_input_widget_ = true;
|
||||
memset(new_password_, 0, sizeof(new_password_));
|
||||
}
|
||||
|
||||
ImGui::SetWindowFontScale(1.0f);
|
||||
|
||||
ImGui::End();
|
||||
ImGui::PopStyleVar();
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndChild();
|
||||
}
|
||||
|
||||
ImGui::EndChild();
|
||||
ImGui::PopStyleVar();
|
||||
|
||||
return 0;
|
||||
}
|
||||
34
src/single_window/main_window.cpp
Normal file
34
src/single_window/main_window.cpp
Normal file
@@ -0,0 +1,34 @@
|
||||
#include "localization.h"
|
||||
#include "rd_log.h"
|
||||
#include "render.h"
|
||||
|
||||
int Render::MainWindow() {
|
||||
ImGui::SetNextWindowPos(ImVec2(0, title_bar_height_), ImGuiCond_Always);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f));
|
||||
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
|
||||
ImGui::BeginChild("DeskWindow",
|
||||
ImVec2(main_window_width_default_, local_window_height_),
|
||||
ImGuiChildFlags_Border,
|
||||
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse |
|
||||
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar |
|
||||
ImGuiWindowFlags_NoBringToFrontOnFocus);
|
||||
ImGui::PopStyleVar();
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
LocalWindow();
|
||||
|
||||
ImDrawList* draw_list = ImGui::GetWindowDrawList();
|
||||
draw_list->AddLine(
|
||||
ImVec2(main_window_width_default_ / 2, title_bar_height_ + 15.0f),
|
||||
ImVec2(main_window_width_default_ / 2, title_bar_height_ + 225.0f),
|
||||
IM_COL32(0, 0, 0, 122), 1.0f);
|
||||
|
||||
RemoteWindow();
|
||||
ImGui::EndChild();
|
||||
|
||||
RecentConnectionsWindow();
|
||||
StatusBar();
|
||||
ConnectionStatusWindow();
|
||||
|
||||
return 0;
|
||||
}
|
||||
273
src/single_window/recent_connections.cpp
Normal file
273
src/single_window/recent_connections.cpp
Normal file
@@ -0,0 +1,273 @@
|
||||
#include "localization.h"
|
||||
#include "rd_log.h"
|
||||
#include "render.h"
|
||||
|
||||
int Render::RecentConnectionsWindow() {
|
||||
ImGui::SetNextWindowPos(
|
||||
ImVec2(0, title_bar_height_ + local_window_height_ - 1.0f),
|
||||
ImGuiCond_Always);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f));
|
||||
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
|
||||
ImGui::BeginChild(
|
||||
"RecentConnectionsWindow",
|
||||
ImVec2(main_window_width_default_,
|
||||
main_window_height_default_ - title_bar_height_ -
|
||||
local_window_height_ - status_bar_height_ + 1.0f),
|
||||
ImGuiChildFlags_Border,
|
||||
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse |
|
||||
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar |
|
||||
ImGuiWindowFlags_NoBringToFrontOnFocus);
|
||||
ImGui::PopStyleVar();
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + main_window_text_y_padding_);
|
||||
ImGui::Indent(main_child_window_x_padding_);
|
||||
|
||||
ImGui::TextColored(
|
||||
ImVec4(0.0f, 0.0f, 0.0f, 0.5f), "%s",
|
||||
localization::recent_connections[localization_language_index_].c_str());
|
||||
|
||||
ShowRecentConnections();
|
||||
|
||||
ImGui::EndChild();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Render::ShowRecentConnections() {
|
||||
ImGui::SetCursorPosX(25.0f);
|
||||
ImVec2 sub_window_pos = ImGui::GetCursorPos();
|
||||
std::map<std::string, ImVec2> sub_containers_pos;
|
||||
float recent_connection_sub_container_width =
|
||||
recent_connection_image_width_ + 16.0f;
|
||||
float recent_connection_sub_container_height =
|
||||
recent_connection_image_height_ + 36.0f;
|
||||
ImGui::PushStyleColor(ImGuiCol_ChildBg,
|
||||
ImVec4(239.0f / 255, 240.0f / 255, 242.0f / 255, 1.0f));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, 10.0f);
|
||||
ImGui::BeginChild("RecentConnectionsContainer",
|
||||
ImVec2(main_window_width_default_ - 50.0f, 145.0f),
|
||||
ImGuiChildFlags_Border,
|
||||
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse |
|
||||
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar |
|
||||
ImGuiWindowFlags_NoBringToFrontOnFocus |
|
||||
ImGuiWindowFlags_AlwaysHorizontalScrollbar |
|
||||
ImGuiWindowFlags_NoScrollbar |
|
||||
ImGuiWindowFlags_NoScrollWithMouse);
|
||||
ImGui::PopStyleVar();
|
||||
ImGui::PopStyleColor();
|
||||
size_t recent_connections_count = recent_connection_textures_.size();
|
||||
int count = 0;
|
||||
float button_width = 22;
|
||||
float button_height = 22;
|
||||
for (auto it = recent_connection_textures_.begin();
|
||||
it != recent_connection_textures_.end(); ++it) {
|
||||
sub_containers_pos[it->first] = ImGui::GetCursorPos();
|
||||
std::string recent_connection_sub_window_name =
|
||||
"RecentConnectionsSubContainer" + it->first;
|
||||
// recent connections sub container
|
||||
|
||||
ImGui::BeginChild(recent_connection_sub_window_name.c_str(),
|
||||
ImVec2(recent_connection_sub_container_width,
|
||||
recent_connection_sub_container_height),
|
||||
ImGuiChildFlags_None,
|
||||
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse |
|
||||
ImGuiWindowFlags_NoMove |
|
||||
ImGuiWindowFlags_NoTitleBar |
|
||||
ImGuiWindowFlags_NoBringToFrontOnFocus |
|
||||
ImGuiWindowFlags_NoScrollbar);
|
||||
std::string connection_info = it->first;
|
||||
std::string remote_id;
|
||||
std::string password;
|
||||
std::string host_name;
|
||||
|
||||
// remote id length is 9
|
||||
// password length is 6
|
||||
// connection_info -> remote_id + 'Y' + password + host_name
|
||||
// -> remote_id + 'N' + host_name
|
||||
if ('Y' == connection_info[9] && connection_info.size() >= 16) {
|
||||
remote_id = connection_info.substr(0, 9);
|
||||
password = connection_info.substr(10, 6);
|
||||
host_name = connection_info.substr(16);
|
||||
} else if ('N' == connection_info[9] && connection_info.size() >= 10) {
|
||||
remote_id = connection_info.substr(0, 9);
|
||||
host_name = connection_info.substr(10);
|
||||
} else {
|
||||
host_name = "unknown";
|
||||
}
|
||||
|
||||
ImVec2 image_screen_pos = ImVec2(ImGui::GetCursorScreenPos().x + 5.0f,
|
||||
ImGui::GetCursorScreenPos().y + 5.0f);
|
||||
ImVec2 image_pos =
|
||||
ImVec2(ImGui::GetCursorPosX() + 5.0f, ImGui::GetCursorPosY() + 5.0f);
|
||||
ImGui::SetCursorPos(image_pos);
|
||||
ImGui::Image((ImTextureID)(intptr_t)it->second,
|
||||
ImVec2((float)recent_connection_image_width_,
|
||||
(float)recent_connection_image_height_));
|
||||
|
||||
// remote id display button
|
||||
{
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0.2f));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0, 0, 0, 0.2f));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0, 0, 0, 0.2f));
|
||||
|
||||
ImVec2 dummy_button_pos =
|
||||
ImVec2(image_pos.x, image_pos.y + recent_connection_image_height_);
|
||||
std::string dummy_button_name = "##DummyButton" + remote_id;
|
||||
ImGui::SetCursorPos(dummy_button_pos);
|
||||
ImGui::SetWindowFontScale(0.6f);
|
||||
ImGui::Button(dummy_button_name.c_str(),
|
||||
ImVec2(recent_connection_image_width_ - 2 * button_width,
|
||||
button_height));
|
||||
ImGui::SetWindowFontScale(1.0f);
|
||||
ImGui::SetCursorPos(
|
||||
ImVec2(dummy_button_pos.x + 2.0f, dummy_button_pos.y + 1.0f));
|
||||
ImGui::SetWindowFontScale(0.65f);
|
||||
ImGui::Text("%s", remote_id.c_str());
|
||||
ImGui::SetWindowFontScale(1.0f);
|
||||
ImGui::PopStyleColor(3);
|
||||
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::BeginTooltip();
|
||||
ImGui::SetWindowFontScale(0.5f);
|
||||
ImGui::Text("%s", host_name.c_str());
|
||||
ImGui::SetWindowFontScale(1.0f);
|
||||
ImGui::EndTooltip();
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0.2f));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered,
|
||||
ImVec4(0.1f, 0.4f, 0.8f, 1.0f));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive,
|
||||
ImVec4(1.0f, 1.0f, 1.0f, 0.7f));
|
||||
ImGui::SetWindowFontScale(0.5f);
|
||||
// trash button
|
||||
{
|
||||
ImVec2 trash_can_button_pos = ImVec2(
|
||||
image_pos.x + recent_connection_image_width_ - 2 * button_width,
|
||||
image_pos.y + recent_connection_image_height_);
|
||||
ImGui::SetCursorPos(trash_can_button_pos);
|
||||
std::string trash_can = ICON_FA_TRASH_CAN;
|
||||
std::string recent_connection_delete_button_name =
|
||||
trash_can + "##RecentConnectionDelete" +
|
||||
std::to_string(trash_can_button_pos.x);
|
||||
if (ImGui::Button(recent_connection_delete_button_name.c_str(),
|
||||
ImVec2(button_width, button_height))) {
|
||||
show_confirm_delete_connection_ = true;
|
||||
}
|
||||
|
||||
if (delete_connection_) {
|
||||
if (!thumbnail_->DeleteThumbnail(it->first)) {
|
||||
reload_recent_connections_ = true;
|
||||
delete_connection_ = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// connect button
|
||||
{
|
||||
ImVec2 connect_button_pos =
|
||||
ImVec2(image_pos.x + recent_connection_image_width_ - button_width,
|
||||
image_pos.y + recent_connection_image_height_);
|
||||
ImGui::SetCursorPos(connect_button_pos);
|
||||
std::string connect = ICON_FA_ARROW_RIGHT_LONG;
|
||||
std::string connect_to_this_connection_button_name =
|
||||
connect + "##ConnectionTo" + it->first;
|
||||
if (ImGui::Button(connect_to_this_connection_button_name.c_str(),
|
||||
ImVec2(button_width, button_height))) {
|
||||
remote_id_ = remote_id;
|
||||
if (!password.empty() && password.size() == 6) {
|
||||
remember_password_ = true;
|
||||
}
|
||||
memcpy(remote_password_, password.c_str(), 6);
|
||||
ConnectTo();
|
||||
}
|
||||
}
|
||||
ImGui::SetWindowFontScale(1.0f);
|
||||
ImGui::PopStyleColor(3);
|
||||
|
||||
ImGui::EndChild();
|
||||
|
||||
if (count != recent_connections_count - 1) {
|
||||
ImVec2 line_start =
|
||||
ImVec2(image_screen_pos.x + recent_connection_image_width_ + 20.0f,
|
||||
image_screen_pos.y);
|
||||
ImVec2 line_end = ImVec2(
|
||||
image_screen_pos.x + recent_connection_image_width_ + 20.0f,
|
||||
image_screen_pos.y + recent_connection_image_height_ + button_height);
|
||||
ImGui::GetForegroundDrawList()->AddLine(line_start, line_end,
|
||||
IM_COL32(0, 0, 0, 122), 1.0f);
|
||||
}
|
||||
|
||||
count++;
|
||||
ImGui::SameLine(0, count != recent_connections_count ? 26.0f : 0.0f);
|
||||
}
|
||||
|
||||
ImGui::EndChild();
|
||||
|
||||
if (show_confirm_delete_connection_) {
|
||||
ConfirmDeleteConnection();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Render::ConfirmDeleteConnection() {
|
||||
const ImGuiViewport* viewport = ImGui::GetMainViewport();
|
||||
ImGui::SetNextWindowPos(ImVec2((viewport->WorkSize.x - viewport->WorkPos.x -
|
||||
connection_status_window_width_) /
|
||||
2,
|
||||
(viewport->WorkSize.y - viewport->WorkPos.y -
|
||||
connection_status_window_height_) /
|
||||
2));
|
||||
|
||||
ImGui::SetNextWindowSize(ImVec2(connection_status_window_width_,
|
||||
connection_status_window_height_));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 3.0f);
|
||||
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(1.0, 1.0, 1.0, 1.0));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 1.0f);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 5.0f);
|
||||
|
||||
ImGui::Begin("ConfirmDeleteConnectionWindow", nullptr,
|
||||
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse |
|
||||
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar |
|
||||
ImGuiWindowFlags_NoSavedSettings);
|
||||
ImGui::PopStyleVar(2);
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
std::string text =
|
||||
localization::confirm_delete_connection[localization_language_index_];
|
||||
ImGui::SetCursorPosX(connection_status_window_width_ * 6 / 19);
|
||||
ImGui::SetCursorPosY(connection_status_window_height_ * 2 / 3);
|
||||
|
||||
// ok
|
||||
ImGui::SetWindowFontScale(0.5f);
|
||||
if (ImGui::Button(localization::ok[localization_language_index_].c_str()) ||
|
||||
ImGui::IsKeyPressed(ImGuiKey_Enter)) {
|
||||
delete_connection_ = true;
|
||||
show_confirm_delete_connection_ = false;
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
// cancel
|
||||
if (ImGui::Button(
|
||||
localization::cancel[localization_language_index_].c_str()) ||
|
||||
ImGui::IsKeyPressed(ImGuiKey_Escape)) {
|
||||
delete_connection_ = false;
|
||||
show_confirm_delete_connection_ = false;
|
||||
}
|
||||
|
||||
auto window_width = ImGui::GetWindowSize().x;
|
||||
auto window_height = ImGui::GetWindowSize().y;
|
||||
|
||||
auto text_width = ImGui::CalcTextSize(text.c_str()).x;
|
||||
ImGui::SetCursorPosX((window_width - text_width) * 0.5f);
|
||||
ImGui::SetCursorPosY(window_height * 0.2f);
|
||||
ImGui::Text("%s", text.c_str());
|
||||
ImGui::SetWindowFontScale(1.0f);
|
||||
|
||||
ImGui::End();
|
||||
ImGui::PopStyleVar();
|
||||
return 0;
|
||||
}
|
||||
135
src/single_window/remote_peer_window.cpp
Normal file
135
src/single_window/remote_peer_window.cpp
Normal file
@@ -0,0 +1,135 @@
|
||||
#include "layout_style.h"
|
||||
#include "localization.h"
|
||||
#include "rd_log.h"
|
||||
#include "render.h"
|
||||
|
||||
static int InputTextCallback(ImGuiInputTextCallbackData *data);
|
||||
|
||||
int Render::RemoteWindow() {
|
||||
ImGui::SetNextWindowPos(ImVec2(local_window_width_ + 1.0f, title_bar_height_),
|
||||
ImGuiCond_Always);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 3.0f);
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
|
||||
ImGui::BeginChild("RemoteDesktopWindow",
|
||||
ImVec2(remote_window_width_, remote_window_height_),
|
||||
ImGuiChildFlags_None,
|
||||
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse |
|
||||
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar |
|
||||
ImGuiWindowFlags_NoBringToFrontOnFocus);
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + main_window_text_y_padding_);
|
||||
ImGui::Indent(main_child_window_x_padding_ - 1.0f);
|
||||
|
||||
ImGui::TextColored(
|
||||
ImVec4(0.0f, 0.0f, 0.0f, 0.5f), "%s",
|
||||
localization::remote_desktop[localization_language_index_].c_str());
|
||||
|
||||
ImGui::Spacing();
|
||||
{
|
||||
ImGui::SetNextWindowPos(
|
||||
ImVec2(local_window_width_ + main_child_window_x_padding_ - 1.0f,
|
||||
title_bar_height_ + main_child_window_y_padding_),
|
||||
ImGuiCond_Always);
|
||||
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(239.0f / 255, 240.0f / 255,
|
||||
242.0f / 255, 1.0f));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, 10.0f);
|
||||
|
||||
ImGui::BeginChild(
|
||||
"RemoteDesktopWindow_1",
|
||||
ImVec2(remote_child_window_width_, remote_child_window_height_),
|
||||
ImGuiChildFlags_Border,
|
||||
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse |
|
||||
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar |
|
||||
ImGuiWindowFlags_NoBringToFrontOnFocus);
|
||||
ImGui::PopStyleVar();
|
||||
ImGui::PopStyleColor();
|
||||
{
|
||||
ImGui::SetWindowFontScale(0.8f);
|
||||
ImGui::Text(
|
||||
"%s", localization::remote_id[localization_language_index_].c_str());
|
||||
|
||||
ImGui::Spacing();
|
||||
ImGui::SetNextItemWidth(IPUT_WINDOW_WIDTH);
|
||||
ImGui::SetWindowFontScale(1.0f);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f);
|
||||
if (re_enter_remote_id_) {
|
||||
ImGui::SetKeyboardFocusHere();
|
||||
re_enter_remote_id_ = false;
|
||||
memset(remote_id_display_, 0, sizeof(remote_id_display_));
|
||||
}
|
||||
bool enter_pressed = ImGui::InputText(
|
||||
"##remote_id_", remote_id_display_, IM_ARRAYSIZE(remote_id_display_),
|
||||
ImGuiInputTextFlags_CharsDecimal |
|
||||
ImGuiInputTextFlags_EnterReturnsTrue |
|
||||
ImGuiInputTextFlags_CallbackEdit,
|
||||
InputTextCallback);
|
||||
|
||||
ImGui::PopStyleVar();
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button(ICON_FA_ARROW_RIGHT_LONG, ImVec2(55, 38)) ||
|
||||
enter_pressed) {
|
||||
connect_button_pressed_ = true;
|
||||
remote_id_ = remote_id_display_;
|
||||
remote_id_.erase(remove_if(remote_id_.begin(), remote_id_.end(),
|
||||
static_cast<int (*)(int)>(&isspace)),
|
||||
remote_id_.end());
|
||||
ConnectTo();
|
||||
}
|
||||
|
||||
if (rejoin_) {
|
||||
ConnectTo();
|
||||
}
|
||||
}
|
||||
ImGui::EndChild();
|
||||
}
|
||||
ImGui::EndChild();
|
||||
ImGui::PopStyleVar();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int InputTextCallback(ImGuiInputTextCallbackData *data) {
|
||||
if (data->BufTextLen > 3 && data->Buf[3] != ' ') {
|
||||
data->InsertChars(3, " ");
|
||||
}
|
||||
|
||||
if (data->BufTextLen > 7 && data->Buf[7] != ' ') {
|
||||
data->InsertChars(7, " ");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Render::ConnectTo() {
|
||||
connection_status_ = ConnectionStatus::Connecting;
|
||||
int ret = -1;
|
||||
if (signal_connected_) {
|
||||
if (!connection_established_) {
|
||||
if (0 == strcmp(remote_id_.c_str(), client_id_) && !peer_reserved_) {
|
||||
peer_reserved_ = CreatePeer(¶ms_);
|
||||
if (peer_reserved_) {
|
||||
LOG_INFO("Create peer[reserved] instance successful");
|
||||
std::string client_id = "C-";
|
||||
client_id += client_id_;
|
||||
Init(peer_reserved_, client_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_.c_str(), remote_password_);
|
||||
if (0 == ret) {
|
||||
is_client_mode_ = true;
|
||||
rejoin_ = false;
|
||||
} else {
|
||||
rejoin_ = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
1097
src/single_window/render.cpp
Normal file
1097
src/single_window/render.cpp
Normal file
File diff suppressed because it is too large
Load Diff
387
src/single_window/render.h
Normal file
387
src/single_window/render.h
Normal file
@@ -0,0 +1,387 @@
|
||||
/*
|
||||
* @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 <fstream>
|
||||
#include <string>
|
||||
|
||||
#include "../../thirdparty/projectx/src/interface/x.h"
|
||||
#include "IconsFontAwesome6.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"
|
||||
#include "speaker_capturer_factory.h"
|
||||
#include "thumbnail.h"
|
||||
|
||||
class Render {
|
||||
public:
|
||||
Render();
|
||||
~Render();
|
||||
|
||||
public:
|
||||
int Run();
|
||||
|
||||
private:
|
||||
int CreateStreamRenderWindow();
|
||||
int TitleBar(bool main_window);
|
||||
int MainWindow();
|
||||
int StreamWindow();
|
||||
int LocalWindow();
|
||||
int RemoteWindow();
|
||||
int RecentConnectionsWindow();
|
||||
int SettingWindow();
|
||||
int ControlWindow();
|
||||
int ControlBar();
|
||||
int AboutWindow();
|
||||
int StatusBar();
|
||||
int ConnectionStatusWindow();
|
||||
int LoadRecentConnections();
|
||||
int ShowRecentConnections();
|
||||
|
||||
private:
|
||||
int CreateRtcConnection();
|
||||
int ConnectTo();
|
||||
int CreateMainWindow();
|
||||
int DestroyMainWindow();
|
||||
int CreateStreamWindow();
|
||||
int DestroyStreamWindow();
|
||||
int SetupFontAndStyle();
|
||||
int SetupMainWindow();
|
||||
int DestroyMainWindowContext();
|
||||
int SetupStreamWindow();
|
||||
int DestroyStreamWindowContext();
|
||||
int DrawMainWindow();
|
||||
int DrawStreamWindow();
|
||||
int ConfirmDeleteConnection();
|
||||
int NetTrafficStats();
|
||||
|
||||
public:
|
||||
static void OnReceiveVideoBufferCb(const XVideoFrame *video_frame,
|
||||
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, const char *user_id,
|
||||
size_t user_id_size, void *user_data);
|
||||
|
||||
static void NetStatusReport(const char *client_id, size_t client_id_size,
|
||||
TraversalMode mode,
|
||||
const XNetTrafficStats *net_traffic_stats,
|
||||
void *user_data);
|
||||
|
||||
static SDL_HitTestResult HitTestCallback(SDL_Window *window,
|
||||
const SDL_Point *area, void *data);
|
||||
|
||||
private:
|
||||
int ProcessMouseKeyEvent(SDL_Event &event);
|
||||
int ProcessMouseEvent(SDL_Event &event);
|
||||
|
||||
int SendKeyEvent(int key_code, bool is_down);
|
||||
int ProcessKeyEvent(int key_code, bool is_down);
|
||||
|
||||
static void SdlCaptureAudioIn(void *userdata, Uint8 *stream, int len);
|
||||
static void SdlCaptureAudioOut(void *userdata, Uint8 *stream, int len);
|
||||
|
||||
private:
|
||||
int SaveSettingsIntoCacheFile();
|
||||
int LoadSettingsFromCacheFile();
|
||||
|
||||
int StartScreenCapturer();
|
||||
int StopScreenCapturer();
|
||||
|
||||
int StartSpeakerCapturer();
|
||||
int StopSpeakerCapturer();
|
||||
|
||||
int StartMouseController();
|
||||
int StopMouseController();
|
||||
|
||||
int StartKeyboardCapturer();
|
||||
int StopKeyboardCapturer();
|
||||
|
||||
int CreateConnectionPeer();
|
||||
|
||||
int AudioDeviceInit();
|
||||
int AudioDeviceDestroy();
|
||||
|
||||
private:
|
||||
typedef struct {
|
||||
char client_id[10];
|
||||
char password[7];
|
||||
int language;
|
||||
int video_quality;
|
||||
int video_encode_format;
|
||||
bool enable_hardware_video_codec;
|
||||
bool enable_turn;
|
||||
|
||||
unsigned char key[16];
|
||||
unsigned char iv[16];
|
||||
} CDCache;
|
||||
|
||||
private:
|
||||
CDCache cd_cache_;
|
||||
std::mutex cd_cache_mutex_;
|
||||
|
||||
ConfigCenter config_center_;
|
||||
ConfigCenter::LANGUAGE localization_language_ =
|
||||
ConfigCenter::LANGUAGE::CHINESE;
|
||||
|
||||
int localization_language_index_ = -1;
|
||||
int localization_language_index_last_ = -1;
|
||||
|
||||
bool modules_inited_ = false;
|
||||
|
||||
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 net_traffic_stats_button_label_ = "Show Net Traffic Stats";
|
||||
std::string mouse_control_button_label_ = "Mouse Control";
|
||||
std::string audio_capture_button_label_ = "Audio Capture";
|
||||
std::string settings_button_label_ = "Setting";
|
||||
char input_password_tmp_[7] = "";
|
||||
char input_password_[7] = "";
|
||||
std::string random_password_ = "";
|
||||
char remote_password_[7] = "";
|
||||
char new_password_[7] = "";
|
||||
char remote_id_display_[12] = "";
|
||||
std::string remote_id_ = "";
|
||||
char client_password_[20] = "";
|
||||
|
||||
private:
|
||||
float title_bar_width_ = 640;
|
||||
float title_bar_height_ = 30;
|
||||
int screen_width_ = 1280;
|
||||
int screen_height_ = 720;
|
||||
float main_window_width_default_ = 640;
|
||||
float main_window_height_default_ = 480;
|
||||
float main_window_width_ = 640;
|
||||
float main_window_height_ = 480;
|
||||
float main_window_width_last_ = 640;
|
||||
float main_window_height_last_ = 480;
|
||||
int stream_window_width_default_ = 1280;
|
||||
int stream_window_height_default_ = 720;
|
||||
float stream_window_width_ = 1280;
|
||||
float stream_window_height_ = 720;
|
||||
int stream_window_width_last_ = 1280;
|
||||
int stream_window_height_last_ = 720;
|
||||
float stream_window_width_before_maximized_ = 1280;
|
||||
float stream_window_height_before_maximized_ = 720;
|
||||
float control_window_min_width_ = 20;
|
||||
float control_window_max_width_ = 200;
|
||||
float control_window_min_height_ = 40;
|
||||
float control_window_max_height_ = 150;
|
||||
float control_window_width_ = 200;
|
||||
float control_window_height_ = 40;
|
||||
float local_window_width_ = 320;
|
||||
float local_window_height_ = 235;
|
||||
float remote_window_width_ = 320;
|
||||
float remote_window_height_ = 235;
|
||||
float local_child_window_width_ = 266;
|
||||
float local_child_window_height_ = 180;
|
||||
float remote_child_window_width_ = 266;
|
||||
float remote_child_window_height_ = 180;
|
||||
float main_window_text_y_padding_ = 10;
|
||||
float main_child_window_x_padding_ = 27;
|
||||
float main_child_window_y_padding_ = 45;
|
||||
float status_bar_height_ = 22;
|
||||
float connection_status_window_width_ = 200;
|
||||
float connection_status_window_height_ = 150;
|
||||
float notification_window_width_ = 200;
|
||||
float notification_window_height_ = 80;
|
||||
float about_window_width_ = 200;
|
||||
float about_window_height_ = 150;
|
||||
|
||||
float control_bar_pos_x_ = 0;
|
||||
float control_bar_pos_y_ = 30;
|
||||
float mouse_diff_control_bar_pos_x_ = 0;
|
||||
float mouse_diff_control_bar_pos_y_ = 0;
|
||||
int mouse_pos_x_ = 0;
|
||||
int mouse_pos_y_ = 0;
|
||||
int mouse_pos_x_last_ = 0;
|
||||
int mouse_pos_y_last_ = 0;
|
||||
|
||||
int main_window_width_real_ = 720;
|
||||
int main_window_height_real_ = 540;
|
||||
float main_window_dpi_scaling_w_ = 1.0f;
|
||||
float main_window_dpi_scaling_h_ = 1.0f;
|
||||
|
||||
int stream_window_width_real_ = 1280;
|
||||
int stream_window_height_real_ = 720;
|
||||
float stream_window_dpi_scaling_w_ = 1.0f;
|
||||
float stream_window_dpi_scaling_h_ = 1.0f;
|
||||
|
||||
int texture_width_ = 1280;
|
||||
int texture_height_ = 720;
|
||||
|
||||
int video_width_ = 1280;
|
||||
int video_height_ = 720;
|
||||
size_t video_size_ = 1280 * 720 * 3;
|
||||
|
||||
SDL_Window *main_window_ = nullptr;
|
||||
SDL_Renderer *main_renderer_ = nullptr;
|
||||
ImGuiContext *main_ctx_ = nullptr;
|
||||
|
||||
SDL_Window *stream_window_ = nullptr;
|
||||
SDL_Renderer *stream_renderer_ = nullptr;
|
||||
ImGuiContext *stream_ctx_ = nullptr;
|
||||
bool stream_window_created_ = false;
|
||||
bool stream_window_inited_ = false;
|
||||
|
||||
// recent connections
|
||||
std::map<std::string, SDL_Texture *> recent_connection_textures_;
|
||||
int recent_connection_image_width_ = 160;
|
||||
int recent_connection_image_height_ = 90;
|
||||
uint32_t recent_connection_image_save_time_ = 0;
|
||||
|
||||
// video window
|
||||
SDL_Texture *stream_texture_ = nullptr;
|
||||
SDL_Rect stream_render_rect_;
|
||||
SDL_Rect stream_render_rect_last_;
|
||||
uint32_t stream_pixformat_ = 0;
|
||||
std::string host_name_ = "";
|
||||
|
||||
unsigned char aes128_key_[16];
|
||||
unsigned char aes128_iv_[16];
|
||||
std::unique_ptr<Thumbnail> thumbnail_;
|
||||
|
||||
bool resizable_ = false;
|
||||
bool label_inited_ = false;
|
||||
bool exit_ = false;
|
||||
bool exit_video_window_ = false;
|
||||
bool connection_established_ = false;
|
||||
bool connect_button_pressed_ = false;
|
||||
bool password_validating_ = false;
|
||||
uint32_t password_validating_time_ = 0;
|
||||
bool fullscreen_button_pressed_ = false;
|
||||
bool net_traffic_stats_button_pressed_ = false;
|
||||
bool mouse_control_button_pressed_ = false;
|
||||
bool audio_capture_button_pressed_ = false;
|
||||
bool show_settings_window_ = false;
|
||||
bool is_create_connection_ = false;
|
||||
bool audio_buffer_fresh_ = false;
|
||||
bool rejoin_ = false;
|
||||
bool control_mouse_ = false;
|
||||
bool stream_window_grabbed_ = false;
|
||||
bool audio_capture_ = true;
|
||||
bool local_id_copied_ = false;
|
||||
bool show_password_ = true;
|
||||
bool password_inited_ = false;
|
||||
bool regenerate_password_ = false;
|
||||
bool show_about_window_ = false;
|
||||
bool show_connection_status_window_ = false;
|
||||
bool show_reset_password_window_ = false;
|
||||
bool focus_on_input_widget_ = true;
|
||||
bool window_maximized_ = false;
|
||||
bool streaming_ = false;
|
||||
bool is_client_mode_ = false;
|
||||
bool is_control_bar_in_left_ = true;
|
||||
bool is_control_bar_in_top_ = true;
|
||||
bool control_bar_hovered_ = false;
|
||||
bool control_bar_expand_ = true;
|
||||
bool reset_control_bar_pos_ = false;
|
||||
bool control_window_width_is_changing_ = false;
|
||||
bool control_window_height_is_changing_ = false;
|
||||
bool reload_recent_connections_ = true;
|
||||
bool hostname_sent_ = false;
|
||||
bool show_confirm_delete_connection_ = false;
|
||||
bool delete_connection_ = false;
|
||||
bool remember_password_ = false;
|
||||
bool re_enter_remote_id_ = false;
|
||||
|
||||
double copy_start_time_ = 0;
|
||||
double regenerate_password_start_time_ = 0;
|
||||
double control_bar_button_pressed_time_ = 0;
|
||||
double net_traffic_stats_button_pressed_time_ = 0;
|
||||
|
||||
ImVec2 control_winodw_pos_;
|
||||
|
||||
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_ = "";
|
||||
bool signal_connected_ = false;
|
||||
bool p2p_mode_ = true;
|
||||
|
||||
private:
|
||||
PeerPtr *peer_ = nullptr;
|
||||
PeerPtr *peer_reserved_ = nullptr;
|
||||
Params params_;
|
||||
TraversalMode traversal_mode_ = TraversalMode::UnknownMode;
|
||||
XNetTrafficStats net_traffic_stats_;
|
||||
|
||||
private:
|
||||
SDL_AudioDeviceID input_dev_;
|
||||
SDL_AudioDeviceID output_dev_;
|
||||
unsigned char audio_buffer_[720];
|
||||
int audio_len_ = 0;
|
||||
unsigned char *dst_buffer_ = nullptr;
|
||||
size_t dst_buffer_capacity_ = 0;
|
||||
|
||||
private:
|
||||
ScreenCapturerFactory *screen_capturer_factory_ = nullptr;
|
||||
ScreenCapturer *screen_capturer_ = nullptr;
|
||||
SpeakerCapturerFactory *speaker_capturer_factory_ = nullptr;
|
||||
SpeakerCapturer *speaker_capturer_ = nullptr;
|
||||
DeviceControllerFactory *device_controller_factory_ = nullptr;
|
||||
MouseController *mouse_controller_ = nullptr;
|
||||
KeyboardCapturer *keyboard_capturer_ = nullptr;
|
||||
uint64_t last_frame_time_;
|
||||
|
||||
private:
|
||||
char client_id_[10] = "";
|
||||
char client_id_display_[12] = "";
|
||||
char password_saved_[7] = "";
|
||||
int language_button_value_ = 0;
|
||||
int video_quality_button_value_ = 0;
|
||||
int video_encode_format_button_value_ = 0;
|
||||
bool enable_hardware_video_codec_ = false;
|
||||
bool enable_turn_ = 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;
|
||||
bool enable_turn_last_ = false;
|
||||
|
||||
private:
|
||||
std::atomic<bool> start_screen_capturer_{false};
|
||||
std::atomic<bool> start_mouse_controller_{false};
|
||||
std::atomic<bool> start_keyboard_capturer_{false};
|
||||
std::atomic<bool> screen_capturer_is_started_{false};
|
||||
std::atomic<bool> mouse_controller_is_started_{false};
|
||||
std::atomic<bool> keyboard_capturer_is_started_{false};
|
||||
|
||||
private:
|
||||
bool settings_window_pos_reset_ = true;
|
||||
};
|
||||
|
||||
#endif
|
||||
373
src/single_window/render_callback_func.cpp
Normal file
373
src/single_window/render_callback_func.cpp
Normal file
@@ -0,0 +1,373 @@
|
||||
#include "device_controller.h"
|
||||
#include "localization.h"
|
||||
#include "platform.h"
|
||||
#include "rd_log.h"
|
||||
#include "render.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 Render::ProcessMouseKeyEvent(SDL_Event &event) {
|
||||
if (!control_mouse_ || !connection_established_) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (SDL_KEYDOWN == event.type || SDL_KEYUP == event.type) {
|
||||
} else {
|
||||
ProcessMouseEvent(event);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Render::ProcessMouseEvent(SDL_Event &event) {
|
||||
float ratio_x = (float)video_width_ / (float)stream_render_rect_.w;
|
||||
float ratio_y = (float)video_height_ / (float)stream_render_rect_.h;
|
||||
|
||||
if (event.button.x <= stream_render_rect_.x) {
|
||||
event.button.x = 0;
|
||||
} else if (event.button.x > stream_render_rect_.x &&
|
||||
event.button.x < stream_render_rect_.x + stream_render_rect_.w) {
|
||||
event.button.x -= stream_render_rect_.x;
|
||||
} else if (event.button.x >= stream_render_rect_.x + stream_render_rect_.w) {
|
||||
event.button.x = stream_render_rect_.w;
|
||||
}
|
||||
|
||||
if (event.button.y <= stream_render_rect_.y) {
|
||||
event.button.y = 0;
|
||||
} else if (event.button.y > stream_render_rect_.y &&
|
||||
event.button.y < stream_render_rect_.y + stream_render_rect_.h) {
|
||||
event.button.y -= stream_render_rect_.y;
|
||||
} else if (event.button.y >= stream_render_rect_.y + stream_render_rect_.h) {
|
||||
event.button.y = stream_render_rect_.h;
|
||||
}
|
||||
|
||||
RemoteAction remote_action;
|
||||
remote_action.m.x = (size_t)(event.button.x * ratio_x);
|
||||
remote_action.m.y = (size_t)(event.button.y * ratio_y);
|
||||
|
||||
if (SDL_MOUSEBUTTONDOWN == event.type) {
|
||||
remote_action.type = ControlType::mouse;
|
||||
if (SDL_BUTTON_LEFT == event.button.button) {
|
||||
remote_action.m.flag = MouseFlag::left_down;
|
||||
} else if (SDL_BUTTON_RIGHT == event.button.button) {
|
||||
remote_action.m.flag = MouseFlag::right_down;
|
||||
}
|
||||
if (control_bar_hovered_) {
|
||||
remote_action.m.flag = MouseFlag::move;
|
||||
}
|
||||
SendDataFrame(peer_, (const char *)&remote_action, sizeof(remote_action));
|
||||
} else if (SDL_MOUSEBUTTONUP == event.type) {
|
||||
remote_action.type = ControlType::mouse;
|
||||
if (SDL_BUTTON_LEFT == event.button.button) {
|
||||
remote_action.m.flag = MouseFlag::left_up;
|
||||
} else if (SDL_BUTTON_RIGHT == event.button.button) {
|
||||
remote_action.m.flag = MouseFlag::right_up;
|
||||
}
|
||||
if (control_bar_hovered_) {
|
||||
remote_action.m.flag = MouseFlag::move;
|
||||
}
|
||||
SendDataFrame(peer_, (const char *)&remote_action, sizeof(remote_action));
|
||||
} else if (SDL_MOUSEMOTION == event.type) {
|
||||
remote_action.type = ControlType::mouse;
|
||||
remote_action.m.flag = MouseFlag::move;
|
||||
SendDataFrame(peer_, (const char *)&remote_action, sizeof(remote_action));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Render::SendKeyEvent(int key_code, bool is_down) {
|
||||
RemoteAction remote_action;
|
||||
remote_action.type = ControlType::keyboard;
|
||||
if (is_down) {
|
||||
remote_action.k.flag = KeyFlag::key_down;
|
||||
} else {
|
||||
remote_action.k.flag = KeyFlag::key_up;
|
||||
}
|
||||
remote_action.k.key_value = key_code;
|
||||
|
||||
SendDataFrame(peer_, (const char *)&remote_action, sizeof(remote_action));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Render::ProcessKeyEvent(int key_code, bool is_down) {
|
||||
if (keyboard_capturer_) {
|
||||
keyboard_capturer_->SendKeyboardCommand(key_code, is_down);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Render::SdlCaptureAudioIn(void *userdata, Uint8 *stream, int len) {
|
||||
Render *render = (Render *)userdata;
|
||||
if (!render) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (1) {
|
||||
if ("Connected" == render->connection_status_str_) {
|
||||
SendAudioFrame(render->peer_, (const char *)stream, len);
|
||||
}
|
||||
} else {
|
||||
memcpy(render->audio_buffer_, stream, len);
|
||||
render->audio_len_ = len;
|
||||
SDL_Delay(10);
|
||||
render->audio_buffer_fresh_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
void Render::SdlCaptureAudioOut([[maybe_unused]] void *userdata,
|
||||
[[maybe_unused]] Uint8 *stream,
|
||||
[[maybe_unused]] int len) {
|
||||
// Render *render = (Render *)userdata;
|
||||
// if ("Connected" == render->connection_status_str_) {
|
||||
// SendAudioFrame(render->peer_, (const char *)stream, len);
|
||||
// }
|
||||
|
||||
// if (!render->audio_buffer_fresh_) {
|
||||
// return;
|
||||
// }
|
||||
|
||||
// SDL_memset(stream, 0, len);
|
||||
|
||||
// if (render->audio_len_ == 0) {
|
||||
// return;
|
||||
// } else {
|
||||
// }
|
||||
|
||||
// len = (len > render->audio_len_ ? render->audio_len_ : len);
|
||||
// SDL_MixAudioFormat(stream, render->audio_buffer_, AUDIO_S16LSB, len,
|
||||
// SDL_MIX_MAXVOLUME);
|
||||
// render->audio_buffer_fresh_ = false;
|
||||
}
|
||||
|
||||
void Render::OnReceiveVideoBufferCb(const XVideoFrame *video_frame,
|
||||
[[maybe_unused]] const char *user_id,
|
||||
[[maybe_unused]] size_t user_id_size,
|
||||
void *user_data) {
|
||||
Render *render = (Render *)user_data;
|
||||
if (!render) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (render->connection_established_) {
|
||||
if (!render->dst_buffer_) {
|
||||
render->dst_buffer_capacity_ = video_frame->size;
|
||||
render->dst_buffer_ = new unsigned char[video_frame->size];
|
||||
}
|
||||
|
||||
if (render->dst_buffer_capacity_ < video_frame->size) {
|
||||
delete render->dst_buffer_;
|
||||
render->dst_buffer_capacity_ = video_frame->size;
|
||||
render->dst_buffer_ = new unsigned char[video_frame->size];
|
||||
}
|
||||
|
||||
memcpy(render->dst_buffer_, video_frame->data, video_frame->size);
|
||||
render->video_width_ = video_frame->width;
|
||||
render->video_height_ = video_frame->height;
|
||||
render->video_size_ = video_frame->size;
|
||||
|
||||
SDL_Event event;
|
||||
event.type = REFRESH_EVENT;
|
||||
SDL_PushEvent(&event);
|
||||
render->streaming_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
void Render::OnReceiveAudioBufferCb(const char *data, size_t size,
|
||||
[[maybe_unused]] const char *user_id,
|
||||
[[maybe_unused]] size_t user_id_size,
|
||||
void *user_data) {
|
||||
Render *render = (Render *)user_data;
|
||||
if (!render) {
|
||||
return;
|
||||
}
|
||||
|
||||
render->audio_buffer_fresh_ = true;
|
||||
SDL_QueueAudio(render->output_dev_, data, (uint32_t)size);
|
||||
}
|
||||
|
||||
void Render::OnReceiveDataBufferCb(const char *data, size_t size,
|
||||
const char *user_id, size_t user_id_size,
|
||||
void *user_data) {
|
||||
Render *render = (Render *)user_data;
|
||||
if (!render) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::string user(user_id, user_id_size);
|
||||
RemoteAction remote_action;
|
||||
memcpy(&remote_action, data, size);
|
||||
|
||||
if (ControlType::mouse == remote_action.type && render->mouse_controller_) {
|
||||
render->mouse_controller_->SendCommand(remote_action);
|
||||
} else if (ControlType::audio_capture == remote_action.type) {
|
||||
if (remote_action.a) {
|
||||
render->StartSpeakerCapturer();
|
||||
} else {
|
||||
render->StopSpeakerCapturer();
|
||||
}
|
||||
} else if (ControlType::keyboard == remote_action.type) {
|
||||
render->ProcessKeyEvent((int)remote_action.k.key_value,
|
||||
remote_action.k.flag == KeyFlag::key_down);
|
||||
} else if (ControlType::host_infomation == remote_action.type) {
|
||||
render->host_name_ =
|
||||
std::string(remote_action.i.host_name, remote_action.i.host_name_size);
|
||||
LOG_INFO("Remote hostname: [{}]", render->host_name_);
|
||||
}
|
||||
}
|
||||
|
||||
void Render::OnSignalStatusCb(SignalStatus status, void *user_data) {
|
||||
Render *render = (Render *)user_data;
|
||||
if (!render) {
|
||||
return;
|
||||
}
|
||||
|
||||
render->signal_status_ = status;
|
||||
if (SignalStatus::SignalConnecting == status) {
|
||||
render->signal_status_str_ = "SignalConnecting";
|
||||
render->signal_connected_ = false;
|
||||
} else if (SignalStatus::SignalConnected == status) {
|
||||
render->signal_status_str_ = "SignalConnected";
|
||||
render->signal_connected_ = true;
|
||||
} else if (SignalStatus::SignalFailed == status) {
|
||||
render->signal_status_str_ = "SignalFailed";
|
||||
render->signal_connected_ = false;
|
||||
} else if (SignalStatus::SignalClosed == status) {
|
||||
render->signal_status_str_ = "SignalClosed";
|
||||
render->signal_connected_ = false;
|
||||
} else if (SignalStatus::SignalReconnecting == status) {
|
||||
render->signal_status_str_ = "SignalReconnecting";
|
||||
render->signal_connected_ = false;
|
||||
} else if (SignalStatus::SignalServerClosed == status) {
|
||||
render->signal_status_str_ = "SignalServerClosed";
|
||||
render->signal_connected_ = false;
|
||||
render->is_create_connection_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
void Render::OnConnectionStatusCb(ConnectionStatus status,
|
||||
[[maybe_unused]] const char *user_id,
|
||||
[[maybe_unused]] const size_t user_id_size,
|
||||
void *user_data) {
|
||||
Render *render = (Render *)user_data;
|
||||
if (!render) {
|
||||
return;
|
||||
}
|
||||
|
||||
render->connection_status_ = status;
|
||||
render->show_connection_status_window_ = true;
|
||||
if (ConnectionStatus::Connecting == status) {
|
||||
render->connection_status_str_ = "Connecting";
|
||||
} else if (ConnectionStatus::Gathering == status) {
|
||||
render->connection_status_str_ = "Gathering";
|
||||
} else if (ConnectionStatus::Connected == status) {
|
||||
render->connection_status_str_ = "Connected";
|
||||
render->connection_established_ = true;
|
||||
if (render->peer_reserved_ || !render->is_client_mode_) {
|
||||
render->start_screen_capturer_ = true;
|
||||
render->start_mouse_controller_ = true;
|
||||
}
|
||||
if (!render->hostname_sent_) {
|
||||
// TODO: self and remote hostname
|
||||
std::string host_name = GetHostName();
|
||||
RemoteAction remote_action;
|
||||
remote_action.type = ControlType::host_infomation;
|
||||
memcpy(&remote_action.i.host_name, host_name.data(), host_name.size());
|
||||
remote_action.i.host_name_size = host_name.size();
|
||||
int ret = SendDataFrame(render->peer_, (const char *)&remote_action,
|
||||
sizeof(remote_action));
|
||||
if (0 == ret) {
|
||||
render->hostname_sent_ = true;
|
||||
}
|
||||
}
|
||||
} else if (ConnectionStatus::Disconnected == status) {
|
||||
render->connection_status_str_ = "Disconnected";
|
||||
render->password_validating_time_ = 0;
|
||||
} else if (ConnectionStatus::Failed == status) {
|
||||
render->connection_status_str_ = "Failed";
|
||||
render->password_validating_time_ = 0;
|
||||
} else if (ConnectionStatus::Closed == status) {
|
||||
render->connection_status_str_ = "Closed";
|
||||
render->password_validating_time_ = 0;
|
||||
render->start_screen_capturer_ = false;
|
||||
render->start_mouse_controller_ = false;
|
||||
render->connection_established_ = false;
|
||||
render->control_mouse_ = false;
|
||||
render->mouse_control_button_pressed_ = false;
|
||||
render->start_keyboard_capturer_ = false;
|
||||
render->hostname_sent_ = false;
|
||||
if (render->audio_capture_) {
|
||||
render->StopSpeakerCapturer();
|
||||
render->audio_capture_ = false;
|
||||
render->audio_capture_button_pressed_ = false;
|
||||
}
|
||||
render->exit_video_window_ = false;
|
||||
if (!render->rejoin_) {
|
||||
memset(render->remote_password_, 0, sizeof(render->remote_password_));
|
||||
}
|
||||
if (render->dst_buffer_) {
|
||||
memset(render->dst_buffer_, 0, render->dst_buffer_capacity_);
|
||||
SDL_UpdateTexture(render->stream_texture_, NULL, render->dst_buffer_,
|
||||
render->texture_width_);
|
||||
}
|
||||
} else if (ConnectionStatus::IncorrectPassword == status) {
|
||||
render->connection_status_str_ = "Incorrect password";
|
||||
render->password_validating_ = false;
|
||||
render->password_validating_time_++;
|
||||
if (render->connect_button_pressed_) {
|
||||
render->connection_established_ = false;
|
||||
render->connect_button_label_ =
|
||||
render->connect_button_pressed_
|
||||
? localization::disconnect[render->localization_language_index_]
|
||||
: localization::connect[render->localization_language_index_];
|
||||
}
|
||||
} else if (ConnectionStatus::NoSuchTransmissionId == status) {
|
||||
render->connection_status_str_ = "No such transmission id";
|
||||
if (render->connect_button_pressed_) {
|
||||
render->connection_established_ = false;
|
||||
render->connect_button_label_ =
|
||||
render->connect_button_pressed_
|
||||
? localization::disconnect[render->localization_language_index_]
|
||||
: localization::connect[render->localization_language_index_];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Render::NetStatusReport(const char *client_id, size_t client_id_size,
|
||||
TraversalMode mode,
|
||||
const XNetTrafficStats *net_traffic_stats,
|
||||
void *user_data) {
|
||||
Render *render = (Render *)user_data;
|
||||
if (!render) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (0 == strcmp(render->client_id_, "")) {
|
||||
memset(&render->client_id_, 0, sizeof(render->client_id_));
|
||||
memcpy(render->client_id_, client_id, client_id_size);
|
||||
LOG_INFO("Use client id [{}] and save id into cache file", client_id);
|
||||
render->SaveSettingsIntoCacheFile();
|
||||
}
|
||||
if (render->traversal_mode_ != mode) {
|
||||
render->traversal_mode_ = mode;
|
||||
LOG_INFO("Net mode: [{}]", int(render->traversal_mode_));
|
||||
}
|
||||
|
||||
if (!net_traffic_stats) {
|
||||
return;
|
||||
}
|
||||
|
||||
// only display client side net status if connected to itself
|
||||
if (!(render->peer_reserved_ && !strstr(client_id, "C-"))) {
|
||||
render->net_traffic_stats_ = *net_traffic_stats;
|
||||
}
|
||||
}
|
||||
284
src/single_window/setting_window.cpp
Normal file
284
src/single_window/setting_window.cpp
Normal file
@@ -0,0 +1,284 @@
|
||||
#include "layout_style.h"
|
||||
#include "localization.h"
|
||||
#include "rd_log.h"
|
||||
#include "render.h"
|
||||
|
||||
int Render::SettingWindow() {
|
||||
if (show_settings_window_) {
|
||||
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::SetWindowFontScale(0.5f);
|
||||
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 5.0f);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 3.0f);
|
||||
|
||||
ImGui::Begin(localization::settings[localization_language_index_].c_str(),
|
||||
nullptr,
|
||||
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse |
|
||||
ImGuiWindowFlags_NoSavedSettings);
|
||||
ImGui::SetWindowFontScale(1.0f);
|
||||
ImGui::SetWindowFontScale(0.5f);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f);
|
||||
{
|
||||
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(
|
||||
"%s", 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();
|
||||
|
||||
if (streaming_) {
|
||||
ImGui::BeginDisabled();
|
||||
}
|
||||
|
||||
{
|
||||
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(
|
||||
"%s",
|
||||
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(
|
||||
"%s",
|
||||
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("%s", 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_);
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
{
|
||||
ImGui::SetCursorPosY(152);
|
||||
ImGui::Text(
|
||||
"%s",
|
||||
localization::enable_turn[localization_language_index_].c_str());
|
||||
|
||||
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
|
||||
ImGui::SetCursorPosX(ENABLE_TURN_CHECKBOX_PADDING_CN);
|
||||
} else {
|
||||
ImGui::SetCursorPosX(ENABLE_TURN_CHECKBOX_PADDING_EN);
|
||||
}
|
||||
ImGui::SetCursorPosY(150);
|
||||
ImGui::Checkbox("##enable_turn", &enable_turn_);
|
||||
}
|
||||
|
||||
if (streaming_) {
|
||||
ImGui::EndDisabled();
|
||||
}
|
||||
|
||||
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
|
||||
ImGui::SetCursorPosX(SETTINGS_OK_BUTTON_PADDING_CN);
|
||||
} else {
|
||||
ImGui::SetCursorPosX(SETTINGS_OK_BUTTON_PADDING_EN);
|
||||
}
|
||||
ImGui::SetCursorPosY(190.0f);
|
||||
ImGui::PopStyleVar();
|
||||
|
||||
// OK
|
||||
if (ImGui::Button(
|
||||
localization::ok[localization_language_index_].c_str())) {
|
||||
show_settings_window_ = 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_;
|
||||
|
||||
// TURN mode
|
||||
if (enable_turn_) {
|
||||
config_center_.SetTurn(true);
|
||||
} else {
|
||||
config_center_.SetTurn(false);
|
||||
}
|
||||
enable_turn_last_ = enable_turn_;
|
||||
|
||||
SaveSettingsIntoCacheFile();
|
||||
settings_window_pos_reset_ = true;
|
||||
|
||||
// Recreate peer instance
|
||||
LoadSettingsFromCacheFile();
|
||||
|
||||
// Recreate peer instance
|
||||
if (!streaming_) {
|
||||
LOG_INFO("Recreate peer instance");
|
||||
DestroyPeer(&peer_);
|
||||
is_create_connection_ = false;
|
||||
CreateConnectionPeer();
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
// Cancel
|
||||
if (ImGui::Button(
|
||||
localization::cancel[localization_language_index_].c_str())) {
|
||||
show_settings_window_ = 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_;
|
||||
}
|
||||
|
||||
if (enable_turn_ != enable_turn_last_) {
|
||||
enable_turn_ = enable_turn_last_;
|
||||
}
|
||||
|
||||
settings_window_pos_reset_ = true;
|
||||
}
|
||||
ImGui::SetWindowFontScale(1.0f);
|
||||
ImGui::SetWindowFontScale(0.5f);
|
||||
ImGui::End();
|
||||
ImGui::PopStyleVar(2);
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::SetWindowFontScale(1.0f);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
38
src/single_window/status_bar.cpp
Normal file
38
src/single_window/status_bar.cpp
Normal file
@@ -0,0 +1,38 @@
|
||||
#include "localization.h"
|
||||
#include "render.h"
|
||||
|
||||
int Render::StatusBar() {
|
||||
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
|
||||
static bool a, b, c, d, e;
|
||||
ImGui::SetNextWindowPos(
|
||||
ImVec2(0, main_window_height_default_ - status_bar_height_ - 1),
|
||||
ImGuiCond_Always);
|
||||
|
||||
ImGui::BeginChild(
|
||||
"StatusBar", ImVec2(main_window_width_, status_bar_height_ + 1),
|
||||
ImGuiChildFlags_Border,
|
||||
ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoBringToFrontOnFocus);
|
||||
|
||||
ImVec2 dot_pos =
|
||||
ImVec2(13, main_window_height_default_ - status_bar_height_ + 11.0f);
|
||||
ImDrawList* draw_list = ImGui::GetWindowDrawList();
|
||||
draw_list->AddCircleFilled(dot_pos, 5.0f,
|
||||
ImColor(signal_connected_ ? 0.0f : 1.0f,
|
||||
signal_connected_ ? 1.0f : 0.0f, 0.0f),
|
||||
100);
|
||||
draw_list->AddCircle(dot_pos, 6.0f, ImColor(1.0f, 1.0f, 1.0f), 100);
|
||||
|
||||
ImGui::SetWindowFontScale(0.6f);
|
||||
draw_list->AddText(
|
||||
ImVec2(25, main_window_height_default_ - status_bar_height_ + 3.0f),
|
||||
ImColor(0.0f, 0.0f, 0.0f),
|
||||
signal_connected_
|
||||
? localization::signal_connected[localization_language_index_].c_str()
|
||||
: localization::signal_disconnected[localization_language_index_]
|
||||
.c_str());
|
||||
ImGui::SetWindowFontScale(1.0f);
|
||||
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::EndChild();
|
||||
return 0;
|
||||
}
|
||||
8423
src/single_window/stb_image.h
Normal file
8423
src/single_window/stb_image.h
Normal file
File diff suppressed because it is too large
Load Diff
2022
src/single_window/stb_image_write.h
Normal file
2022
src/single_window/stb_image_write.h
Normal file
File diff suppressed because it is too large
Load Diff
26
src/single_window/stream_window.cpp
Normal file
26
src/single_window/stream_window.cpp
Normal file
@@ -0,0 +1,26 @@
|
||||
#include "localization.h"
|
||||
#include "rd_log.h"
|
||||
#include "render.h"
|
||||
|
||||
int Render::StreamWindow() {
|
||||
ImGui::SetNextWindowPos(
|
||||
ImVec2(0, fullscreen_button_pressed_ ? 0 : title_bar_height_),
|
||||
ImGuiCond_Always);
|
||||
ImGui::SetNextWindowSize(
|
||||
ImVec2(stream_window_width_, stream_window_height_ - title_bar_height_),
|
||||
ImGuiCond_Always);
|
||||
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(1, 1, 1, 0));
|
||||
ImGui::PushStyleColor(ImGuiCol_Border,
|
||||
ImVec4(178 / 255.0f, 178 / 255.0f, 178 / 255.0f,
|
||||
fullscreen_button_pressed_ ? 0 : 1.0f));
|
||||
ImGui::Begin("VideoBg", nullptr,
|
||||
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoDecoration |
|
||||
ImGuiWindowFlags_NoBringToFrontOnFocus);
|
||||
ImGui::PopStyleColor(2);
|
||||
|
||||
ControlWindow();
|
||||
|
||||
ImGui::End();
|
||||
|
||||
return 0;
|
||||
}
|
||||
363
src/single_window/thumbnail.cpp
Normal file
363
src/single_window/thumbnail.cpp
Normal file
@@ -0,0 +1,363 @@
|
||||
#include "thumbnail.h"
|
||||
|
||||
#include <openssl/aes.h>
|
||||
#include <openssl/crypto.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/rand.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "libyuv.h"
|
||||
#include "rd_log.h"
|
||||
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#include "stb_image.h"
|
||||
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
||||
#include "stb_image_write.h"
|
||||
|
||||
static std::string test;
|
||||
|
||||
void ScaleYUV420pToABGR(char* dst_buffer_, int video_width_, int video_height_,
|
||||
int scaled_video_width_, int scaled_video_height_,
|
||||
char* rgba_buffer_) {
|
||||
int src_y_size = video_width_ * video_height_;
|
||||
int src_uv_size = (video_width_ + 1) / 2 * (video_height_ + 1) / 2;
|
||||
int dst_y_size = scaled_video_width_ * scaled_video_height_;
|
||||
int dst_uv_size =
|
||||
(scaled_video_width_ + 1) / 2 * (scaled_video_height_ + 1) / 2;
|
||||
|
||||
uint8_t* src_y = reinterpret_cast<uint8_t*>(dst_buffer_);
|
||||
uint8_t* src_u = src_y + src_y_size;
|
||||
uint8_t* src_v = src_u + src_uv_size;
|
||||
|
||||
std::unique_ptr<uint8_t[]> dst_y(new uint8_t[dst_y_size]);
|
||||
std::unique_ptr<uint8_t[]> dst_u(new uint8_t[dst_uv_size]);
|
||||
std::unique_ptr<uint8_t[]> dst_v(new uint8_t[dst_uv_size]);
|
||||
|
||||
libyuv::I420Scale(src_y, video_width_, src_u, (video_width_ + 1) / 2, src_v,
|
||||
(video_width_ + 1) / 2, video_width_, video_height_,
|
||||
dst_y.get(), scaled_video_width_, dst_u.get(),
|
||||
(scaled_video_width_ + 1) / 2, dst_v.get(),
|
||||
(scaled_video_width_ + 1) / 2, scaled_video_width_,
|
||||
scaled_video_height_, libyuv::kFilterBilinear);
|
||||
|
||||
libyuv::I420ToABGR(
|
||||
dst_y.get(), scaled_video_width_, dst_u.get(),
|
||||
(scaled_video_width_ + 1) / 2, dst_v.get(), (scaled_video_width_ + 1) / 2,
|
||||
reinterpret_cast<uint8_t*>(rgba_buffer_), scaled_video_width_ * 4,
|
||||
scaled_video_width_, scaled_video_height_);
|
||||
}
|
||||
|
||||
Thumbnail::Thumbnail() {
|
||||
RAND_bytes(aes128_key_, sizeof(aes128_key_));
|
||||
RAND_bytes(aes128_iv_, sizeof(aes128_iv_));
|
||||
std::filesystem::create_directory(image_path_);
|
||||
}
|
||||
|
||||
Thumbnail::Thumbnail(unsigned char* aes128_key, unsigned char* aes128_iv) {
|
||||
memcpy(aes128_key_, aes128_key, sizeof(aes128_key_));
|
||||
memcpy(aes128_iv_, aes128_iv, sizeof(aes128_iv_));
|
||||
std::filesystem::create_directory(image_path_);
|
||||
}
|
||||
|
||||
Thumbnail::~Thumbnail() {
|
||||
if (rgba_buffer_) {
|
||||
delete[] rgba_buffer_;
|
||||
rgba_buffer_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
int Thumbnail::SaveToThumbnail(const char* yuv420p, int width, int height,
|
||||
const std::string& remote_id,
|
||||
const std::string& host_name,
|
||||
const std::string& password) {
|
||||
if (!rgba_buffer_) {
|
||||
rgba_buffer_ = new char[thumbnail_width_ * thumbnail_height_ * 4];
|
||||
}
|
||||
|
||||
if (yuv420p) {
|
||||
ScaleYUV420pToABGR((char*)yuv420p, width, height, thumbnail_width_,
|
||||
thumbnail_height_, rgba_buffer_);
|
||||
|
||||
std::string image_name;
|
||||
if (password.empty()) {
|
||||
image_name = remote_id + 'N' + host_name;
|
||||
} else {
|
||||
// delete the file which has no password in its name
|
||||
std::string filename_without_password = remote_id + "N" + host_name;
|
||||
DeleteThumbnail(filename_without_password);
|
||||
|
||||
image_name = remote_id + 'Y' + password + host_name;
|
||||
}
|
||||
|
||||
std::string ciphertext = AES_encrypt(image_name, aes128_key_, aes128_iv_);
|
||||
std::string file_path = image_path_ + ciphertext;
|
||||
stbi_write_png(file_path.data(), thumbnail_width_, thumbnail_height_, 4,
|
||||
rgba_buffer_, thumbnail_width_ * 4);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool LoadTextureFromMemory(const void* data, size_t data_size,
|
||||
SDL_Renderer* renderer, SDL_Texture** out_texture,
|
||||
int* out_width, int* out_height) {
|
||||
int image_width = 0;
|
||||
int image_height = 0;
|
||||
int channels = 4;
|
||||
unsigned char* image_data =
|
||||
stbi_load_from_memory((const unsigned char*)data, (int)data_size,
|
||||
&image_width, &image_height, NULL, 4);
|
||||
if (image_data == nullptr) {
|
||||
LOG_ERROR("Failed to load image: [{}]", stbi_failure_reason());
|
||||
return false;
|
||||
}
|
||||
|
||||
// ABGR
|
||||
SDL_Surface* surface = SDL_CreateRGBSurfaceFrom(
|
||||
(void*)image_data, image_width, image_height, channels * 8,
|
||||
channels * image_width, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000);
|
||||
if (surface == nullptr) {
|
||||
LOG_ERROR("Failed to create SDL surface: [{}]", SDL_GetError());
|
||||
return false;
|
||||
}
|
||||
|
||||
SDL_Texture* texture = SDL_CreateTextureFromSurface(renderer, surface);
|
||||
if (texture == nullptr) {
|
||||
LOG_ERROR("Failed to create SDL texture: [{}]", SDL_GetError());
|
||||
}
|
||||
|
||||
*out_texture = texture;
|
||||
*out_width = image_width;
|
||||
*out_height = image_height;
|
||||
|
||||
SDL_FreeSurface(surface);
|
||||
stbi_image_free(image_data);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LoadTextureFromFile(const char* file_name, SDL_Renderer* renderer,
|
||||
SDL_Texture** out_texture, int* out_width,
|
||||
int* out_height) {
|
||||
std::filesystem::path file_path(file_name);
|
||||
if (!std::filesystem::exists(file_path)) return false;
|
||||
std::ifstream file(file_path, std::ios::binary);
|
||||
if (!file) return false;
|
||||
file.seekg(0, std::ios::end);
|
||||
size_t file_size = file.tellg();
|
||||
file.seekg(0, std::ios::beg);
|
||||
if (file_size == -1) return false;
|
||||
char* file_data = new char[file_size];
|
||||
if (!file_data) return false;
|
||||
file.read(file_data, file_size);
|
||||
bool ret = LoadTextureFromMemory(file_data, file_size, renderer, out_texture,
|
||||
out_width, out_height);
|
||||
delete[] file_data;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::vector<std::filesystem::path> Thumbnail::FindThumbnailPath(
|
||||
const std::filesystem::path& directory) {
|
||||
std::vector<std::filesystem::path> thumbnails_path;
|
||||
|
||||
if (!std::filesystem::is_directory(directory)) {
|
||||
LOG_ERROR("No such directory [{}]", directory.string());
|
||||
return thumbnails_path;
|
||||
}
|
||||
|
||||
thumbnails_sorted_by_write_time_.clear();
|
||||
|
||||
for (const auto& entry : std::filesystem::directory_iterator(directory)) {
|
||||
if (entry.is_regular_file()) {
|
||||
std::time_t last_write_time = std::chrono::system_clock::to_time_t(
|
||||
time_point_cast<std::chrono::system_clock::duration>(
|
||||
entry.last_write_time() -
|
||||
std::filesystem::file_time_type::clock::now() +
|
||||
std::chrono::system_clock::now()));
|
||||
|
||||
thumbnails_sorted_by_write_time_[last_write_time] = entry.path();
|
||||
}
|
||||
}
|
||||
|
||||
for (auto it = thumbnails_sorted_by_write_time_.rbegin();
|
||||
it != thumbnails_sorted_by_write_time_.rend(); ++it) {
|
||||
thumbnails_path.push_back(it->second);
|
||||
}
|
||||
|
||||
return thumbnails_path;
|
||||
}
|
||||
|
||||
int Thumbnail::LoadThumbnail(SDL_Renderer* renderer,
|
||||
std::map<std::string, SDL_Texture*>& textures,
|
||||
int* width, int* height) {
|
||||
for (auto& it : textures) {
|
||||
if (it.second != nullptr) {
|
||||
SDL_DestroyTexture(it.second);
|
||||
it.second = nullptr;
|
||||
}
|
||||
}
|
||||
textures.clear();
|
||||
|
||||
std::vector<std::filesystem::path> image_paths =
|
||||
FindThumbnailPath(image_path_);
|
||||
|
||||
if (image_paths.size() == 0) {
|
||||
return -1;
|
||||
} else {
|
||||
for (int i = 0; i < image_paths.size(); i++) {
|
||||
size_t pos1 = image_paths[i].string().find('/') + 1;
|
||||
std::string cipher_image_name = image_paths[i].string().substr(pos1);
|
||||
std::string original_image_name =
|
||||
AES_decrypt(cipher_image_name, aes128_key_, aes128_iv_);
|
||||
std::string image_path = image_path_ + cipher_image_name;
|
||||
textures[original_image_name] = nullptr;
|
||||
LoadTextureFromFile(image_path.c_str(), renderer,
|
||||
&(textures[original_image_name]), width, height);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Thumbnail::DeleteThumbnail(const std::string& file_name) {
|
||||
std::string ciphertext = AES_encrypt(file_name, aes128_key_, aes128_iv_);
|
||||
std::string file_path = image_path_ + ciphertext;
|
||||
if (std::filesystem::exists(file_path)) {
|
||||
std::filesystem::remove(file_path);
|
||||
return 0;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int Thumbnail::DeleteAllFilesInDirectory() {
|
||||
if (std::filesystem::exists(image_path_) &&
|
||||
std::filesystem::is_directory(image_path_)) {
|
||||
for (const auto& entry : std::filesystem::directory_iterator(image_path_)) {
|
||||
if (std::filesystem::is_regular_file(entry.status())) {
|
||||
std::filesystem::remove(entry.path());
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::string Thumbnail::AES_encrypt(const std::string& plaintext,
|
||||
unsigned char* key, unsigned char* iv) {
|
||||
EVP_CIPHER_CTX* ctx;
|
||||
int len;
|
||||
int ciphertext_len;
|
||||
int ret = 0;
|
||||
std::vector<unsigned char> ciphertext(plaintext.size() + AES_BLOCK_SIZE);
|
||||
|
||||
ctx = EVP_CIPHER_CTX_new();
|
||||
if (!ctx) {
|
||||
LOG_ERROR("Error in EVP_CIPHER_CTX_new");
|
||||
return plaintext;
|
||||
}
|
||||
|
||||
ret = EVP_EncryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, key, iv);
|
||||
if (1 != ret) {
|
||||
LOG_ERROR("Error in EVP_EncryptInit_ex");
|
||||
EVP_CIPHER_CTX_free(ctx);
|
||||
return plaintext;
|
||||
}
|
||||
|
||||
ret = EVP_EncryptUpdate(
|
||||
ctx, ciphertext.data(), &len,
|
||||
reinterpret_cast<const unsigned char*>(plaintext.data()),
|
||||
(int)plaintext.size());
|
||||
if (1 != ret) {
|
||||
LOG_ERROR("Error in EVP_EncryptUpdate");
|
||||
EVP_CIPHER_CTX_free(ctx);
|
||||
return plaintext;
|
||||
}
|
||||
|
||||
ciphertext_len = len;
|
||||
ret = EVP_EncryptFinal_ex(ctx, ciphertext.data() + len, &len);
|
||||
if (1 != ret) {
|
||||
LOG_ERROR("Error in EVP_EncryptFinal_ex");
|
||||
EVP_CIPHER_CTX_free(ctx);
|
||||
return plaintext;
|
||||
}
|
||||
|
||||
ciphertext_len += len;
|
||||
|
||||
unsigned char hex_str[256];
|
||||
size_t hex_str_len = 0;
|
||||
ret = OPENSSL_buf2hexstr_ex((char*)hex_str, sizeof(hex_str), &hex_str_len,
|
||||
ciphertext.data(), ciphertext_len, '\0');
|
||||
if (1 != ret) {
|
||||
LOG_ERROR("Error in OPENSSL_buf2hexstr_ex");
|
||||
EVP_CIPHER_CTX_free(ctx);
|
||||
return plaintext;
|
||||
}
|
||||
|
||||
EVP_CIPHER_CTX_free(ctx);
|
||||
|
||||
std::string str(reinterpret_cast<char*>(hex_str), hex_str_len);
|
||||
return str;
|
||||
}
|
||||
|
||||
std::string Thumbnail::AES_decrypt(const std::string& ciphertext,
|
||||
unsigned char* key, unsigned char* iv) {
|
||||
unsigned char ciphertext_buf[256];
|
||||
size_t ciphertext_buf_len = 0;
|
||||
unsigned char plaintext[256];
|
||||
int plaintext_len = 0;
|
||||
int plaintext_final_len = 0;
|
||||
EVP_CIPHER_CTX* ctx;
|
||||
int ret = 0;
|
||||
|
||||
ret = OPENSSL_hexstr2buf_ex(ciphertext_buf, sizeof(ciphertext_buf),
|
||||
&ciphertext_buf_len, ciphertext.c_str(), '\0');
|
||||
if (1 != ret) {
|
||||
LOG_ERROR("Error in OPENSSL_hexstr2buf_ex");
|
||||
return ciphertext;
|
||||
}
|
||||
|
||||
ctx = EVP_CIPHER_CTX_new();
|
||||
if (!ctx) {
|
||||
LOG_ERROR("Error in EVP_CIPHER_CTX_new");
|
||||
return ciphertext;
|
||||
}
|
||||
|
||||
ret = EVP_DecryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, key, iv);
|
||||
if (1 != ret) {
|
||||
LOG_ERROR("Error in EVP_DecryptInit_ex");
|
||||
|
||||
EVP_CIPHER_CTX_free(ctx);
|
||||
return ciphertext;
|
||||
}
|
||||
|
||||
ret = EVP_DecryptUpdate(ctx, plaintext, &plaintext_len, ciphertext_buf,
|
||||
(int)ciphertext_buf_len);
|
||||
if (1 != ret) {
|
||||
LOG_ERROR("Error in EVP_DecryptUpdate");
|
||||
|
||||
EVP_CIPHER_CTX_free(ctx);
|
||||
return ciphertext;
|
||||
}
|
||||
|
||||
ret =
|
||||
EVP_DecryptFinal_ex(ctx, plaintext + plaintext_len, &plaintext_final_len);
|
||||
if (1 != ret) {
|
||||
LOG_ERROR("Error in EVP_DecryptFinal_ex");
|
||||
|
||||
EVP_CIPHER_CTX_free(ctx);
|
||||
return ciphertext;
|
||||
}
|
||||
plaintext_len += plaintext_final_len;
|
||||
|
||||
EVP_CIPHER_CTX_free(ctx);
|
||||
|
||||
return std::string(reinterpret_cast<char*>(plaintext), plaintext_len);
|
||||
}
|
||||
74
src/single_window/thumbnail.h
Normal file
74
src/single_window/thumbnail.h
Normal file
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* @Author: DI JUNKUN
|
||||
* @Date: 2024-11-07
|
||||
* Copyright (c) 2024 by DI JUNKUN, All Rights Reserved.
|
||||
*/
|
||||
|
||||
#ifndef _THUMBNAIL_H_
|
||||
#define _THUMBNAIL_H_
|
||||
|
||||
#include <SDL.h>
|
||||
|
||||
#include <filesystem>
|
||||
#include <map>
|
||||
|
||||
class Thumbnail {
|
||||
public:
|
||||
Thumbnail();
|
||||
explicit Thumbnail(unsigned char* aes128_key, unsigned char* aes128_iv);
|
||||
~Thumbnail();
|
||||
|
||||
public:
|
||||
int SaveToThumbnail(const char* yuv420p, int width, int height,
|
||||
const std::string& remote_id,
|
||||
const std::string& host_name,
|
||||
const std::string& password);
|
||||
|
||||
int LoadThumbnail(SDL_Renderer* renderer,
|
||||
std::map<std::string, SDL_Texture*>& textures, int* width,
|
||||
int* height);
|
||||
|
||||
int DeleteThumbnail(const std::string& file_name);
|
||||
|
||||
int DeleteAllFilesInDirectory();
|
||||
|
||||
int GetKey(unsigned char* aes128_key) {
|
||||
memcpy(aes128_key, aes128_key_, sizeof(aes128_key_));
|
||||
return sizeof(aes128_key_);
|
||||
}
|
||||
|
||||
int GetIv(unsigned char* aes128_iv) {
|
||||
memcpy(aes128_iv, aes128_iv_, sizeof(aes128_iv_));
|
||||
return sizeof(aes128_iv_);
|
||||
}
|
||||
|
||||
int GetKeyAndIv(unsigned char* aes128_key, unsigned char* aes128_iv) {
|
||||
memcpy(aes128_key, aes128_key_, sizeof(aes128_key_));
|
||||
memcpy(aes128_iv, aes128_iv_, sizeof(aes128_iv_));
|
||||
return 0;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<std::filesystem::path> FindThumbnailPath(
|
||||
const std::filesystem::path& directory);
|
||||
|
||||
std::string AES_encrypt(const std::string& plaintext, unsigned char* key,
|
||||
unsigned char* iv);
|
||||
|
||||
std::string AES_decrypt(const std::string& ciphertext, unsigned char* key,
|
||||
unsigned char* iv);
|
||||
|
||||
private:
|
||||
int thumbnail_width_ = 160;
|
||||
int thumbnail_height_ = 90;
|
||||
char* rgba_buffer_ = nullptr;
|
||||
std::string image_path_ = "thumbnails/";
|
||||
std::map<std::time_t, std::filesystem::path> thumbnails_sorted_by_write_time_;
|
||||
|
||||
unsigned char aes128_key_[16];
|
||||
unsigned char aes128_iv_[16];
|
||||
unsigned char ciphertext_[64];
|
||||
unsigned char decryptedtext_[64];
|
||||
};
|
||||
|
||||
#endif
|
||||
164
src/single_window/title_bar.cpp
Normal file
164
src/single_window/title_bar.cpp
Normal file
@@ -0,0 +1,164 @@
|
||||
#include "localization.h"
|
||||
#include "render.h"
|
||||
|
||||
#define BUTTON_PADDING 36.0f
|
||||
|
||||
int Render::TitleBar(bool main_window) {
|
||||
ImGui::PushStyleColor(ImGuiCol_MenuBarBg, ImVec4(1.0f, 1.0f, 1.0f, 0.0f));
|
||||
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
|
||||
ImGui::SetNextWindowPos(ImVec2(0, 0), ImGuiCond_Always);
|
||||
ImGui::SetWindowFontScale(0.8f);
|
||||
ImGui::BeginChild(
|
||||
main_window ? "MainTitleBar" : "StreamTitleBar",
|
||||
ImVec2(main_window ? main_window_width_ : stream_window_width_,
|
||||
title_bar_height_),
|
||||
ImGuiChildFlags_Border,
|
||||
ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoDecoration |
|
||||
ImGuiWindowFlags_NoBringToFrontOnFocus);
|
||||
ImGui::SetWindowFontScale(1.0f);
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
ImDrawList* draw_list = ImGui::GetWindowDrawList();
|
||||
if (ImGui::BeginMenuBar()) {
|
||||
ImGui::SetCursorPosX(
|
||||
(main_window ? main_window_width_ : stream_window_width_) -
|
||||
(BUTTON_PADDING * 3 - 3));
|
||||
ImGui::PushStyleColor(ImGuiCol_HeaderHovered, ImVec4(0, 0, 0, 0.1f));
|
||||
ImGui::PushStyleColor(ImGuiCol_HeaderActive,
|
||||
ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
|
||||
if (main_window) {
|
||||
float bar_pos_x = ImGui::GetCursorPosX() + 6;
|
||||
float bar_pos_y = ImGui::GetCursorPosY() + 15;
|
||||
std::string menu_button = " "; // ICON_FA_BARS;
|
||||
if (ImGui::BeginMenu(menu_button.c_str())) {
|
||||
ImGui::SetWindowFontScale(0.5f);
|
||||
if (ImGui::MenuItem(
|
||||
localization::settings[localization_language_index_].c_str())) {
|
||||
show_settings_window_ = true;
|
||||
}
|
||||
if (ImGui::MenuItem(
|
||||
localization::about[localization_language_index_].c_str())) {
|
||||
show_about_window_ = true;
|
||||
}
|
||||
ImGui::SetWindowFontScale(1.0f);
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
float menu_bar_line_size = 15.0f;
|
||||
draw_list->AddLine(ImVec2(bar_pos_x, bar_pos_y - 6),
|
||||
ImVec2(bar_pos_x + menu_bar_line_size, bar_pos_y - 6),
|
||||
IM_COL32(0, 0, 0, 255));
|
||||
draw_list->AddLine(ImVec2(bar_pos_x, bar_pos_y),
|
||||
ImVec2(bar_pos_x + menu_bar_line_size, bar_pos_y),
|
||||
IM_COL32(0, 0, 0, 255));
|
||||
draw_list->AddLine(ImVec2(bar_pos_x, bar_pos_y + 6),
|
||||
ImVec2(bar_pos_x + menu_bar_line_size, bar_pos_y + 6),
|
||||
IM_COL32(0, 0, 0, 255));
|
||||
|
||||
{
|
||||
SettingWindow();
|
||||
AboutWindow();
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::PopStyleColor(2);
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0));
|
||||
ImGui::SetCursorPosX(main_window
|
||||
? (main_window_width_ - BUTTON_PADDING * 2)
|
||||
: (stream_window_width_ - BUTTON_PADDING * 3));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0, 0, 0, 0.1f));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive,
|
||||
ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
|
||||
|
||||
float minimize_pos_x = ImGui::GetCursorPosX() + 12;
|
||||
float minimize_pos_y = ImGui::GetCursorPosY() + 15;
|
||||
std::string window_minimize_button = "##minimize"; // ICON_FA_MINUS;
|
||||
if (ImGui::Button(window_minimize_button.c_str(),
|
||||
ImVec2(BUTTON_PADDING, 30))) {
|
||||
SDL_MinimizeWindow(main_window ? main_window_ : stream_window_);
|
||||
}
|
||||
draw_list->AddLine(ImVec2(minimize_pos_x, minimize_pos_y),
|
||||
ImVec2(minimize_pos_x + 12, minimize_pos_y),
|
||||
IM_COL32(0, 0, 0, 255));
|
||||
ImGui::PopStyleColor(2);
|
||||
|
||||
if (!main_window) {
|
||||
ImGui::SetCursorPosX(stream_window_width_ - BUTTON_PADDING * 2);
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0, 0, 0, 0.1f));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive,
|
||||
ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
|
||||
|
||||
if (window_maximized_) {
|
||||
float pos_x_top = ImGui::GetCursorPosX() + 11;
|
||||
float pos_y_top = ImGui::GetCursorPosY() + 11;
|
||||
float pos_x_bottom = ImGui::GetCursorPosX() + 13;
|
||||
float pos_y_bottom = ImGui::GetCursorPosY() + 9;
|
||||
std::string window_restore_button =
|
||||
"##restore"; // ICON_FA_WINDOW_RESTORE;
|
||||
if (ImGui::Button(window_restore_button.c_str(),
|
||||
ImVec2(BUTTON_PADDING, 30))) {
|
||||
SDL_RestoreWindow(stream_window_);
|
||||
window_maximized_ = false;
|
||||
}
|
||||
draw_list->AddRect(ImVec2(pos_x_top, pos_y_top),
|
||||
ImVec2(pos_x_top + 12, pos_y_top + 12),
|
||||
IM_COL32(0, 0, 0, 255));
|
||||
draw_list->AddRect(ImVec2(pos_x_bottom, pos_y_bottom),
|
||||
ImVec2(pos_x_bottom + 12, pos_y_bottom + 12),
|
||||
IM_COL32(0, 0, 0, 255));
|
||||
draw_list->AddRectFilled(ImVec2(pos_x_top + 1, pos_y_top + 1),
|
||||
ImVec2(pos_x_top + 11, pos_y_top + 11),
|
||||
IM_COL32(255, 255, 255, 255));
|
||||
} else {
|
||||
float maximize_pos_x = ImGui::GetCursorPosX() + 12;
|
||||
float maximize_pos_y = ImGui::GetCursorPosY() + 10;
|
||||
std::string window_maximize_button =
|
||||
"##maximize"; // ICON_FA_SQUARE_FULL;
|
||||
if (ImGui::Button(window_maximize_button.c_str(),
|
||||
ImVec2(BUTTON_PADDING, 30))) {
|
||||
SDL_MaximizeWindow(stream_window_);
|
||||
window_maximized_ = !window_maximized_;
|
||||
}
|
||||
draw_list->AddRect(ImVec2(maximize_pos_x, maximize_pos_y),
|
||||
ImVec2(maximize_pos_x + 12, maximize_pos_y + 12),
|
||||
IM_COL32(0, 0, 0, 255));
|
||||
}
|
||||
ImGui::PopStyleColor(2);
|
||||
}
|
||||
|
||||
ImGui::SetCursorPosX(
|
||||
(main_window ? main_window_width_ : stream_window_width_) -
|
||||
BUTTON_PADDING);
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(1.0f, 0, 0, 1.0f));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(1.0f, 0, 0, 0.5f));
|
||||
|
||||
float xmark_pos_x = ImGui::GetCursorPosX() + 18;
|
||||
float xmark_pos_y = ImGui::GetCursorPosY() + 16;
|
||||
float xmark_size = 12.0f;
|
||||
std::string close_button = "##xmark"; // ICON_FA_XMARK;
|
||||
if (ImGui::Button(close_button.c_str(), ImVec2(BUTTON_PADDING, 30))) {
|
||||
SDL_Event event;
|
||||
event.type = SDL_QUIT;
|
||||
SDL_PushEvent(&event);
|
||||
}
|
||||
draw_list->AddLine(ImVec2(xmark_pos_x - xmark_size / 2 - 0.25f,
|
||||
xmark_pos_y - xmark_size / 2 + 0.75f),
|
||||
ImVec2(xmark_pos_x + xmark_size / 2 - 1.5f,
|
||||
xmark_pos_y + xmark_size / 2 - 0.5f),
|
||||
IM_COL32(0, 0, 0, 255));
|
||||
draw_list->AddLine(ImVec2(xmark_pos_x + xmark_size / 2 - 1.75f,
|
||||
xmark_pos_y - xmark_size / 2 + 0.75f),
|
||||
ImVec2(xmark_pos_x - xmark_size / 2,
|
||||
xmark_pos_y + xmark_size / 2 - 1.0f),
|
||||
IM_COL32(0, 0, 0, 255));
|
||||
|
||||
ImGui::PopStyleColor(2);
|
||||
|
||||
ImGui::PopStyleColor();
|
||||
}
|
||||
|
||||
ImGui::EndMenuBar();
|
||||
ImGui::EndChild();
|
||||
ImGui::PopStyleColor();
|
||||
return 0;
|
||||
}
|
||||
13
src/speaker_capturer/linux/speaker_capturer_linux.cpp
Normal file
13
src/speaker_capturer/linux/speaker_capturer_linux.cpp
Normal file
@@ -0,0 +1,13 @@
|
||||
#include "speaker_capturer_linux.h"
|
||||
|
||||
SpeakerCapturerLinux::SpeakerCapturerLinux() {}
|
||||
|
||||
SpeakerCapturerLinux::~SpeakerCapturerLinux() {}
|
||||
|
||||
int SpeakerCapturerLinux::Init(speaker_data_cb cb) { return 0; }
|
||||
int SpeakerCapturerLinux::Destroy() { return 0; }
|
||||
int SpeakerCapturerLinux::Start() { return 0; }
|
||||
int SpeakerCapturerLinux::Stop() { return 0; }
|
||||
|
||||
int SpeakerCapturerLinux::Pause() { return 0; }
|
||||
int SpeakerCapturerLinux::Resume() { return 0; }
|
||||
38
src/speaker_capturer/linux/speaker_capturer_linux.h
Normal file
38
src/speaker_capturer/linux/speaker_capturer_linux.h
Normal file
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* @Author: DI JUNKUN
|
||||
* @Date: 2024-08-02
|
||||
* Copyright (c) 2024 by DI JUNKUN, All Rights Reserved.
|
||||
*/
|
||||
|
||||
#ifndef _SPEAKER_CAPTURER_LINUX_H_
|
||||
#define _SPEAKER_CAPTURER_LINUX_H_
|
||||
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
#include "speaker_capturer.h"
|
||||
|
||||
class SpeakerCapturerLinux : public SpeakerCapturer {
|
||||
public:
|
||||
SpeakerCapturerLinux();
|
||||
~SpeakerCapturerLinux();
|
||||
|
||||
public:
|
||||
virtual int Init(speaker_data_cb cb);
|
||||
virtual int Destroy();
|
||||
virtual int Start();
|
||||
virtual int Stop();
|
||||
|
||||
int Pause();
|
||||
int Resume();
|
||||
|
||||
private:
|
||||
speaker_data_cb cb_ = nullptr;
|
||||
|
||||
private:
|
||||
bool inited_ = false;
|
||||
// thread
|
||||
std::thread capture_thread_;
|
||||
};
|
||||
|
||||
#endif
|
||||
14
src/speaker_capturer/macosx/speaker_capturer_macosx.cpp
Normal file
14
src/speaker_capturer/macosx/speaker_capturer_macosx.cpp
Normal file
@@ -0,0 +1,14 @@
|
||||
|
||||
#include "speaker_capturer_macosx.h"
|
||||
|
||||
SpeakerCapturerMacosx::SpeakerCapturerMacosx() {}
|
||||
|
||||
SpeakerCapturerMacosx::~SpeakerCapturerMacosx() {}
|
||||
|
||||
int SpeakerCapturerMacosx::Init(speaker_data_cb cb) { return 0; }
|
||||
int SpeakerCapturerMacosx::Destroy() { return 0; }
|
||||
int SpeakerCapturerMacosx::Start() { return 0; }
|
||||
int SpeakerCapturerMacosx::Stop() { return 0; }
|
||||
|
||||
int SpeakerCapturerMacosx::Pause() { return 0; }
|
||||
int SpeakerCapturerMacosx::Resume() { return 0; }
|
||||
38
src/speaker_capturer/macosx/speaker_capturer_macosx.h
Normal file
38
src/speaker_capturer/macosx/speaker_capturer_macosx.h
Normal file
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* @Author: DI JUNKUN
|
||||
* @Date: 2024-08-02
|
||||
* Copyright (c) 2024 by DI JUNKUN, All Rights Reserved.
|
||||
*/
|
||||
|
||||
#ifndef _SPEAKER_CAPTURER_MACOSX_H_
|
||||
#define _SPEAKER_CAPTURER_MACOSX_H_
|
||||
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
#include "speaker_capturer.h"
|
||||
|
||||
class SpeakerCapturerMacosx : public SpeakerCapturer {
|
||||
public:
|
||||
SpeakerCapturerMacosx();
|
||||
~SpeakerCapturerMacosx();
|
||||
|
||||
public:
|
||||
virtual int Init(speaker_data_cb cb);
|
||||
virtual int Destroy();
|
||||
virtual int Start();
|
||||
virtual int Stop();
|
||||
|
||||
int Pause();
|
||||
int Resume();
|
||||
|
||||
private:
|
||||
speaker_data_cb cb_ = nullptr;
|
||||
|
||||
private:
|
||||
bool inited_ = false;
|
||||
// thread
|
||||
std::thread capture_thread_;
|
||||
};
|
||||
|
||||
#endif
|
||||
26
src/speaker_capturer/speaker_capturer.h
Normal file
26
src/speaker_capturer/speaker_capturer.h
Normal file
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* @Author: DI JUNKUN
|
||||
* @Date: 2024-07-22
|
||||
* Copyright (c) 2024 by DI JUNKUN, All Rights Reserved.
|
||||
*/
|
||||
|
||||
#ifndef _SPEAKER_CAPTURER_H_
|
||||
#define _SPEAKER_CAPTURER_H_
|
||||
|
||||
#include <functional>
|
||||
|
||||
class SpeakerCapturer {
|
||||
public:
|
||||
typedef std::function<void(unsigned char *, size_t)> speaker_data_cb;
|
||||
|
||||
public:
|
||||
virtual ~SpeakerCapturer() {}
|
||||
|
||||
public:
|
||||
virtual int Init(speaker_data_cb cb) = 0;
|
||||
virtual int Destroy() = 0;
|
||||
virtual int Start() = 0;
|
||||
virtual int Stop() = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
36
src/speaker_capturer/speaker_capturer_factory.h
Normal file
36
src/speaker_capturer/speaker_capturer_factory.h
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* @Author: DI JUNKUN
|
||||
* @Date: 2024-07-22
|
||||
* Copyright (c) 2024 by DI JUNKUN, All Rights Reserved.
|
||||
*/
|
||||
|
||||
#ifndef _SPEAKER_CAPTURER_FACTORY_H_
|
||||
#define _SPEAKER_CAPTURER_FACTORY_H_
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "speaker_capturer_wasapi.h"
|
||||
#elif __linux__
|
||||
#include "speaker_capturer_linux.h"
|
||||
#elif __APPLE__
|
||||
#include "speaker_capturer_macosx.h"
|
||||
#endif
|
||||
|
||||
class SpeakerCapturerFactory {
|
||||
public:
|
||||
virtual ~SpeakerCapturerFactory() {}
|
||||
|
||||
public:
|
||||
SpeakerCapturer* Create() {
|
||||
#ifdef _WIN32
|
||||
return new SpeakerCapturerWasapi();
|
||||
#elif __linux__
|
||||
return new SpeakerCapturerLinux();
|
||||
#elif __APPLE__
|
||||
return new SpeakerCapturerMacosx();
|
||||
#else
|
||||
return nullptr;
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
100
src/speaker_capturer/windows/speaker_capturer_wasapi.cpp
Normal file
100
src/speaker_capturer/windows/speaker_capturer_wasapi.cpp
Normal file
@@ -0,0 +1,100 @@
|
||||
#include "speaker_capturer_wasapi.h"
|
||||
|
||||
#include "rd_log.h"
|
||||
|
||||
#define MINIAUDIO_IMPLEMENTATION
|
||||
#include "miniaudio.h"
|
||||
|
||||
#define SAVE_AUDIO_FILE 0
|
||||
|
||||
static ma_device_config device_config_;
|
||||
static ma_device device_;
|
||||
static ma_format format_ = ma_format_s16;
|
||||
static ma_uint32 sample_rate_ = ma_standard_sample_rate_48000;
|
||||
static ma_uint32 channels_ = 1;
|
||||
static FILE* fp_ = nullptr;
|
||||
|
||||
void data_callback(ma_device* pDevice, void* pOutput, const void* pInput,
|
||||
ma_uint32 frameCount) {
|
||||
SpeakerCapturerWasapi* ptr = (SpeakerCapturerWasapi*)pDevice->pUserData;
|
||||
if (ptr) {
|
||||
if (SAVE_AUDIO_FILE) {
|
||||
fwrite(pInput, frameCount * ma_get_bytes_per_frame(format_, channels_), 1,
|
||||
fp_);
|
||||
}
|
||||
|
||||
ptr->GetCallback()((unsigned char*)pInput,
|
||||
frameCount * ma_get_bytes_per_frame(format_, channels_));
|
||||
}
|
||||
|
||||
(void)pOutput;
|
||||
}
|
||||
|
||||
SpeakerCapturerWasapi::speaker_data_cb SpeakerCapturerWasapi::GetCallback() {
|
||||
return cb_;
|
||||
}
|
||||
|
||||
SpeakerCapturerWasapi::SpeakerCapturerWasapi() {}
|
||||
|
||||
SpeakerCapturerWasapi::~SpeakerCapturerWasapi() {
|
||||
if (SAVE_AUDIO_FILE) {
|
||||
fclose(fp_);
|
||||
}
|
||||
}
|
||||
|
||||
int SpeakerCapturerWasapi::Init(speaker_data_cb cb) {
|
||||
if (inited_) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
cb_ = cb;
|
||||
|
||||
if (SAVE_AUDIO_FILE) {
|
||||
fopen_s(&fp_, "system_audio.pcm", "wb");
|
||||
}
|
||||
|
||||
ma_result result;
|
||||
ma_backend backends[] = {ma_backend_wasapi};
|
||||
|
||||
device_config_ = ma_device_config_init(ma_device_type_loopback);
|
||||
device_config_.capture.pDeviceID = NULL;
|
||||
device_config_.capture.format = format_;
|
||||
device_config_.capture.channels = channels_;
|
||||
device_config_.sampleRate = sample_rate_;
|
||||
device_config_.dataCallback = data_callback;
|
||||
device_config_.pUserData = this;
|
||||
|
||||
result = ma_device_init_ex(backends, sizeof(backends) / sizeof(backends[0]),
|
||||
NULL, &device_config_, &device_);
|
||||
if (result != MA_SUCCESS) {
|
||||
LOG_ERROR("Failed to initialize loopback device");
|
||||
return -1;
|
||||
}
|
||||
|
||||
inited_ = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int SpeakerCapturerWasapi::Start() {
|
||||
ma_result result = ma_device_start(&device_);
|
||||
if (result != MA_SUCCESS) {
|
||||
ma_device_uninit(&device_);
|
||||
LOG_ERROR("Failed to start device");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int SpeakerCapturerWasapi::Stop() {
|
||||
ma_device_stop(&device_);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int SpeakerCapturerWasapi::Destroy() {
|
||||
ma_device_uninit(&device_);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int SpeakerCapturerWasapi::Pause() { return 0; }
|
||||
35
src/speaker_capturer/windows/speaker_capturer_wasapi.h
Normal file
35
src/speaker_capturer/windows/speaker_capturer_wasapi.h
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* @Author: DI JUNKUN
|
||||
* @Date: 2024-08-15
|
||||
* Copyright (c) 2024 by DI JUNKUN, All Rights Reserved.
|
||||
*/
|
||||
|
||||
#ifndef _SPEAKER_CAPTURER_WASAPI_H_
|
||||
#define _SPEAKER_CAPTURER_WASAPI_H_
|
||||
|
||||
#include "speaker_capturer.h"
|
||||
|
||||
class SpeakerCapturerWasapi : public SpeakerCapturer {
|
||||
public:
|
||||
SpeakerCapturerWasapi();
|
||||
~SpeakerCapturerWasapi();
|
||||
|
||||
public:
|
||||
virtual int Init(speaker_data_cb cb);
|
||||
virtual int Destroy();
|
||||
virtual int Start();
|
||||
virtual int Stop();
|
||||
|
||||
int Pause();
|
||||
int Resume();
|
||||
|
||||
speaker_data_cb GetCallback();
|
||||
|
||||
private:
|
||||
speaker_data_cb cb_ = nullptr;
|
||||
|
||||
private:
|
||||
bool inited_ = false;
|
||||
};
|
||||
|
||||
#endif
|
||||
303
test/audio_capture/audio_capture_wasapi.cpp
Normal file
303
test/audio_capture/audio_capture_wasapi.cpp
Normal file
@@ -0,0 +1,303 @@
|
||||
// MyAudioSink.cpp : 定义控制台应用程序的入口点。
|
||||
//
|
||||
|
||||
// #define _CRT_SECURE_NO_WARNINGS
|
||||
|
||||
#include <Audioclient.h>
|
||||
#include <Devicetopology.h>
|
||||
#include <Endpointvolume.h>
|
||||
#include <Mmdeviceapi.h>
|
||||
#include <tchar.h>
|
||||
|
||||
#include <iostream>
|
||||
//-----------------------------------------------------------
|
||||
// Record an audio stream from the default audio capture
|
||||
// device. The RecordAudioStream function allocates a shared
|
||||
// buffer big enough to hold one second of PCM audio data.
|
||||
// The function uses this buffer to stream data from the
|
||||
// capture device. The main loop runs every 1/2 second.
|
||||
//-----------------------------------------------------------
|
||||
|
||||
// REFERENCE_TIME time units per second and per millisecond
|
||||
#define REFTIMES_PER_SEC 10000000
|
||||
#define REFTIMES_PER_MILLISEC 10000
|
||||
|
||||
#define EXIT_ON_ERROR(hres) \
|
||||
if (FAILED(hres)) { \
|
||||
goto Exit; \
|
||||
}
|
||||
|
||||
#define SAFE_RELEASE(punk) \
|
||||
if ((punk) != NULL) { \
|
||||
(punk)->Release(); \
|
||||
(punk) = NULL; \
|
||||
}
|
||||
|
||||
#define IS_INPUT_DEVICE 0 // 切换输入和输出音频设备
|
||||
|
||||
#define BUFFER_TIME_100NS (5 * 10000000)
|
||||
|
||||
const CLSID CLSID_MMDeviceEnumerator = __uuidof(MMDeviceEnumerator);
|
||||
const IID IID_IMMDeviceEnumerator = __uuidof(IMMDeviceEnumerator);
|
||||
const IID IID_IAudioClient = __uuidof(IAudioClient);
|
||||
const IID IID_IAudioCaptureClient = __uuidof(IAudioCaptureClient);
|
||||
|
||||
const IID IID_IDeviceTopology = __uuidof(IDeviceTopology);
|
||||
const IID IID_IAudioVolumeLevel = __uuidof(IAudioVolumeLevel);
|
||||
const IID IID_IPart = __uuidof(IPart);
|
||||
const IID IID_IConnector = __uuidof(IConnector);
|
||||
const IID IID_IAudioEndpointVolume = __uuidof(IAudioEndpointVolume);
|
||||
|
||||
class MyAudioSink {
|
||||
public:
|
||||
// WAVEFORMATEX *pwfx = NULL;
|
||||
int SetFormat(WAVEFORMATEX *pwfx);
|
||||
|
||||
int CopyData(SHORT *pData, UINT32 numFramesAvailable, BOOL *pbDone);
|
||||
};
|
||||
|
||||
int MyAudioSink::SetFormat(WAVEFORMATEX *pwfx) {
|
||||
printf("wFormatTag is %x\n", pwfx->wFormatTag);
|
||||
printf("nChannels is %x\n", pwfx->nChannels);
|
||||
printf("nSamplesPerSec is %d\n", pwfx->nSamplesPerSec);
|
||||
printf("nAvgBytesPerSec is %d\n", pwfx->nAvgBytesPerSec);
|
||||
printf("wBitsPerSample is %d\n", pwfx->wBitsPerSample);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
FILE *fp;
|
||||
|
||||
int MyAudioSink::CopyData(SHORT *pData, UINT32 numFramesAvailable,
|
||||
BOOL *pbDone) {
|
||||
if (pData != NULL) {
|
||||
size_t t = sizeof(SHORT);
|
||||
for (int i = 0; i < numFramesAvailable / t; i++) {
|
||||
double dbVal = pData[i];
|
||||
pData[i] = dbVal; // 可以通过不同的分母来控制声音大小
|
||||
}
|
||||
fwrite(pData, numFramesAvailable, 1, fp);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// pwfx->nSamplesPerSec = 44100;
|
||||
/// 不支持修改采样率, 看来只能等得到数据之后再 swr 转换了
|
||||
BOOL AdjustFormatTo16Bits(WAVEFORMATEX *pwfx) {
|
||||
BOOL bRet(FALSE);
|
||||
|
||||
if (pwfx->wFormatTag == WAVE_FORMAT_IEEE_FLOAT) {
|
||||
pwfx->wFormatTag = WAVE_FORMAT_PCM;
|
||||
pwfx->wBitsPerSample = 16;
|
||||
pwfx->nBlockAlign = pwfx->nChannels * pwfx->wBitsPerSample / 8;
|
||||
pwfx->nAvgBytesPerSec = pwfx->nBlockAlign * pwfx->nSamplesPerSec;
|
||||
|
||||
bRet = TRUE;
|
||||
} else if (pwfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
|
||||
PWAVEFORMATEXTENSIBLE pEx = reinterpret_cast<PWAVEFORMATEXTENSIBLE>(pwfx);
|
||||
if (IsEqualGUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, pEx->SubFormat)) {
|
||||
pEx->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
|
||||
pEx->Samples.wValidBitsPerSample = 16;
|
||||
pwfx->wBitsPerSample = 16;
|
||||
pwfx->nBlockAlign = pwfx->nChannels * pwfx->wBitsPerSample / 8;
|
||||
pwfx->nAvgBytesPerSec = pwfx->nBlockAlign * pwfx->nSamplesPerSec;
|
||||
|
||||
bRet = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
return bRet;
|
||||
}
|
||||
|
||||
typedef unsigned long long uint64_t;
|
||||
static bool have_clockfreq = false;
|
||||
static LARGE_INTEGER clock_freq;
|
||||
static inline uint64_t get_clockfreq(void) {
|
||||
if (!have_clockfreq) QueryPerformanceFrequency(&clock_freq);
|
||||
return clock_freq.QuadPart;
|
||||
}
|
||||
uint64_t os_gettime_ns(void) {
|
||||
LARGE_INTEGER current_time;
|
||||
double time_val;
|
||||
|
||||
QueryPerformanceCounter(¤t_time);
|
||||
time_val = (double)current_time.QuadPart;
|
||||
time_val *= 1000000000.0;
|
||||
time_val /= (double)get_clockfreq();
|
||||
|
||||
return (uint64_t)time_val;
|
||||
}
|
||||
|
||||
HRESULT RecordAudioStream(MyAudioSink *pMySink) {
|
||||
HRESULT hr;
|
||||
REFERENCE_TIME hnsActualDuration;
|
||||
UINT32 bufferFrameCount;
|
||||
UINT32 numFramesAvailable;
|
||||
BYTE *pData;
|
||||
DWORD flags;
|
||||
REFERENCE_TIME hnsDefaultDevicePeriod(0);
|
||||
|
||||
REFERENCE_TIME hnsRequestedDuration = REFTIMES_PER_SEC;
|
||||
IMMDeviceEnumerator *pEnumerator = NULL;
|
||||
IMMDevice *pDevice = NULL;
|
||||
IAudioClient *pAudioClient = NULL;
|
||||
IAudioCaptureClient *pCaptureClient = NULL;
|
||||
WAVEFORMATEX *pwfx = NULL;
|
||||
UINT32 packetLength = 0;
|
||||
BOOL bDone = FALSE;
|
||||
HANDLE hTimerWakeUp = NULL;
|
||||
UINT64 pos, ts;
|
||||
|
||||
hr = CoCreateInstance(CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL,
|
||||
IID_IMMDeviceEnumerator, (void **)&pEnumerator);
|
||||
|
||||
EXIT_ON_ERROR(hr)
|
||||
|
||||
if (IS_INPUT_DEVICE)
|
||||
hr = pEnumerator->GetDefaultAudioEndpoint(eCapture, eCommunications,
|
||||
&pDevice); // 输入
|
||||
else
|
||||
hr = pEnumerator->GetDefaultAudioEndpoint(eRender, eConsole,
|
||||
&pDevice); // 输出
|
||||
|
||||
// wchar_t *w_id;
|
||||
// os_utf8_to_wcs_ptr(device_id.c_str(), device_id.size(), &w_id);
|
||||
// hr = pEnumerator->GetDevice(w_id, &pDevice);
|
||||
// bfree(w_id);
|
||||
|
||||
EXIT_ON_ERROR(hr)
|
||||
|
||||
hr = pDevice->Activate(IID_IAudioClient, CLSCTX_ALL, NULL,
|
||||
(void **)&pAudioClient);
|
||||
|
||||
EXIT_ON_ERROR(hr)
|
||||
|
||||
hr = pAudioClient->GetMixFormat(&pwfx);
|
||||
|
||||
EXIT_ON_ERROR(hr)
|
||||
|
||||
// The GetDevicePeriod method retrieves the length of the periodic interval
|
||||
// separating successive processing passes by the audio engine on the data in
|
||||
// the endpoint buffer.
|
||||
hr = pAudioClient->GetDevicePeriod(&hnsDefaultDevicePeriod, NULL);
|
||||
|
||||
EXIT_ON_ERROR(hr)
|
||||
|
||||
AdjustFormatTo16Bits(pwfx);
|
||||
|
||||
// 平时创建定时器使用的是WINAPI SetTimer,不过该函数一般用于有界面的时候。
|
||||
// 无界面的情况下,可以选择微软提供的CreateWaitableTimer和SetWaitableTimer
|
||||
// API。
|
||||
hTimerWakeUp = CreateWaitableTimer(NULL, FALSE, NULL);
|
||||
|
||||
DWORD flag;
|
||||
if (IS_INPUT_DEVICE)
|
||||
flag = 0;
|
||||
else
|
||||
flag = AUDCLNT_STREAMFLAGS_LOOPBACK;
|
||||
|
||||
if (IS_INPUT_DEVICE)
|
||||
hr = pAudioClient->Initialize(AUDCLNT_SHAREMODE_SHARED, flag /*0*/, 0, 0,
|
||||
pwfx, NULL); // 输入
|
||||
else
|
||||
hr = pAudioClient->Initialize(AUDCLNT_SHAREMODE_SHARED, flag /*0*/, 0, 0,
|
||||
pwfx, NULL); // 输出
|
||||
|
||||
EXIT_ON_ERROR(hr)
|
||||
|
||||
// Get the size of the allocated buffer.
|
||||
hr = pAudioClient->GetBufferSize(&bufferFrameCount);
|
||||
EXIT_ON_ERROR(hr)
|
||||
|
||||
hr = pAudioClient->GetService(IID_IAudioCaptureClient,
|
||||
(void **)&pCaptureClient);
|
||||
|
||||
EXIT_ON_ERROR(hr)
|
||||
|
||||
LARGE_INTEGER liFirstFire;
|
||||
liFirstFire.QuadPart =
|
||||
-hnsDefaultDevicePeriod / 2; // negative means relative time
|
||||
LONG lTimeBetweenFires = (LONG)hnsDefaultDevicePeriod / 2 /
|
||||
(10 * 1000); // convert to milliseconds
|
||||
|
||||
BOOL bOK = SetWaitableTimer(hTimerWakeUp, &liFirstFire, lTimeBetweenFires,
|
||||
NULL, NULL, FALSE);
|
||||
|
||||
// Notify the audio sink which format to use.
|
||||
hr = pMySink->SetFormat(pwfx);
|
||||
EXIT_ON_ERROR(hr)
|
||||
|
||||
// Calculate the actual duration of the allocated buffer.
|
||||
hnsActualDuration =
|
||||
(double)REFTIMES_PER_SEC * bufferFrameCount / pwfx->nSamplesPerSec;
|
||||
|
||||
/*************************************************************/
|
||||
hr = pAudioClient->Start(); // Start recording.
|
||||
EXIT_ON_ERROR(hr)
|
||||
HANDLE waitArray[1] = {/*htemp hEventStop,*/ hTimerWakeUp};
|
||||
|
||||
// Each loop fills about half of the shared buffer.
|
||||
while (bDone == FALSE) {
|
||||
// Sleep for half the buffer duration.
|
||||
// Sleep(hnsActualDuration/REFTIMES_PER_MILLISEC/2);//这句貌似不加也可以
|
||||
// WaitForSingleObject(hTimerWakeUp,INFINITE);
|
||||
int a = sizeof(waitArray);
|
||||
int aa = sizeof(waitArray[0]);
|
||||
WaitForMultipleObjects(sizeof(waitArray) / sizeof(waitArray[0]), waitArray,
|
||||
FALSE, INFINITE);
|
||||
// WaitForMultipleObjects(sizeof(waitArray) / sizeof(waitArray[0]),
|
||||
// waitArray, FALSE, INFINITE);
|
||||
|
||||
hr = pCaptureClient->GetNextPacketSize(&packetLength);
|
||||
EXIT_ON_ERROR(hr)
|
||||
|
||||
while (packetLength != 0) {
|
||||
// Get the available data in the shared buffer.
|
||||
hr = pCaptureClient->GetBuffer(&pData, &numFramesAvailable, &flags, NULL,
|
||||
&ts);
|
||||
ts = ts * 100;
|
||||
uint64_t timestamp =
|
||||
os_gettime_ns(); // ts是设备时间,timestamp是系统时间
|
||||
EXIT_ON_ERROR(hr)
|
||||
|
||||
// 位运算,flags的标志符为2(静音状态)时,将pData置为NULL
|
||||
if (flags & AUDCLNT_BUFFERFLAGS_SILENT) {
|
||||
pData = NULL; // Tell CopyData to write silence.
|
||||
}
|
||||
|
||||
// Copy the available capture data to the audio sink.
|
||||
hr = pMySink->CopyData((SHORT *)pData,
|
||||
numFramesAvailable * pwfx->nBlockAlign, &bDone);
|
||||
EXIT_ON_ERROR(hr)
|
||||
|
||||
hr = pCaptureClient->ReleaseBuffer(numFramesAvailable);
|
||||
EXIT_ON_ERROR(hr)
|
||||
|
||||
hr = pCaptureClient->GetNextPacketSize(&packetLength);
|
||||
EXIT_ON_ERROR(hr)
|
||||
}
|
||||
}
|
||||
|
||||
hr = pAudioClient->Stop(); // Stop recording.
|
||||
EXIT_ON_ERROR(hr)
|
||||
|
||||
Exit:
|
||||
CoTaskMemFree(pwfx);
|
||||
SAFE_RELEASE(pEnumerator)
|
||||
SAFE_RELEASE(pDevice)
|
||||
SAFE_RELEASE(pAudioClient)
|
||||
SAFE_RELEASE(pCaptureClient)
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
int _tmain(int argc, _TCHAR *argv[]) {
|
||||
fopen_s(&fp, "record.pcm", "wb");
|
||||
CoInitialize(NULL);
|
||||
MyAudioSink test;
|
||||
|
||||
RecordAudioStream(&test);
|
||||
|
||||
return 0;
|
||||
}
|
||||
96
test/audio_capture/miniaudio.cpp
Normal file
96
test/audio_capture/miniaudio.cpp
Normal file
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
Demonstrates how to implement loopback recording.
|
||||
|
||||
This example simply captures data from your default playback device until you
|
||||
press Enter. The output is saved to the file specified on the command line.
|
||||
|
||||
Loopback mode is when you record audio that is played from a given speaker. It
|
||||
is only supported on WASAPI, but can be used indirectly with PulseAudio by
|
||||
choosing the appropriate loopback device after enumeration.
|
||||
|
||||
To use loopback mode you just need to set the device type to
|
||||
ma_device_type_loopback and set the capture device config properties. The output
|
||||
buffer in the callback will be null whereas the input buffer will be valid.
|
||||
*/
|
||||
#define MINIAUDIO_IMPLEMENTATION
|
||||
#include "miniaudio.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
FILE* fp;
|
||||
|
||||
void data_callback(ma_device* pDevice, void* pOutput, const void* pInput,
|
||||
ma_uint32 frameCount) {
|
||||
// ma_encoder* pEncoder = (ma_encoder*)pDevice->pUserData;
|
||||
// MA_ASSERT(pEncoder != NULL);
|
||||
|
||||
// ma_encoder_write_pcm_frames(pEncoder, pInput, frameCount, NULL);
|
||||
|
||||
fwrite(pInput, frameCount * ma_get_bytes_per_frame(ma_format_s16, 1), 1, fp);
|
||||
|
||||
(void)pOutput;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
ma_result result;
|
||||
ma_encoder_config encoderConfig;
|
||||
ma_encoder encoder;
|
||||
ma_device_config deviceConfig;
|
||||
ma_device device;
|
||||
|
||||
fopen_s(&fp, "miniaudio.pcm", "wb");
|
||||
|
||||
/* Loopback mode is currently only supported on WASAPI. */
|
||||
ma_backend backends[] = {ma_backend_wasapi};
|
||||
|
||||
// if (argc < 2) {
|
||||
// printf("No output file.\n");
|
||||
// return -1;
|
||||
// }
|
||||
|
||||
// encoderConfig =
|
||||
// ma_encoder_config_init(ma_encoding_format_wav, ma_format_s16, 1,
|
||||
// 48000);
|
||||
|
||||
// if (ma_encoder_init_file(argv[1], &encoderConfig, &encoder) != MA_SUCCESS)
|
||||
// {
|
||||
// printf("Failed to initialize output file.\n");
|
||||
// return -1;
|
||||
// }
|
||||
|
||||
deviceConfig = ma_device_config_init(ma_device_type_loopback);
|
||||
deviceConfig.capture.pDeviceID =
|
||||
NULL; /* Use default device for this example. Set this to the ID of a
|
||||
_playback_ device if you want to capture from a specific device.
|
||||
*/
|
||||
deviceConfig.capture.format = ma_format_s16;
|
||||
deviceConfig.capture.channels = 1;
|
||||
deviceConfig.sampleRate = 48000;
|
||||
deviceConfig.dataCallback = data_callback;
|
||||
deviceConfig.pUserData = nullptr;
|
||||
|
||||
result = ma_device_init_ex(backends, sizeof(backends) / sizeof(backends[0]),
|
||||
NULL, &deviceConfig, &device);
|
||||
if (result != MA_SUCCESS) {
|
||||
printf("Failed to initialize loopback device.\n");
|
||||
return -2;
|
||||
}
|
||||
|
||||
result = ma_device_start(&device);
|
||||
if (result != MA_SUCCESS) {
|
||||
ma_device_uninit(&device);
|
||||
printf("Failed to start device.\n");
|
||||
return -3;
|
||||
}
|
||||
|
||||
printf("Press Enter to stop recording...\n");
|
||||
getchar();
|
||||
|
||||
fclose(fp);
|
||||
|
||||
ma_device_uninit(&device);
|
||||
// ma_encoder_uninit(&encoder);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -228,7 +228,7 @@ int main(int argc, char *argv[]) {
|
||||
// Event Loop
|
||||
SDL_Event event;
|
||||
|
||||
last_frame_time = std::chrono::high_resolution_clock::now();
|
||||
last_frame_time = std::chrono::steady_clock::now();
|
||||
|
||||
for (;;) {
|
||||
// Wait
|
||||
@@ -249,7 +249,7 @@ int main(int argc, char *argv[]) {
|
||||
}
|
||||
printf("xxxxxxxxxxxxxxxxxxx\n");
|
||||
if (!got_picture) {
|
||||
auto now_time = std::chrono::high_resolution_clock::now();
|
||||
auto now_time = std::chrono::steady_clock::now();
|
||||
std::chrono::duration<double> duration = now_time - last_frame_time;
|
||||
auto tc = duration.count() * 1000;
|
||||
printf("duration: %f\n", tc);
|
||||
|
||||
2
thirdparty/projectx
vendored
2
thirdparty/projectx
vendored
Submodule thirdparty/projectx updated: a309627ca3...49d6307154
10
thirdparty/sdl2/xmake.lua
vendored
10
thirdparty/sdl2/xmake.lua
vendored
@@ -1,10 +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")
|
||||
|
||||
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()
|
||||
2
thirdparty/xmake.lua
vendored
2
thirdparty/xmake.lua
vendored
@@ -1,4 +1,4 @@
|
||||
includes("sdl2", "projectx")
|
||||
includes("projectx")
|
||||
if is_plat("windows") then
|
||||
elseif is_plat("linux") then
|
||||
includes("ffmpeg")
|
||||
|
||||
138
xmake.lua
138
xmake.lua
@@ -1,9 +1,12 @@
|
||||
set_project("remote_desk")
|
||||
set_version("0.0.1")
|
||||
set_license("LGPL-3.0")
|
||||
|
||||
set_version("0.0.1")
|
||||
add_defines("RD_VERSION=\"0.0.1\"");
|
||||
|
||||
add_rules("mode.release", "mode.debug")
|
||||
set_languages("c++17")
|
||||
set_encodings("utf-8")
|
||||
|
||||
-- set_policy("build.warning", true)
|
||||
-- set_warnings("all", "extra")
|
||||
@@ -13,19 +16,19 @@ 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 v1.91.5-docking", {configs = {sdl2 = true, sdl2_renderer = true}})
|
||||
add_requires("miniaudio 0.11.21")
|
||||
add_requires("openssl3 3.3.2", {system = false})
|
||||
|
||||
if is_os("windows") then
|
||||
add_requires("libyuv")
|
||||
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")
|
||||
add_cxflags("/WX")
|
||||
elseif is_os("linux") then
|
||||
add_requires("ffmpeg 5.1.2", {system = false})
|
||||
add_packages("ffmpeg")
|
||||
add_syslinks("pthread", "dl")
|
||||
add_linkdirs("thirdparty/projectx/thirdparty/nvcodec/Lib/x64")
|
||||
add_links("SDL2", "cuda", "nvidia-encode", "nvcuvid")
|
||||
@@ -34,73 +37,134 @@ elseif is_os("linux") then
|
||||
"-lasound", "-lxcb-shape", "-lxcb-xfixes", "-lsndio", "-lxcb",
|
||||
"-lxcb-shm", "-lXext", "-lX11", "-lXv", "-ldl", "-lpthread",
|
||||
{force = true})
|
||||
add_cxflags("-Wno-unused-variable")
|
||||
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")
|
||||
add_cxflags("-Wno-unused-variable")
|
||||
add_frameworks("OpenGL", "IOSurface", "ScreenCaptureKit")
|
||||
end
|
||||
|
||||
add_packages("spdlog", "sdl2", "imgui")
|
||||
add_packages("spdlog", "imgui")
|
||||
|
||||
includes("thirdparty")
|
||||
|
||||
target("log")
|
||||
target("rd_log")
|
||||
set_kind("object")
|
||||
add_packages("spdlog")
|
||||
add_headerfiles("src/log/log.h")
|
||||
add_files("src/log/rd_log.cpp")
|
||||
add_includedirs("src/log", {public = true})
|
||||
|
||||
target("common")
|
||||
set_kind("object")
|
||||
add_deps("log")
|
||||
add_deps("rd_log")
|
||||
add_files("src/common/*.cpp")
|
||||
add_includedirs("src/common", {public = true})
|
||||
|
||||
target("screen_capturer")
|
||||
set_kind("object")
|
||||
add_deps("log")
|
||||
add_deps("rd_log")
|
||||
add_includedirs("src/screen_capturer", {public = true})
|
||||
if is_os("windows") then
|
||||
add_packages("libyuv")
|
||||
add_files("src/screen_capturer/windows/*.cpp")
|
||||
add_includedirs("src/screen_capturer/windows", {public = true})
|
||||
elseif is_os("macosx") then
|
||||
add_files("src/screen_capturer/macosx/*.cpp")
|
||||
add_includedirs("src/screen_capturer/macosx", {public = true})
|
||||
add_packages("ffmpeg")
|
||||
add_files("src/screen_capturer/macosx/avfoundation/*.cpp",
|
||||
"src/screen_capturer/macosx/screen_capturer_kit/*.cpp",
|
||||
"src/screen_capturer/macosx/screen_capturer_kit/*.mm")
|
||||
add_includedirs("src/screen_capturer/macosx/avfoundation",
|
||||
"src/screen_capturer/macosx/screen_capturer_kit", {public = true})
|
||||
elseif is_os("linux") then
|
||||
add_files("src/screen_capturer/linux/*.cpp")
|
||||
add_includedirs("src/screen_capturer/linux", {public = true})
|
||||
add_packages("ffmpeg")
|
||||
add_files("src/screen_capturer/linux/*.cpp")
|
||||
add_includedirs("src/screen_capturer/linux", {public = true})
|
||||
end
|
||||
|
||||
target("speaker_capturer")
|
||||
set_kind("object")
|
||||
add_deps("rd_log")
|
||||
add_includedirs("src/speaker_capturer", {public = true})
|
||||
if is_os("windows") then
|
||||
add_packages("miniaudio")
|
||||
add_files("src/speaker_capturer/windows/*.cpp")
|
||||
add_includedirs("src/speaker_capturer/windows", {public = true})
|
||||
elseif is_os("macosx") then
|
||||
add_files("src/speaker_capturer/macosx/*.cpp")
|
||||
add_includedirs("src/speaker_capturer/macosx", {public = true})
|
||||
elseif is_os("linux") then
|
||||
add_files("src/speaker_capturer/linux/*.cpp")
|
||||
add_includedirs("src/speaker_capturer/linux", {public = true})
|
||||
end
|
||||
|
||||
target("device_controller")
|
||||
set_kind("object")
|
||||
add_deps("log")
|
||||
add_deps("rd_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})
|
||||
add_files("src/device_controller/mouse/windows/*.cpp",
|
||||
"src/device_controller/keyboard/windows/*.cpp")
|
||||
add_includedirs("src/device_controller/mouse/windows",
|
||||
"src/device_controller/keyboard/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})
|
||||
add_files("src/device_controller/mouse/mac/*.cpp",
|
||||
"src/device_controller/keyboard/mac/*.cpp")
|
||||
add_includedirs("src/device_controller/mouse/mac",
|
||||
"src/device_controller/keyboard/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})
|
||||
add_files("src/device_controller/mouse/linux/*.cpp",
|
||||
"src/device_controller/keyboard/linux/*.cpp")
|
||||
add_includedirs("src/device_controller/mouse/linux",
|
||||
"src/device_controller/keyboard/linux", {public = true})
|
||||
end
|
||||
|
||||
target("config_center")
|
||||
set_kind("object")
|
||||
add_deps("rd_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("single_window")
|
||||
set_kind("object")
|
||||
add_packages("libyuv", "openssl3")
|
||||
add_deps("rd_log", "common", "localization", "config_center", "projectx", "screen_capturer", "speaker_capturer", "device_controller")
|
||||
if is_os("macosx") then
|
||||
add_packages("ffmpeg")
|
||||
elseif is_os("linux") then
|
||||
add_packages("ffmpeg")
|
||||
end
|
||||
add_files("src/single_window/*.cpp")
|
||||
add_includedirs("src/single_window", {public = true})
|
||||
add_includedirs("fonts", {public = true})
|
||||
|
||||
target("remote_desk")
|
||||
set_kind("binary")
|
||||
add_deps("log", "common", "screen_capturer", "device_controller", "projectx")
|
||||
add_deps("rd_log", "common", "single_window")
|
||||
if is_os("windows") then
|
||||
add_files("icon/app.rc")
|
||||
elseif is_os("macosx") then
|
||||
add_packages("ffmpeg")
|
||||
-- add_rules("xcode.application")
|
||||
-- add_files("Info.plist")
|
||||
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("miniaudio_capture")
|
||||
-- set_kind("binary")
|
||||
-- add_packages("miniaudio")
|
||||
-- if is_os("windows") then
|
||||
-- add_files("test/audio_capture/miniaudio.cpp")
|
||||
-- end
|
||||
|
||||
-- target("screen_capturer")
|
||||
-- set_kind("binary")
|
||||
@@ -157,7 +221,7 @@ target("remote_desk")
|
||||
-- "-lxcb-shm", "-lXext", "-lX11", "-lXv", "-lpthread", "-lSDL2", "-lopenh264",
|
||||
-- "-ldl", {force = true})
|
||||
|
||||
target("mouse_control")
|
||||
set_kind("binary")
|
||||
add_files("test/linux_mouse_control/mouse_control.cpp")
|
||||
add_includedirs("test/linux_mouse_control")
|
||||
-- target("mouse_control")
|
||||
-- set_kind("binary")
|
||||
-- add_files("test/linux_mouse_control/mouse_control.cpp")
|
||||
-- add_includedirs("test/linux_mouse_control")
|
||||
Reference in New Issue
Block a user