55 Commits

Author SHA1 Message Date
dijunkun
418ab7a1d2 [fix] fix net traffic stats display 2024-12-02 11:03:55 +08:00
dijunkun
6a2c9af316 [feat] loss rate display supported 2024-12-01 17:00:59 +08:00
dijunkun
eaabf478cc [feat] update net statistics module 2024-11-29 17:54:15 +08:00
dijunkun
ffe3ca76af [feat] switch rtc module into new branch 'qos' 2024-11-28 18:27:34 +08:00
dijunkun
c5a6302220 [fix] update rtc module 2024-11-28 15:16:48 +08:00
dijunkun
9b2f81690f [fix] fix stream window scale error 2024-11-27 20:28:42 +08:00
dijunkun
9621e6b570 [fix] fix right shift key mapping on MacOSX 2024-11-27 17:33:54 +08:00
dijunkun
ce3ae03bef [fix] prevent cursor relocation when stream window resized 2024-11-27 17:24:45 +08:00
dijunkun
e0457213ea [fix] fix display errors when stream window resized 2024-11-27 16:14:14 +08:00
dijunkun
10cdc440a0 [fix] fix key press/release event 2024-11-27 14:34:54 +08:00
dijunkun
ddc62c90bb [fix] fix all unused variables and type conversions on Windows 2024-11-27 11:17:43 +08:00
dijunkun
9e70d0e8fc [fix] fix all unused variables and type conversions on MacOSX 2024-11-27 10:57:09 +08:00
dijunkun
4533d53ba8 [fix] fix all unused variables and type conversions 2024-11-26 23:31:06 +08:00
dijunkun
0caa243006 [fix] fix unused variables and type conversions 2024-11-26 17:31:37 +08:00
dijunkun
1e58abdfdd [feat] rewrite log module 2024-11-26 16:05:28 +08:00
dijunkun
370ac08d09 [fix] use new rtc api 2024-11-26 15:06:55 +08:00
dijunkun
31b6b2736c [fix] fix control and shift keys convertion from Win to MacOSX 2024-11-25 17:29:49 +08:00
dijunkun
abd22ab7f1 [fix] fix Win/MacOSX keycodes convertion 2024-11-25 17:15:22 +08:00
dijunkun
5ab68988aa [fix] fix stream window grabbing cannot be released after connection closed 2024-11-22 18:12:36 +08:00
dijunkun
ba3edcc02a [feat] keyboard control supported on MacOSX 2024-11-22 18:05:13 +08:00
dijunkun
8414a57a5b [feat] keyboard control supported on Windows 2024-11-22 17:17:33 +08:00
dijunkun
3f777e4662 [fix] fix compile error 2024-11-22 16:45:14 +08:00
dijunkun
52828183a1 [feat] keyboard capture supported on Windows 2024-11-22 16:39:01 +08:00
dijunkun
df7489f8e2 [fix] update imgui to v1.91.5-docking to fix: inputText cannot input text after add a space in remote id 2024-11-21 18:40:42 +08:00
dijunkun
4fea7d86e1 [feat] cursor will move into remote id input box automaticlly if the last input remote id does not exist 2024-11-21 16:20:25 +08:00
dijunkun
cb17b7c8db [fix] fix setting item [enable turn] 2024-11-21 16:05:10 +08:00
dijunkun
cf7ef89bf2 [fix] fix cursor position when stream window initialization 2024-11-21 10:34:04 +08:00
dijunkun
2d2a578800 [fix] fix control window position when stream window size changed 2024-11-20 19:17:33 +08:00
dijunkun
0ba12f3ccf [fix] only display client side net status if connected to itself 2024-11-19 22:33:59 +08:00
dijunkun
5ac603977d [fix] fix net traffic stats display 2024-11-19 17:31:31 +08:00
dijunkun
25d5a80bee [fix] reset net traffic stats after connection closed 2024-11-19 17:26:01 +08:00
dijunkun
c9d452a025 [feat] disable settings modification during streaming 2024-11-19 17:21:27 +08:00
dijunkun
8132d62c02 [feat] show net traffic stats in control bar 2024-11-19 16:56:30 +08:00
dijunkun
ca32ebeefe [feat] net traffic stats supported 2024-11-18 17:33:09 +08:00
dijunkun
5bf5e9ee25 [feat] change UI layouts 2024-11-18 16:08:54 +08:00
dijunkun
bf097008e7 [feat] use SDL_WINDOW_HIDDEN to delay showing the main window to avoid black window during initialization 2024-11-18 15:10:02 +08:00
dijunkun
59b1208321 [feat] load different size fonts in initialization 2024-11-15 18:19:54 +08:00
dijunkun
84194188f8 [fix] fix redundant recent connection cache file due to remember_password_ flag not being set correctly 2024-11-14 17:26:08 +08:00
dijunkun
a067441fb9 [feat] change recent connections layout 2024-11-14 17:13:38 +08:00
dijunkun
6eac8380b6 [feat] generate random AES128 key and iv during initialization, save them in cache file and load when program starts 2024-11-14 00:49:30 +08:00
dijunkun
5aed8317ca [feat] quick connecting supported 2024-11-13 23:27:06 +08:00
dijunkun
9aed7a19cf [fix] fix AES encrypt and decrypt 2024-11-13 17:58:59 +08:00
dijunkun
c0154be1aa [fix] use APIs in evp.h to encrypt and decrypt 2024-11-13 00:47:54 +08:00
dijunkun
f9d024e971 [feat] use AES to encrypt image names 2024-11-12 17:30:28 +08:00
dijunkun
526eb4bb31 [feat] add trash and connect buttons on recent connection images 2024-11-11 17:05:47 +08:00
dijunkun
f9347cbd49 [feat] use confirm window to confirm recent connections deletion 2024-11-11 10:49:07 +08:00
dijunkun
b6671bdbe7 [feat] delete recent connection supported 2024-11-08 17:52:19 +08:00
dijunkun
edcf5d408c [feat] use horizontal scroll bar to show all recent connections 2024-11-08 14:51:58 +08:00
dijunkun
8c8731909e [feat] remote host names will be shown below thumbnails 2024-11-07 17:30:55 +08:00
dijunkun
de721ac6e3 [feat] use a list to show thumbnails of recent connections 2024-11-07 16:58:25 +08:00
dijunkun
963f1da1d8 [fix] fix redefinition error on MacOSX 2024-11-07 16:32:35 +08:00
dijunkun
4c6159e4d4 [feat] write and load thumbnails supported 2024-11-07 16:29:02 +08:00
dijunkun
e3c2e9ec6d [fix] fix png images write and read 2024-11-07 02:38:35 +08:00
dijunkun
02022bdcdf [feat] add recent connections window 2024-11-06 17:28:11 +08:00
dijunkun
19ea426efc [feat] change UI layouts 2024-11-05 17:29:39 +08:00
42 changed files with 54889 additions and 42290 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -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;
}
@@ -99,4 +99,27 @@ std::string GetMac() {
close(sock);
#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;
}

View File

@@ -10,5 +10,6 @@
#include <iostream>
std::string GetMac();
std::string GetHostName();
#endif

View File

@@ -9,7 +9,12 @@
#include <stdio.h>
typedef enum { mouse = 0, keyboard, audio_capture } 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,23 +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

View File

@@ -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;
}

View 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;
}

View 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

View 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;
}

View 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

View 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

View 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;
}

View 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

View File

@@ -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;
ip.mi.dy = remote_action.m.y;
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) {

View File

@@ -9,7 +9,7 @@
#include "rd_log.h"
#include "render.h"
int main(int argc, char *argv[]) {
int main([[maybe_unused]] int argc, [[maybe_unused]] char *argv[]) {
LOG_INFO("Remote desk");
Render render;

View File

@@ -22,13 +22,26 @@ 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> 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> 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:"};
@@ -61,6 +74,9 @@ static std::vector<std::string> validate_password = {u8"验证密码中...",
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"未连接服务器",
@@ -80,6 +96,8 @@ 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

37
src/log/rd_log.cpp Normal file
View 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;
}

View File

@@ -1,6 +1,6 @@
/*
* @Author: DI JUNKUN
* @Date: 2024-07-17
* @Date: 2024-11-26
* Copyright (c) 2024 by DI JUNKUN, All Rights Reserved.
*/
@@ -26,97 +26,14 @@ using namespace std::chrono;
constexpr auto LOGGER_NAME = "rd";
#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__); \
}
std::shared_ptr<spdlog::logger> get_logger();
#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_INFO(...) SPDLOG_LOGGER_INFO(get_logger(), __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_WARN(...) SPDLOG_LOGGER_WARN(get_logger(), __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__); \
}
#define LOG_ERROR(...) SPDLOG_LOGGER_ERROR(get_logger(), __VA_ARGS__);
#define LOG_FATAL(...) SPDLOG_LOGGER_CRITICAL(get_logger(), __VA_ARGS__);
#endif

View File

@@ -9,8 +9,8 @@
#include "libyuv.h"
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);

View File

@@ -148,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);
@@ -213,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;

View File

@@ -1,4 +1,3 @@
#include "IconsFontAwesome6.h"
#include "layout_style.h"
#include "localization.h"
#include "rd_log.h"

View File

@@ -88,6 +88,7 @@ int Render::ConnectionStatusWindow() {
} 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);
@@ -100,10 +101,22 @@ int Render::ConnectionStatusWindow() {
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);
@@ -139,8 +152,9 @@ int Render::ConnectionStatusWindow() {
// ok
if (ImGui::Button(
localization::ok[localization_language_index_].c_str()) ||
ImGui::IsKeyPressed(ImGuiKey_Escape)) {
ImGui::IsKeyPressed(ImGuiKey_Enter)) {
show_connection_status_window_ = false;
re_enter_remote_id_ = true;
}
}

View File

@@ -1,15 +1,41 @@
#include "IconsFontAwesome6.h"
#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) : 41.0f);
is_control_bar_in_left_ ? (control_window_width_ + 5.0f) : 38.0f);
// mouse control button
ImDrawList* draw_list = ImGui::GetWindowDrawList();
@@ -22,6 +48,7 @@ int Render::ControlBar() {
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
@@ -29,6 +56,7 @@ int Render::ControlBar() {
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_
@@ -69,8 +97,8 @@ int Render::ControlBar() {
RemoteAction remote_action;
remote_action.type = ControlType::audio_capture;
remote_action.a = audio_capture_button_pressed_;
SendData(peer_, DATA_TYPE::DATA, (const char*)&remote_action,
sizeof(remote_action));
SendDataFrame(peer_, (const char*)&remote_action,
sizeof(remote_action));
}
}
if (!audio_capture_button_pressed_) {
@@ -86,6 +114,31 @@ int Render::ControlBar() {
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 =
@@ -96,11 +149,15 @@ int Render::ControlBar() {
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(main_window_, SDL_WINDOW_FULLSCREEN_DESKTOP);
SDL_SetWindowFullscreen(stream_window_, SDL_WINDOW_FULLSCREEN_DESKTOP);
} else {
SDL_SetWindowFullscreen(main_window_, SDL_FALSE);
SDL_SetWindowFullscreen(stream_window_, SDL_FALSE);
}
reset_control_bar_pos_ = true;
}
ImGui::SameLine();
@@ -125,7 +182,7 @@ int Render::ControlBar() {
}
ImGui::SetCursorPosX(
is_control_bar_in_left_ ? (control_window_width_ * 2 - 18.0f) : 3.0f);
is_control_bar_in_left_ ? (control_window_width_ * 2 - 20.0f) : 5.0f);
std::string control_bar =
control_bar_expand_
@@ -136,9 +193,89 @@ int Render::ControlBar() {
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;
}

View File

@@ -2,18 +2,33 @@
#include "render.h"
int Render::ControlWindow() {
auto time_duration = ImGui::GetTime() - control_bar_button_pressed_time_;
double time_duration = ImGui::GetTime() - control_bar_button_pressed_time_;
if (control_window_width_is_changing_) {
if (control_bar_expand_) {
control_window_width_ =
control_window_min_width_ +
(control_window_max_width_ - control_window_min_width_) * 4 *
time_duration;
(float)(control_window_min_width_ +
(control_window_max_width_ - control_window_min_width_) * 4 *
time_duration);
} else {
control_window_width_ =
control_window_max_width_ -
(control_window_max_width_ - control_window_min_width_) * 4 *
time_duration;
(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);
}
}
@@ -26,20 +41,81 @@ int Render::ControlWindow() {
ImGui::SetNextWindowSize(
ImVec2(control_window_width_, control_window_height_), ImGuiCond_Always);
ImGui::SetNextWindowPos(ImVec2(0, title_bar_height_), ImGuiCond_Once);
if (ImGui::IsMouseReleased(ImGuiPopupFlags_MouseButtonLeft) ||
control_window_width_is_changing_) {
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) {
int pos_x = 0;
int pos_y =
float pos_x = 0;
float pos_y =
(control_winodw_pos_.y >=
(fullscreen_button_pressed_ ? 0 : title_bar_height_) &&
(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_)
? (fullscreen_button_pressed_ ? 0 : title_bar_height_)
: (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_) {
@@ -60,16 +136,18 @@ int Render::ControlWindow() {
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) {
int pos_x = 0;
int pos_y =
float pos_x = 0;
float pos_y =
(control_winodw_pos_.y >=
(fullscreen_button_pressed_ ? 0 : title_bar_height_) &&
(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_)
? (fullscreen_button_pressed_ ? 0 : title_bar_height_)
: (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_) {
@@ -96,13 +174,34 @@ int Render::ControlWindow() {
}
}
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 |
ImGuiWindowFlags_NoBringToFrontOnFocus);
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(
@@ -127,24 +226,5 @@ int Render::ControlWindow() {
ImGui::PopStyleVar(4);
ImGui::PopStyleColor();
ImGui::SetNextWindowPos(
ImVec2(0, fullscreen_button_pressed_ ? 0 : title_bar_height_),
ImGuiCond_Always);
ImGui::SetNextWindowSize(
ImVec2(stream_window_width_,
stream_window_height_ -
(fullscreen_button_pressed_ ? 0 : title_bar_height_)),
ImGuiCond_Always);
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(0, 0, 0, 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_NoTitleBar |
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoScrollbar |
ImGuiWindowFlags_NoBringToFrontOnFocus);
ImGui::PopStyleColor(2);
ImGui::End();
return 0;
}

View File

@@ -1,44 +1,50 @@
#include <random>
#include "IconsFontAwesome6.h"
#include "layout_style.h"
#include "localization.h"
#include "rd_log.h"
#include "render.h"
int Render::LocalWindow() {
ImGui::SetNextWindowPos(ImVec2(0, title_bar_height_), ImGuiCond_Always);
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_, main_window_height_default_ -
title_bar_height_ - status_bar_height_),
ImGuiChildFlags_Border,
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse |
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar |
ImGuiWindowFlags_NoBringToFrontOnFocus);
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::SetWindowFontScale(1.0f);
ImGui::Text(
"%s", localization::local_desktop[localization_language_index_].c_str());
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::PushStyleColor(ImGuiCol_ChildBg,
ImVec4(239.0 / 255, 240.0 / 255, 242.0 / 255, 1.0f));
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(330, 180),
ImGuiChildFlags_Border,
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse |
ImGuiWindowFlags_NoTitleBar |
ImGuiWindowFlags_NoBringToFrontOnFocus);
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.5f);
ImGui::SetWindowFontScale(0.8f);
ImGui::Text("%s",
localization::local_id[localization_language_index_].c_str());
@@ -48,34 +54,35 @@ int Render::LocalWindow() {
ImGui::SetWindowFontScale(1.0f);
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f);
char client_id_display[12] = "";
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] = ' ';
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),
"##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);
auto time_duration = ImGui::GetTime() - copy_start_time_;
double time_duration = ImGui::GetTime() - copy_start_time_;
if (local_id_copied_ && time_duration < 1.0f) {
const ImGuiViewport *viewport = ImGui::GetMainViewport();
ImGui::SetNextWindowPos(
@@ -88,8 +95,9 @@ int Render::LocalWindow() {
ImGui::SetNextWindowSize(
ImVec2(notification_window_width_, notification_window_height_));
ImGui::PushStyleColor(ImGuiCol_WindowBg,
ImVec4(1.0, 1.0, 1.0, 1.0 - time_duration));
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,
@@ -107,7 +115,7 @@ int Render::LocalWindow() {
ImGui::SetCursorPosX((window_width - text_width) * 0.5f);
ImGui::SetCursorPosY(window_height * 0.5f);
ImGui::PushStyleColor(ImGuiCol_Text,
ImVec4(0, 0, 0, 1.0 - time_duration));
ImVec4(0, 0, 0, 1.0f - (float)time_duration));
ImGui::Text("%s", text.c_str());
ImGui::PopStyleColor();
ImGui::SetWindowFontScale(1.0f);
@@ -115,16 +123,14 @@ int Render::LocalWindow() {
ImGui::End();
}
ImGui::SetWindowFontScale(1.0f);
ImGui::Spacing();
ImGui::Separator();
ImGui::Spacing();
ImGui::SetWindowFontScale(0.5f);
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();
@@ -132,24 +138,25 @@ int Render::LocalWindow() {
if (!password_inited_) {
char a[] = {
"123456789QWERTYUPASDFGHJKLZXCVBNMqwertyupasdfghijkzxcvbnm"};
std::mt19937 generator(
std::chrono::system_clock::now().time_since_epoch().count());
std::uniform_int_distribution<int> distribution(0, strlen(a) - 1);
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, len = strlen(a); i < 6; i++) {
for (int i = 0; i < 6; i++) {
random_password_ += a[distribution(generator)];
}
password_inited_ = true;
if (0 != strcmp(random_password_.c_str(), password_saved_)) {
strncpy(password_saved_, random_password_.c_str(),
sizeof(password_saved_));
memcpy(password_saved_, random_password_.c_str(),
sizeof(password_saved_));
LOG_INFO("Generate new password and save into cache file");
SaveSettingsIntoCacheFile();
}
}
ImGui::SetWindowFontScale(1.0f);
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f);
ImGui::InputTextWithHint(
"##server_pwd",
@@ -166,7 +173,6 @@ int Render::LocalWindow() {
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;
@@ -238,9 +244,9 @@ int Render::LocalWindow() {
auto window_height = ImGui::GetWindowSize().y;
std::string text =
localization::new_password[localization_language_index_];
auto text_width = ImGui::CalcTextSize(text.c_str()).x;
ImGui::SetWindowFontScale(0.5f);
ImGui::SetCursorPosX((window_width - text_width / 2) * 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());
@@ -276,7 +282,7 @@ int Render::LocalWindow() {
} else {
show_reset_password_window_ = false;
LOG_INFO("Generate new password and save into cache file");
strncpy(password_saved_, new_password_, sizeof(password_saved_));
memcpy(password_saved_, new_password_, sizeof(password_saved_));
memset(new_password_, 0, sizeof(new_password_));
SaveSettingsIntoCacheFile();
LeaveConnection(peer_, client_id_);

View File

@@ -1,15 +1,34 @@
#include "localization.h"
#include "rd_log.h"
#include "render.h"
int Render::MainWindow() {
ImGui::SetNextWindowPos(ImVec2(0, 0), ImGuiCond_Always);
ImGui::SetNextWindowSize(
ImVec2(main_window_width_default_, main_window_height_default_),
ImGuiCond_Always);
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;
}
}

View 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;
}

View File

@@ -1,9 +1,95 @@
#include "IconsFontAwesome6.h"
#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, " ");
@@ -16,95 +102,34 @@ static int InputTextCallback(ImGuiInputTextCallbackData *data) {
return 0;
}
int Render::RemoteWindow() {
ImGui::SetNextWindowPos(ImVec2(local_window_width_ - 1, 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(main_window_width_ - local_window_width_ + 1,
main_window_height_default_ - title_bar_height_ -
status_bar_height_),
ImGuiChildFlags_Border,
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse |
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar |
ImGuiWindowFlags_NoBringToFrontOnFocus);
ImGui::PopStyleColor();
ImGui::SetWindowFontScale(1.0f);
ImGui::Text(
"%s", localization::remote_desktop[localization_language_index_].c_str());
ImGui::Spacing();
ImGui::PushStyleColor(ImGuiCol_ChildBg,
ImVec4(239.0 / 255, 240.0 / 255, 242.0 / 255, 1.0f));
ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, 10.0f);
ImGui::BeginChild("RemoteDesktopWindow_1", ImVec2(330, 180),
ImGuiChildFlags_Border,
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse |
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar |
ImGuiWindowFlags_NoBringToFrontOnFocus);
ImGui::PopStyleVar();
ImGui::PopStyleColor();
{
ImGui::SetWindowFontScale(0.5f);
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);
bool enter_pressed = ImGui::InputText(
"##remote_id_", remote_id_display_, IM_ARRAYSIZE(remote_id_display_),
ImGuiInputTextFlags_CharsUppercase |
ImGuiInputTextFlags_EnterReturnsTrue |
ImGuiInputTextFlags_CallbackEdit | ImGuiInputTextFlags_CharsNoBlank,
InputTextCallback);
ImGui::PopStyleVar();
ImGui::SameLine();
if (ImGui::Button(ICON_FA_ARROW_RIGHT_LONG, ImVec2(55, 38)) ||
enter_pressed || rejoin_) {
connect_button_pressed_ = true;
connection_status_ = ConnectionStatus::Connecting;
int ret = -1;
if (signal_connected_) {
if (!connection_established_) {
remote_id_ = remote_id_display_;
remote_id_.erase(remove_if(remote_id_.begin(), remote_id_.end(),
static_cast<int (*)(int)>(&isspace)),
remote_id_.end());
if (0 == strcmp(remote_id_.c_str(), client_id_) && !peer_reserved_) {
peer_reserved_ = CreatePeer(&params_);
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;
}
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(&params_);
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;
}
}
}
ImGui::EndChild();
ImGui::EndChild();
ImGui::PopStyleVar();
return 0;
}

View File

@@ -1,10 +1,10 @@
#include "render.h"
#include <filesystem>
#include <fstream>
#include <iostream>
#include <string>
#include "IconsFontAwesome6.h"
#include "OPPOSans_Regular.h"
#include "device_controller_factory.h"
#include "fa_regular_400.h"
@@ -21,9 +21,9 @@
#define MOUSE_GRAB_PADDING 5
SDL_HitTestResult Render::HitTestCallback(SDL_Window *window,
const SDL_Point *area, void *data) {
Render *render = (Render *)data;
SDL_HitTestResult Render::HitTestCallback(SDL_Window* window,
const SDL_Point* area, void* data) {
Render* render = (Render*)data;
if (!render) {
return SDL_HITTEST_NORMAL;
}
@@ -69,22 +69,22 @@ SDL_HitTestResult Render::HitTestCallback(SDL_Window *window,
return SDL_HITTEST_NORMAL;
}
Render::Render() {}
Render::Render() { memset(&net_traffic_stats_, 0, sizeof(net_traffic_stats_)); }
Render::~Render() {}
int Render::SaveSettingsIntoCacheFile() {
std::lock_guard<std::mutex> lock(cd_cache_mutex_);
cd_cache_file_ = fopen("cache.cd", "w+");
if (!cd_cache_file_) {
cd_cache_mutex_.lock();
std::ofstream cd_cache_file("cache.cd", std::ios::binary);
if (!cd_cache_file) {
cd_cache_mutex_.unlock();
return -1;
}
fseek(cd_cache_file_, 0, SEEK_SET);
memset(&cd_cache_.client_id, 0, sizeof(cd_cache_.client_id));
strncpy(cd_cache_.client_id, client_id_, sizeof(client_id_));
memcpy(cd_cache_.client_id, client_id_, sizeof(client_id_));
memset(&cd_cache_.password, 0, sizeof(cd_cache_.password));
strncpy(cd_cache_.password, password_saved_, sizeof(password_saved_));
memcpy(cd_cache_.password, password_saved_, sizeof(password_saved_));
memcpy(&cd_cache_.language, &language_button_value_,
sizeof(language_button_value_));
memcpy(&cd_cache_.video_quality, &video_quality_button_value_,
@@ -93,8 +93,11 @@ int Render::SaveSettingsIntoCacheFile() {
sizeof(video_encode_format_button_value_));
memcpy(&cd_cache_.enable_hardware_video_codec, &enable_hardware_video_codec_,
sizeof(enable_hardware_video_codec_));
fwrite(&cd_cache_, sizeof(cd_cache_), 1, cd_cache_file_);
fclose(cd_cache_file_);
memcpy(&cd_cache_.enable_turn, &enable_turn_, sizeof(enable_turn_));
cd_cache_file.write(reinterpret_cast<char*>(&cd_cache_), sizeof(CDCache));
cd_cache_file.close();
cd_cache_mutex_.unlock();
config_center_.SetLanguage((ConfigCenter::LANGUAGE)language_button_value_);
config_center_.SetVideoQuality(
@@ -102,6 +105,7 @@ int Render::SaveSettingsIntoCacheFile() {
config_center_.SetVideoEncodeFormat(
(ConfigCenter::VIDEO_ENCODE_FORMAT)video_encode_format_button_value_);
config_center_.SetHardwareVideoCodec(enable_hardware_video_codec_);
config_center_.SetTurn(enable_turn_);
LOG_INFO("Save settings into cache file success");
@@ -109,15 +113,20 @@ int Render::SaveSettingsIntoCacheFile() {
}
int Render::LoadSettingsFromCacheFile() {
std::lock_guard<std::mutex> lock(cd_cache_mutex_);
cd_cache_file_ = fopen("cache.cd", "r+");
if (!cd_cache_file_) {
cd_cache_mutex_.lock();
std::ifstream cd_cache_file("cache.cd", std::ios::binary);
if (!cd_cache_file) {
cd_cache_mutex_.unlock();
LOG_INFO("Init cache file by using default settings");
memset(password_saved_, 0, sizeof(password_saved_));
memset(aes128_key_, 0, sizeof(aes128_key_));
memset(aes128_iv_, 0, sizeof(aes128_iv_));
language_button_value_ = 0;
video_quality_button_value_ = 0;
video_encode_format_button_value_ = 1;
enable_hardware_video_codec_ = false;
enable_turn_ = false;
config_center_.SetLanguage((ConfigCenter::LANGUAGE)language_button_value_);
config_center_.SetVideoQuality(
@@ -125,29 +134,44 @@ int Render::LoadSettingsFromCacheFile() {
config_center_.SetVideoEncodeFormat(
(ConfigCenter::VIDEO_ENCODE_FORMAT)video_encode_format_button_value_);
config_center_.SetHardwareVideoCodec(enable_hardware_video_codec_);
config_center_.SetTurn(enable_turn_);
thumbnail_ = std::make_unique<Thumbnail>();
thumbnail_->GetKeyAndIv(aes128_key_, aes128_iv_);
thumbnail_->DeleteAllFilesInDirectory();
SaveSettingsIntoCacheFile();
return -1;
}
fseek(cd_cache_file_, 0, SEEK_SET);
fread(&cd_cache_, sizeof(cd_cache_), 1, cd_cache_file_);
fclose(cd_cache_file_);
cd_cache_file.read(reinterpret_cast<char*>(&cd_cache_), sizeof(CDCache));
cd_cache_file.close();
cd_cache_mutex_.unlock();
memset(&client_id_, 0, sizeof(client_id_));
strncpy(client_id_, cd_cache_.client_id, sizeof(client_id_));
strncpy(password_saved_, cd_cache_.password, sizeof(password_saved_));
memcpy(client_id_, cd_cache_.client_id, sizeof(client_id_));
memcpy(password_saved_, cd_cache_.password, sizeof(password_saved_));
if (0 != strcmp(password_saved_, "") && 7 == sizeof(password_saved_)) {
password_inited_ = true;
}
memcpy(aes128_key_, cd_cache_.key, sizeof(cd_cache_.key));
memcpy(aes128_iv_, cd_cache_.iv, sizeof(cd_cache_.iv));
thumbnail_ = std::make_unique<Thumbnail>(aes128_key_, aes128_iv_);
language_button_value_ = cd_cache_.language;
video_quality_button_value_ = cd_cache_.video_quality;
video_encode_format_button_value_ = cd_cache_.video_encode_format;
enable_hardware_video_codec_ = cd_cache_.enable_hardware_video_codec;
enable_turn_ = cd_cache_.enable_turn;
language_button_value_last_ = language_button_value_;
video_quality_button_value_last_ = video_quality_button_value_;
video_encode_format_button_value_last_ = video_encode_format_button_value_;
enable_hardware_video_codec_last_ = enable_hardware_video_codec_;
enable_turn_last_ = enable_turn_;
config_center_.SetLanguage((ConfigCenter::LANGUAGE)language_button_value_);
config_center_.SetVideoQuality(
@@ -155,30 +179,28 @@ int Render::LoadSettingsFromCacheFile() {
config_center_.SetVideoEncodeFormat(
(ConfigCenter::VIDEO_ENCODE_FORMAT)video_encode_format_button_value_);
config_center_.SetHardwareVideoCodec(enable_hardware_video_codec_);
config_center_.SetTurn(enable_turn_);
LOG_INFO("Load settings from cache file");
return 0;
}
int Render::StartScreenCapture() {
screen_capturer_ = (ScreenCapturer *)screen_capturer_factory_->Create();
int Render::StartScreenCapturer() {
screen_capturer_ = (ScreenCapturer*)screen_capturer_factory_->Create();
last_frame_time_ = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::steady_clock::now().time_since_epoch())
.count();
int screen_capturer_init_ret = screen_capturer_->Init(
60, [this](unsigned char *data, int size, int width, int height) -> void {
60, [this](unsigned char* data, int size, int width, int height) -> void {
auto now_time = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::steady_clock::now().time_since_epoch())
.count();
auto duration = now_time - last_frame_time_;
if (duration >= 0 && connection_established_) {
// SendData(peer_, DATA_TYPE::VIDEO, (const char *)data,
// NV12_BUFFER_SIZE);
XVideoFrame frame;
frame.data = (const char *)data;
frame.data = (const char*)data;
frame.size = size;
frame.width = width;
frame.height = height;
@@ -199,7 +221,7 @@ int Render::StartScreenCapture() {
return 0;
}
int Render::StopScreenCapture() {
int Render::StopScreenCapturer() {
if (screen_capturer_) {
LOG_INFO("Stop screen capturer")
screen_capturer_->Stop();
@@ -211,13 +233,13 @@ int Render::StopScreenCapture() {
return 0;
}
int Render::StartSpeakerCapture() {
int Render::StartSpeakerCapturer() {
if (!speaker_capturer_) {
speaker_capturer_ = (SpeakerCapturer *)speaker_capturer_factory_->Create();
speaker_capturer_ = (SpeakerCapturer*)speaker_capturer_factory_->Create();
int speaker_capturer_init_ret = speaker_capturer_->Init(
[this](unsigned char *data, size_t size) -> void {
[this](unsigned char* data, size_t size) -> void {
if (connection_established_) {
SendData(peer_, DATA_TYPE::AUDIO, (const char *)data, size);
SendAudioFrame(peer_, (const char*)data, size);
}
});
@@ -234,7 +256,7 @@ int Render::StartSpeakerCapture() {
return 0;
}
int Render::StopSpeakerCapture() {
int Render::StopSpeakerCapturer() {
if (speaker_capturer_) {
speaker_capturer_->Stop();
}
@@ -242,9 +264,12 @@ int Render::StopSpeakerCapture() {
return 0;
}
int Render::StartMouseControl() {
device_controller_factory_ = new DeviceControllerFactory();
mouse_controller_ = (MouseController *)device_controller_factory_->Create(
int Render::StartMouseController() {
if (!device_controller_factory_) {
LOG_INFO("Device controller factory is nullptr");
return -1;
}
mouse_controller_ = (MouseController*)device_controller_factory_->Create(
DeviceControllerFactory::Device::Mouse);
int mouse_controller_init_ret =
mouse_controller_->Init(screen_width_, screen_height_);
@@ -257,7 +282,7 @@ int Render::StartMouseControl() {
return 0;
}
int Render::StopMouseControl() {
int Render::StopMouseController() {
if (mouse_controller_) {
mouse_controller_->Destroy();
delete mouse_controller_;
@@ -266,6 +291,37 @@ int Render::StopMouseControl() {
return 0;
}
int Render::StartKeyboardCapturer() {
if (!keyboard_capturer_) {
LOG_INFO("keyboard capturer is nullptr");
return -1;
}
int keyboard_capturer_init_ret = keyboard_capturer_->Hook(
[](int key_code, bool is_down, void* user_ptr) {
if (user_ptr) {
Render* render = (Render*)user_ptr;
render->SendKeyEvent(key_code, is_down);
}
},
this);
if (0 != keyboard_capturer_init_ret) {
LOG_ERROR("Start keyboard capturer failed");
} else {
LOG_INFO("Start keyboard capturer");
}
return 0;
}
int Render::StopKeyboardCapturer() {
if (keyboard_capturer_) {
keyboard_capturer_->Unhook();
LOG_INFO("Stop keyboard capturer");
}
return 0;
}
int Render::CreateConnectionPeer() {
mac_addr_str_ = GetMac();
@@ -309,7 +365,7 @@ int Render::CreateConnectionPeer() {
int Render::AudioDeviceInit() {
// Audio
SDL_AudioSpec want_in, have_in, want_out, have_out;
SDL_AudioSpec want_in, want_out;
SDL_zero(want_in);
want_in.freq = 48000;
want_in.format = AUDIO_S16LSB;
@@ -361,20 +417,28 @@ int Render::CreateRtcConnection() {
CreateConnection(peer_, client_id_, password_saved_) ? false : true;
}
if (start_screen_capture_ && !screen_capture_is_started_) {
StartScreenCapture();
screen_capture_is_started_ = true;
} else if (!start_screen_capture_ && screen_capture_is_started_) {
StopScreenCapture();
screen_capture_is_started_ = false;
if (start_screen_capturer_ && !screen_capturer_is_started_) {
StartScreenCapturer();
screen_capturer_is_started_ = true;
} else if (!start_screen_capturer_ && screen_capturer_is_started_) {
StopScreenCapturer();
screen_capturer_is_started_ = false;
}
if (start_mouse_control_ && !mouse_control_is_started_) {
StartMouseControl();
mouse_control_is_started_ = true;
} else if (!start_mouse_control_ && mouse_control_is_started_) {
StopMouseControl();
mouse_control_is_started_ = false;
if (start_mouse_controller_ && !mouse_controller_is_started_) {
StartMouseController();
mouse_controller_is_started_ = true;
} else if (!start_mouse_controller_ && mouse_controller_is_started_) {
StopMouseController();
mouse_controller_is_started_ = false;
}
if (start_keyboard_capturer_ && !keyboard_capturer_is_started_) {
StartKeyboardCapturer();
keyboard_capturer_is_started_ = true;
} else if (!start_keyboard_capturer_ && keyboard_capturer_is_started_) {
StopKeyboardCapturer();
keyboard_capturer_is_started_ = false;
}
return 0;
@@ -390,10 +454,12 @@ int Render::CreateMainWindow() {
ImGui::SetCurrentContext(main_ctx_);
SDL_WindowFlags window_flags =
(SDL_WindowFlags)(SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_BORDERLESS);
main_window_ = SDL_CreateWindow(
"Remote Desk", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
main_window_width_default_, main_window_height_default_, window_flags);
(SDL_WindowFlags)(SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_BORDERLESS |
SDL_WINDOW_HIDDEN);
main_window_ =
SDL_CreateWindow("Remote Desk", SDL_WINDOWPOS_UNDEFINED,
SDL_WINDOWPOS_UNDEFINED, (int)main_window_width_default_,
(int)main_window_height_default_, window_flags);
main_renderer_ = SDL_CreateRenderer(
main_window_, -1, SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_ACCELERATED);
@@ -463,12 +529,22 @@ int Render::CreateStreamWindow() {
// for window region action
SDL_SetWindowHitTest(stream_window_, HitTestCallback, this);
// change stream_render_rect_
SDL_Event event;
event.type = SDL_WINDOWEVENT;
event.window.windowID = SDL_GetWindowID(stream_window_);
event.window.event = SDL_WINDOWEVENT_SIZE_CHANGED;
SDL_PushEvent(&event);
stream_window_created_ = true;
return 0;
}
int Render::DestroyStreamWindow() {
stream_window_width_ = (float)stream_window_width_default_;
stream_window_height_ = (float)stream_window_height_default_;
if (stream_ctx_) {
ImGui::SetCurrentContext(stream_ctx_);
}
@@ -488,36 +564,23 @@ int Render::DestroyStreamWindow() {
int Render::SetupFontAndStyle() {
// Setup Dear ImGui style
ImGuiIO &io = ImGui::GetIO();
// Master keyboard navigation enable flag. Enable full Tabbing + directional
// arrows + space/enter to activate.
ImGuiIO& io = ImGui::GetIO();
io.ConfigFlags |=
ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls
io.ConfigFlags |=
ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking
ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls
// Load Fonts
ImFontConfig config;
config.FontDataOwnedByAtlas = false;
io.Fonts->AddFontFromMemoryTTF(OPPOSans_Regular_ttf,
sizeof(OPPOSans_Regular_ttf), 32.0f, &config,
io.Fonts->AddFontFromMemoryTTF(OPPOSans_Regular_ttf, OPPOSans_Regular_ttf_len,
32.0f, &config,
io.Fonts->GetGlyphRangesChineseFull());
config.MergeMode = true;
config.GlyphMinAdvanceX =
13.0f; // Use if you want to make the icon monospaced
static const ImWchar icon_ranges[] = {ICON_MIN_FA, ICON_MAX_FA, 0};
// io.Fonts->AddFontFromMemoryTTF(fa_regular_400_ttf,
// sizeof(fa_regular_400_ttf),
// 30.0f, &config, icon_ranges);
io.Fonts->AddFontFromMemoryTTF(fa_solid_900_ttf, sizeof(fa_solid_900_ttf),
30.0f, &config, icon_ranges);
io.Fonts->AddFontFromMemoryTTF(fa_solid_900_ttf, fa_solid_900_ttf_len, 30.0f,
&config, icon_ranges);
io.Fonts->Build();
// Setup Dear ImGui style
// ImGui::StyleColorsDark();
ImGui::StyleColorsLight();
return 0;
@@ -535,10 +598,8 @@ int Render::SetupMainWindow() {
SDL_GL_GetDrawableSize(main_window_, &main_window_width_real_,
&main_window_height_real_);
main_window_dpi_scaling_w_ =
(float)main_window_width_real_ / (float)main_window_width_;
main_window_dpi_scaling_h_ =
(float)main_window_width_real_ / (float)main_window_width_;
main_window_dpi_scaling_w_ = main_window_width_real_ / main_window_width_;
main_window_dpi_scaling_h_ = main_window_width_real_ / main_window_width_;
SDL_RenderSetScale(main_renderer_, main_window_dpi_scaling_w_,
main_window_dpi_scaling_h_);
LOG_INFO("Use dpi scaling [{}x{}] for main window",
@@ -575,10 +636,12 @@ int Render::SetupStreamWindow() {
SDL_GL_GetDrawableSize(stream_window_, &stream_window_width_real_,
&stream_window_height_real_);
stream_window_dpi_scaling_w_ =
(float)stream_window_width_real_ / (float)stream_window_width_;
stream_window_width_real_ / stream_window_width_;
stream_window_dpi_scaling_h_ =
(float)stream_window_width_real_ / (float)stream_window_width_;
stream_window_width_real_ / stream_window_width_;
SDL_RenderSetScale(stream_renderer_, stream_window_dpi_scaling_w_,
stream_window_dpi_scaling_h_);
LOG_INFO("Use dpi scaling [{}x{}] for stream window",
@@ -651,22 +714,23 @@ int Render::DrawStreamWindow() {
ImGui_ImplSDL2_NewFrame();
ImGui::NewFrame();
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
ImGui::SetNextWindowPos(ImVec2(0, 0), ImGuiCond_Always);
ImGui::SetNextWindowSize(
ImVec2(stream_window_width_,
fullscreen_button_pressed_ ? 0 : title_bar_height_),
ImGuiCond_Always);
ImGui::Begin("StreamRender", nullptr,
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar |
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoScrollbar |
ImGuiWindowFlags_NoBringToFrontOnFocus);
ImGui::PopStyleColor();
if (!fullscreen_button_pressed_) {
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
ImGui::SetNextWindowPos(ImVec2(0, 0), ImGuiCond_Always);
ImGui::SetNextWindowSize(
ImVec2(stream_window_width_,
fullscreen_button_pressed_ ? 0 : title_bar_height_),
ImGuiCond_Always);
ImGui::Begin("StreamRender", nullptr,
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoDecoration |
ImGuiWindowFlags_NoBringToFrontOnFocus);
ImGui::PopStyleColor();
TitleBar(false);
ControlWindow();
TitleBar(false);
ImGui::End();
}
ImGui::End();
StreamWindow();
// Rendering
ImGui::Render();
@@ -678,11 +742,18 @@ int Render::DrawStreamWindow() {
return 0;
}
int Render::LoadRecentConnections() { return 0; }
int Render::Run() {
LoadSettingsFromCacheFile();
localization_language_ = (ConfigCenter::LANGUAGE)language_button_value_;
localization_language_index_ = language_button_value_;
if (localization_language_index_ != 0 && localization_language_index_ != 1) {
localization_language_index_ = 0;
LOG_ERROR("Invalid language index: [{}], use [0] by default",
localization_language_index_);
}
// Setup SDL
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER |
@@ -698,9 +769,9 @@ int Render::Run() {
screen_height_ = DM.h;
stream_render_rect_.x = 0;
stream_render_rect_.y = title_bar_height_;
stream_render_rect_.w = stream_window_width_;
stream_render_rect_.h = stream_window_height_ - title_bar_height_;
stream_render_rect_.y = (int)title_bar_height_;
stream_render_rect_.w = (int)stream_window_width_;
stream_render_rect_.h = (int)(stream_window_height_ - title_bar_height_);
// use linear filtering to render textures otherwise the graphics will be
// blurry
@@ -720,8 +791,10 @@ int Render::Run() {
// speaker capture init
speaker_capturer_factory_ = new SpeakerCapturerFactory();
// mouse control
// mouse control/keyboard capturer
device_controller_factory_ = new DeviceControllerFactory();
keyboard_capturer_ = (KeyboardCapturer*)device_controller_factory_->Create(
DeviceControllerFactory::Device::Keyboard);
// RTC
CreateConnectionPeer();
@@ -733,6 +806,11 @@ int Render::Run() {
CreateMainWindow();
SetupMainWindow();
const int scaled_video_width_ = 160;
const int scaled_video_height_ = 90;
char* argb_buffer_ =
new char[scaled_video_width_ * scaled_video_height_ * 40];
// Main loop
while (!exit_) {
if (!label_inited_ ||
@@ -786,9 +864,17 @@ int Render::Run() {
if (event.type == SDL_QUIT) {
if (streaming_) {
LOG_INFO("Destroy stream window");
SDL_SetWindowGrab(stream_window_, SDL_FALSE);
DestroyStreamWindow();
DestroyStreamWindowContext();
if (dst_buffer_) {
thumbnail_->SaveToThumbnail(
(char*)dst_buffer_, video_width_, video_height_, remote_id_,
host_name_, remember_password_ ? remote_password_ : "");
recent_connection_image_save_time_ = SDL_GetTicks();
}
LOG_INFO("[{}] Leave connection [{}]", client_id_, remote_id_);
LeaveConnection(peer_reserved_ ? peer_reserved_ : peer_,
remote_id_.c_str());
@@ -800,20 +886,15 @@ int Render::Run() {
streaming_ = false;
rejoin_ = false;
connection_established_ = false;
received_frame_ = false;
is_client_mode_ = false;
audio_capture_button_pressed_ = false;
fullscreen_button_pressed_ = false;
SDL_SetWindowFullscreen(main_window_, SDL_FALSE);
memset(audio_buffer_, 0, 960);
SDL_SetWindowSize(main_window_, main_window_width_default_,
main_window_height_default_);
reload_recent_connections_ = true;
remember_password_ = false;
// SDL_Rect display_bounds;
// SDL_GetDisplayBounds(0, &display_bounds);
// int center_x = (display_bounds.w - main_window_width_default_) / 2;
// int center_y = (display_bounds.h - main_window_height_default_) /
// 2; SDL_SetWindowPosition(main_window_, center_x, center_y);
memset(&net_traffic_stats_, 0, sizeof(net_traffic_stats_));
SDL_SetWindowFullscreen(main_window_, SDL_FALSE);
memset(audio_buffer_, 0, 720);
continue;
} else {
@@ -824,40 +905,55 @@ int Render::Run() {
} else if (event.window.event == SDL_WINDOWEVENT_MINIMIZED) {
} else if (event.window.event == SDL_WINDOWEVENT_RESTORED) {
window_maximized_ = false;
} else if (event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) {
SDL_GetWindowSize(stream_window_, &stream_window_width_,
&stream_window_height_);
} else if (event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED &&
stream_window_created_ &&
event.window.windowID == SDL_GetWindowID(stream_window_)) {
// to prevent cursor relocation
if (!reset_control_bar_pos_) {
mouse_diff_control_bar_pos_x_ = 0;
mouse_diff_control_bar_pos_y_ = 0;
}
reset_control_bar_pos_ = true;
int stream_window_width, stream_window_height;
SDL_GetWindowSize(stream_window_, &stream_window_width,
&stream_window_height);
stream_window_width_ = (float)stream_window_width;
stream_window_height_ = (float)stream_window_height;
float video_ratio = (float)video_width_ / (float)video_height_;
float video_ratio_reverse = (float)video_height_ / (float)video_width_;
int render_area_width = stream_window_width_;
int render_area_height =
float render_area_width = stream_window_width_;
float render_area_height =
stream_window_height_ -
(fullscreen_button_pressed_ ? 0 : title_bar_height_);
stream_render_rect_last_ = stream_render_rect_;
if (render_area_width < render_area_height * video_ratio) {
stream_render_rect_.x = 0;
stream_render_rect_.y =
abs(render_area_height -
render_area_width * video_ratio_reverse) /
2 +
(fullscreen_button_pressed_ ? 0 : title_bar_height_);
stream_render_rect_.w = render_area_width;
stream_render_rect_.h = render_area_width * video_ratio_reverse;
(int)(abs(render_area_height -
render_area_width * video_ratio_reverse) /
2 +
(fullscreen_button_pressed_ ? 0 : title_bar_height_));
stream_render_rect_.w = (int)render_area_width;
stream_render_rect_.h =
(int)(render_area_width * video_ratio_reverse);
} else if (render_area_width > render_area_height * video_ratio) {
stream_render_rect_.x =
abs(render_area_width - render_area_height * video_ratio) / 2;
(int)abs(render_area_width - render_area_height * video_ratio) /
2;
stream_render_rect_.y =
fullscreen_button_pressed_ ? 0 : title_bar_height_;
stream_render_rect_.w = render_area_height * video_ratio;
stream_render_rect_.h = render_area_height;
fullscreen_button_pressed_ ? 0 : (int)title_bar_height_;
stream_render_rect_.w = (int)(render_area_height * video_ratio);
stream_render_rect_.h = (int)render_area_height;
} else {
stream_render_rect_.x = 0;
stream_render_rect_.y =
fullscreen_button_pressed_ ? 0 : title_bar_height_;
stream_render_rect_.w = render_area_width;
stream_render_rect_.h = render_area_height;
fullscreen_button_pressed_ ? 0 : (int)title_bar_height_;
stream_render_rect_.w = (int)render_area_width;
stream_render_rect_.h = (int)render_area_height;
}
} else if (event.type == SDL_WINDOWEVENT &&
event.window.event == SDL_WINDOWEVENT_CLOSE) {
@@ -886,6 +982,20 @@ int Render::Run() {
}
}
if (reload_recent_connections_ && main_renderer_) {
// loal recent connection thumbnails after saving for 0.5 second
uint32_t now_time = SDL_GetTicks();
if (now_time - recent_connection_image_save_time_ >= 500) {
int ret = thumbnail_->LoadThumbnail(
main_renderer_, recent_connection_textures_,
&recent_connection_image_width_, &recent_connection_image_height_);
if (!ret) {
LOG_INFO("Load recent connection thumbnails");
}
reload_recent_connections_ = false;
}
}
if (connection_established_ && streaming_) {
CreateStreamWindow();
SetupStreamWindow();
@@ -893,7 +1003,6 @@ int Render::Run() {
if (!stream_window_grabbed_ && control_mouse_) {
SDL_SetWindowGrab(stream_window_, SDL_TRUE);
stream_window_grabbed_ = true;
LOG_INFO("Grabbing input events");
} else if (stream_window_grabbed_ && !control_mouse_) {
SDL_SetWindowGrab(stream_window_, SDL_FALSE);
stream_window_grabbed_ = false;
@@ -902,6 +1011,10 @@ int Render::Run() {
DrawMainWindow();
if (SDL_WINDOW_HIDDEN & SDL_GetWindowFlags(main_window_)) {
SDL_ShowWindow(main_window_);
}
if (stream_window_inited_) {
DrawStreamWindow();
}
@@ -924,6 +1037,8 @@ int Render::Run() {
// }
}
delete[] argb_buffer_;
// Cleanup
if (screen_capturer_) {
screen_capturer_->Destroy();

View File

@@ -11,9 +11,11 @@
#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"
@@ -21,6 +23,7 @@
#include "imgui_impl_sdlrenderer2.h"
#include "screen_capturer_factory.h"
#include "speaker_capturer_factory.h"
#include "thumbnail.h"
class Render {
public:
@@ -34,17 +37,22 @@ class Render {
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();
@@ -56,6 +64,8 @@ class Render {
int DestroyStreamWindowContext();
int DrawMainWindow();
int DrawStreamWindow();
int ConfirmDeleteConnection();
int NetTrafficStats();
public:
static void OnReceiveVideoBufferCb(const XVideoFrame *video_frame,
@@ -76,17 +86,20 @@ class Render {
size_t user_id_size, void *user_data);
static void NetStatusReport(const char *client_id, size_t client_id_size,
TraversalMode mode, const unsigned short send,
const unsigned short receive, void *user_data);
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 ProcessKeyEvent(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);
@@ -94,14 +107,17 @@ class Render {
int SaveSettingsIntoCacheFile();
int LoadSettingsFromCacheFile();
int StartScreenCapture();
int StopScreenCapture();
int StartScreenCapturer();
int StopScreenCapturer();
int StartSpeakerCapture();
int StopSpeakerCapture();
int StartSpeakerCapturer();
int StopSpeakerCapturer();
int StartMouseControl();
int StopMouseControl();
int StartMouseController();
int StopMouseController();
int StartKeyboardCapturer();
int StopKeyboardCapturer();
int CreateConnectionPeer();
@@ -116,10 +132,13 @@ class Render {
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:
FILE *cd_cache_file_ = nullptr;
CDCache cd_cache_;
std::mutex cd_cache_mutex_;
@@ -137,6 +156,7 @@ class Render {
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";
@@ -150,41 +170,59 @@ class Render {
char client_password_[20] = "";
private:
int title_bar_width_ = 960;
int title_bar_height_ = 30;
float title_bar_width_ = 640;
float title_bar_height_ = 30;
int screen_width_ = 1280;
int screen_height_ = 720;
int main_window_width_default_ = 960;
int main_window_height_default_ = 570;
int main_window_width_ = 960;
int main_window_height_ = 570;
int main_window_width_last_ = 960;
int main_window_height_last_ = 540;
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;
int stream_window_width_ = 1280;
int stream_window_height_ = 720;
float stream_window_width_ = 1280;
float stream_window_height_ = 720;
int stream_window_width_last_ = 1280;
int stream_window_height_last_ = 720;
int main_window_width_before_maximized_ = 960;
int main_window_height_before_maximized_ = 570;
int control_window_min_width_ = 20;
int control_window_max_width_ = 170;
int control_window_width_ = 170;
int control_window_height_ = 40;
int local_window_width_ = 350;
int status_bar_height_ = 20;
int connection_status_window_width_ = 200;
int connection_status_window_height_ = 150;
int notification_window_width_ = 200;
int notification_window_height_ = 80;
int about_window_width_ = 200;
int about_window_height_ = 150;
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;
int control_bar_pos_x_ = 0;
int control_bar_pos_y_ = 30;
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_ = 960;
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;
@@ -199,7 +237,7 @@ class Render {
int video_width_ = 1280;
int video_height_ = 720;
int video_size_ = 1280 * 720 * 3;
size_t video_size_ = 1280 * 720 * 3;
SDL_Window *main_window_ = nullptr;
SDL_Renderer *main_renderer_ = nullptr;
@@ -211,26 +249,36 @@ class Render {
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 control_bar_hovered_ = false;
bool connect_button_pressed_ = false;
bool password_validating_ = false;
uint32_t password_validating_time_ = 0;
bool control_bar_expand_ = true;
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 received_frame_ = false;
bool is_create_connection_ = false;
bool audio_buffer_fresh_ = false;
bool rejoin_ = false;
@@ -249,11 +297,23 @@ class Render {
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_;
@@ -275,14 +335,16 @@ class Render {
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_[960];
unsigned char audio_buffer_[720];
int audio_len_ = 0;
unsigned char *dst_buffer_ = nullptr;
int dst_buffer_capacity_ = 0;
size_t dst_buffer_capacity_ = 0;
private:
ScreenCapturerFactory *screen_capturer_factory_ = nullptr;
@@ -291,10 +353,12 @@ class Render {
SpeakerCapturer *speaker_capturer_ = nullptr;
DeviceControllerFactory *device_controller_factory_ = nullptr;
MouseController *mouse_controller_ = nullptr;
uint32_t last_frame_time_;
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;
@@ -309,10 +373,12 @@ class Render {
bool enable_turn_last_ = false;
private:
std::atomic<bool> start_screen_capture_{false};
std::atomic<bool> start_mouse_control_{false};
std::atomic<bool> screen_capture_is_started_{false};
std::atomic<bool> mouse_control_is_started_{false};
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;

View File

@@ -1,5 +1,6 @@
#include "device_controller.h"
#include "localization.h"
#include "platform.h"
#include "rd_log.h"
#include "render.h"
@@ -18,7 +19,6 @@ int Render::ProcessMouseKeyEvent(SDL_Event &event) {
}
if (SDL_KEYDOWN == event.type || SDL_KEYUP == event.type) {
ProcessKeyEvent(event);
} else {
ProcessMouseEvent(event);
}
@@ -62,8 +62,7 @@ int Render::ProcessMouseEvent(SDL_Event &event) {
if (control_bar_hovered_) {
remote_action.m.flag = MouseFlag::move;
}
SendData(peer_, DATA_TYPE::DATA, (const char *)&remote_action,
sizeof(remote_action));
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) {
@@ -74,25 +73,34 @@ int Render::ProcessMouseEvent(SDL_Event &event) {
if (control_bar_hovered_) {
remote_action.m.flag = MouseFlag::move;
}
SendData(peer_, DATA_TYPE::DATA, (const char *)&remote_action,
sizeof(remote_action));
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;
SendData(peer_, DATA_TYPE::DATA, (const char *)&remote_action,
sizeof(remote_action));
SendDataFrame(peer_, (const char *)&remote_action, sizeof(remote_action));
}
return 0;
}
int Render::ProcessKeyEvent(SDL_Event &event) {
int Render::SendKeyEvent(int key_code, bool is_down) {
RemoteAction remote_action;
SDL_Keycode key = event.key.keysym.sym;
if (SDL_KEYDOWN == event.type) {
std::cout << "Key pressed: " << SDL_GetKeyName(key) << std::endl;
} else if (SDL_KEYUP == event.type) {
std::cout << "Key released: " << SDL_GetKeyName(key) << std::endl;
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;
@@ -106,7 +114,7 @@ void Render::SdlCaptureAudioIn(void *userdata, Uint8 *stream, int len) {
if (1) {
if ("Connected" == render->connection_status_str_) {
SendData(render->peer_, DATA_TYPE::AUDIO, (const char *)stream, len);
SendAudioFrame(render->peer_, (const char *)stream, len);
}
} else {
memcpy(render->audio_buffer_, stream, len);
@@ -116,10 +124,12 @@ void Render::SdlCaptureAudioIn(void *userdata, Uint8 *stream, int len) {
}
}
void Render::SdlCaptureAudioOut(void *userdata, Uint8 *stream, int len) {
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_) {
// SendData(render->peer_, DATA_TYPE::AUDIO, (const char *)stream, len);
// SendAudioFrame(render->peer_, (const char *)stream, len);
// }
// if (!render->audio_buffer_fresh_) {
@@ -140,7 +150,8 @@ void Render::SdlCaptureAudioOut(void *userdata, Uint8 *stream, int len) {
}
void Render::OnReceiveVideoBufferCb(const XVideoFrame *video_frame,
const char *user_id, size_t user_id_size,
[[maybe_unused]] const char *user_id,
[[maybe_unused]] size_t user_id_size,
void *user_data) {
Render *render = (Render *)user_data;
if (!render) {
@@ -151,11 +162,6 @@ void Render::OnReceiveVideoBufferCb(const XVideoFrame *video_frame,
if (!render->dst_buffer_) {
render->dst_buffer_capacity_ = video_frame->size;
render->dst_buffer_ = new unsigned char[video_frame->size];
// Adapt stream_render_rect_ to the video resolution
SDL_Event event;
event.type = SDL_WINDOWEVENT;
event.window.event = SDL_WINDOWEVENT_SIZE_CHANGED;
SDL_PushEvent(&event);
}
if (render->dst_buffer_capacity_ < video_frame->size) {
@@ -172,13 +178,13 @@ void Render::OnReceiveVideoBufferCb(const XVideoFrame *video_frame,
SDL_Event event;
event.type = REFRESH_EVENT;
SDL_PushEvent(&event);
render->received_frame_ = true;
render->streaming_ = true;
}
}
void Render::OnReceiveAudioBufferCb(const char *data, size_t size,
const char *user_id, size_t user_id_size,
[[maybe_unused]] const char *user_id,
[[maybe_unused]] size_t user_id_size,
void *user_data) {
Render *render = (Render *)user_data;
if (!render) {
@@ -199,16 +205,23 @@ void Render::OnReceiveDataBufferCb(const char *data, size_t size,
std::string user(user_id, user_id_size);
RemoteAction remote_action;
memcpy(&remote_action, data, sizeof(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->StartSpeakerCapture();
render->StartSpeakerCapturer();
} else {
render->StopSpeakerCapture();
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_);
}
}
@@ -241,8 +254,10 @@ void Render::OnSignalStatusCb(SignalStatus status, void *user_data) {
}
}
void Render::OnConnectionStatusCb(ConnectionStatus status, const char *user_id,
const size_t user_id_size, void *user_data) {
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;
@@ -258,8 +273,21 @@ void Render::OnConnectionStatusCb(ConnectionStatus status, const char *user_id,
render->connection_status_str_ = "Connected";
render->connection_established_ = true;
if (render->peer_reserved_ || !render->is_client_mode_) {
render->start_screen_capture_ = true;
render->start_mouse_control_ = true;
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";
@@ -270,12 +298,15 @@ void Render::OnConnectionStatusCb(ConnectionStatus status, const char *user_id,
} else if (ConnectionStatus::Closed == status) {
render->connection_status_str_ = "Closed";
render->password_validating_time_ = 0;
render->start_screen_capture_ = false;
render->start_mouse_control_ = false;
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->StopSpeakerCapture();
render->StopSpeakerCapturer();
render->audio_capture_ = false;
render->audio_capture_button_pressed_ = false;
}
@@ -312,8 +343,9 @@ void Render::OnConnectionStatusCb(ConnectionStatus status, const char *user_id,
}
void Render::NetStatusReport(const char *client_id, size_t client_id_size,
TraversalMode mode, const unsigned short send,
const unsigned short receive, void *user_data) {
TraversalMode mode,
const XNetTrafficStats *net_traffic_stats,
void *user_data) {
Render *render = (Render *)user_data;
if (!render) {
return;
@@ -321,11 +353,21 @@ void Render::NetStatusReport(const char *client_id, size_t client_id_size,
if (0 == strcmp(render->client_id_, "")) {
memset(&render->client_id_, 0, sizeof(render->client_id_));
strncpy(render->client_id_, client_id, client_id_size);
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 (mode != TraversalMode::UnknownMode) {
LOG_INFO("Net mode: [{}]", int(mode));
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;
}
}

View File

@@ -1,4 +1,3 @@
#include "IconsFontAwesome6.h"
#include "layout_style.h"
#include "localization.h"
#include "rd_log.h"
@@ -71,6 +70,10 @@ int Render::SettingWindow() {
ImGui::Separator();
if (streaming_) {
ImGui::BeginDisabled();
}
{
const char *video_quality_items[] = {
localization::video_quality_high[localization_language_index_]
@@ -158,6 +161,10 @@ int Render::SettingWindow() {
ImGui::Checkbox("##enable_turn", &enable_turn_);
}
if (streaming_) {
ImGui::EndDisabled();
}
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
ImGui::SetCursorPosX(SETTINGS_OK_BUTTON_PADDING_CN);
} else {
@@ -227,7 +234,7 @@ int Render::SettingWindow() {
LoadSettingsFromCacheFile();
// Recreate peer instance
{
if (!streaming_) {
LOG_INFO("Recreate peer instance");
DestroyPeer(&peer_);
is_create_connection_ = false;

View File

@@ -13,17 +13,16 @@ int Render::StatusBar() {
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(
ImVec2(15, main_window_height_default_ - status_bar_height_ + 9.0f), 5,
ImColor(signal_connected_ ? 0.0f : 1.0f, signal_connected_ ? 1.0f : 0.0f,
0.0f),
100);
draw_list->AddCircle(
ImVec2(15, main_window_height_default_ - status_bar_height_ + 10.0f), 6,
ImColor(1.0f, 1.0f, 1.0f), 100);
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.5f);
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),

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View 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;
}

View 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);
}

View 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

View File

@@ -1,30 +1,32 @@
#include "IconsFontAwesome6.h"
#include "localization.h"
#include "render.h"
#define BUTTON_PADDING 36.0f
int Render::TitleBar(bool main_window) {
ImGui::PushStyleColor(ImGuiCol_MenuBarBg, ImVec4(1, 1, 1, 0.0f));
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(
"TitleBar",
main_window ? "MainTitleBar" : "StreamTitleBar",
ImVec2(main_window ? main_window_width_ : stream_window_width_,
title_bar_height_),
ImGuiChildFlags_None,
ImGuiChildFlags_Border,
ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoDecoration |
ImGuiWindowFlags_NoBringToFrontOnFocus);
ImDrawList* draw_list = ImGui::GetWindowDrawList();
ImGui::SetWindowFontScale(1.0f);
ImGui::PopStyleColor();
ImDrawList* draw_list = ImGui::GetWindowDrawList();
if (ImGui::BeginMenuBar()) {
ImGui::SetCursorPosX(
(main_window ? main_window_width_ : stream_window_width_) -
(streaming_ ? BUTTON_PADDING * 4 - 3 : BUTTON_PADDING * 3 - 3));
(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 (!streaming_) {
if (main_window) {
float bar_pos_x = ImGui::GetCursorPosX() + 6;
float bar_pos_y = ImGui::GetCursorPosY() + 15;
std::string menu_button = " "; // ICON_FA_BARS;
@@ -61,9 +63,9 @@ int Render::TitleBar(bool main_window) {
ImGui::PopStyleColor(2);
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0));
ImGui::SetCursorPosX(
(main_window ? main_window_width_ : stream_window_width_) -
(streaming_ ? BUTTON_PADDING * 3 : BUTTON_PADDING * 2));
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));
@@ -80,10 +82,8 @@ int Render::TitleBar(bool main_window) {
IM_COL32(0, 0, 0, 255));
ImGui::PopStyleColor(2);
if (streaming_) {
ImGui::SetCursorPosX(
(main_window ? main_window_width_ : stream_window_width_) -
BUTTON_PADDING * 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));
@@ -97,7 +97,7 @@ int Render::TitleBar(bool main_window) {
"##restore"; // ICON_FA_WINDOW_RESTORE;
if (ImGui::Button(window_restore_button.c_str(),
ImVec2(BUTTON_PADDING, 30))) {
SDL_RestoreWindow(main_window ? main_window_ : stream_window_);
SDL_RestoreWindow(stream_window_);
window_maximized_ = false;
}
draw_list->AddRect(ImVec2(pos_x_top, pos_y_top),
@@ -116,7 +116,7 @@ int Render::TitleBar(bool main_window) {
"##maximize"; // ICON_FA_SQUARE_FULL;
if (ImGui::Button(window_maximize_button.c_str(),
ImVec2(BUTTON_PADDING, 30))) {
SDL_MaximizeWindow(main_window ? main_window_ : stream_window_);
SDL_MaximizeWindow(stream_window_);
window_maximized_ = !window_maximized_;
}
draw_list->AddRect(ImVec2(maximize_pos_x, maximize_pos_y),
@@ -158,8 +158,6 @@ int Render::TitleBar(bool main_window) {
}
ImGui::EndMenuBar();
ImGui::SetWindowFontScale(1.0f);
ImGui::EndChild();
ImGui::PopStyleColor();
return 0;

View File

@@ -17,14 +17,16 @@ if is_mode("debug") then
end
add_requires("spdlog 1.14.1", {system = false})
add_requires("imgui v1.91.4-docking", {configs = {sdl2 = true, sdl2_renderer = true}})
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_cxflags("/WX")
elseif is_os("linux") then
add_requires("ffmpeg 5.1.2", {system = false})
add_syslinks("pthread", "dl")
@@ -35,12 +37,14 @@ 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("libxcb")
add_links("SDL2", "SDL2main")
add_ldflags("-Wl,-ld_classic")
add_cxflags("-Wno-unused-variable")
add_frameworks("OpenGL", "IOSurface", "ScreenCaptureKit")
end
@@ -51,7 +55,7 @@ includes("thirdparty")
target("rd_log")
set_kind("object")
add_packages("spdlog")
add_headerfiles("src/log/rd_log.h")
add_files("src/log/rd_log.cpp")
add_includedirs("src/log", {public = true})
target("common")
@@ -102,14 +106,20 @@ target("device_controller")
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")
@@ -124,6 +134,7 @@ target("localization")
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")