mirror of
				https://github.com/kunkundi/crossdesk.git
				synced 2025-10-30 04:50:11 +08:00 
			
		
		
		
	Compare commits
	
		
			116 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | d18af6cbc6 | ||
|  | b5bb62bd22 | ||
|  | 9ed3ab9929 | ||
|  | dca18762e0 | ||
|  | fed7c3b103 | ||
|  | d246b7a04d | ||
|  | a49ca813e0 | ||
|  | 0c688efaee | ||
|  | be3561d46f | ||
|  | c3af40a3f0 | ||
|  | d493b9a131 | ||
|  | 4e4e84ae4d | ||
|  | fea545e5e7 | ||
|  | 9096769a85 | ||
|  | 04ab157ecb | ||
|  | 2331f08283 | ||
|  | 9f8f99f21b | ||
|  | 56dadb6a49 | ||
|  | 59c9ca8d53 | ||
|  | f16a4e8aa2 | ||
|  | 890615e13a | ||
|  | 2f72e3957e | ||
|  | 1292018f51 | ||
|  | 8ae9513104 | ||
|  | c1efe2f4ac | ||
|  | 1210a0b631 | ||
|  | 39863c597e | ||
|  | 8a964f0030 | ||
|  | 74e29f25bf | ||
|  | 1e5bea2b1e | ||
|  | d8297ebb74 | ||
|  | 93d7f71cf2 | ||
|  | 887a217828 | ||
|  | 89b12136e4 | ||
|  | def7025abf | ||
|  | 35af5aab43 | ||
|  | 9ea67df0fd | ||
|  | 72fda8a728 | ||
|  | 070b48d7a7 | ||
|  | 6168009cef | ||
|  | 06a7243ac1 | ||
|  | c8602b0d89 | ||
|  | e3c730fd5f | ||
|  | b252cb6ddd | ||
|  | 8ca1e8e5a1 | ||
|  | a7d45b78c8 | ||
|  | 018231eee4 | ||
|  | 4704d494ec | ||
|  | 65927c2091 | ||
|  | 574b9d10ab | ||
|  | ff510a3b44 | ||
|  | 4d3c864950 | ||
|  | 2cde54cf30 | ||
|  | d1f3d11318 | ||
|  | 436228946b | ||
|  | 2b4083ee10 | ||
|  | e3abb4e3de | ||
|  | 4b7cd1005b | ||
|  | 03ea96096d | ||
|  | 0ea8916426 | ||
|  | 43b36eb893 | ||
|  | 03b6a187b3 | ||
|  | 664412dd4e | ||
|  | b37e08a202 | ||
|  | a05d72ec67 | ||
|  | f77e9fe6a8 | ||
|  | 1f9614e060 | ||
|  | 50d92a763a | ||
|  | ec23656334 | ||
|  | 880c2949c3 | ||
|  | 07f5fe81c8 | ||
|  | 5a992b6589 | ||
|  | 8e03e8e79b | ||
|  | ceb3d9fe20 | ||
|  | 0dc0b87bc4 | ||
|  | 3a4284fece | ||
|  | 502a90f121 | ||
|  | 88cd4aca4a | ||
|  | 3395004f93 | ||
|  | e4c05e1f4d | ||
|  | d17c70c2c8 | ||
|  | 7b42923418 | ||
|  | 5b6bdee25a | ||
|  | 05deb73c29 | ||
|  | 3685acc549 | ||
|  | 8f5a53937a | ||
|  | b9c5db41ab | ||
|  | a99a4230af | ||
|  | f446154747 | ||
|  | 5a1e2c5ed9 | ||
|  | ff6f295fac | ||
|  | 3111b3a641 | ||
|  | 20bb13ce85 | ||
|  | 5aa05f3a13 | ||
|  | c911aa2eb1 | ||
|  | d0cd2fe9ab | ||
|  | 9702805331 | ||
|  | 872152f1be | ||
|  | b822221d7f | ||
|  | 95ad605b36 | ||
|  | af32e25149 | ||
|  | e63b384d1e | ||
|  | 7f25f7426c | ||
|  | eed93ea953 | ||
|  | b5f8e92526 | ||
|  | af04b0571e | ||
|  | 75452a3e76 | ||
|  | 3f717f1df2 | ||
|  | ad6f2c2c70 | ||
|  | 8076e7f662 | ||
|  | be78496992 | ||
|  | a3f745d441 | ||
|  | e693d920d3 | ||
|  | 0f1b89eda9 | ||
|  | 172b8836fd | ||
|  | 71178ffa33 | 
							
								
								
									
										39
									
								
								Info.plist
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								Info.plist
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,39 @@ | |||||||
|  | <?xml version="1.0" encoding="UTF-8"?> | ||||||
|  | <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | ||||||
|  | <plist version="1.0"> | ||||||
|  | <dict> | ||||||
|  |     <!-- 应用的Bundle identifier,通常使用反向域名标记 --> | ||||||
|  |     <key>CFBundleIdentifier</key> | ||||||
|  |     <string>com.yourcompany.yourappname</string> | ||||||
|  |  | ||||||
|  |     <!-- 应用的显示名称 --> | ||||||
|  |     <key>CFBundleName</key> | ||||||
|  |     <string>Your App Name</string> | ||||||
|  |  | ||||||
|  |     <!-- 应用的版本号 --> | ||||||
|  |     <key>CFBundleShortVersionString</key> | ||||||
|  |     <string>1.0.0</string> | ||||||
|  |  | ||||||
|  |     <!-- 应用的构建版本号 --> | ||||||
|  |     <key>CFBundleVersion</key> | ||||||
|  |     <string>1</string> | ||||||
|  |  | ||||||
|  |     <!-- 请求麦克风访问权限 --> | ||||||
|  |     <key>NSMicrophoneUsageDescription</key> | ||||||
|  |     <string>App requires access to the microphone for audio recording.</string> | ||||||
|  |  | ||||||
|  |     <!-- 请求相机访问权限 --> | ||||||
|  |     <key>NSCameraUsageDescription</key> | ||||||
|  |     <string>App requires access to the camera for video recording.</string> | ||||||
|  |  | ||||||
|  |     <!-- 请求使用连续相机设备 --> | ||||||
|  |     <key>NSCameraUseContinuityCameraDeviceType</key> | ||||||
|  |     <string>Your usage description here</string> | ||||||
|  |  | ||||||
|  |     <!-- High DPI -->> | ||||||
|  |     <key>NSHighResolutionCapable</key> | ||||||
|  |     <true/> | ||||||
|  |  | ||||||
|  |     <!-- 其他权限和配置可以在这里添加 --> | ||||||
|  | </dict> | ||||||
|  | </plist> | ||||||
							
								
								
									
										2025
									
								
								fonts/OPPOSans_Regular.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2025
									
								
								fonts/OPPOSans_Regular.h
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										5658
									
								
								fonts/fa_regular_400.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5658
									
								
								fonts/fa_regular_400.h
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										35031
									
								
								fonts/fa_solid_900.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35031
									
								
								fonts/fa_solid_900.h
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										1
									
								
								icon/app.rc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								icon/app.rc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | |||||||
|  | IDI_ICON1 ICON "app_icon.ico" | ||||||
							
								
								
									
										
											BIN
										
									
								
								icon/app_icon.ico
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								icon/app_icon.ico
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 687 B | 
							
								
								
									
										
											BIN
										
									
								
								icon/捕获.PNG
									
									
									
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								icon/捕获.PNG
									
									
									
									
									
								
							
										
											Binary file not shown.
										
									
								
							| Before Width: | Height: | Size: 30 KiB | 
							
								
								
									
										
											BIN
										
									
								
								icon/捕获.ico
									
									
									
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								icon/捕获.ico
									
									
									
									
									
								
							
										
											Binary file not shown.
										
									
								
							| Before Width: | Height: | Size: 84 KiB | 
| @@ -1,6 +1,6 @@ | |||||||
| #include "platform.h" | #include "platform.h" | ||||||
|  |  | ||||||
| #include "log.h" | #include "rd_log.h" | ||||||
|  |  | ||||||
| #ifdef _WIN32 | #ifdef _WIN32 | ||||||
| #include <Winsock2.h> | #include <Winsock2.h> | ||||||
| @@ -55,7 +55,8 @@ std::string GetMac() { | |||||||
|         const unsigned char *base = |         const unsigned char *base = | ||||||
|             (const unsigned char *)&dlAddr->sdl_data[dlAddr->sdl_nlen]; |             (const unsigned char *)&dlAddr->sdl_data[dlAddr->sdl_nlen]; | ||||||
|         for (int i = 0; i < dlAddr->sdl_alen; i++) { |         for (int i = 0; i < dlAddr->sdl_alen; i++) { | ||||||
|           len += sprintf(mac_addr + len, "%.2X", base[i]); |           len += | ||||||
|  |               snprintf(mac_addr + len, sizeof(mac_addr) - len, "%.2X", base[i]); | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
|       cursor = cursor->ifa_next; |       cursor = cursor->ifa_next; | ||||||
|   | |||||||
| @@ -25,6 +25,11 @@ int ConfigCenter::SetHardwareVideoCodec(bool hardware_video_codec) { | |||||||
|   return 0; |   return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | int ConfigCenter::SetTurn(bool enable_turn) { | ||||||
|  |   enable_turn_ = enable_turn; | ||||||
|  |   return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
| ConfigCenter::LANGUAGE ConfigCenter::GetLanguage() { return language_; } | ConfigCenter::LANGUAGE ConfigCenter::GetLanguage() { return language_; } | ||||||
|  |  | ||||||
| ConfigCenter::VIDEO_QUALITY ConfigCenter::GetVideoQuality() { | ConfigCenter::VIDEO_QUALITY ConfigCenter::GetVideoQuality() { | ||||||
| @@ -36,3 +41,5 @@ ConfigCenter::VIDEO_ENCODE_FORMAT ConfigCenter::GetVideoEncodeFormat() { | |||||||
| } | } | ||||||
|  |  | ||||||
| bool ConfigCenter::IsHardwareVideoCodec() { return hardware_video_codec_; } | bool ConfigCenter::IsHardwareVideoCodec() { return hardware_video_codec_; } | ||||||
|  |  | ||||||
|  | bool ConfigCenter::IsEnableTurn() { return enable_turn_; } | ||||||
| @@ -22,12 +22,14 @@ class ConfigCenter { | |||||||
|   int SetVideoQuality(VIDEO_QUALITY video_quality); |   int SetVideoQuality(VIDEO_QUALITY video_quality); | ||||||
|   int SetVideoEncodeFormat(VIDEO_ENCODE_FORMAT video_encode_format); |   int SetVideoEncodeFormat(VIDEO_ENCODE_FORMAT video_encode_format); | ||||||
|   int SetHardwareVideoCodec(bool hardware_video_codec); |   int SetHardwareVideoCodec(bool hardware_video_codec); | ||||||
|  |   int SetTurn(bool enable_turn); | ||||||
|  |  | ||||||
|  public: |  public: | ||||||
|   LANGUAGE GetLanguage(); |   LANGUAGE GetLanguage(); | ||||||
|   VIDEO_QUALITY GetVideoQuality(); |   VIDEO_QUALITY GetVideoQuality(); | ||||||
|   VIDEO_ENCODE_FORMAT GetVideoEncodeFormat(); |   VIDEO_ENCODE_FORMAT GetVideoEncodeFormat(); | ||||||
|   bool IsHardwareVideoCodec(); |   bool IsHardwareVideoCodec(); | ||||||
|  |   bool IsEnableTurn(); | ||||||
|  |  | ||||||
|  private: |  private: | ||||||
|   // Default value should be same with parameters in localization.h |   // Default value should be same with parameters in localization.h | ||||||
| @@ -35,6 +37,7 @@ class ConfigCenter { | |||||||
|   VIDEO_QUALITY video_quality_ = VIDEO_QUALITY::MEDIUM; |   VIDEO_QUALITY video_quality_ = VIDEO_QUALITY::MEDIUM; | ||||||
|   VIDEO_ENCODE_FORMAT video_encode_format_ = VIDEO_ENCODE_FORMAT::AV1; |   VIDEO_ENCODE_FORMAT video_encode_format_ = VIDEO_ENCODE_FORMAT::AV1; | ||||||
|   bool hardware_video_codec_ = false; |   bool hardware_video_codec_ = false; | ||||||
|  |   bool enable_turn_ = false; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| #endif | #endif | ||||||
| @@ -9,7 +9,7 @@ | |||||||
|  |  | ||||||
| #include <stdio.h> | #include <stdio.h> | ||||||
|  |  | ||||||
| typedef enum { mouse = 0, keyboard } ControlType; | typedef enum { mouse = 0, keyboard, audio_capture } ControlType; | ||||||
| typedef enum { move = 0, left_down, left_up, right_down, right_up } MouseFlag; | typedef enum { move = 0, left_down, left_up, right_down, right_up } MouseFlag; | ||||||
| typedef enum { key_down = 0, key_up } KeyFlag; | typedef enum { key_down = 0, key_up } KeyFlag; | ||||||
| typedef struct { | typedef struct { | ||||||
| @@ -28,6 +28,7 @@ typedef struct { | |||||||
|   union { |   union { | ||||||
|     Mouse m; |     Mouse m; | ||||||
|     Key k; |     Key k; | ||||||
|  |     bool a; | ||||||
|   }; |   }; | ||||||
| } RemoteAction; | } RemoteAction; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -49,8 +49,8 @@ int MouseController::Init(int screen_width, int screen_height) { | |||||||
| int MouseController::Destroy() { return 0; } | int MouseController::Destroy() { return 0; } | ||||||
|  |  | ||||||
| int MouseController::SendCommand(RemoteAction remote_action) { | int MouseController::SendCommand(RemoteAction remote_action) { | ||||||
|   int mouse_pos_x = remote_action.m.x * screen_width_ / 1280; |   int mouse_pos_x = remote_action.m.x; | ||||||
|   int mouse_pos_y = remote_action.m.y * screen_height_ / 720; |   int mouse_pos_y = remote_action.m.y; | ||||||
|  |  | ||||||
|   if (remote_action.type == ControlType::mouse) { |   if (remote_action.type == ControlType::mouse) { | ||||||
|     struct input_event event; |     struct input_event event; | ||||||
|   | |||||||
| @@ -2,7 +2,7 @@ | |||||||
|  |  | ||||||
| #include <ApplicationServices/ApplicationServices.h> | #include <ApplicationServices/ApplicationServices.h> | ||||||
|  |  | ||||||
| #include "log.h" | #include "rd_log.h" | ||||||
|  |  | ||||||
| MouseController::MouseController() {} | MouseController::MouseController() {} | ||||||
|  |  | ||||||
| @@ -12,14 +12,19 @@ int MouseController::Init(int screen_width, int screen_height) { | |||||||
|   screen_width_ = screen_width; |   screen_width_ = screen_width; | ||||||
|   screen_height_ = screen_height; |   screen_height_ = screen_height; | ||||||
|  |  | ||||||
|  |   pixel_width_ = | ||||||
|  |       CGDisplayModeGetPixelWidth(CGDisplayCopyDisplayMode(CGMainDisplayID())); | ||||||
|  |   pixel_height_ = | ||||||
|  |       CGDisplayModeGetPixelHeight(CGDisplayCopyDisplayMode(CGMainDisplayID())); | ||||||
|  |  | ||||||
|   return 0; |   return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| int MouseController::Destroy() { return 0; } | int MouseController::Destroy() { return 0; } | ||||||
|  |  | ||||||
| int MouseController::SendCommand(RemoteAction remote_action) { | int MouseController::SendCommand(RemoteAction remote_action) { | ||||||
|   int mouse_pos_x = remote_action.m.x * screen_width_ / 1280; |   int mouse_pos_x = remote_action.m.x * screen_width_ / pixel_width_; | ||||||
|   int mouse_pos_y = remote_action.m.y * screen_height_ / 720; |   int mouse_pos_y = remote_action.m.y * screen_height_ / pixel_height_; | ||||||
|  |  | ||||||
|   if (remote_action.type == ControlType::mouse) { |   if (remote_action.type == ControlType::mouse) { | ||||||
|     CGEventRef mouse_event; |     CGEventRef mouse_event; | ||||||
|   | |||||||
| @@ -22,6 +22,9 @@ class MouseController : public DeviceController { | |||||||
|  private: |  private: | ||||||
|   int screen_width_ = 0; |   int screen_width_ = 0; | ||||||
|   int screen_height_ = 0; |   int screen_height_ = 0; | ||||||
|  |  | ||||||
|  |   int pixel_width_ = 0; | ||||||
|  |   int pixel_height_ = 0; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| #endif | #endif | ||||||
| @@ -1,6 +1,6 @@ | |||||||
| #include "mouse_controller.h" | #include "mouse_controller.h" | ||||||
|  |  | ||||||
| #include "log.h" | #include "rd_log.h" | ||||||
|  |  | ||||||
| MouseController::MouseController() {} | MouseController::MouseController() {} | ||||||
|  |  | ||||||
| @@ -20,8 +20,8 @@ int MouseController::SendCommand(RemoteAction remote_action) { | |||||||
|  |  | ||||||
|   if (remote_action.type == ControlType::mouse) { |   if (remote_action.type == ControlType::mouse) { | ||||||
|     ip.type = INPUT_MOUSE; |     ip.type = INPUT_MOUSE; | ||||||
|     ip.mi.dx = remote_action.m.x * screen_width_ / 1280; |     ip.mi.dx = remote_action.m.x; | ||||||
|     ip.mi.dy = remote_action.m.y * screen_height_ / 720; |     ip.mi.dy = remote_action.m.y; | ||||||
|     if (remote_action.m.flag == MouseFlag::left_down) { |     if (remote_action.m.flag == MouseFlag::left_down) { | ||||||
|       ip.mi.dwFlags = MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_ABSOLUTE; |       ip.mi.dwFlags = MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_ABSOLUTE; | ||||||
|     } else if (remote_action.m.flag == MouseFlag::left_up) { |     } else if (remote_action.m.flag == MouseFlag::left_up) { | ||||||
|   | |||||||
| @@ -6,14 +6,14 @@ | |||||||
| #endif | #endif | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| #include "log.h" | #include "rd_log.h" | ||||||
| #include "main_window.h" | #include "render.h" | ||||||
|  |  | ||||||
| int main(int argc, char *argv[]) { | int main(int argc, char *argv[]) { | ||||||
|   LOG_INFO("Remote desk"); |   LOG_INFO("Remote desk"); | ||||||
|   MainWindow main_window; |   Render render; | ||||||
|  |  | ||||||
|   main_window.Run(); |   render.Run(); | ||||||
|  |  | ||||||
|   return 0; |   return 0; | ||||||
| } | } | ||||||
| @@ -1,874 +0,0 @@ | |||||||
| #include <SDL.h> |  | ||||||
| #include <stdio.h> |  | ||||||
| #ifdef _WIN32 |  | ||||||
| #ifdef REMOTE_DESK_DEBUG |  | ||||||
| #pragma comment(linker, "/subsystem:\"console\"") |  | ||||||
| #else |  | ||||||
| #pragma comment(linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"") |  | ||||||
| #endif |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| #include <stdio.h> |  | ||||||
|  |  | ||||||
| #include <atomic> |  | ||||||
| #include <chrono> |  | ||||||
| #include <cstring> |  | ||||||
| #include <fstream> |  | ||||||
| #include <iostream> |  | ||||||
| #include <string> |  | ||||||
| #include <thread> |  | ||||||
|  |  | ||||||
| #include "../../thirdparty/projectx/src/interface/x.h" |  | ||||||
| #include "config_center.h" |  | ||||||
| #include "device_controller_factory.h" |  | ||||||
| #include "imgui.h" |  | ||||||
| #include "imgui_impl_sdl2.h" |  | ||||||
| #include "imgui_impl_sdlrenderer2.h" |  | ||||||
| #include "log.h" |  | ||||||
| #include "platform.h" |  | ||||||
| #include "screen_capturer_factory.h" |  | ||||||
|  |  | ||||||
| #define NV12_BUFFER_SIZE 1280 * 720 * 3 / 2 |  | ||||||
|  |  | ||||||
| #ifdef REMOTE_DESK_DEBUG |  | ||||||
| #define MOUSE_CONTROL 0 |  | ||||||
| #else |  | ||||||
| #define MOUSE_CONTROL 1 |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| #define CHINESE_FONT 1 |  | ||||||
|  |  | ||||||
| int screen_w = 1280, screen_h = 720; |  | ||||||
| int window_w = 1280, window_h = 720; |  | ||||||
| const int pixel_w = 1280, pixel_h = 720; |  | ||||||
|  |  | ||||||
| unsigned char dst_buffer[pixel_w * pixel_h * 3 / 2]; |  | ||||||
| unsigned char audio_buffer[960]; |  | ||||||
| SDL_Texture *sdlTexture = nullptr; |  | ||||||
| SDL_Renderer *sdlRenderer = nullptr; |  | ||||||
| SDL_Rect sdlRect; |  | ||||||
| SDL_Window *window; |  | ||||||
| static SDL_AudioDeviceID input_dev; |  | ||||||
| static SDL_AudioDeviceID output_dev; |  | ||||||
|  |  | ||||||
| uint32_t start_time, end_time, elapsed_time; |  | ||||||
| uint32_t frame_count = 0; |  | ||||||
| int fps = 0; |  | ||||||
|  |  | ||||||
| static std::atomic<bool> audio_buffer_fresh{false}; |  | ||||||
| static uint32_t last_ts = 0; |  | ||||||
|  |  | ||||||
| int dst_bufsize; |  | ||||||
| struct SwrContext *swr_ctx; |  | ||||||
|  |  | ||||||
| int ret; |  | ||||||
|  |  | ||||||
| int audio_len = 0; |  | ||||||
|  |  | ||||||
| std::string window_title = "Remote Desk Client"; |  | ||||||
| std::string connection_status_str = "-"; |  | ||||||
| std::string signal_status_str = "-"; |  | ||||||
|  |  | ||||||
| std::atomic<SignalStatus> signal_status{SignalStatus::SignalClosed}; |  | ||||||
| std::atomic<ConnectionStatus> connection_status{ConnectionStatus::Closed}; |  | ||||||
|  |  | ||||||
| // Refresh Event |  | ||||||
| #define REFRESH_EVENT (SDL_USEREVENT + 1) |  | ||||||
| #define QUIT_EVENT (SDL_USEREVENT + 2) |  | ||||||
|  |  | ||||||
| typedef struct { |  | ||||||
|   char password[7]; |  | ||||||
| } CDCache; |  | ||||||
|  |  | ||||||
| int thread_exit = 0; |  | ||||||
| PeerPtr *peer_server = nullptr; |  | ||||||
| // PeerPtr *peer_server = nullptr; |  | ||||||
| bool joined = false; |  | ||||||
| bool received_frame = false; |  | ||||||
| bool menu_hovered = false; |  | ||||||
|  |  | ||||||
| static bool connect_button_pressed = false; |  | ||||||
| static bool fullscreen_button_pressed = false; |  | ||||||
|  |  | ||||||
| #if CHINESE_FONT |  | ||||||
| static const char *connect_label = u8"连接"; |  | ||||||
| static const char *fullscreen_label = u8"全屏"; |  | ||||||
| #else |  | ||||||
| static const char *connect_label = "Connect"; |  | ||||||
| static const char *fullscreen_label = "FULLSCREEN"; |  | ||||||
| #endif |  | ||||||
| static char input_password[7] = ""; |  | ||||||
| static FILE *cd_cache_file = nullptr; |  | ||||||
| static CDCache cd_cache; |  | ||||||
|  |  | ||||||
| static bool is_create_connection = false; |  | ||||||
| static bool done = false; |  | ||||||
|  |  | ||||||
| ScreenCapturerFactory *screen_capturer_factory = nullptr; |  | ||||||
| ScreenCapturer *screen_capturer = nullptr; |  | ||||||
|  |  | ||||||
| DeviceControllerFactory *device_controller_factory = nullptr; |  | ||||||
| MouseController *mouse_controller = nullptr; |  | ||||||
|  |  | ||||||
| ConfigCenter config_center; |  | ||||||
|  |  | ||||||
| char *nv12_buffer = nullptr; |  | ||||||
|  |  | ||||||
| #ifdef __linux__ |  | ||||||
| std::chrono::_V2::system_clock::time_point last_frame_time_; |  | ||||||
| #else |  | ||||||
| std::chrono::steady_clock::time_point last_frame_time_; |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| inline int ProcessMouseKeyEven(SDL_Event &ev) { |  | ||||||
|   float ratio = (float)(1280.0 / window_w); |  | ||||||
|  |  | ||||||
|   RemoteAction remote_action; |  | ||||||
|   remote_action.m.x = (size_t)(ev.button.x * ratio); |  | ||||||
|   remote_action.m.y = (size_t)(ev.button.y * ratio); |  | ||||||
|  |  | ||||||
|   if (SDL_KEYDOWN == ev.type)  // SDL_KEYUP |  | ||||||
|   { |  | ||||||
|     // printf("SDLK_DOWN: %d\n", SDL_KeyCode(ev.key.keysym.sym)); |  | ||||||
|     if (SDLK_DOWN == ev.key.keysym.sym) { |  | ||||||
|       // printf("SDLK_DOWN  \n"); |  | ||||||
|  |  | ||||||
|     } else if (SDLK_UP == ev.key.keysym.sym) { |  | ||||||
|       // printf("SDLK_UP  \n"); |  | ||||||
|  |  | ||||||
|     } else if (SDLK_LEFT == ev.key.keysym.sym) { |  | ||||||
|       // printf("SDLK_LEFT  \n"); |  | ||||||
|  |  | ||||||
|     } else if (SDLK_RIGHT == ev.key.keysym.sym) { |  | ||||||
|       // printf("SDLK_RIGHT  \n"); |  | ||||||
|     } |  | ||||||
|   } else if (SDL_MOUSEBUTTONDOWN == ev.type) { |  | ||||||
|     remote_action.type = ControlType::mouse; |  | ||||||
|     if (SDL_BUTTON_LEFT == ev.button.button) { |  | ||||||
|       remote_action.m.flag = MouseFlag::left_down; |  | ||||||
|     } else if (SDL_BUTTON_RIGHT == ev.button.button) { |  | ||||||
|       remote_action.m.flag = MouseFlag::right_down; |  | ||||||
|     } |  | ||||||
|     SendData(peer_server, DATA_TYPE::DATA, (const char *)&remote_action, |  | ||||||
|              sizeof(remote_action)); |  | ||||||
|   } else if (SDL_MOUSEBUTTONUP == ev.type) { |  | ||||||
|     remote_action.type = ControlType::mouse; |  | ||||||
|     if (SDL_BUTTON_LEFT == ev.button.button) { |  | ||||||
|       remote_action.m.flag = MouseFlag::left_up; |  | ||||||
|     } else if (SDL_BUTTON_RIGHT == ev.button.button) { |  | ||||||
|       remote_action.m.flag = MouseFlag::right_up; |  | ||||||
|     } |  | ||||||
|     SendData(peer_server, DATA_TYPE::DATA, (const char *)&remote_action, |  | ||||||
|              sizeof(remote_action)); |  | ||||||
|   } else if (SDL_MOUSEMOTION == ev.type) { |  | ||||||
|     remote_action.type = ControlType::mouse; |  | ||||||
|     remote_action.m.flag = MouseFlag::move; |  | ||||||
|     SendData(peer_server, DATA_TYPE::DATA, (const char *)&remote_action, |  | ||||||
|              sizeof(remote_action)); |  | ||||||
|   } else if (SDL_QUIT == ev.type) { |  | ||||||
|     SDL_Event event; |  | ||||||
|     event.type = SDL_QUIT; |  | ||||||
|     SDL_PushEvent(&event); |  | ||||||
|     printf("SDL_QUIT\n"); |  | ||||||
|     return 0; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void SdlCaptureAudioIn(void *userdata, Uint8 *stream, int len) { |  | ||||||
|   if (1) { |  | ||||||
|     if ("Connected" == connection_status_str) { |  | ||||||
|       SendData(peer_server, DATA_TYPE::AUDIO, (const char *)stream, len); |  | ||||||
|     } |  | ||||||
|   } else { |  | ||||||
|     memcpy(audio_buffer, stream, len); |  | ||||||
|     audio_len = len; |  | ||||||
|     SDL_Delay(10); |  | ||||||
|     audio_buffer_fresh = true; |  | ||||||
|   } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void SdlCaptureAudioOut(void *userdata, Uint8 *stream, int len) { |  | ||||||
|   if (!audio_buffer_fresh) { |  | ||||||
|     return; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   SDL_memset(stream, 0, len); |  | ||||||
|  |  | ||||||
|   if (audio_len == 0) { |  | ||||||
|     return; |  | ||||||
|   } else { |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   len = (len > audio_len ? audio_len : len); |  | ||||||
|   SDL_MixAudioFormat(stream, audio_buffer, AUDIO_S16LSB, len, |  | ||||||
|                      SDL_MIX_MAXVOLUME); |  | ||||||
|   audio_buffer_fresh = false; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void ServerReceiveVideoBuffer(const char *data, size_t size, |  | ||||||
|                               const char *user_id, size_t user_id_size) { |  | ||||||
|   if (joined) { |  | ||||||
|     memcpy(dst_buffer, data, size); |  | ||||||
|  |  | ||||||
|     SDL_Event event; |  | ||||||
|     event.type = REFRESH_EVENT; |  | ||||||
|     SDL_PushEvent(&event); |  | ||||||
|     received_frame = true; |  | ||||||
|   } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void ClientReceiveVideoBuffer(const char *data, size_t size, |  | ||||||
|                               const char *user_id, size_t user_id_size) { |  | ||||||
|   // std::cout << "Receive: [" << user_id << "] " << std::endl; |  | ||||||
|   if (joined) { |  | ||||||
|     memcpy(dst_buffer, data, size); |  | ||||||
|  |  | ||||||
|     SDL_Event event; |  | ||||||
|     event.type = REFRESH_EVENT; |  | ||||||
|     SDL_PushEvent(&event); |  | ||||||
|     received_frame = true; |  | ||||||
|   } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void ServerReceiveAudioBuffer(const char *data, size_t size, |  | ||||||
|                               const char *user_id, size_t user_id_size) { |  | ||||||
|   // memset(audio_buffer, 0, size); |  | ||||||
|   // memcpy(audio_buffer, data, size); |  | ||||||
|   // audio_len = size; |  | ||||||
|   audio_buffer_fresh = true; |  | ||||||
|  |  | ||||||
|   SDL_QueueAudio(output_dev, data, (Uint32)size); |  | ||||||
|   // printf("queue audio\n"); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void ClientReceiveAudioBuffer(const char *data, size_t size, |  | ||||||
|                               const char *user_id, size_t user_id_size) { |  | ||||||
|   // std::cout << "Client receive audio, size " << size << ", user [" << user_id |  | ||||||
|   //           << "] " << std::endl; |  | ||||||
|   SDL_QueueAudio(output_dev, data, (Uint32)size); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void ServerReceiveDataBuffer(const char *data, size_t size, const char *user_id, |  | ||||||
|                              size_t user_id_size) { |  | ||||||
|   std::string user(user_id, user_id_size); |  | ||||||
|  |  | ||||||
|   RemoteAction remote_action; |  | ||||||
|   memcpy(&remote_action, data, sizeof(remote_action)); |  | ||||||
|  |  | ||||||
|   // std::cout << "remote_action: " << remote_action.type << " " |  | ||||||
|   //           << remote_action.m.flag << " " << remote_action.m.x << " " |  | ||||||
|   //           << remote_action.m.y << std::endl; |  | ||||||
| #if MOUSE_CONTROL |  | ||||||
|   if (mouse_controller) { |  | ||||||
|     mouse_controller->SendCommand(remote_action); |  | ||||||
|   } |  | ||||||
| #endif |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void ClientReceiveDataBuffer(const char *data, size_t size, const char *user_id, |  | ||||||
|                              size_t user_id_size) {} |  | ||||||
|  |  | ||||||
| void SignalStatus(SignalStatus status) { |  | ||||||
|   signal_status = status; |  | ||||||
|   if (SignalStatus::SignalConnecting == status) { |  | ||||||
|     signal_status_str = "SignalConnecting"; |  | ||||||
|   } else if (SignalStatus::SignalConnected == status) { |  | ||||||
|     signal_status_str = "SignalConnected"; |  | ||||||
|   } else if (SignalStatus::SignalFailed == status) { |  | ||||||
|     signal_status_str = "SignalFailed"; |  | ||||||
|   } else if (SignalStatus::SignalClosed == status) { |  | ||||||
|     signal_status_str = "SignalClosed"; |  | ||||||
|   } else if (SignalStatus::SignalReconnecting == status) { |  | ||||||
|     signal_status_str = "SignalReconnecting"; |  | ||||||
|   } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void ConnectionStatus(ConnectionStatus status) { |  | ||||||
|   connection_status = status; |  | ||||||
|   if (ConnectionStatus::Connecting == status) { |  | ||||||
|     connection_status_str = "Connecting"; |  | ||||||
|   } else if (ConnectionStatus::Connected == status) { |  | ||||||
|     connection_status_str = "Connected"; |  | ||||||
|     joined = true; |  | ||||||
|   } else if (ConnectionStatus::Disconnected == status) { |  | ||||||
|     connection_status_str = "Disconnected"; |  | ||||||
|   } else if (ConnectionStatus::Failed == status) { |  | ||||||
|     connection_status_str = "Failed"; |  | ||||||
|   } else if (ConnectionStatus::Closed == status) { |  | ||||||
|     connection_status_str = "Closed"; |  | ||||||
|   } else if (ConnectionStatus::IncorrectPassword == status) { |  | ||||||
|     connection_status_str = "Incorrect password"; |  | ||||||
|     if (connect_button_pressed) { |  | ||||||
|       connect_button_pressed = false; |  | ||||||
|       joined = false; |  | ||||||
|       connect_label = connect_button_pressed ? "Disconnect" : "Connect"; |  | ||||||
|     } |  | ||||||
|   } else if (ConnectionStatus::NoSuchTransmissionId == status) { |  | ||||||
|     connection_status_str = "No such transmission id"; |  | ||||||
|     if (connect_button_pressed) { |  | ||||||
|       connect_button_pressed = false; |  | ||||||
|       joined = false; |  | ||||||
|       connect_label = connect_button_pressed ? "Disconnect" : "Connect"; |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int main(int argc, char *argv[]) { |  | ||||||
|   LOG_INFO("Remote desk"); |  | ||||||
|  |  | ||||||
|   last_ts = static_cast<uint32_t>( |  | ||||||
|       std::chrono::duration_cast<std::chrono::milliseconds>( |  | ||||||
|           std::chrono::high_resolution_clock::now().time_since_epoch()) |  | ||||||
|           .count()); |  | ||||||
|  |  | ||||||
|   cd_cache_file = fopen("cache.cd", "r+"); |  | ||||||
|   if (cd_cache_file) { |  | ||||||
|     fseek(cd_cache_file, 0, SEEK_SET); |  | ||||||
|     fread(&cd_cache.password, sizeof(cd_cache.password), 1, cd_cache_file); |  | ||||||
|     fclose(cd_cache_file); |  | ||||||
|     strncpy(input_password, cd_cache.password, sizeof(cd_cache.password)); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   // Setup SDL |  | ||||||
|   if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER | |  | ||||||
|                SDL_INIT_GAMECONTROLLER) != 0) { |  | ||||||
|     printf("Error: %s\n", SDL_GetError()); |  | ||||||
|     return -1; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   // From 2.0.18: Enable native IME. |  | ||||||
| #ifdef SDL_HINT_IME_SHOW_UI |  | ||||||
|   SDL_SetHint(SDL_HINT_IME_SHOW_UI, "1"); |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
|   // Create window with SDL_Renderer graphics context |  | ||||||
|   SDL_WindowFlags window_flags = |  | ||||||
|       (SDL_WindowFlags)(SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI); |  | ||||||
|   window = SDL_CreateWindow("Remote Desk", SDL_WINDOWPOS_CENTERED, |  | ||||||
|                             SDL_WINDOWPOS_CENTERED, window_w, window_h, |  | ||||||
|                             window_flags); |  | ||||||
|  |  | ||||||
|   SDL_DisplayMode DM; |  | ||||||
|   SDL_GetCurrentDisplayMode(0, &DM); |  | ||||||
|   screen_w = DM.w; |  | ||||||
|   screen_h = DM.h; |  | ||||||
|  |  | ||||||
|   sdlRenderer = SDL_CreateRenderer( |  | ||||||
|       window, -1, SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_ACCELERATED); |  | ||||||
|   if (sdlRenderer == nullptr) { |  | ||||||
|     SDL_Log("Error creating SDL_Renderer!"); |  | ||||||
|     return 0; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   Uint32 pixformat = 0; |  | ||||||
|   pixformat = SDL_PIXELFORMAT_NV12; |  | ||||||
|  |  | ||||||
|   sdlTexture = SDL_CreateTexture(sdlRenderer, pixformat, |  | ||||||
|                                  SDL_TEXTUREACCESS_STREAMING, pixel_w, pixel_h); |  | ||||||
|  |  | ||||||
|   // Audio |  | ||||||
|   SDL_AudioSpec want_in, have_in, want_out, have_out; |  | ||||||
|   SDL_zero(want_in); |  | ||||||
|   want_in.freq = 48000; |  | ||||||
|   want_in.format = AUDIO_S16LSB; |  | ||||||
|   want_in.channels = 1; |  | ||||||
|   want_in.samples = 480; |  | ||||||
|   want_in.callback = SdlCaptureAudioIn; |  | ||||||
|  |  | ||||||
|   input_dev = SDL_OpenAudioDevice(NULL, 1, &want_in, &have_in, 0); |  | ||||||
|   if (input_dev == 0) { |  | ||||||
|     SDL_Log("Failed to open input: %s", SDL_GetError()); |  | ||||||
|     // return 1; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   SDL_zero(want_out); |  | ||||||
|   want_out.freq = 48000; |  | ||||||
|   want_out.format = AUDIO_S16LSB; |  | ||||||
|   want_out.channels = 1; |  | ||||||
|   // want_out.silence = 0; |  | ||||||
|   want_out.samples = 480; |  | ||||||
|   want_out.callback = NULL; |  | ||||||
|  |  | ||||||
|   output_dev = SDL_OpenAudioDevice(NULL, 0, &want_out, &have_out, 0); |  | ||||||
|   if (output_dev == 0) { |  | ||||||
|     SDL_Log("Failed to open input: %s", SDL_GetError()); |  | ||||||
|     // return 1; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   SDL_PauseAudioDevice(input_dev, 0); |  | ||||||
|   SDL_PauseAudioDevice(output_dev, 0); |  | ||||||
|  |  | ||||||
|   // Setup Dear ImGui context |  | ||||||
|   IMGUI_CHECKVERSION(); |  | ||||||
|   ImGui::CreateContext(); |  | ||||||
|   ImGuiIO &io = ImGui::GetIO(); |  | ||||||
|  |  | ||||||
|   io.ConfigFlags |= |  | ||||||
|       ImGuiConfigFlags_NavEnableKeyboard;  // Enable Keyboard Controls |  | ||||||
|   io.ConfigFlags |= |  | ||||||
|       ImGuiConfigFlags_NavEnableGamepad;  // Enable Gamepad Controls |  | ||||||
|  |  | ||||||
| #if CHINESE_FONT |  | ||||||
|   // Load Fonts |  | ||||||
| #ifdef _WIN32 |  | ||||||
|   std::string default_font_path = "c:/windows/fonts/simhei.ttf"; |  | ||||||
|   std::ifstream font_path_f(default_font_path.c_str()); |  | ||||||
|   std::string font_path = |  | ||||||
|       font_path_f.good() ? "c:/windows/fonts/simhei.ttf" : ""; |  | ||||||
|   if (!font_path.empty()) { |  | ||||||
|     io.Fonts->AddFontFromFileTTF(font_path.c_str(), 13.0f, NULL, |  | ||||||
|                                  io.Fonts->GetGlyphRangesChineseFull()); |  | ||||||
|   } |  | ||||||
| #elif __APPLE__ |  | ||||||
|   std::string default_font_path = "/System/Library/Fonts/PingFang.ttc"; |  | ||||||
|   std::ifstream font_path_f(default_font_path.c_str()); |  | ||||||
|   std::string font_path = |  | ||||||
|       font_path_f.good() ? "/System/Library/Fonts/PingFang.ttc" : ""; |  | ||||||
|   if (!font_path.empty()) { |  | ||||||
|     io.Fonts->AddFontFromFileTTF(font_path.c_str(), 13.0f, NULL, |  | ||||||
|                                  io.Fonts->GetGlyphRangesChineseFull()); |  | ||||||
|   } |  | ||||||
| #elif __linux__ |  | ||||||
|   io.Fonts->AddFontFromFileTTF("c:/windows/fonts/msyh.ttc", 13.0f, NULL, |  | ||||||
|                                io.Fonts->GetGlyphRangesChineseFull()); |  | ||||||
| #endif |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
|   // Setup Dear ImGui style |  | ||||||
|   // ImGui::StyleColorsDark(); |  | ||||||
|   ImGui::StyleColorsLight(); |  | ||||||
|  |  | ||||||
|   // Setup Platform/Renderer backends |  | ||||||
|   ImGui_ImplSDL2_InitForSDLRenderer(window, sdlRenderer); |  | ||||||
|   ImGui_ImplSDLRenderer2_Init(sdlRenderer); |  | ||||||
|  |  | ||||||
|   // Our state |  | ||||||
|   bool show_demo_window = true; |  | ||||||
|   bool show_another_window = false; |  | ||||||
|   ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f); |  | ||||||
|  |  | ||||||
|   std::string mac_addr_str = GetMac(); |  | ||||||
|  |  | ||||||
|   std::thread rtc_thread( |  | ||||||
|       [](int screen_width, int screen_height) { |  | ||||||
|         std::string default_cfg_path = "../../../../config/config.ini"; |  | ||||||
|         std::ifstream f(default_cfg_path.c_str()); |  | ||||||
|  |  | ||||||
|         std::string mac_addr_str = GetMac(); |  | ||||||
|  |  | ||||||
|         Params server_params; |  | ||||||
|         server_params.cfg_path = |  | ||||||
|             f.good() ? "../../../../config/config.ini" : "config.ini"; |  | ||||||
|         server_params.on_receive_video_buffer = ServerReceiveVideoBuffer; |  | ||||||
|         server_params.on_receive_audio_buffer = ServerReceiveAudioBuffer; |  | ||||||
|         server_params.on_receive_data_buffer = ServerReceiveDataBuffer; |  | ||||||
|         server_params.on_signal_status = SignalStatus; |  | ||||||
|         server_params.on_connection_status = ConnectionStatus; |  | ||||||
|  |  | ||||||
|         std::string transmission_id = "000001"; |  | ||||||
|  |  | ||||||
|         peer_server = CreatePeer(&server_params); |  | ||||||
|         LOG_INFO("Create peer_server"); |  | ||||||
|         std::string server_user_id = "S-" + mac_addr_str; |  | ||||||
|         Init(peer_server, server_user_id.c_str()); |  | ||||||
|         LOG_INFO("peer_server init finish"); |  | ||||||
|  |  | ||||||
|         { |  | ||||||
|           while (SignalStatus::SignalConnected != signal_status && !done) { |  | ||||||
|             std::this_thread::sleep_for(std::chrono::seconds(1)); |  | ||||||
|           } |  | ||||||
|  |  | ||||||
|           if (done) { |  | ||||||
|             return; |  | ||||||
|           } |  | ||||||
|  |  | ||||||
|           std::string user_id = "S-" + mac_addr_str; |  | ||||||
|           is_create_connection = |  | ||||||
|               CreateConnection(peer_server, mac_addr_str.c_str(), |  | ||||||
|                                input_password) |  | ||||||
|                   ? false |  | ||||||
|                   : true; |  | ||||||
|  |  | ||||||
|           nv12_buffer = new char[NV12_BUFFER_SIZE]; |  | ||||||
|  |  | ||||||
|           // Screen capture |  | ||||||
|           screen_capturer_factory = new ScreenCapturerFactory(); |  | ||||||
|           screen_capturer = (ScreenCapturer *)screen_capturer_factory->Create(); |  | ||||||
|  |  | ||||||
|           last_frame_time_ = std::chrono::high_resolution_clock::now(); |  | ||||||
|           ScreenCapturer::RECORD_DESKTOP_RECT rect; |  | ||||||
|           rect.left = 0; |  | ||||||
|           rect.top = 0; |  | ||||||
|           rect.right = screen_w; |  | ||||||
|           rect.bottom = screen_h; |  | ||||||
|  |  | ||||||
|           int screen_capturer_init_ret = screen_capturer->Init( |  | ||||||
|               rect, 60, |  | ||||||
|               [](unsigned char *data, int size, int width, int height) -> void { |  | ||||||
|                 auto now_time = std::chrono::high_resolution_clock::now(); |  | ||||||
|                 std::chrono::duration<double> duration = |  | ||||||
|                     now_time - last_frame_time_; |  | ||||||
|                 auto tc = duration.count() * 1000; |  | ||||||
|  |  | ||||||
|                 if (tc >= 0) { |  | ||||||
|                   SendData(peer_server, DATA_TYPE::VIDEO, (const char *)data, |  | ||||||
|                            NV12_BUFFER_SIZE); |  | ||||||
|                   last_frame_time_ = now_time; |  | ||||||
|                 } |  | ||||||
|               }); |  | ||||||
|  |  | ||||||
|           if (0 == screen_capturer_init_ret) { |  | ||||||
|             screen_capturer->Start(); |  | ||||||
|           } else { |  | ||||||
|             screen_capturer->Destroy(); |  | ||||||
|             screen_capturer = nullptr; |  | ||||||
|           } |  | ||||||
|  |  | ||||||
|           // Mouse control |  | ||||||
|           device_controller_factory = new DeviceControllerFactory(); |  | ||||||
|           mouse_controller = |  | ||||||
|               (MouseController *)device_controller_factory->Create( |  | ||||||
|                   DeviceControllerFactory::Device::Mouse); |  | ||||||
|           int mouse_controller_init_ret = |  | ||||||
|               mouse_controller->Init(screen_w, screen_h); |  | ||||||
|           if (0 != mouse_controller_init_ret) { |  | ||||||
|             mouse_controller->Destroy(); |  | ||||||
|             mouse_controller = nullptr; |  | ||||||
|           } |  | ||||||
|         } |  | ||||||
|       }, |  | ||||||
|       screen_w, screen_h); |  | ||||||
|  |  | ||||||
|   // Main loop |  | ||||||
|   while (!done) { |  | ||||||
|     // Start the Dear ImGui frame |  | ||||||
|     ImGui_ImplSDLRenderer2_NewFrame(); |  | ||||||
|     ImGui_ImplSDL2_NewFrame(); |  | ||||||
|     ImGui::NewFrame(); |  | ||||||
|  |  | ||||||
|     if (joined && !menu_hovered) { |  | ||||||
|       ImGui::SetMouseCursor(ImGuiMouseCursor_None); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     { |  | ||||||
|       static float f = 0.0f; |  | ||||||
|       static int counter = 0; |  | ||||||
|  |  | ||||||
|       const ImGuiViewport *main_viewport = ImGui::GetMainViewport(); |  | ||||||
|       ImGui::SetNextWindowPos(ImVec2(0, 0), ImGuiCond_Once); |  | ||||||
|  |  | ||||||
|       // ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0.0f, 0.0f)); |  | ||||||
|  |  | ||||||
| #if CHINESE_FONT |  | ||||||
|       ImGui::SetNextWindowSize(ImVec2(160, 210)); |  | ||||||
| #else |  | ||||||
|       ImGui::SetNextWindowSize(ImVec2(180, 210)); |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| #if CHINESE_FONT |  | ||||||
|       if (!joined) { |  | ||||||
|         ImGui::SetNextWindowPos(ImVec2(0, 0), ImGuiCond_Always); |  | ||||||
|         ImGui::Begin(u8"菜单", nullptr, |  | ||||||
|                      ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse | |  | ||||||
|                          ImGuiWindowFlags_NoMove); |  | ||||||
|       } else { |  | ||||||
|         ImGui::SetNextWindowCollapsed(true, ImGuiCond_Once); |  | ||||||
|         ImGui::Begin(u8"菜单", nullptr, ImGuiWindowFlags_None); |  | ||||||
|       } |  | ||||||
| #else |  | ||||||
|       if (!joined) { |  | ||||||
|         ImGui::SetNextWindowPos(ImVec2(0, 0), ImGuiCond_Always); |  | ||||||
|         ImGui::Begin("Menu", nullptr, |  | ||||||
|                      ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse | |  | ||||||
|                          ImGuiWindowFlags_NoMove); |  | ||||||
|       } else { |  | ||||||
|         ImGui::SetNextWindowCollapsed(true, ImGuiCond_Once); |  | ||||||
|         ImGui::Begin("Menu", nullptr, ImGuiWindowFlags_None); |  | ||||||
|       } |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
|       { |  | ||||||
|         menu_hovered = ImGui::IsWindowHovered(); |  | ||||||
| #if CHINESE_FONT |  | ||||||
|         ImGui::Text(u8"本机ID:"); |  | ||||||
| #else |  | ||||||
|         ImGui::Text("LOCAL ID:"); |  | ||||||
| #endif |  | ||||||
|         ImGui::SameLine(); |  | ||||||
|         ImGui::SetNextItemWidth(90); |  | ||||||
| #if CHINESE_FONT |  | ||||||
|         ImGui::SetCursorPosX(60.0f); |  | ||||||
| #else |  | ||||||
|         ImGui::SetCursorPosX(80.0f); |  | ||||||
| #endif |  | ||||||
|         ImGui::InputText( |  | ||||||
|             "##local_id", (char *)mac_addr_str.c_str(), |  | ||||||
|             mac_addr_str.length() + 1, |  | ||||||
|             ImGuiInputTextFlags_CharsUppercase | ImGuiInputTextFlags_ReadOnly); |  | ||||||
|  |  | ||||||
| #if CHINESE_FONT |  | ||||||
|         ImGui::Text(u8"密码:"); |  | ||||||
| #else |  | ||||||
|         ImGui::Text("PASSWORD:"); |  | ||||||
| #endif |  | ||||||
|         ImGui::SameLine(); |  | ||||||
|         char input_password_tmp[7] = ""; |  | ||||||
|         std::string input_password_str = "123456"; |  | ||||||
|         strncpy(input_password_tmp, input_password, sizeof(input_password)); |  | ||||||
|         ImGui::SetNextItemWidth(90); |  | ||||||
| #if CHINESE_FONT |  | ||||||
|         ImGui::SetCursorPosX(60.0f); |  | ||||||
|         ImGui::InputTextWithHint("##server_pwd", u8"最长6个字符", |  | ||||||
|                                  input_password, IM_ARRAYSIZE(input_password), |  | ||||||
|                                  ImGuiInputTextFlags_CharsNoBlank); |  | ||||||
| #else |  | ||||||
|         ImGui::SetCursorPosX(80.0f); |  | ||||||
|         ImGui::InputTextWithHint("##server_pwd", "max 6 chars", input_password, |  | ||||||
|                                  IM_ARRAYSIZE(input_password), |  | ||||||
|                                  ImGuiInputTextFlags_CharsNoBlank); |  | ||||||
| #endif |  | ||||||
|         if (strcmp(input_password_tmp, input_password)) { |  | ||||||
|           cd_cache_file = fopen("cache.cd", "w+"); |  | ||||||
|           if (cd_cache_file) { |  | ||||||
|             fseek(cd_cache_file, 0, SEEK_SET); |  | ||||||
|             strncpy(cd_cache.password, input_password, sizeof(input_password)); |  | ||||||
|             fwrite(&cd_cache.password, sizeof(cd_cache.password), 1, |  | ||||||
|                    cd_cache_file); |  | ||||||
|             fclose(cd_cache_file); |  | ||||||
|           } |  | ||||||
|           LeaveConnection(peer_server); |  | ||||||
|           CreateConnection(peer_server, mac_addr_str.c_str(), input_password); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         ImGui::Spacing(); |  | ||||||
|  |  | ||||||
|         ImGui::Separator(); |  | ||||||
|  |  | ||||||
|         ImGui::Spacing(); |  | ||||||
|         { |  | ||||||
|           { |  | ||||||
|             static char remote_id[20] = ""; |  | ||||||
| #if CHINESE_FONT |  | ||||||
|             ImGui::Text(u8"远端ID:"); |  | ||||||
| #else |  | ||||||
|             ImGui::Text("REMOTE ID:"); |  | ||||||
| #endif |  | ||||||
|             ImGui::SameLine(); |  | ||||||
|             ImGui::SetNextItemWidth(90); |  | ||||||
| #if CHINESE_FONT |  | ||||||
|             ImGui::SetCursorPosX(60.0f); |  | ||||||
| #else |  | ||||||
|             ImGui::SetCursorPosX(80.0f); |  | ||||||
| #endif |  | ||||||
|             ImGui::InputTextWithHint("##remote_id", mac_addr_str.c_str(), |  | ||||||
|                                      remote_id, IM_ARRAYSIZE(remote_id), |  | ||||||
|                                      ImGuiInputTextFlags_CharsUppercase | |  | ||||||
|                                          ImGuiInputTextFlags_CharsNoBlank); |  | ||||||
|  |  | ||||||
|             ImGui::Spacing(); |  | ||||||
|  |  | ||||||
| #if CHINESE_FONT |  | ||||||
|             ImGui::Text(u8"密码:"); |  | ||||||
| #else |  | ||||||
|             ImGui::Text("PASSWORD:"); |  | ||||||
| #endif |  | ||||||
|             ImGui::SameLine(); |  | ||||||
|             ImGui::SetNextItemWidth(90); |  | ||||||
|             static char client_password[20] = ""; |  | ||||||
| #if CHINESE_FONT |  | ||||||
|             ImGui::SetCursorPosX(60.0f); |  | ||||||
|             ImGui::InputTextWithHint("##client_pwd", u8"最长6个字符", |  | ||||||
|                                      client_password, |  | ||||||
|                                      IM_ARRAYSIZE(client_password), |  | ||||||
|                                      ImGuiInputTextFlags_CharsNoBlank); |  | ||||||
| #else |  | ||||||
|             ImGui::SetCursorPosX(80.0f); |  | ||||||
|             ImGui::InputTextWithHint("##client_pwd", "max 6 chars", |  | ||||||
|                                      client_password, |  | ||||||
|                                      IM_ARRAYSIZE(client_password), |  | ||||||
|                                      ImGuiInputTextFlags_CharsNoBlank); |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
|             ImGui::Spacing(); |  | ||||||
|             ImGui::Separator(); |  | ||||||
|             ImGui::Spacing(); |  | ||||||
|  |  | ||||||
|             if (ImGui::Button(connect_label)) { |  | ||||||
|               int ret = -1; |  | ||||||
|               if ("SignalConnected" == signal_status_str) { |  | ||||||
| #if CHINESE_FONT |  | ||||||
|                 if (strcmp(connect_label, u8"连接") == 0 && !joined) { |  | ||||||
| #else |  | ||||||
|                 if (strcmp(connect_label, "Connect") == 0 && !joined) { |  | ||||||
| #endif |  | ||||||
|                   std::string user_id = "C-" + mac_addr_str; |  | ||||||
|                   ret = JoinConnection(peer_server, remote_id, client_password); |  | ||||||
|                   if (0 == ret) { |  | ||||||
|                     // joined = true; |  | ||||||
|                   } |  | ||||||
| #if CHINESE_FONT |  | ||||||
|                 } else if (strcmp(connect_label, u8"断开连接") == 0 && joined) { |  | ||||||
| #else |  | ||||||
|                 } else if (strcmp(connect_label, "Disconnect") == 0 && joined) { |  | ||||||
| #endif |  | ||||||
|                   ret = LeaveConnection(peer_server); |  | ||||||
|                   CreateConnection(peer_server, mac_addr_str.c_str(), |  | ||||||
|                                    input_password); |  | ||||||
|                   memset(audio_buffer, 0, 960); |  | ||||||
|                   if (0 == ret) { |  | ||||||
|                     joined = false; |  | ||||||
|                     received_frame = false; |  | ||||||
|                   } |  | ||||||
|                 } |  | ||||||
|  |  | ||||||
|                 if (0 == ret) { |  | ||||||
|                   connect_button_pressed = !connect_button_pressed; |  | ||||||
| #if CHINESE_FONT |  | ||||||
|                   connect_label = |  | ||||||
|                       connect_button_pressed ? u8"断开连接" : u8"连接"; |  | ||||||
| #else |  | ||||||
|                   connect_label = |  | ||||||
|                       connect_button_pressed ? "Disconnect" : "Connect"; |  | ||||||
| #endif |  | ||||||
|                 } |  | ||||||
|               } |  | ||||||
|             } |  | ||||||
|           } |  | ||||||
|         } |  | ||||||
|       } |  | ||||||
|  |  | ||||||
|       ImGui::Spacing(); |  | ||||||
|  |  | ||||||
|       ImGui::Separator(); |  | ||||||
|  |  | ||||||
|       ImGui::Spacing(); |  | ||||||
|  |  | ||||||
| #if CHINESE_FONT |  | ||||||
|       if (ImGui::Button(fullscreen_label)) { |  | ||||||
|         if (strcmp(fullscreen_label, u8"全屏") == 0) { |  | ||||||
| #else |  | ||||||
|       if (ImGui::Button(fullscreen_label)) { |  | ||||||
|         if (strcmp(fullscreen_label, "FULLSCREEN") == 0) { |  | ||||||
| #endif |  | ||||||
|           SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN_DESKTOP); |  | ||||||
|         } else { |  | ||||||
|           SDL_SetWindowFullscreen(window, SDL_FALSE); |  | ||||||
|         } |  | ||||||
|         fullscreen_button_pressed = !fullscreen_button_pressed; |  | ||||||
| #if CHINESE_FONT |  | ||||||
|         fullscreen_label = fullscreen_button_pressed ? u8"退出全屏" : u8"全屏"; |  | ||||||
| #else |  | ||||||
|         fullscreen_label = |  | ||||||
|             fullscreen_button_pressed ? "EXIT FULLSCREEN" : "FULLSCREEN"; |  | ||||||
| #endif |  | ||||||
|       } |  | ||||||
|       ImGui::End(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     // Rendering |  | ||||||
|     ImGui::Render(); |  | ||||||
|     SDL_RenderSetScale(sdlRenderer, io.DisplayFramebufferScale.x, |  | ||||||
|                        io.DisplayFramebufferScale.y); |  | ||||||
|  |  | ||||||
|     SDL_Event event; |  | ||||||
|     while (SDL_PollEvent(&event)) { |  | ||||||
|       ImGui_ImplSDL2_ProcessEvent(&event); |  | ||||||
|       if (event.type == SDL_QUIT) { |  | ||||||
|         done = true; |  | ||||||
|       } else if (event.type == SDL_WINDOWEVENT && |  | ||||||
|                  event.window.event == SDL_WINDOWEVENT_RESIZED) { |  | ||||||
|         // SDL_GetWindowSize(window, &window_w, &window_h); |  | ||||||
|  |  | ||||||
|         int window_w_last = window_w; |  | ||||||
|         int window_h_last = window_h; |  | ||||||
|  |  | ||||||
|         SDL_GetWindowSize(window, &window_w, &window_h); |  | ||||||
|  |  | ||||||
|         int w_change_ratio = abs(window_w - window_w_last) / 16; |  | ||||||
|         int h_change_ratio = abs(window_h - window_h_last) / 9; |  | ||||||
|  |  | ||||||
|         if (w_change_ratio > h_change_ratio) { |  | ||||||
|           window_h = window_w * 9 / 16; |  | ||||||
|         } else { |  | ||||||
|           window_w = window_h * 16 / 9; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         SDL_SetWindowSize(window, window_w, window_h); |  | ||||||
|       } else if (event.type == SDL_WINDOWEVENT && |  | ||||||
|                  event.window.event == SDL_WINDOWEVENT_CLOSE && |  | ||||||
|                  event.window.windowID == SDL_GetWindowID(window)) { |  | ||||||
|         done = true; |  | ||||||
|       } else if (event.type == REFRESH_EVENT) { |  | ||||||
|         sdlRect.x = 0; |  | ||||||
|         sdlRect.y = 0; |  | ||||||
|         sdlRect.w = window_w; |  | ||||||
|         sdlRect.h = window_h; |  | ||||||
|  |  | ||||||
|         SDL_UpdateTexture(sdlTexture, NULL, dst_buffer, pixel_w); |  | ||||||
|       } else { |  | ||||||
|         if (joined) { |  | ||||||
|           ProcessMouseKeyEven(event); |  | ||||||
|         } |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     SDL_RenderClear(sdlRenderer); |  | ||||||
|     SDL_RenderCopy(sdlRenderer, sdlTexture, NULL, &sdlRect); |  | ||||||
|  |  | ||||||
|     if (!joined || !received_frame) { |  | ||||||
|       SDL_RenderClear(sdlRenderer); |  | ||||||
|       SDL_SetRenderDrawColor( |  | ||||||
|           sdlRenderer, (Uint8)(clear_color.x * 0), (Uint8)(clear_color.y * 0), |  | ||||||
|           (Uint8)(clear_color.z * 0), (Uint8)(clear_color.w * 0)); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     ImGui_ImplSDLRenderer2_RenderDrawData(ImGui::GetDrawData()); |  | ||||||
|     SDL_RenderPresent(sdlRenderer); |  | ||||||
|  |  | ||||||
|     frame_count++; |  | ||||||
|     end_time = SDL_GetTicks(); |  | ||||||
|     elapsed_time = end_time - start_time; |  | ||||||
|     if (elapsed_time >= 1000) { |  | ||||||
|       fps = frame_count / (elapsed_time / 1000); |  | ||||||
|       frame_count = 0; |  | ||||||
|       window_title = "Remote Desk Client FPS [" + std::to_string(fps) + |  | ||||||
|                      "] status [" + signal_status_str + "|" + |  | ||||||
|                      connection_status_str + "]"; |  | ||||||
|       // For MacOS, UI frameworks can only be called from the main thread |  | ||||||
|       SDL_SetWindowTitle(window, window_title.c_str()); |  | ||||||
|       start_time = end_time; |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   // Cleanup |  | ||||||
|  |  | ||||||
|   if (is_create_connection) { |  | ||||||
|     LeaveConnection(peer_server); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   rtc_thread.join(); |  | ||||||
|   SDL_CloseAudioDevice(output_dev); |  | ||||||
|   SDL_CloseAudioDevice(input_dev); |  | ||||||
|  |  | ||||||
|   if (screen_capturer) { |  | ||||||
|     screen_capturer->Destroy(); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   if (mouse_controller) { |  | ||||||
|     mouse_controller->Destroy(); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   ImGui_ImplSDLRenderer2_Shutdown(); |  | ||||||
|   ImGui_ImplSDL2_Shutdown(); |  | ||||||
|   ImGui::DestroyContext(); |  | ||||||
|  |  | ||||||
|   SDL_DestroyRenderer(sdlRenderer); |  | ||||||
|   SDL_DestroyWindow(window); |  | ||||||
|  |  | ||||||
|   SDL_CloseAudio(); |  | ||||||
|   SDL_Quit(); |  | ||||||
|  |  | ||||||
|   return 0; |  | ||||||
| } |  | ||||||
| @@ -1,4 +1,4 @@ | |||||||
| /* | /* | ||||||
|  * @Author: DI JUNKUN |  * @Author: DI JUNKUN | ||||||
|  * @Date: 2024-05-29 |  * @Date: 2024-05-29 | ||||||
|  * Copyright (c) 2024 by DI JUNKUN, All Rights Reserved. |  * Copyright (c) 2024 by DI JUNKUN, All Rights Reserved. | ||||||
| @@ -10,19 +10,26 @@ | |||||||
| #include <vector> | #include <vector> | ||||||
| namespace localization { | namespace localization { | ||||||
|  |  | ||||||
| static std::vector<std::string> menu = {u8"菜单", "Menu"}; | static std::vector<std::string> local_desktop = {u8"本桌面", "Local Desktop"}; | ||||||
| static std::vector<std::string> local_id = {u8"本机ID:", "Local ID:"}; | static std::vector<std::string> local_id = {u8"本机ID", "Local ID"}; | ||||||
| static std::vector<std::string> password = {u8"密码:", "Password:"}; | static std::vector<std::string> local_id_copied_to_clipboard = { | ||||||
|  |     u8"已复制到剪贴板", "Copied to clipboard"}; | ||||||
|  | static std::vector<std::string> password = {u8"密码", "Password"}; | ||||||
| static std::vector<std::string> max_password_len = {u8"最大6个字符", | static std::vector<std::string> max_password_len = {u8"最大6个字符", | ||||||
|                                                     "Max 6 chars"}; |                                                     "Max 6 chars"}; | ||||||
| static std::vector<std::string> remote_id = {u8"对端ID:", "Remote ID:"}; |  | ||||||
|  | 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> connect = {u8"连接", "Connect"}; | ||||||
| static std::vector<std::string> disconnect = {u8"断开连接", "Disconnect"}; | static std::vector<std::string> disconnect = {u8"断开连接", "Disconnect"}; | ||||||
| static std::vector<std::string> fullscreen = {u8"全屏", "Fullscreen"}; | static std::vector<std::string> fullscreen = {u8" 全屏", " Fullscreen"}; | ||||||
| static std::vector<std::string> exit_fullscreen = {u8"退出全屏", | static std::vector<std::string> exit_fullscreen = {u8" 退出全屏", | ||||||
|                                                    "Exit fullscreen"}; |                                                    " Exit fullscreen"}; | ||||||
| static std::vector<std::string> control_mouse = {u8"控制鼠标", "Mouse Control"}; | static std::vector<std::string> control_mouse = {u8" 控制", " Control"}; | ||||||
| static std::vector<std::string> release_mouse = {u8"释放鼠标", "Release Mouse"}; | 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> settings = {u8"设置", "Settings"}; | ||||||
| static std::vector<std::string> language = {u8"语言:", "Language:"}; | static std::vector<std::string> language = {u8"语言:", "Language:"}; | ||||||
| static std::vector<std::string> language_zh = {u8"中文", "Chinese"}; | static std::vector<std::string> language_zh = {u8"中文", "Chinese"}; | ||||||
| @@ -38,10 +45,41 @@ static std::vector<std::string> av1 = {u8"AV1", "AV1"}; | |||||||
| static std::vector<std::string> h264 = {u8"H.264", "H.264"}; | static std::vector<std::string> h264 = {u8"H.264", "H.264"}; | ||||||
| static std::vector<std::string> enable_hardware_video_codec = { | static std::vector<std::string> enable_hardware_video_codec = { | ||||||
|     u8"启用硬件编解码器:", "Enable Hardware Video Codec:"}; |     u8"启用硬件编解码器:", "Enable Hardware Video Codec:"}; | ||||||
|  | static std::vector<std::string> enable_turn = {u8"启用中继服务:", | ||||||
|  |                                                "Enable TURN Service:"}; | ||||||
|  |  | ||||||
| static std::vector<std::string> ok = {u8"确认", "OK"}; | static std::vector<std::string> ok = {u8"确认", "OK"}; | ||||||
| static std::vector<std::string> cancel = {u8"取消", "Cancel"}; | static std::vector<std::string> cancel = {u8"取消", "Cancel"}; | ||||||
|  |  | ||||||
|  | static std::vector<std::string> new_password = { | ||||||
|  |     u8"请输入六位密码:", "Please input a six-char password:"}; | ||||||
|  |  | ||||||
|  | static std::vector<std::string> input_password = {u8"请输入密码:", | ||||||
|  |                                                   "Please input password:"}; | ||||||
|  | static std::vector<std::string> validate_password = {u8"验证密码中...", | ||||||
|  |                                                      "Validate password ..."}; | ||||||
|  | static std::vector<std::string> reinput_password = { | ||||||
|  |     u8"请重新输入密码", "Please input password again"}; | ||||||
|  |  | ||||||
|  | static std::vector<std::string> signal_connected = {u8"已连接服务器", | ||||||
|  |                                                     "Connected"}; | ||||||
|  | static std::vector<std::string> signal_disconnected = {u8"未连接服务器", | ||||||
|  |                                                        "Disconnected"}; | ||||||
|  |  | ||||||
|  | static std::vector<std::string> p2p_connected = {u8"对等连接已建立", | ||||||
|  |                                                  "P2P Connected"}; | ||||||
|  | static std::vector<std::string> p2p_disconnected = {u8"对等连接已断开", | ||||||
|  |                                                     "P2P Disconnected"}; | ||||||
|  | static std::vector<std::string> p2p_connecting = {u8"正在建立对等连接...", | ||||||
|  |                                                   "P2P Connecting ..."}; | ||||||
|  | static std::vector<std::string> p2p_failed = {u8"对等连接失败", "P2P Failed"}; | ||||||
|  | static std::vector<std::string> p2p_closed = {u8"对等连接已关闭", "P2P closed"}; | ||||||
|  |  | ||||||
|  | static std::vector<std::string> no_such_id = {u8"无此ID", "No such ID"}; | ||||||
|  |  | ||||||
|  | static std::vector<std::string> about = {u8"关于", "About"}; | ||||||
|  | static std::vector<std::string> version = {u8"版本", "Version"}; | ||||||
|  |  | ||||||
| }  // namespace localization | }  // namespace localization | ||||||
|  |  | ||||||
| #endif | #endif | ||||||
| @@ -1,5 +1,11 @@ | |||||||
| #ifndef _LOG_H_ | /*
 | ||||||
| #define _LOG_H_ |  * @Author: DI JUNKUN | ||||||
|  |  * @Date: 2024-07-17 | ||||||
|  |  * Copyright (c) 2024 by DI JUNKUN, All Rights Reserved. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #ifndef _RD_LOG_H_ | ||||||
|  | #define _RD_LOG_H_ | ||||||
| 
 | 
 | ||||||
| #include <chrono> | #include <chrono> | ||||||
| #include <iomanip> | #include <iomanip> | ||||||
| @@ -18,18 +24,7 @@ using namespace std::chrono; | |||||||
| 
 | 
 | ||||||
| #define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_INFO | #define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_INFO | ||||||
| 
 | 
 | ||||||
| // SPDLOG_TRACE(...)
 | constexpr auto LOGGER_NAME = "rd"; | ||||||
| // SPDLOG_DEBUG(...)
 |  | ||||||
| // SPDLOG_INFO(...)
 |  | ||||||
| // SPDLOG_WARN(...)
 |  | ||||||
| // SPDLOG_ERROR(...)
 |  | ||||||
| // SPDLOG_CRITICAL(...)
 |  | ||||||
| 
 |  | ||||||
| #ifdef SIGNAL_LOGGER |  | ||||||
| constexpr auto LOGGER_NAME = "siganl_server"; |  | ||||||
| #else |  | ||||||
| constexpr auto LOGGER_NAME = "remote_desk"; |  | ||||||
| #endif |  | ||||||
| 
 | 
 | ||||||
| #define LOG_INFO(...)                                                         \ | #define LOG_INFO(...)                                                         \ | ||||||
|   if (nullptr == spdlog::get(LOGGER_NAME)) {                                  \ |   if (nullptr == spdlog::get(LOGGER_NAME)) {                                  \ | ||||||
| @@ -1,81 +0,0 @@ | |||||||
| /* |  | ||||||
|  * @Author: DI JUNKUN |  | ||||||
|  * @Date: 2024-06-14 |  | ||||||
|  * Copyright (c) 2024 by DI JUNKUN, All Rights Reserved. |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| #ifndef _LAYOUT_STYLE_H_ |  | ||||||
| #define _LAYOUT_STYLE_H_ |  | ||||||
|  |  | ||||||
| #ifdef _WIN32 |  | ||||||
| #define MENU_WINDOW_WIDTH_CN 160 |  | ||||||
| #define MENU_WINDOW_HEIGHT_CN 245 |  | ||||||
| #define MENU_WINDOW_WIDTH_EN 190 |  | ||||||
| #define MENU_WINDOW_HEIGHT_EN 245 |  | ||||||
| #define IPUT_WINDOW_WIDTH 86 |  | ||||||
| #define INPUT_WINDOW_PADDING_CN 66 |  | ||||||
| #define INPUT_WINDOW_PADDING_EN 96 |  | ||||||
| #define SETTINGS_WINDOW_WIDTH_CN 181 |  | ||||||
| #define SETTINGS_WINDOW_WIDTH_EN 228 |  | ||||||
| #define SETTINGS_WINDOW_HEIGHT_CN 190 |  | ||||||
| #define SETTINGS_WINDOW_HEIGHT_EN 190 |  | ||||||
| #define LANGUAGE_SELECT_WINDOW_PADDING_CN 100 |  | ||||||
| #define LANGUAGE_SELECT_WINDOW_PADDING_EN 147 |  | ||||||
| #define VIDEO_QUALITY_SELECT_WINDOW_PADDING_CN 100 |  | ||||||
| #define VIDEO_QUALITY_SELECT_WINDOW_PADDING_EN 147 |  | ||||||
| #define VIDEO_ENCODE_FORMAT_SELECT_WINDOW_PADDING_CN 100 |  | ||||||
| #define VIDEO_ENCODE_FORMAT_SELECT_WINDOW_PADDING_EN 147 |  | ||||||
| #define ENABLE_HARDWARE_VIDEO_CODEC_CHECKBOX_PADDING_CN 154 |  | ||||||
| #define ENABLE_HARDWARE_VIDEO_CODEC_CHECKBOX_PADDING_EN 201 |  | ||||||
| #define SETTINGS_SELECT_WINDOW_WIDTH 73 |  | ||||||
| #define SETTINGS_OK_BUTTON_PADDING_CN 55 |  | ||||||
| #define SETTINGS_OK_BUTTON_PADDING_EN 78 |  | ||||||
| #elif __linux__ |  | ||||||
| #define MENU_WINDOW_WIDTH_CN 160 |  | ||||||
| #define MENU_WINDOW_HEIGHT_CN 245 |  | ||||||
| #define MENU_WINDOW_WIDTH_EN 190 |  | ||||||
| #define MENU_WINDOW_HEIGHT_EN 245 |  | ||||||
| #define IPUT_WINDOW_WIDTH 90 |  | ||||||
| #define INPUT_WINDOW_PADDING_CN 60 |  | ||||||
| #define INPUT_WINDOW_PADDING_EN 80 |  | ||||||
| #define SETTINGS_WINDOW_WIDTH_CN 188 |  | ||||||
| #define SETTINGS_WINDOW_WIDTH_EN 228 |  | ||||||
| #define SETTINGS_WINDOW_HEIGHT_CN 190 |  | ||||||
| #define SETTINGS_WINDOW_HEIGHT_EN 190 |  | ||||||
| #define LANGUAGE_SELECT_WINDOW_PADDING_CN 100 |  | ||||||
| #define LANGUAGE_SELECT_WINDOW_PADDING_EN 140 |  | ||||||
| #define VIDEO_QUALITY_SELECT_WINDOW_PADDING_CN 100 |  | ||||||
| #define VIDEO_QUALITY_SELECT_WINDOW_PADDING_EN 140 |  | ||||||
| #define VIDEO_ENCODE_FORMAT_SELECT_WINDOW_PADDING_CN 100 |  | ||||||
| #define VIDEO_ENCODE_FORMAT_SELECT_WINDOW_PADDING_EN 140 |  | ||||||
| #define ENABLE_HARDWARE_VIDEO_CODEC_CHECKBOX_PADDING_CN 161 |  | ||||||
| #define ENABLE_HARDWARE_VIDEO_CODEC_CHECKBOX_PADDING_EN 201 |  | ||||||
| #define SETTINGS_SELECT_WINDOW_WIDTH 60 |  | ||||||
| #define SETTINGS_OK_BUTTON_PADDING_CN 60 |  | ||||||
| #define SETTINGS_OK_BUTTON_PADDING_EN 80 |  | ||||||
| #elif __APPLE__ |  | ||||||
| #define MENU_WINDOW_WIDTH_CN 148 |  | ||||||
| #define MENU_WINDOW_HEIGHT_CN 244 |  | ||||||
| #define MENU_WINDOW_WIDTH_EN 148 |  | ||||||
| #define MENU_WINDOW_HEIGHT_EN 244 |  | ||||||
| #define IPUT_WINDOW_WIDTH 77 |  | ||||||
| #define INPUT_WINDOW_PADDING_CN 63 |  | ||||||
| #define INPUT_WINDOW_PADDING_EN 63 |  | ||||||
| #define SETTINGS_WINDOW_WIDTH_CN 160 |  | ||||||
| #define SETTINGS_WINDOW_WIDTH_EN 220 |  | ||||||
| #define SETTINGS_WINDOW_HEIGHT_CN 190 |  | ||||||
| #define SETTINGS_WINDOW_HEIGHT_EN 190 |  | ||||||
| #define LANGUAGE_SELECT_WINDOW_PADDING_CN 90 |  | ||||||
| #define LANGUAGE_SELECT_WINDOW_PADDING_EN 150 |  | ||||||
| #define VIDEO_QUALITY_SELECT_WINDOW_PADDING_CN 90 |  | ||||||
| #define VIDEO_QUALITY_SELECT_WINDOW_PADDING_EN 150 |  | ||||||
| #define VIDEO_ENCODE_FORMAT_SELECT_WINDOW_PADDING_CN 90 |  | ||||||
| #define VIDEO_ENCODE_FORMAT_SELECT_WINDOW_PADDING_EN 150 |  | ||||||
| #define ENABLE_HARDWARE_VIDEO_CODEC_CHECK_WINDOW_PADDING_CN 133 |  | ||||||
| #define ENABLE_HARDWARE_VIDEO_CODEC_CHECK_WINDOW_PADDING_EN 193 |  | ||||||
| #define SETTINGS_SELECT_WINDOW_WIDTH 62 |  | ||||||
| #define SETTINGS_OK_BUTTON_PADDING_CN 50 |  | ||||||
| #define SETTINGS_OK_BUTTON_PADDING_EN 80 |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| #endif |  | ||||||
| @@ -1,888 +0,0 @@ | |||||||
| #include "main_window.h" |  | ||||||
|  |  | ||||||
| #include <fstream> |  | ||||||
| #include <iostream> |  | ||||||
| #include <string> |  | ||||||
|  |  | ||||||
| #include "device_controller_factory.h" |  | ||||||
| #include "layout_style.h" |  | ||||||
| #include "localization.h" |  | ||||||
| #include "log.h" |  | ||||||
| #include "platform.h" |  | ||||||
| #include "screen_capturer_factory.h" |  | ||||||
|  |  | ||||||
| // Refresh Event |  | ||||||
| #define REFRESH_EVENT (SDL_USEREVENT + 1) |  | ||||||
| #define NV12_BUFFER_SIZE 1280 * 720 * 3 / 2 |  | ||||||
|  |  | ||||||
| MainWindow::MainWindow() {} |  | ||||||
|  |  | ||||||
| MainWindow::~MainWindow() {} |  | ||||||
|  |  | ||||||
| int MainWindow::SaveSettingsIntoCacheFile() { |  | ||||||
|   cd_cache_file_ = fopen("cache.cd", "w+"); |  | ||||||
|   if (!cd_cache_file_) { |  | ||||||
|     return -1; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   fseek(cd_cache_file_, 0, SEEK_SET); |  | ||||||
|   strncpy(cd_cache_.password, input_password_, sizeof(input_password_)); |  | ||||||
|   memcpy(&cd_cache_.language, &language_button_value_, |  | ||||||
|          sizeof(language_button_value_)); |  | ||||||
|   memcpy(&cd_cache_.video_quality, &video_quality_button_value_, |  | ||||||
|          sizeof(video_quality_button_value_)); |  | ||||||
|   memcpy(&cd_cache_.video_encode_format, &video_encode_format_button_value_, |  | ||||||
|          sizeof(video_encode_format_button_value_)); |  | ||||||
|   memcpy(&cd_cache_.enable_hardware_video_codec, &enable_hardware_video_codec_, |  | ||||||
|          sizeof(enable_hardware_video_codec_)); |  | ||||||
|   fwrite(&cd_cache_, sizeof(cd_cache_), 1, cd_cache_file_); |  | ||||||
|   fclose(cd_cache_file_); |  | ||||||
|  |  | ||||||
|   return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int MainWindow::LoadSettingsIntoCacheFile() { |  | ||||||
|   cd_cache_file_ = fopen("cache.cd", "r+"); |  | ||||||
|   if (!cd_cache_file_) { |  | ||||||
|     return -1; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   fseek(cd_cache_file_, 0, SEEK_SET); |  | ||||||
|   fread(&cd_cache_, sizeof(cd_cache_), 1, cd_cache_file_); |  | ||||||
|   fclose(cd_cache_file_); |  | ||||||
|   strncpy(input_password_, cd_cache_.password, sizeof(cd_cache_.password)); |  | ||||||
|   language_button_value_ = cd_cache_.language; |  | ||||||
|   video_quality_button_value_ = cd_cache_.video_quality; |  | ||||||
|   video_encode_format_button_value_ = cd_cache_.video_encode_format; |  | ||||||
|   enable_hardware_video_codec_ = cd_cache_.enable_hardware_video_codec; |  | ||||||
|  |  | ||||||
|   return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int MainWindow::StartScreenCapture() { |  | ||||||
|   screen_capturer_ = (ScreenCapturer *)screen_capturer_factory_->Create(); |  | ||||||
|   ScreenCapturer::RECORD_DESKTOP_RECT rect; |  | ||||||
|   rect.left = 0; |  | ||||||
|   rect.top = 0; |  | ||||||
|   rect.right = screen_width_; |  | ||||||
|   rect.bottom = screen_height_; |  | ||||||
|   last_frame_time_ = std::chrono::high_resolution_clock::now(); |  | ||||||
|  |  | ||||||
|   int screen_capturer_init_ret = screen_capturer_->Init( |  | ||||||
|       rect, 60, |  | ||||||
|       [this](unsigned char *data, int size, int width, int height) -> void { |  | ||||||
|         auto now_time = std::chrono::high_resolution_clock::now(); |  | ||||||
|         std::chrono::duration<double> duration = now_time - last_frame_time_; |  | ||||||
|         auto tc = duration.count() * 1000; |  | ||||||
|  |  | ||||||
|         if (tc >= 0) { |  | ||||||
|           SendData(peer_, DATA_TYPE::VIDEO, (const char *)data, |  | ||||||
|                    NV12_BUFFER_SIZE); |  | ||||||
|           last_frame_time_ = now_time; |  | ||||||
|         } |  | ||||||
|       }); |  | ||||||
|  |  | ||||||
|   if (0 == screen_capturer_init_ret) { |  | ||||||
|     screen_capturer_->Start(); |  | ||||||
|   } else { |  | ||||||
|     screen_capturer_->Destroy(); |  | ||||||
|     delete screen_capturer_; |  | ||||||
|     screen_capturer_ = nullptr; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int MainWindow::StopScreenCapture() { |  | ||||||
|   if (screen_capturer_) { |  | ||||||
|     LOG_INFO("Destroy screen capturer") |  | ||||||
|     screen_capturer_->Destroy(); |  | ||||||
|     delete screen_capturer_; |  | ||||||
|     screen_capturer_ = nullptr; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int MainWindow::StartMouseControl() { |  | ||||||
|   device_controller_factory_ = new DeviceControllerFactory(); |  | ||||||
|   mouse_controller_ = (MouseController *)device_controller_factory_->Create( |  | ||||||
|       DeviceControllerFactory::Device::Mouse); |  | ||||||
|   int mouse_controller_init_ret = |  | ||||||
|       mouse_controller_->Init(screen_width_, screen_height_); |  | ||||||
|   if (0 != mouse_controller_init_ret) { |  | ||||||
|     LOG_INFO("Destroy mouse controller") |  | ||||||
|     mouse_controller_->Destroy(); |  | ||||||
|     mouse_controller_ = nullptr; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int MainWindow::StopMouseControl() { |  | ||||||
|   if (mouse_controller_) { |  | ||||||
|     mouse_controller_->Destroy(); |  | ||||||
|     delete mouse_controller_; |  | ||||||
|     mouse_controller_ = nullptr; |  | ||||||
|   } |  | ||||||
|   return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int MainWindow::CreateConnectionPeer() { |  | ||||||
|   mac_addr_str_ = GetMac(); |  | ||||||
|  |  | ||||||
|   params_.use_cfg_file = false; |  | ||||||
|   params_.signal_server_ip = "150.158.81.30"; |  | ||||||
|   params_.signal_server_port = 9099; |  | ||||||
|   params_.stun_server_ip = "150.158.81.30"; |  | ||||||
|   params_.stun_server_port = 3478; |  | ||||||
|   params_.turn_server_ip = "150.158.81.30"; |  | ||||||
|   params_.turn_server_port = 3478; |  | ||||||
|   params_.turn_server_username = "dijunkun"; |  | ||||||
|   params_.turn_server_password = "dijunkunpw"; |  | ||||||
|   params_.hardware_acceleration = config_center_.IsHardwareVideoCodec(); |  | ||||||
|   params_.av1_encoding = config_center_.GetVideoEncodeFormat() == |  | ||||||
|                                  ConfigCenter::VIDEO_ENCODE_FORMAT::AV1 |  | ||||||
|                              ? true |  | ||||||
|                              : false; |  | ||||||
|   params_.on_receive_video_buffer = OnReceiveVideoBufferCb; |  | ||||||
|   params_.on_receive_audio_buffer = OnReceiveAudioBufferCb; |  | ||||||
|   params_.on_receive_data_buffer = OnReceiveDataBufferCb; |  | ||||||
|   params_.on_signal_status = OnSignalStatusCb; |  | ||||||
|   params_.on_connection_status = OnConnectionStatusCb; |  | ||||||
|   params_.user_data = this; |  | ||||||
|  |  | ||||||
|   peer_ = CreatePeer(¶ms_); |  | ||||||
|   if (peer_) { |  | ||||||
|     LOG_INFO("Create peer instance successful"); |  | ||||||
|     local_id_ = mac_addr_str_; |  | ||||||
|     Init(peer_, local_id_.c_str()); |  | ||||||
|     LOG_INFO("Peer init finish"); |  | ||||||
|   } else { |  | ||||||
|     LOG_INFO("Create peer instance failed"); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int MainWindow::Run() { |  | ||||||
|   LoadSettingsIntoCacheFile(); |  | ||||||
|  |  | ||||||
|   localization_language_ = (ConfigCenter::LANGUAGE)language_button_value_; |  | ||||||
|   localization_language_index_ = language_button_value_; |  | ||||||
|  |  | ||||||
|   // Setup SDL |  | ||||||
|   if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER | |  | ||||||
|                SDL_INIT_GAMECONTROLLER) != 0) { |  | ||||||
|     printf("Error: %s\n", SDL_GetError()); |  | ||||||
|     return -1; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   // From 2.0.18: Enable native IME. |  | ||||||
| #ifdef SDL_HINT_IME_SHOW_UI |  | ||||||
|   SDL_SetHint(SDL_HINT_IME_SHOW_UI, "1"); |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
|   // Create main window with SDL_Renderer graphics context |  | ||||||
|   SDL_WindowFlags window_flags = |  | ||||||
|       (SDL_WindowFlags)(SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI); |  | ||||||
|   main_window_ = SDL_CreateWindow("Remote Desk", SDL_WINDOWPOS_CENTERED, |  | ||||||
|                                   SDL_WINDOWPOS_CENTERED, main_window_width_, |  | ||||||
|                                   main_window_height_, window_flags); |  | ||||||
|  |  | ||||||
|   SDL_DisplayMode DM; |  | ||||||
|   SDL_GetCurrentDisplayMode(0, &DM); |  | ||||||
|   screen_width_ = DM.w; |  | ||||||
|   screen_height_ = DM.h; |  | ||||||
|  |  | ||||||
|   sdl_renderer_ = SDL_CreateRenderer( |  | ||||||
|       main_window_, -1, SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_ACCELERATED); |  | ||||||
|   if (sdl_renderer_ == nullptr) { |  | ||||||
|     SDL_Log("Error creating SDL_Renderer!"); |  | ||||||
|     return 0; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   pixformat_ = SDL_PIXELFORMAT_NV12; |  | ||||||
|  |  | ||||||
|   sdl_texture_ = |  | ||||||
|       SDL_CreateTexture(sdl_renderer_, pixformat_, SDL_TEXTUREACCESS_STREAMING, |  | ||||||
|                         texture_width_, texture_height_); |  | ||||||
|  |  | ||||||
|   // Setup Dear ImGui context |  | ||||||
|   IMGUI_CHECKVERSION(); |  | ||||||
|   ImGui::CreateContext(); |  | ||||||
|   ImGuiIO &io = ImGui::GetIO(); |  | ||||||
|  |  | ||||||
|   io.ConfigFlags |= |  | ||||||
|       ImGuiConfigFlags_NavEnableKeyboard;  // Enable Keyboard Controls |  | ||||||
|   io.ConfigFlags |= |  | ||||||
|       ImGuiConfigFlags_NavEnableGamepad;  // Enable Gamepad Controls |  | ||||||
|  |  | ||||||
|   if (config_center_.GetLanguage() == ConfigCenter::LANGUAGE::CHINESE) { |  | ||||||
|     // Load Fonts |  | ||||||
| #ifdef _WIN32 |  | ||||||
|     std::string default_font_path = "c:/windows/fonts/simhei.ttf"; |  | ||||||
|     std::ifstream font_path_f(default_font_path.c_str()); |  | ||||||
|     std::string font_path = |  | ||||||
|         font_path_f.good() ? "c:/windows/fonts/simhei.ttf" : ""; |  | ||||||
|     if (!font_path.empty()) { |  | ||||||
|       io.Fonts->AddFontFromFileTTF(font_path.c_str(), 13.0f, NULL, |  | ||||||
|                                    io.Fonts->GetGlyphRangesChineseFull()); |  | ||||||
|     } |  | ||||||
| #elif __APPLE__ |  | ||||||
|     std::string default_font_path = "/System/Library/Fonts/PingFang.ttc"; |  | ||||||
|     std::ifstream font_path_f(default_font_path.c_str()); |  | ||||||
|     std::string font_path = |  | ||||||
|         font_path_f.good() ? "/System/Library/Fonts/PingFang.ttc" : ""; |  | ||||||
|     if (!font_path.empty()) { |  | ||||||
|       io.Fonts->AddFontFromFileTTF(font_path.c_str(), 13.0f, NULL, |  | ||||||
|                                    io.Fonts->GetGlyphRangesChineseFull()); |  | ||||||
|     } |  | ||||||
| #elif __linux__ |  | ||||||
|     io.Fonts->AddFontFromFileTTF("c:/windows/fonts/msyh.ttc", 13.0f, NULL, |  | ||||||
|                                  io.Fonts->GetGlyphRangesChineseFull()); |  | ||||||
| #endif |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   // Setup Dear ImGui style |  | ||||||
|   // ImGui::StyleColorsDark(); |  | ||||||
|   ImGui::StyleColorsLight(); |  | ||||||
|  |  | ||||||
|   // Setup Platform/Renderer backends |  | ||||||
|   ImGui_ImplSDL2_InitForSDLRenderer(main_window_, sdl_renderer_); |  | ||||||
|   ImGui_ImplSDLRenderer2_Init(sdl_renderer_); |  | ||||||
|  |  | ||||||
|   // Our state |  | ||||||
|   ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f); |  | ||||||
|  |  | ||||||
|   CreateConnectionPeer(); |  | ||||||
|  |  | ||||||
|   { |  | ||||||
|     nv12_buffer_ = new char[NV12_BUFFER_SIZE]; |  | ||||||
|  |  | ||||||
|     // Screen capture |  | ||||||
|     screen_capturer_factory_ = new ScreenCapturerFactory(); |  | ||||||
|  |  | ||||||
|     // Mouse control |  | ||||||
|     device_controller_factory_ = new DeviceControllerFactory(); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   // Main loop |  | ||||||
|   while (!exit_) { |  | ||||||
|     if (SignalStatus::SignalConnected == signal_status_ && |  | ||||||
|         !is_create_connection_) { |  | ||||||
|       is_create_connection_ = |  | ||||||
|           CreateConnection(peer_, mac_addr_str_.c_str(), input_password_) |  | ||||||
|               ? false |  | ||||||
|               : true; |  | ||||||
|       LOG_INFO("Connected with signal server, create p2p connection"); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (!inited_ || |  | ||||||
|         localization_language_index_last_ != localization_language_index_) { |  | ||||||
|       connect_button_label_ = |  | ||||||
|           connect_button_pressed_ |  | ||||||
|               ? localization::disconnect[localization_language_index_] |  | ||||||
|               : localization::connect[localization_language_index_]; |  | ||||||
|       fullscreen_button_label_ = |  | ||||||
|           fullscreen_button_pressed_ |  | ||||||
|               ? localization::exit_fullscreen[localization_language_index_] |  | ||||||
|               : localization::fullscreen[localization_language_index_]; |  | ||||||
|  |  | ||||||
|       mouse_control_button_label_ = |  | ||||||
|           mouse_control_button_pressed_ |  | ||||||
|               ? localization::release_mouse[localization_language_index_] |  | ||||||
|               : localization::control_mouse[localization_language_index_]; |  | ||||||
|  |  | ||||||
|       settings_button_label_ = |  | ||||||
|           localization::settings[localization_language_index_]; |  | ||||||
|       inited_ = true; |  | ||||||
|       localization_language_index_last_ = localization_language_index_; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (start_screen_capture_ && !screen_capture_is_started_) { |  | ||||||
|       StartScreenCapture(); |  | ||||||
|       screen_capture_is_started_ = true; |  | ||||||
|     } else if (!start_screen_capture_ && screen_capture_is_started_) { |  | ||||||
|       StopScreenCapture(); |  | ||||||
|       screen_capture_is_started_ = false; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (start_mouse_control_ && !mouse_control_is_started_) { |  | ||||||
|       StartMouseControl(); |  | ||||||
|       mouse_control_is_started_ = true; |  | ||||||
|     } else if (!start_mouse_control_ && mouse_control_is_started_) { |  | ||||||
|       StopMouseControl(); |  | ||||||
|       mouse_control_is_started_ = false; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     // Start the Dear ImGui frame |  | ||||||
|     ImGui_ImplSDLRenderer2_NewFrame(); |  | ||||||
|     ImGui_ImplSDL2_NewFrame(); |  | ||||||
|     ImGui::NewFrame(); |  | ||||||
|  |  | ||||||
|     if (connection_established_ && !subwindow_hovered_ && control_mouse_) { |  | ||||||
|       ImGui::SetMouseCursor(ImGuiMouseCursor_None); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     // main window layout |  | ||||||
|     { |  | ||||||
|       ImGui::SetNextWindowPos(ImVec2(0, 0), ImGuiCond_Once); |  | ||||||
|  |  | ||||||
|       if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) { |  | ||||||
|         ImGui::SetNextWindowSize( |  | ||||||
|             ImVec2(MENU_WINDOW_WIDTH_CN, MENU_WINDOW_HEIGHT_CN)); |  | ||||||
|       } else { |  | ||||||
|         ImGui::SetNextWindowSize( |  | ||||||
|             ImVec2(MENU_WINDOW_WIDTH_EN, MENU_WINDOW_HEIGHT_EN)); |  | ||||||
|       } |  | ||||||
|  |  | ||||||
|       if (!connection_established_) { |  | ||||||
|         ImGui::SetNextWindowPos(ImVec2(0, 0), ImGuiCond_Always); |  | ||||||
|         ImGui::Begin(localization::menu[localization_language_index_].c_str(), |  | ||||||
|                      nullptr, |  | ||||||
|                      ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse | |  | ||||||
|                          ImGuiWindowFlags_NoMove); |  | ||||||
|       } else { |  | ||||||
|         // ImGui::SetNextWindowCollapsed(true, ImGuiCond_Once); |  | ||||||
|         ImGui::Begin(localization::menu[localization_language_index_].c_str(), |  | ||||||
|                      nullptr, ImGuiWindowFlags_None); |  | ||||||
|       } |  | ||||||
|  |  | ||||||
|       { |  | ||||||
|         subwindow_hovered_ = ImGui::IsWindowHovered(); |  | ||||||
|  |  | ||||||
|         // local |  | ||||||
|         { |  | ||||||
|           ImGui::Text( |  | ||||||
|               localization::local_id[localization_language_index_].c_str()); |  | ||||||
|  |  | ||||||
|           ImGui::SameLine(); |  | ||||||
|           ImGui::SetNextItemWidth(IPUT_WINDOW_WIDTH); |  | ||||||
|           if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) { |  | ||||||
|             ImGui::SetCursorPosX(INPUT_WINDOW_PADDING_CN); |  | ||||||
|           } else { |  | ||||||
|             ImGui::SetCursorPosX(INPUT_WINDOW_PADDING_EN); |  | ||||||
|           } |  | ||||||
|           ImGui::InputText("##local_id", (char *)mac_addr_str_.c_str(), |  | ||||||
|                            mac_addr_str_.length() + 1, |  | ||||||
|                            ImGuiInputTextFlags_CharsUppercase | |  | ||||||
|                                ImGuiInputTextFlags_ReadOnly); |  | ||||||
|  |  | ||||||
|           ImGui::Text( |  | ||||||
|               localization::password[localization_language_index_].c_str()); |  | ||||||
|  |  | ||||||
|           ImGui::SameLine(); |  | ||||||
|  |  | ||||||
|           strncpy(input_password_tmp_, input_password_, |  | ||||||
|                   sizeof(input_password_)); |  | ||||||
|           ImGui::SetNextItemWidth(IPUT_WINDOW_WIDTH); |  | ||||||
|           if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) { |  | ||||||
|             ImGui::SetCursorPosX(INPUT_WINDOW_PADDING_CN); |  | ||||||
|           } else { |  | ||||||
|             ImGui::SetCursorPosX(INPUT_WINDOW_PADDING_EN); |  | ||||||
|           } |  | ||||||
|           ImGui::InputTextWithHint( |  | ||||||
|               "##server_pwd", |  | ||||||
|               localization::max_password_len[localization_language_index_] |  | ||||||
|                   .c_str(), |  | ||||||
|               input_password_, IM_ARRAYSIZE(input_password_), |  | ||||||
|               ImGuiInputTextFlags_CharsNoBlank); |  | ||||||
|  |  | ||||||
|           if (strcmp(input_password_tmp_, input_password_)) { |  | ||||||
|             SaveSettingsIntoCacheFile(); |  | ||||||
|           } |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         ImGui::Spacing(); |  | ||||||
|         ImGui::Separator(); |  | ||||||
|         ImGui::Spacing(); |  | ||||||
|  |  | ||||||
|         // remote |  | ||||||
|         { |  | ||||||
|           ImGui::Text( |  | ||||||
|               localization::remote_id[localization_language_index_].c_str()); |  | ||||||
|  |  | ||||||
|           ImGui::SameLine(); |  | ||||||
|           ImGui::SetNextItemWidth(IPUT_WINDOW_WIDTH); |  | ||||||
|           if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) { |  | ||||||
|             ImGui::SetCursorPosX(INPUT_WINDOW_PADDING_CN); |  | ||||||
|           } else { |  | ||||||
|             ImGui::SetCursorPosX(INPUT_WINDOW_PADDING_EN); |  | ||||||
|           } |  | ||||||
|           ImGui::InputTextWithHint("##remote_id_", mac_addr_str_.c_str(), |  | ||||||
|                                    remote_id_, IM_ARRAYSIZE(remote_id_), |  | ||||||
|                                    ImGuiInputTextFlags_CharsUppercase | |  | ||||||
|                                        ImGuiInputTextFlags_CharsNoBlank); |  | ||||||
|  |  | ||||||
|           ImGui::Spacing(); |  | ||||||
|  |  | ||||||
|           ImGui::Text( |  | ||||||
|               localization::password[localization_language_index_].c_str()); |  | ||||||
|  |  | ||||||
|           ImGui::SameLine(); |  | ||||||
|           ImGui::SetNextItemWidth(IPUT_WINDOW_WIDTH); |  | ||||||
|  |  | ||||||
|           if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) { |  | ||||||
|             ImGui::SetCursorPosX(INPUT_WINDOW_PADDING_CN); |  | ||||||
|           } else { |  | ||||||
|             ImGui::SetCursorPosX(INPUT_WINDOW_PADDING_EN); |  | ||||||
|           } |  | ||||||
|  |  | ||||||
|           ImGui::InputTextWithHint( |  | ||||||
|               "##client_pwd", |  | ||||||
|               localization::max_password_len[localization_language_index_] |  | ||||||
|                   .c_str(), |  | ||||||
|               client_password_, IM_ARRAYSIZE(client_password_), |  | ||||||
|               ImGuiInputTextFlags_CharsNoBlank); |  | ||||||
|  |  | ||||||
|           ImGui::Spacing(); |  | ||||||
|           ImGui::Separator(); |  | ||||||
|           ImGui::Spacing(); |  | ||||||
|  |  | ||||||
|           if (ImGui::Button(connect_button_label_.c_str()) || rejoin_) { |  | ||||||
|             int ret = -1; |  | ||||||
|             if ("SignalConnected" == signal_status_str_) { |  | ||||||
|               if (connect_button_label_ == |  | ||||||
|                       localization::connect[localization_language_index_] && |  | ||||||
|                   !connection_established_ && strlen(remote_id_)) { |  | ||||||
|                 if (remote_id_ == local_id_ && !peer_reserved_) { |  | ||||||
|                   peer_reserved_ = CreatePeer(¶ms_); |  | ||||||
|                   if (peer_reserved_) { |  | ||||||
|                     LOG_INFO("Create peer[reserved] instance successful"); |  | ||||||
|                     std::string local_id = "C-" + mac_addr_str_; |  | ||||||
|                     Init(peer_reserved_, local_id.c_str()); |  | ||||||
|                     LOG_INFO("Peer[reserved] init finish"); |  | ||||||
|                   } else { |  | ||||||
|                     LOG_INFO("Create peer[reserved] instance failed"); |  | ||||||
|                   } |  | ||||||
|                 } |  | ||||||
|                 ret = JoinConnection(peer_reserved_ ? peer_reserved_ : peer_, |  | ||||||
|                                      remote_id_, client_password_); |  | ||||||
|                 if (0 == ret) { |  | ||||||
|                   if (!peer_reserved_) { |  | ||||||
|                     is_client_mode_ = true; |  | ||||||
|                   } |  | ||||||
|                   rejoin_ = false; |  | ||||||
|                 } else { |  | ||||||
|                   rejoin_ = true; |  | ||||||
|                 } |  | ||||||
|  |  | ||||||
|               } else if (connect_button_label_ == |  | ||||||
|                              localization::disconnect |  | ||||||
|                                  [localization_language_index_] && |  | ||||||
|                          connection_established_) { |  | ||||||
|                 ret = LeaveConnection(peer_reserved_ ? peer_reserved_ : peer_); |  | ||||||
|  |  | ||||||
|                 if (0 == ret) { |  | ||||||
|                   rejoin_ = false; |  | ||||||
|                   memset(audio_buffer_, 0, 960); |  | ||||||
|                   connection_established_ = false; |  | ||||||
|                   received_frame_ = false; |  | ||||||
|                   is_client_mode_ = false; |  | ||||||
|                 } |  | ||||||
|               } |  | ||||||
|  |  | ||||||
|               if (0 == ret) { |  | ||||||
|                 connect_button_pressed_ = !connect_button_pressed_; |  | ||||||
|                 connect_button_label_ = |  | ||||||
|                     connect_button_pressed_ |  | ||||||
|                         ? localization::disconnect[localization_language_index_] |  | ||||||
|                         : localization::connect[localization_language_index_]; |  | ||||||
|               } |  | ||||||
|             } |  | ||||||
|           } |  | ||||||
|         } |  | ||||||
|       } |  | ||||||
|  |  | ||||||
|       ImGui::Spacing(); |  | ||||||
|  |  | ||||||
|       ImGui::Separator(); |  | ||||||
|  |  | ||||||
|       ImGui::Spacing(); |  | ||||||
|       // Mouse control |  | ||||||
|       if (ImGui::Button(mouse_control_button_label_.c_str())) { |  | ||||||
|         if (mouse_control_button_label_ == |  | ||||||
|                 localization::control_mouse[localization_language_index_] && |  | ||||||
|             connection_established_) { |  | ||||||
|           mouse_control_button_pressed_ = true; |  | ||||||
|           control_mouse_ = true; |  | ||||||
|           mouse_control_button_label_ = |  | ||||||
|               localization::release_mouse[localization_language_index_]; |  | ||||||
|         } else { |  | ||||||
|           control_mouse_ = false; |  | ||||||
|           mouse_control_button_label_ = |  | ||||||
|               localization::control_mouse[localization_language_index_]; |  | ||||||
|         } |  | ||||||
|         mouse_control_button_pressed_ = !mouse_control_button_pressed_; |  | ||||||
|       } |  | ||||||
|  |  | ||||||
|       ImGui::SameLine(); |  | ||||||
|       // Fullscreen |  | ||||||
|       if (ImGui::Button(fullscreen_button_label_.c_str())) { |  | ||||||
|         if (fullscreen_button_label_ == |  | ||||||
|             localization::fullscreen[localization_language_index_]) { |  | ||||||
|           main_window_width_before_fullscreen_ = main_window_width_; |  | ||||||
|           main_window_height_before_fullscreen_ = main_window_height_; |  | ||||||
|           SDL_SetWindowFullscreen(main_window_, SDL_WINDOW_FULLSCREEN_DESKTOP); |  | ||||||
|           fullscreen_button_label_ = |  | ||||||
|               localization::exit_fullscreen[localization_language_index_]; |  | ||||||
|         } else { |  | ||||||
|           SDL_SetWindowFullscreen(main_window_, SDL_FALSE); |  | ||||||
|           SDL_SetWindowSize(main_window_, main_window_width_before_fullscreen_, |  | ||||||
|                             main_window_height_before_fullscreen_); |  | ||||||
|           main_window_width_ = main_window_width_before_fullscreen_; |  | ||||||
|           main_window_height_ = main_window_height_before_fullscreen_; |  | ||||||
|           fullscreen_button_label_ = |  | ||||||
|               localization::fullscreen[localization_language_index_]; |  | ||||||
|         } |  | ||||||
|         fullscreen_button_pressed_ = !fullscreen_button_pressed_; |  | ||||||
|       } |  | ||||||
|  |  | ||||||
|       ImGui::Spacing(); |  | ||||||
|  |  | ||||||
|       ImGui::Separator(); |  | ||||||
|  |  | ||||||
|       ImGui::Spacing(); |  | ||||||
|  |  | ||||||
|       if (ImGui::Button(settings_button_label_.c_str())) { |  | ||||||
|         settings_button_pressed_ = !settings_button_pressed_; |  | ||||||
|         settings_window_pos_reset_ = true; |  | ||||||
|       } |  | ||||||
|  |  | ||||||
|       if (settings_button_pressed_) { |  | ||||||
|         if (settings_window_pos_reset_) { |  | ||||||
|           const ImGuiViewport *viewport = ImGui::GetMainViewport(); |  | ||||||
|           if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) { |  | ||||||
|             ImGui::SetNextWindowPos( |  | ||||||
|                 ImVec2((viewport->WorkSize.x - viewport->WorkPos.x - |  | ||||||
|                         SETTINGS_WINDOW_WIDTH_CN) / |  | ||||||
|                            2, |  | ||||||
|                        (viewport->WorkSize.y - viewport->WorkPos.y - |  | ||||||
|                         SETTINGS_WINDOW_HEIGHT_CN) / |  | ||||||
|                            2)); |  | ||||||
|  |  | ||||||
|             ImGui::SetNextWindowSize( |  | ||||||
|                 ImVec2(SETTINGS_WINDOW_WIDTH_CN, SETTINGS_WINDOW_HEIGHT_CN)); |  | ||||||
|           } else { |  | ||||||
|             ImGui::SetNextWindowPos( |  | ||||||
|                 ImVec2((viewport->WorkSize.x - viewport->WorkPos.x - |  | ||||||
|                         SETTINGS_WINDOW_WIDTH_EN) / |  | ||||||
|                            2, |  | ||||||
|                        (viewport->WorkSize.y - viewport->WorkPos.y - |  | ||||||
|                         SETTINGS_WINDOW_HEIGHT_EN) / |  | ||||||
|                            2)); |  | ||||||
|  |  | ||||||
|             ImGui::SetNextWindowSize( |  | ||||||
|                 ImVec2(SETTINGS_WINDOW_WIDTH_EN, SETTINGS_WINDOW_HEIGHT_EN)); |  | ||||||
|           } |  | ||||||
|  |  | ||||||
|           settings_window_pos_reset_ = false; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         // Settings |  | ||||||
|         ImGui::Begin( |  | ||||||
|             localization::settings[localization_language_index_].c_str(), |  | ||||||
|             nullptr, |  | ||||||
|             ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse | |  | ||||||
|                 ImGuiWindowFlags_NoSavedSettings); |  | ||||||
|  |  | ||||||
|         { |  | ||||||
|           subwindow_hovered_ = ImGui::IsWindowHovered(); |  | ||||||
|  |  | ||||||
|           const char *language_items[] = { |  | ||||||
|               localization::language_zh[localization_language_index_].c_str(), |  | ||||||
|               localization::language_en[localization_language_index_].c_str()}; |  | ||||||
|  |  | ||||||
|           ImGui::SetCursorPosY(32); |  | ||||||
|           ImGui::Text( |  | ||||||
|               localization::language[localization_language_index_].c_str()); |  | ||||||
|           if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) { |  | ||||||
|             ImGui::SetCursorPosX(LANGUAGE_SELECT_WINDOW_PADDING_CN); |  | ||||||
|           } else { |  | ||||||
|             ImGui::SetCursorPosX(LANGUAGE_SELECT_WINDOW_PADDING_EN); |  | ||||||
|           } |  | ||||||
|           ImGui::SetCursorPosY(30); |  | ||||||
|           ImGui::SetNextItemWidth(SETTINGS_SELECT_WINDOW_WIDTH); |  | ||||||
|  |  | ||||||
|           ImGui::Combo("##language", &language_button_value_, language_items, |  | ||||||
|                        IM_ARRAYSIZE(language_items)); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         ImGui::Separator(); |  | ||||||
|  |  | ||||||
|         { |  | ||||||
|           const char *video_quality_items[] = { |  | ||||||
|               localization::video_quality_high[localization_language_index_] |  | ||||||
|                   .c_str(), |  | ||||||
|               localization::video_quality_medium[localization_language_index_] |  | ||||||
|                   .c_str(), |  | ||||||
|               localization::video_quality_low[localization_language_index_] |  | ||||||
|                   .c_str()}; |  | ||||||
|  |  | ||||||
|           ImGui::SetCursorPosY(62); |  | ||||||
|           ImGui::Text(localization::video_quality[localization_language_index_] |  | ||||||
|                           .c_str()); |  | ||||||
|  |  | ||||||
|           if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) { |  | ||||||
|             ImGui::SetCursorPosX(VIDEO_QUALITY_SELECT_WINDOW_PADDING_CN); |  | ||||||
|           } else { |  | ||||||
|             ImGui::SetCursorPosX(VIDEO_QUALITY_SELECT_WINDOW_PADDING_EN); |  | ||||||
|           } |  | ||||||
|           ImGui::SetCursorPosY(60); |  | ||||||
|           ImGui::SetNextItemWidth(SETTINGS_SELECT_WINDOW_WIDTH); |  | ||||||
|  |  | ||||||
|           ImGui::Combo("##video_quality", &video_quality_button_value_, |  | ||||||
|                        video_quality_items, IM_ARRAYSIZE(video_quality_items)); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         ImGui::Separator(); |  | ||||||
|  |  | ||||||
|         { |  | ||||||
|           const char *video_encode_format_items[] = { |  | ||||||
|               localization::av1[localization_language_index_].c_str(), |  | ||||||
|               localization::h264[localization_language_index_].c_str()}; |  | ||||||
|  |  | ||||||
|           ImGui::SetCursorPosY(92); |  | ||||||
|           ImGui::Text( |  | ||||||
|               localization::video_encode_format[localization_language_index_] |  | ||||||
|                   .c_str()); |  | ||||||
|  |  | ||||||
|           if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) { |  | ||||||
|             ImGui::SetCursorPosX(VIDEO_ENCODE_FORMAT_SELECT_WINDOW_PADDING_CN); |  | ||||||
|           } else { |  | ||||||
|             ImGui::SetCursorPosX(VIDEO_ENCODE_FORMAT_SELECT_WINDOW_PADDING_EN); |  | ||||||
|           } |  | ||||||
|           ImGui::SetCursorPosY(90); |  | ||||||
|           ImGui::SetNextItemWidth(SETTINGS_SELECT_WINDOW_WIDTH); |  | ||||||
|  |  | ||||||
|           ImGui::Combo("##video_encode_format", |  | ||||||
|                        &video_encode_format_button_value_, |  | ||||||
|                        video_encode_format_items, |  | ||||||
|                        IM_ARRAYSIZE(video_encode_format_items)); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         ImGui::Separator(); |  | ||||||
|  |  | ||||||
|         { |  | ||||||
|           ImGui::SetCursorPosY(122); |  | ||||||
|           ImGui::Text(localization::enable_hardware_video_codec |  | ||||||
|                           [localization_language_index_] |  | ||||||
|                               .c_str()); |  | ||||||
|  |  | ||||||
|           if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) { |  | ||||||
|             ImGui::SetCursorPosX( |  | ||||||
|                 ENABLE_HARDWARE_VIDEO_CODEC_CHECKBOX_PADDING_CN); |  | ||||||
|           } else { |  | ||||||
|             ImGui::SetCursorPosX( |  | ||||||
|                 ENABLE_HARDWARE_VIDEO_CODEC_CHECKBOX_PADDING_EN); |  | ||||||
|           } |  | ||||||
|           ImGui::SetCursorPosY(120); |  | ||||||
|           ImGui::Checkbox("##enable_hardware_video_codec", |  | ||||||
|                           &enable_hardware_video_codec_); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) { |  | ||||||
|           ImGui::SetCursorPosX(SETTINGS_OK_BUTTON_PADDING_CN); |  | ||||||
|         } else { |  | ||||||
|           ImGui::SetCursorPosX(SETTINGS_OK_BUTTON_PADDING_EN); |  | ||||||
|         } |  | ||||||
|         ImGui::SetCursorPosY(160.0f); |  | ||||||
|  |  | ||||||
|         // OK |  | ||||||
|         if (ImGui::Button( |  | ||||||
|                 localization::ok[localization_language_index_].c_str())) { |  | ||||||
|           settings_button_pressed_ = false; |  | ||||||
|  |  | ||||||
|           // Language |  | ||||||
|           if (language_button_value_ == 0) { |  | ||||||
|             config_center_.SetLanguage(ConfigCenter::LANGUAGE::CHINESE); |  | ||||||
|           } else { |  | ||||||
|             config_center_.SetLanguage(ConfigCenter::LANGUAGE::ENGLISH); |  | ||||||
|           } |  | ||||||
|           language_button_value_last_ = language_button_value_; |  | ||||||
|           localization_language_ = |  | ||||||
|               (ConfigCenter::LANGUAGE)language_button_value_; |  | ||||||
|           localization_language_index_ = language_button_value_; |  | ||||||
|           LOG_INFO("Set localization language: {}", |  | ||||||
|                    localization_language_index_ == 0 ? "zh" : "en"); |  | ||||||
|  |  | ||||||
|           // Video quality |  | ||||||
|           if (video_quality_button_value_ == 0) { |  | ||||||
|             config_center_.SetVideoQuality(ConfigCenter::VIDEO_QUALITY::HIGH); |  | ||||||
|           } else if (video_quality_button_value_ == 1) { |  | ||||||
|             config_center_.SetVideoQuality(ConfigCenter::VIDEO_QUALITY::MEDIUM); |  | ||||||
|           } else { |  | ||||||
|             config_center_.SetVideoQuality(ConfigCenter::VIDEO_QUALITY::LOW); |  | ||||||
|           } |  | ||||||
|           video_quality_button_value_last_ = video_quality_button_value_; |  | ||||||
|  |  | ||||||
|           // Video encode format |  | ||||||
|           if (video_encode_format_button_value_ == 0) { |  | ||||||
|             config_center_.SetVideoEncodeFormat( |  | ||||||
|                 ConfigCenter::VIDEO_ENCODE_FORMAT::AV1); |  | ||||||
|           } else if (video_encode_format_button_value_ == 1) { |  | ||||||
|             config_center_.SetVideoEncodeFormat( |  | ||||||
|                 ConfigCenter::VIDEO_ENCODE_FORMAT::H264); |  | ||||||
|           } |  | ||||||
|           video_encode_format_button_value_last_ = |  | ||||||
|               video_encode_format_button_value_; |  | ||||||
|  |  | ||||||
|           // Hardware video codec |  | ||||||
|           if (enable_hardware_video_codec_) { |  | ||||||
|             config_center_.SetHardwareVideoCodec(true); |  | ||||||
|           } else { |  | ||||||
|             config_center_.SetHardwareVideoCodec(false); |  | ||||||
|           } |  | ||||||
|           enable_hardware_video_codec_last_ = enable_hardware_video_codec_; |  | ||||||
|  |  | ||||||
|           SaveSettingsIntoCacheFile(); |  | ||||||
|           settings_window_pos_reset_ = true; |  | ||||||
|  |  | ||||||
|           // Recreate peer instance |  | ||||||
|           LoadSettingsIntoCacheFile(); |  | ||||||
|  |  | ||||||
|           // Recreate peer instance |  | ||||||
|           { |  | ||||||
|             DestroyPeer(peer_); |  | ||||||
|             CreateConnectionPeer(); |  | ||||||
|             LOG_INFO("Recreate peer instance successful"); |  | ||||||
|           } |  | ||||||
|         } |  | ||||||
|         ImGui::SameLine(); |  | ||||||
|         // Cancel |  | ||||||
|         if (ImGui::Button( |  | ||||||
|                 localization::cancel[localization_language_index_].c_str())) { |  | ||||||
|           settings_button_pressed_ = false; |  | ||||||
|           if (language_button_value_ != language_button_value_last_) { |  | ||||||
|             language_button_value_ = language_button_value_last_; |  | ||||||
|           } |  | ||||||
|  |  | ||||||
|           if (video_quality_button_value_ != video_quality_button_value_last_) { |  | ||||||
|             video_quality_button_value_ = video_quality_button_value_last_; |  | ||||||
|           } |  | ||||||
|  |  | ||||||
|           if (video_encode_format_button_value_ != |  | ||||||
|               video_encode_format_button_value_last_) { |  | ||||||
|             video_encode_format_button_value_ = |  | ||||||
|                 video_encode_format_button_value_last_; |  | ||||||
|           } |  | ||||||
|  |  | ||||||
|           if (enable_hardware_video_codec_ != |  | ||||||
|               enable_hardware_video_codec_last_) { |  | ||||||
|             enable_hardware_video_codec_ = enable_hardware_video_codec_last_; |  | ||||||
|           } |  | ||||||
|  |  | ||||||
|           settings_window_pos_reset_ = true; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         ImGui::End(); |  | ||||||
|       } |  | ||||||
|  |  | ||||||
|       ImGui::End(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     // Rendering |  | ||||||
|     ImGui::Render(); |  | ||||||
|     SDL_RenderSetScale(sdl_renderer_, io.DisplayFramebufferScale.x, |  | ||||||
|                        io.DisplayFramebufferScale.y); |  | ||||||
|  |  | ||||||
|     SDL_Event event; |  | ||||||
|     while (SDL_PollEvent(&event)) { |  | ||||||
|       ImGui_ImplSDL2_ProcessEvent(&event); |  | ||||||
|       if (event.type == SDL_QUIT) { |  | ||||||
|         exit_ = true; |  | ||||||
|       } else if (event.type == SDL_WINDOWEVENT && |  | ||||||
|                  event.window.event == SDL_WINDOWEVENT_RESIZED) { |  | ||||||
|         int window_w_last = main_window_width_; |  | ||||||
|         int window_h_last = main_window_height_; |  | ||||||
|  |  | ||||||
|         SDL_GetWindowSize(main_window_, &main_window_width_, |  | ||||||
|                           &main_window_height_); |  | ||||||
|  |  | ||||||
|         int w_change_ratio = abs(main_window_width_ - window_w_last) / 16; |  | ||||||
|         int h_change_ratio = abs(main_window_height_ - window_h_last) / 9; |  | ||||||
|  |  | ||||||
|         if (w_change_ratio > h_change_ratio) { |  | ||||||
|           main_window_height_ = main_window_width_ * 9 / 16; |  | ||||||
|         } else { |  | ||||||
|           main_window_width_ = main_window_height_ * 16 / 9; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         SDL_SetWindowSize(main_window_, main_window_width_, |  | ||||||
|                           main_window_height_); |  | ||||||
|       } else if (event.type == SDL_WINDOWEVENT && |  | ||||||
|                  event.window.event == SDL_WINDOWEVENT_CLOSE && |  | ||||||
|                  event.window.windowID == SDL_GetWindowID(main_window_)) { |  | ||||||
|         exit_ = true; |  | ||||||
|       } else if (event.type == REFRESH_EVENT) { |  | ||||||
|         sdl_rect_.x = 0; |  | ||||||
|         sdl_rect_.y = 0; |  | ||||||
|         sdl_rect_.w = main_window_width_; |  | ||||||
|         sdl_rect_.h = main_window_height_; |  | ||||||
|  |  | ||||||
|         SDL_UpdateTexture(sdl_texture_, NULL, dst_buffer_, 1280); |  | ||||||
|       } else { |  | ||||||
|         if (connection_established_) { |  | ||||||
|           ProcessMouseKeyEven(event); |  | ||||||
|         } |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     SDL_RenderClear(sdl_renderer_); |  | ||||||
|     SDL_RenderCopy(sdl_renderer_, sdl_texture_, NULL, &sdl_rect_); |  | ||||||
|  |  | ||||||
|     if (!connection_established_ || !received_frame_) { |  | ||||||
|       SDL_RenderClear(sdl_renderer_); |  | ||||||
|       SDL_SetRenderDrawColor( |  | ||||||
|           sdl_renderer_, (Uint8)(clear_color.x * 0), (Uint8)(clear_color.y * 0), |  | ||||||
|           (Uint8)(clear_color.z * 0), (Uint8)(clear_color.w * 0)); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     ImGui_ImplSDLRenderer2_RenderDrawData(ImGui::GetDrawData()); |  | ||||||
|     SDL_RenderPresent(sdl_renderer_); |  | ||||||
|  |  | ||||||
|     frame_count_++; |  | ||||||
|     end_time_ = SDL_GetTicks(); |  | ||||||
|     elapsed_time_ = end_time_ - start_time_; |  | ||||||
|     if (elapsed_time_ >= 1000) { |  | ||||||
|       fps_ = frame_count_ / (elapsed_time_ / 1000); |  | ||||||
|       frame_count_ = 0; |  | ||||||
|       window_title = "Remote Desk Client FPS [" + std::to_string(fps_) + |  | ||||||
|                      "] status [" + connection_status_str_ + "|" + |  | ||||||
|                      connection_status_str_ + "]"; |  | ||||||
|       // For MacOS, UI frameworks can only be called from the main thread |  | ||||||
|       SDL_SetWindowTitle(main_window_, window_title.c_str()); |  | ||||||
|       start_time_ = end_time_; |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   // Cleanup |  | ||||||
|   if (is_create_connection_) { |  | ||||||
|     LeaveConnection(peer_); |  | ||||||
|     is_client_mode_ = false; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   if (peer_) { |  | ||||||
|     DestroyPeer(peer_); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   if (peer_reserved_) { |  | ||||||
|     DestroyPeer(peer_reserved_); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   SDL_CloseAudioDevice(output_dev_); |  | ||||||
|   SDL_CloseAudioDevice(input_dev_); |  | ||||||
|  |  | ||||||
|   ImGui_ImplSDLRenderer2_Shutdown(); |  | ||||||
|   ImGui_ImplSDL2_Shutdown(); |  | ||||||
|   ImGui::DestroyContext(); |  | ||||||
|  |  | ||||||
|   SDL_DestroyRenderer(sdl_renderer_); |  | ||||||
|   SDL_DestroyWindow(main_window_); |  | ||||||
|  |  | ||||||
|   SDL_CloseAudio(); |  | ||||||
|   SDL_Quit(); |  | ||||||
|  |  | ||||||
|   return 0; |  | ||||||
| } |  | ||||||
| @@ -1,190 +0,0 @@ | |||||||
| /* |  | ||||||
|  * @Author: DI JUNKUN |  | ||||||
|  * @Date: 2024-05-29 |  | ||||||
|  * Copyright (c) 2024 by DI JUNKUN, All Rights Reserved. |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| #ifndef _MAIN_WINDOW_H_ |  | ||||||
| #define _MAIN_WINDOW_H_ |  | ||||||
|  |  | ||||||
| #include <SDL.h> |  | ||||||
|  |  | ||||||
| #include <atomic> |  | ||||||
| #include <chrono> |  | ||||||
| #include <string> |  | ||||||
|  |  | ||||||
| #include "../../thirdparty/projectx/src/interface/x.h" |  | ||||||
| #include "config_center.h" |  | ||||||
| #include "device_controller_factory.h" |  | ||||||
| #include "imgui.h" |  | ||||||
| #include "imgui_impl_sdl2.h" |  | ||||||
| #include "imgui_impl_sdlrenderer2.h" |  | ||||||
| #include "screen_capturer_factory.h" |  | ||||||
|  |  | ||||||
| class MainWindow { |  | ||||||
|  public: |  | ||||||
|   MainWindow(); |  | ||||||
|   ~MainWindow(); |  | ||||||
|  |  | ||||||
|  public: |  | ||||||
|   int Run(); |  | ||||||
|  |  | ||||||
|  public: |  | ||||||
|   static void OnReceiveVideoBufferCb(const char *data, size_t size, |  | ||||||
|                                      const char *user_id, size_t user_id_size, |  | ||||||
|                                      void *user_data); |  | ||||||
|  |  | ||||||
|   static void OnReceiveAudioBufferCb(const char *data, size_t size, |  | ||||||
|                                      const char *user_id, size_t user_id_size, |  | ||||||
|                                      void *user_data); |  | ||||||
|  |  | ||||||
|   static void OnReceiveDataBufferCb(const char *data, size_t size, |  | ||||||
|                                     const char *user_id, size_t user_id_size, |  | ||||||
|                                     void *user_data); |  | ||||||
|  |  | ||||||
|   static void OnSignalStatusCb(SignalStatus status, void *user_data); |  | ||||||
|  |  | ||||||
|   static void OnConnectionStatusCb(ConnectionStatus status, void *user_data); |  | ||||||
|  |  | ||||||
|  private: |  | ||||||
|   int ProcessMouseKeyEven(SDL_Event &ev); |  | ||||||
|   void SdlCaptureAudioIn(void *userdata, Uint8 *stream, int len); |  | ||||||
|   void SdlCaptureAudioOut(void *userdata, Uint8 *stream, int len); |  | ||||||
|  |  | ||||||
|  private: |  | ||||||
|   int SaveSettingsIntoCacheFile(); |  | ||||||
|   int LoadSettingsIntoCacheFile(); |  | ||||||
|  |  | ||||||
|   int StartScreenCapture(); |  | ||||||
|   int StopScreenCapture(); |  | ||||||
|  |  | ||||||
|   int StartMouseControl(); |  | ||||||
|   int StopMouseControl(); |  | ||||||
|  |  | ||||||
|   int CreateConnectionPeer(); |  | ||||||
|  |  | ||||||
|  private: |  | ||||||
|   typedef struct { |  | ||||||
|     char password[7]; |  | ||||||
|     int language; |  | ||||||
|     int video_quality; |  | ||||||
|     int video_encode_format; |  | ||||||
|     bool enable_hardware_video_codec; |  | ||||||
|   } CDCache; |  | ||||||
|  |  | ||||||
|  private: |  | ||||||
|   FILE *cd_cache_file_ = nullptr; |  | ||||||
|   CDCache cd_cache_; |  | ||||||
|  |  | ||||||
|   ConfigCenter config_center_; |  | ||||||
|  |  | ||||||
|   ConfigCenter::LANGUAGE localization_language_ = |  | ||||||
|       ConfigCenter::LANGUAGE::CHINESE; |  | ||||||
|  |  | ||||||
|   int localization_language_index_ = -1; |  | ||||||
|   int localization_language_index_last_ = -1; |  | ||||||
|  |  | ||||||
|  private: |  | ||||||
|   std::string window_title = "Remote Desk Client"; |  | ||||||
|   std::string mac_addr_str_ = ""; |  | ||||||
|   std::string connect_button_label_ = "Connect"; |  | ||||||
|   std::string fullscreen_button_label_ = "Fullscreen"; |  | ||||||
|   std::string mouse_control_button_label_ = "Mouse Control"; |  | ||||||
|   std::string settings_button_label_ = "Setting"; |  | ||||||
|   char input_password_tmp_[7] = ""; |  | ||||||
|   char input_password_[7] = ""; |  | ||||||
|   std::string local_id_ = ""; |  | ||||||
|   char remote_id_[20] = ""; |  | ||||||
|   char client_password_[20] = ""; |  | ||||||
|   bool is_client_mode_ = false; |  | ||||||
|  |  | ||||||
|  private: |  | ||||||
|   int screen_width_ = 1280; |  | ||||||
|   int screen_height_ = 720; |  | ||||||
|   int main_window_width_ = 1280; |  | ||||||
|   int main_window_height_ = 720; |  | ||||||
|   int main_window_width_before_fullscreen_ = 1280; |  | ||||||
|   int main_window_height_before_fullscreen_ = 720; |  | ||||||
|  |  | ||||||
|   int texture_width_ = 1280; |  | ||||||
|   int texture_height_ = 720; |  | ||||||
|  |  | ||||||
|   SDL_Texture *sdl_texture_ = nullptr; |  | ||||||
|   SDL_Renderer *sdl_renderer_ = nullptr; |  | ||||||
|   SDL_Rect sdl_rect_; |  | ||||||
|   SDL_Window *main_window_; |  | ||||||
|   uint32_t pixformat_ = 0; |  | ||||||
|  |  | ||||||
|   bool inited_ = false; |  | ||||||
|   bool exit_ = false; |  | ||||||
|   bool connection_established_ = false; |  | ||||||
|   bool subwindow_hovered_ = false; |  | ||||||
|   bool connect_button_pressed_ = false; |  | ||||||
|   bool fullscreen_button_pressed_ = false; |  | ||||||
|   bool mouse_control_button_pressed_ = false; |  | ||||||
|   bool settings_button_pressed_ = false; |  | ||||||
|   bool received_frame_ = false; |  | ||||||
|   bool is_create_connection_ = false; |  | ||||||
|   bool audio_buffer_fresh_ = false; |  | ||||||
|   bool rejoin_ = false; |  | ||||||
|   bool control_mouse_ = false; |  | ||||||
|  |  | ||||||
|   int fps_ = 0; |  | ||||||
|   uint32_t start_time_; |  | ||||||
|   uint32_t end_time_; |  | ||||||
|   uint32_t elapsed_time_; |  | ||||||
|   uint32_t frame_count_ = 0; |  | ||||||
|  |  | ||||||
|  private: |  | ||||||
|   ConnectionStatus connection_status_ = ConnectionStatus::Closed; |  | ||||||
|   SignalStatus signal_status_ = SignalStatus::SignalClosed; |  | ||||||
|   std::string signal_status_str_ = ""; |  | ||||||
|   std::string connection_status_str_ = ""; |  | ||||||
|  |  | ||||||
|  private: |  | ||||||
|   PeerPtr *peer_ = nullptr; |  | ||||||
|   PeerPtr *peer_reserved_ = nullptr; |  | ||||||
|   Params params_; |  | ||||||
|  |  | ||||||
|  private: |  | ||||||
|   SDL_AudioDeviceID input_dev_; |  | ||||||
|   SDL_AudioDeviceID output_dev_; |  | ||||||
|   unsigned char audio_buffer_[960]; |  | ||||||
|   int audio_len_ = 0; |  | ||||||
|   char *nv12_buffer_ = nullptr; |  | ||||||
|   unsigned char *dst_buffer_ = new unsigned char[1280 * 720 * 3]; |  | ||||||
|  |  | ||||||
|  private: |  | ||||||
|   ScreenCapturerFactory *screen_capturer_factory_ = nullptr; |  | ||||||
|   ScreenCapturer *screen_capturer_ = nullptr; |  | ||||||
|   DeviceControllerFactory *device_controller_factory_ = nullptr; |  | ||||||
|   MouseController *mouse_controller_ = nullptr; |  | ||||||
|  |  | ||||||
| #ifdef __linux__ |  | ||||||
|   std::chrono::_V2::system_clock::time_point last_frame_time_; |  | ||||||
| #else |  | ||||||
|   std::chrono::steady_clock::time_point last_frame_time_; |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
|  private: |  | ||||||
|   int language_button_value_ = 0; |  | ||||||
|   int video_quality_button_value_ = 0; |  | ||||||
|   int video_encode_format_button_value_ = 0; |  | ||||||
|   bool enable_hardware_video_codec_ = false; |  | ||||||
|  |  | ||||||
|   int language_button_value_last_ = 0; |  | ||||||
|   int video_quality_button_value_last_ = 0; |  | ||||||
|   int video_encode_format_button_value_last_ = 0; |  | ||||||
|   bool enable_hardware_video_codec_last_ = false; |  | ||||||
|  |  | ||||||
|  private: |  | ||||||
|   std::atomic<bool> start_screen_capture_{false}; |  | ||||||
|   std::atomic<bool> start_mouse_control_{false}; |  | ||||||
|   std::atomic<bool> screen_capture_is_started_{false}; |  | ||||||
|   std::atomic<bool> mouse_control_is_started_{false}; |  | ||||||
|  |  | ||||||
|  private: |  | ||||||
|   bool settings_window_pos_reset_ = true; |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| #endif |  | ||||||
| @@ -1,216 +0,0 @@ | |||||||
| #include "device_controller.h" |  | ||||||
| #include "localization.h" |  | ||||||
| #include "main_window.h" |  | ||||||
|  |  | ||||||
| // Refresh Event |  | ||||||
| #define REFRESH_EVENT (SDL_USEREVENT + 1) |  | ||||||
| #define NV12_BUFFER_SIZE 1280 * 720 * 3 / 2 |  | ||||||
|  |  | ||||||
| #ifdef REMOTE_DESK_DEBUG |  | ||||||
| #else |  | ||||||
| #define MOUSE_CONTROL 1 |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| int MainWindow::ProcessMouseKeyEven(SDL_Event &ev) { |  | ||||||
|   if (!control_mouse_) { |  | ||||||
|     return 0; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   float ratio = (float)(1280.0 / main_window_width_); |  | ||||||
|  |  | ||||||
|   RemoteAction remote_action; |  | ||||||
|   remote_action.m.x = (size_t)(ev.button.x * ratio); |  | ||||||
|   remote_action.m.y = (size_t)(ev.button.y * ratio); |  | ||||||
|  |  | ||||||
|   if (SDL_KEYDOWN == ev.type)  // SDL_KEYUP |  | ||||||
|   { |  | ||||||
|     // printf("SDLK_DOWN: %d\n", SDL_KeyCode(ev.key.keysym.sym)); |  | ||||||
|     if (SDLK_DOWN == ev.key.keysym.sym) { |  | ||||||
|       // printf("SDLK_DOWN  \n"); |  | ||||||
|  |  | ||||||
|     } else if (SDLK_UP == ev.key.keysym.sym) { |  | ||||||
|       // printf("SDLK_UP  \n"); |  | ||||||
|  |  | ||||||
|     } else if (SDLK_LEFT == ev.key.keysym.sym) { |  | ||||||
|       // printf("SDLK_LEFT  \n"); |  | ||||||
|  |  | ||||||
|     } else if (SDLK_RIGHT == ev.key.keysym.sym) { |  | ||||||
|       // printf("SDLK_RIGHT  \n"); |  | ||||||
|     } |  | ||||||
|   } else if (SDL_MOUSEBUTTONDOWN == ev.type) { |  | ||||||
|     remote_action.type = ControlType::mouse; |  | ||||||
|     if (SDL_BUTTON_LEFT == ev.button.button) { |  | ||||||
|       remote_action.m.flag = MouseFlag::left_down; |  | ||||||
|     } else if (SDL_BUTTON_RIGHT == ev.button.button) { |  | ||||||
|       remote_action.m.flag = MouseFlag::right_down; |  | ||||||
|     } |  | ||||||
|     if (subwindow_hovered_) { |  | ||||||
|       remote_action.m.flag = MouseFlag::move; |  | ||||||
|     } |  | ||||||
|     SendData(peer_, DATA_TYPE::DATA, (const char *)&remote_action, |  | ||||||
|              sizeof(remote_action)); |  | ||||||
|   } else if (SDL_MOUSEBUTTONUP == ev.type) { |  | ||||||
|     remote_action.type = ControlType::mouse; |  | ||||||
|     if (SDL_BUTTON_LEFT == ev.button.button) { |  | ||||||
|       remote_action.m.flag = MouseFlag::left_up; |  | ||||||
|     } else if (SDL_BUTTON_RIGHT == ev.button.button) { |  | ||||||
|       remote_action.m.flag = MouseFlag::right_up; |  | ||||||
|     } |  | ||||||
|     if (subwindow_hovered_) { |  | ||||||
|       remote_action.m.flag = MouseFlag::move; |  | ||||||
|     } |  | ||||||
|     SendData(peer_, DATA_TYPE::DATA, (const char *)&remote_action, |  | ||||||
|              sizeof(remote_action)); |  | ||||||
|   } else if (SDL_MOUSEMOTION == ev.type) { |  | ||||||
|     remote_action.type = ControlType::mouse; |  | ||||||
|     remote_action.m.flag = MouseFlag::move; |  | ||||||
|     SendData(peer_, DATA_TYPE::DATA, (const char *)&remote_action, |  | ||||||
|              sizeof(remote_action)); |  | ||||||
|   } else if (SDL_QUIT == ev.type) { |  | ||||||
|     SDL_Event event; |  | ||||||
|     event.type = SDL_QUIT; |  | ||||||
|     SDL_PushEvent(&event); |  | ||||||
|     printf("SDL_QUIT\n"); |  | ||||||
|     return 0; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void MainWindow::SdlCaptureAudioIn(void *userdata, Uint8 *stream, int len) { |  | ||||||
|   if (1) { |  | ||||||
|     if ("Connected" == connection_status_str_) { |  | ||||||
|       SendData(peer_, DATA_TYPE::AUDIO, (const char *)stream, len); |  | ||||||
|     } |  | ||||||
|   } else { |  | ||||||
|     memcpy(audio_buffer_, stream, len); |  | ||||||
|     audio_len_ = len; |  | ||||||
|     SDL_Delay(10); |  | ||||||
|     audio_buffer_fresh_ = true; |  | ||||||
|   } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void MainWindow::SdlCaptureAudioOut(void *userdata, Uint8 *stream, int len) { |  | ||||||
|   if (!audio_buffer_fresh_) { |  | ||||||
|     return; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   SDL_memset(stream, 0, len); |  | ||||||
|  |  | ||||||
|   if (audio_len_ == 0) { |  | ||||||
|     return; |  | ||||||
|   } else { |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   len = (len > audio_len_ ? audio_len_ : len); |  | ||||||
|   SDL_MixAudioFormat(stream, audio_buffer_, AUDIO_S16LSB, len, |  | ||||||
|                      SDL_MIX_MAXVOLUME); |  | ||||||
|   audio_buffer_fresh_ = false; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void MainWindow::OnReceiveVideoBufferCb(const char *data, size_t size, |  | ||||||
|                                         const char *user_id, |  | ||||||
|                                         size_t user_id_size, void *user_data) { |  | ||||||
|   MainWindow *main_window = (MainWindow *)user_data; |  | ||||||
|   if (main_window->connection_established_) { |  | ||||||
|     memcpy(main_window->dst_buffer_, data, size); |  | ||||||
|     SDL_Event event; |  | ||||||
|     event.type = REFRESH_EVENT; |  | ||||||
|     SDL_PushEvent(&event); |  | ||||||
|     main_window->received_frame_ = true; |  | ||||||
|   } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void MainWindow::OnReceiveAudioBufferCb(const char *data, size_t size, |  | ||||||
|                                         const char *user_id, |  | ||||||
|                                         size_t user_id_size, void *user_data) { |  | ||||||
|   MainWindow *main_window = (MainWindow *)user_data; |  | ||||||
|   main_window->audio_buffer_fresh_ = true; |  | ||||||
|   SDL_QueueAudio(main_window->output_dev_, data, (uint32_t)size); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void MainWindow::OnReceiveDataBufferCb(const char *data, size_t size, |  | ||||||
|                                        const char *user_id, size_t user_id_size, |  | ||||||
|                                        void *user_data) { |  | ||||||
|   MainWindow *main_window = (MainWindow *)user_data; |  | ||||||
|   std::string user(user_id, user_id_size); |  | ||||||
|   RemoteAction remote_action; |  | ||||||
|   memcpy(&remote_action, data, sizeof(remote_action)); |  | ||||||
|  |  | ||||||
| #if MOUSE_CONTROL |  | ||||||
|   if (main_window->mouse_controller_) { |  | ||||||
|     main_window->mouse_controller_->SendCommand(remote_action); |  | ||||||
|   } |  | ||||||
| #endif |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void MainWindow::OnSignalStatusCb(SignalStatus status, void *user_data) { |  | ||||||
|   MainWindow *main_window = (MainWindow *)user_data; |  | ||||||
|   main_window->signal_status_ = status; |  | ||||||
|   if (SignalStatus::SignalConnecting == status) { |  | ||||||
|     main_window->signal_status_str_ = "SignalConnecting"; |  | ||||||
|   } else if (SignalStatus::SignalConnected == status) { |  | ||||||
|     main_window->signal_status_str_ = "SignalConnected"; |  | ||||||
|   } else if (SignalStatus::SignalFailed == status) { |  | ||||||
|     main_window->signal_status_str_ = "SignalFailed"; |  | ||||||
|   } else if (SignalStatus::SignalClosed == status) { |  | ||||||
|     main_window->signal_status_str_ = "SignalClosed"; |  | ||||||
|   } else if (SignalStatus::SignalReconnecting == status) { |  | ||||||
|     main_window->signal_status_str_ = "SignalReconnecting"; |  | ||||||
|   } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void MainWindow::OnConnectionStatusCb(ConnectionStatus status, |  | ||||||
|                                       void *user_data) { |  | ||||||
|   MainWindow *main_window = (MainWindow *)user_data; |  | ||||||
|   main_window->connection_status_ = status; |  | ||||||
|   if (ConnectionStatus::Connecting == status) { |  | ||||||
|     main_window->connection_status_str_ = "Connecting"; |  | ||||||
|   } else if (ConnectionStatus::Connected == status) { |  | ||||||
|     main_window->connection_status_str_ = "Connected"; |  | ||||||
|     main_window->connection_established_ = true; |  | ||||||
|     if (!main_window->is_client_mode_) { |  | ||||||
|       main_window->start_screen_capture_ = true; |  | ||||||
|       main_window->start_mouse_control_ = true; |  | ||||||
|     } |  | ||||||
|   } else if (ConnectionStatus::Disconnected == status) { |  | ||||||
|     main_window->connection_status_str_ = "Disconnected"; |  | ||||||
|   } else if (ConnectionStatus::Failed == status) { |  | ||||||
|     main_window->connection_status_str_ = "Failed"; |  | ||||||
|   } else if (ConnectionStatus::Closed == status) { |  | ||||||
|     main_window->connection_status_str_ = "Closed"; |  | ||||||
|     main_window->start_screen_capture_ = false; |  | ||||||
|     main_window->start_mouse_control_ = false; |  | ||||||
|     main_window->connection_established_ = false; |  | ||||||
|     main_window->control_mouse_ = false; |  | ||||||
|     if (main_window->dst_buffer_) { |  | ||||||
|       memset(main_window->dst_buffer_, 0, 1280 * 720 * 3); |  | ||||||
|       SDL_UpdateTexture(main_window->sdl_texture_, NULL, |  | ||||||
|                         main_window->dst_buffer_, 1280); |  | ||||||
|     } |  | ||||||
|   } else if (ConnectionStatus::IncorrectPassword == status) { |  | ||||||
|     main_window->connection_status_str_ = "Incorrect password"; |  | ||||||
|     if (main_window->connect_button_pressed_) { |  | ||||||
|       main_window->connect_button_pressed_ = false; |  | ||||||
|       main_window->connection_established_ = false; |  | ||||||
|       main_window->connect_button_label_ = |  | ||||||
|           main_window->connect_button_pressed_ |  | ||||||
|               ? localization::disconnect[main_window |  | ||||||
|                                              ->localization_language_index_] |  | ||||||
|               : localization::connect[main_window |  | ||||||
|                                           ->localization_language_index_]; |  | ||||||
|     } |  | ||||||
|   } else if (ConnectionStatus::NoSuchTransmissionId == status) { |  | ||||||
|     main_window->connection_status_str_ = "No such transmission id"; |  | ||||||
|     if (main_window->connect_button_pressed_) { |  | ||||||
|       main_window->connect_button_pressed_ = false; |  | ||||||
|       main_window->connection_established_ = false; |  | ||||||
|       main_window->connect_button_label_ = |  | ||||||
|           main_window->connect_button_pressed_ |  | ||||||
|               ? localization::disconnect[main_window |  | ||||||
|                                              ->localization_language_index_] |  | ||||||
|               : localization::connect[main_window |  | ||||||
|                                           ->localization_language_index_]; |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| @@ -10,8 +10,8 @@ unsigned char nv12_buffer_[NV12_BUFFER_SIZE]; | |||||||
| ScreenCapturerX11::ScreenCapturerX11() {} | ScreenCapturerX11::ScreenCapturerX11() {} | ||||||
|  |  | ||||||
| ScreenCapturerX11::~ScreenCapturerX11() { | ScreenCapturerX11::~ScreenCapturerX11() { | ||||||
|   if (inited_ && capture_thread_->joinable()) { |   if (inited_ && capture_thread_.joinable()) { | ||||||
|     capture_thread_->join(); |     capture_thread_.join(); | ||||||
|     inited_ = false; |     inited_ = false; | ||||||
|   } |   } | ||||||
| } | } | ||||||
| @@ -32,6 +32,8 @@ int ScreenCapturerX11::Init(const RECORD_DESKTOP_RECT &rect, const int fps, | |||||||
|  |  | ||||||
|   // grabbing frame rate |   // grabbing frame rate | ||||||
|   av_dict_set(&options_, "framerate", "30", 0); |   av_dict_set(&options_, "framerate", "30", 0); | ||||||
|  |   // show remote cursor | ||||||
|  |   av_dict_set(&options_, "capture_cursor", "0", 0); | ||||||
|   // Make the grabbed area follow the mouse |   // Make the grabbed area follow the mouse | ||||||
|   // av_dict_set(&options_, "follow_mouse", "centered", 0); |   // av_dict_set(&options_, "follow_mouse", "centered", 0); | ||||||
|   // Video frame size. The default is to capture the full screen |   // Video frame size. The default is to capture the full screen | ||||||
| @@ -101,11 +103,14 @@ int ScreenCapturerX11::Init(const RECORD_DESKTOP_RECT &rect, const int fps, | |||||||
|   return 0; |   return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| int ScreenCapturerX11::Destroy() { return 0; } | int ScreenCapturerX11::Destroy() { | ||||||
|  |   running_ = false; | ||||||
|  |   return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
| int ScreenCapturerX11::Start() { | int ScreenCapturerX11::Start() { | ||||||
|   capture_thread_.reset(new std::thread([this]() { |   capture_thread_ = std::thread([this]() { | ||||||
|     while (1) { |     while (running_) { | ||||||
|       if (av_read_frame(pFormatCtx_, packet_) >= 0) { |       if (av_read_frame(pFormatCtx_, packet_) >= 0) { | ||||||
|         if (packet_->stream_index == videoindex_) { |         if (packet_->stream_index == videoindex_) { | ||||||
|           avcodec_send_packet(pCodecCtx_, packet_); |           avcodec_send_packet(pCodecCtx_, packet_); | ||||||
| @@ -128,12 +133,15 @@ int ScreenCapturerX11::Start() { | |||||||
|         } |         } | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|   })); |   }); | ||||||
|  |  | ||||||
|   return 0; |   return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| int ScreenCapturerX11::Stop() { return 0; } | int ScreenCapturerX11::Stop() { | ||||||
|  |   running_ = false; | ||||||
|  |   return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
| int ScreenCapturerX11::Pause() { return 0; } | int ScreenCapturerX11::Pause() { return 0; } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -77,7 +77,8 @@ class ScreenCapturerX11 : public ScreenCapturer { | |||||||
|   struct SwsContext *img_convert_ctx_ = nullptr; |   struct SwsContext *img_convert_ctx_ = nullptr; | ||||||
|  |  | ||||||
|   // thread |   // thread | ||||||
|   std::unique_ptr<std::thread> capture_thread_ = nullptr; |   std::thread capture_thread_; | ||||||
|  |   std::atomic_bool running_; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| #endif | #endif | ||||||
							
								
								
									
										212
									
								
								src/screen_capturer/macosx/avfoundation/screen_capturer_avf.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										212
									
								
								src/screen_capturer/macosx/avfoundation/screen_capturer_avf.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,212 @@ | |||||||
|  | #include "screen_capturer_avf.h" | ||||||
|  |  | ||||||
|  | #include <ApplicationServices/ApplicationServices.h> | ||||||
|  |  | ||||||
|  | #include <iostream> | ||||||
|  |  | ||||||
|  | #include "rd_log.h" | ||||||
|  |  | ||||||
|  | #define USE_SCALE_FACTOR 0 | ||||||
|  |  | ||||||
|  | ScreenCapturerAvf::ScreenCapturerAvf() {} | ||||||
|  |  | ||||||
|  | ScreenCapturerAvf::~ScreenCapturerAvf() { | ||||||
|  |   if (inited_ && capture_thread_.joinable()) { | ||||||
|  |     capture_thread_.join(); | ||||||
|  |     inited_ = false; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   if (nv12_frame_) { | ||||||
|  |     delete[] nv12_frame_; | ||||||
|  |     nv12_frame_ = nullptr; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   if (pFormatCtx_) { | ||||||
|  |     avformat_close_input(&pFormatCtx_); | ||||||
|  |     pFormatCtx_ = nullptr; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   if (pCodecCtx_) { | ||||||
|  |     avcodec_free_context(&pCodecCtx_); | ||||||
|  |     pCodecCtx_ = nullptr; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   if (options_) { | ||||||
|  |     av_dict_free(&options_); | ||||||
|  |     options_ = nullptr; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   if (pFrame_) { | ||||||
|  |     av_frame_free(&pFrame_); | ||||||
|  |     pFrame_ = nullptr; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   if (packet_) { | ||||||
|  |     av_packet_free(&packet_); | ||||||
|  |     packet_ = nullptr; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  | #if USE_SCALE_FACTOR | ||||||
|  |   if (img_convert_ctx_) { | ||||||
|  |     sws_freeContext(img_convert_ctx_); | ||||||
|  |     img_convert_ctx_ = nullptr; | ||||||
|  |   } | ||||||
|  | #endif | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int ScreenCapturerAvf::Init(const int fps, cb_desktop_data cb) { | ||||||
|  |   if (cb) { | ||||||
|  |     _on_data = cb; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   av_log_set_level(AV_LOG_QUIET); | ||||||
|  |  | ||||||
|  |   pFormatCtx_ = avformat_alloc_context(); | ||||||
|  |  | ||||||
|  |   avdevice_register_all(); | ||||||
|  |  | ||||||
|  |   // grabbing frame rate | ||||||
|  |   av_dict_set(&options_, "framerate", "60", 0); | ||||||
|  |   av_dict_set(&options_, "pixel_format", "nv12", 0); | ||||||
|  |   // show remote cursor | ||||||
|  |   av_dict_set(&options_, "capture_cursor", "0", 0); | ||||||
|  |   // Make the grabbed area follow the mouse | ||||||
|  |   // av_dict_set(&options_, "follow_mouse", "centered", 0); | ||||||
|  |   // Video frame size. The default is to capture the full screen | ||||||
|  |   // av_dict_set(&options_, "video_size", "1440x900", 0); | ||||||
|  |   ifmt_ = (AVInputFormat *)av_find_input_format("avfoundation"); | ||||||
|  |   if (!ifmt_) { | ||||||
|  |     printf("Couldn't find_input_format\n"); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   // Grab at position 10,20 | ||||||
|  |   if (avformat_open_input(&pFormatCtx_, "Capture screen 0", ifmt_, &options_) != | ||||||
|  |       0) { | ||||||
|  |     printf("Couldn't open input stream.\n"); | ||||||
|  |     return -1; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   if (avformat_find_stream_info(pFormatCtx_, NULL) < 0) { | ||||||
|  |     printf("Couldn't find stream information.\n"); | ||||||
|  |     return -1; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   videoindex_ = -1; | ||||||
|  |   for (i_ = 0; i_ < pFormatCtx_->nb_streams; i_++) | ||||||
|  |     if (pFormatCtx_->streams[i_]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { | ||||||
|  |       videoindex_ = i_; | ||||||
|  |       break; | ||||||
|  |     } | ||||||
|  |   if (videoindex_ == -1) { | ||||||
|  |     printf("Didn't find a video stream.\n"); | ||||||
|  |     return -1; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   pCodecParam_ = pFormatCtx_->streams[videoindex_]->codecpar; | ||||||
|  |  | ||||||
|  |   pCodecCtx_ = avcodec_alloc_context3(NULL); | ||||||
|  |   avcodec_parameters_to_context(pCodecCtx_, pCodecParam_); | ||||||
|  |  | ||||||
|  |   pCodec_ = const_cast<AVCodec *>(avcodec_find_decoder(pCodecCtx_->codec_id)); | ||||||
|  |   if (pCodec_ == NULL) { | ||||||
|  |     printf("Codec not found.\n"); | ||||||
|  |     return -1; | ||||||
|  |   } | ||||||
|  |   if (avcodec_open2(pCodecCtx_, pCodec_, NULL) < 0) { | ||||||
|  |     printf("Could not open codec.\n"); | ||||||
|  |     return -1; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   const int screen_w = pFormatCtx_->streams[videoindex_]->codecpar->width; | ||||||
|  |   const int screen_h = pFormatCtx_->streams[videoindex_]->codecpar->height; | ||||||
|  |  | ||||||
|  |   pFrame_ = av_frame_alloc(); | ||||||
|  |   pFrame_->width = screen_w; | ||||||
|  |   pFrame_->height = screen_h; | ||||||
|  |  | ||||||
|  | #if USE_SCALE_FACTOR | ||||||
|  |   pFrame_resized_ = av_frame_alloc(); | ||||||
|  |   pFrame_resized_->width = CGDisplayPixelsWide(CGMainDisplayID()); | ||||||
|  |   pFrame_resized_->height = CGDisplayPixelsHigh(CGMainDisplayID()); | ||||||
|  |  | ||||||
|  |   img_convert_ctx_ = | ||||||
|  |       sws_getContext(pFrame_->width, pFrame_->height, pCodecCtx_->pix_fmt, | ||||||
|  |                      pFrame_resized_->width, pFrame_resized_->height, | ||||||
|  |                      AV_PIX_FMT_NV12, SWS_BICUBIC, NULL, NULL, NULL); | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  |   if (!nv12_frame_) { | ||||||
|  |     nv12_frame_ = new unsigned char[screen_w * screen_h * 3 / 2]; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   packet_ = (AVPacket *)av_malloc(sizeof(AVPacket)); | ||||||
|  |  | ||||||
|  |   inited_ = true; | ||||||
|  |  | ||||||
|  |   return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int ScreenCapturerAvf::Destroy() { | ||||||
|  |   running_ = false; | ||||||
|  |   return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int ScreenCapturerAvf::Start() { | ||||||
|  |   if (running_) { | ||||||
|  |     return 0; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   running_ = true; | ||||||
|  |   capture_thread_ = std::thread([this]() { | ||||||
|  |     while (running_) { | ||||||
|  |       if (av_read_frame(pFormatCtx_, packet_) >= 0) { | ||||||
|  |         if (packet_->stream_index == videoindex_) { | ||||||
|  |           avcodec_send_packet(pCodecCtx_, packet_); | ||||||
|  |           av_packet_unref(packet_); | ||||||
|  |           got_picture_ = avcodec_receive_frame(pCodecCtx_, pFrame_); | ||||||
|  |  | ||||||
|  |           if (!got_picture_) { | ||||||
|  | #if USE_SCALE_FACTOR | ||||||
|  |             av_image_fill_arrays(pFrame_resized_->data, | ||||||
|  |                                  pFrame_resized_->linesize, nv12_frame_, | ||||||
|  |                                  AV_PIX_FMT_NV12, pFrame_resized_->width, | ||||||
|  |                                  pFrame_resized_->height, 1); | ||||||
|  |  | ||||||
|  |             sws_scale(img_convert_ctx_, pFrame_->data, pFrame_->linesize, 0, | ||||||
|  |                       pFrame_->height, pFrame_resized_->data, | ||||||
|  |                       pFrame_resized_->linesize); | ||||||
|  |  | ||||||
|  |             _on_data((unsigned char *)nv12_frame_, | ||||||
|  |                      pFrame_resized_->width * pFrame_resized_->height * 3 / 2, | ||||||
|  |                      pFrame_resized_->width, pFrame_resized_->height); | ||||||
|  | #else | ||||||
|  |             memcpy(nv12_frame_, pFrame_->data[0], | ||||||
|  |                    pFrame_->linesize[0] * pFrame_->height); | ||||||
|  |             memcpy(nv12_frame_ + pFrame_->linesize[0] * pFrame_->height, | ||||||
|  |                    pFrame_->data[1], | ||||||
|  |                    pFrame_->linesize[1] * pFrame_->height / 2); | ||||||
|  |             _on_data((unsigned char *)nv12_frame_, | ||||||
|  |                      pFrame_->width * pFrame_->height * 3 / 2, pFrame_->width, | ||||||
|  |                      pFrame_->height); | ||||||
|  | #endif | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int ScreenCapturerAvf::Stop() { | ||||||
|  |   running_ = false; | ||||||
|  |   return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int ScreenCapturerAvf::Pause() { return 0; } | ||||||
|  |  | ||||||
|  | int ScreenCapturerAvf::Resume() { return 0; } | ||||||
|  |  | ||||||
|  | void ScreenCapturerAvf::OnFrame() {} | ||||||
|  |  | ||||||
|  | void ScreenCapturerAvf::CleanUp() {} | ||||||
| @@ -32,8 +32,8 @@ class ScreenCapturerAvf : public ScreenCapturer { | |||||||
|   ~ScreenCapturerAvf(); |   ~ScreenCapturerAvf(); | ||||||
| 
 | 
 | ||||||
|  public: |  public: | ||||||
|   virtual int Init(const RECORD_DESKTOP_RECT &rect, const int fps, |   virtual int Init(const int fps, cb_desktop_data cb); | ||||||
|                    cb_desktop_data cb); | 
 | ||||||
|   virtual int Destroy(); |   virtual int Destroy(); | ||||||
| 
 | 
 | ||||||
|   virtual int Start(); |   virtual int Start(); | ||||||
| @@ -49,7 +49,6 @@ class ScreenCapturerAvf : public ScreenCapturer { | |||||||
|   void CleanUp(); |   void CleanUp(); | ||||||
| 
 | 
 | ||||||
|  private: |  private: | ||||||
|   std::atomic_bool _running; |  | ||||||
|   std::atomic_bool _paused; |   std::atomic_bool _paused; | ||||||
|   std::atomic_bool _inited; |   std::atomic_bool _inited; | ||||||
| 
 | 
 | ||||||
| @@ -57,8 +56,6 @@ class ScreenCapturerAvf : public ScreenCapturer { | |||||||
| 
 | 
 | ||||||
|   std::string _device_name; |   std::string _device_name; | ||||||
| 
 | 
 | ||||||
|   RECORD_DESKTOP_RECT _rect; |  | ||||||
| 
 |  | ||||||
|   int _fps; |   int _fps; | ||||||
| 
 | 
 | ||||||
|   cb_desktop_data _on_data; |   cb_desktop_data _on_data; | ||||||
| @@ -77,12 +74,14 @@ class ScreenCapturerAvf : public ScreenCapturer { | |||||||
|   AVDictionary *options_ = nullptr; |   AVDictionary *options_ = nullptr; | ||||||
|   AVInputFormat *ifmt_ = nullptr; |   AVInputFormat *ifmt_ = nullptr; | ||||||
|   AVFrame *pFrame_ = nullptr; |   AVFrame *pFrame_ = nullptr; | ||||||
|   AVFrame *pFrameNv12_ = nullptr; |   AVFrame *pFrame_resized_ = nullptr; | ||||||
|   AVPacket *packet_ = nullptr; |   AVPacket *packet_ = nullptr; | ||||||
|   struct SwsContext *img_convert_ctx_ = nullptr; |   struct SwsContext *img_convert_ctx_ = nullptr; | ||||||
|  |   unsigned char *nv12_frame_ = nullptr; | ||||||
| 
 | 
 | ||||||
|   // thread
 |   // thread
 | ||||||
|   std::unique_ptr<std::thread> capture_thread_ = nullptr; |   std::thread capture_thread_; | ||||||
|  |   std::atomic_bool running_; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| #endif | #endif | ||||||
							
								
								
									
										104
									
								
								src/screen_capturer/macosx/core_graphics/screen_capturer_cg.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										104
									
								
								src/screen_capturer/macosx/core_graphics/screen_capturer_cg.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,104 @@ | |||||||
|  | #include <IOSurface/IOSurface.h> | ||||||
|  |  | ||||||
|  | #include <utility> | ||||||
|  |  | ||||||
|  | #include "rd_log.h" | ||||||
|  | #include "screen_capturer_cgd.h" | ||||||
|  |  | ||||||
|  | ScreenCapturerCg::ScreenCapturerCg() {} | ||||||
|  |  | ||||||
|  | ScreenCapturerCg::~ScreenCapturerCg() {} | ||||||
|  |  | ||||||
|  | int ScreenCapturerCg::Init(const int fps, cb_desktop_data cb) { | ||||||
|  |   if (cb) { | ||||||
|  |     _on_data = cb; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   size_t pixel_width = 1280; | ||||||
|  |   size_t pixel_height = 720; | ||||||
|  |   CGDirectDisplayID display_id = 0; | ||||||
|  |  | ||||||
|  |   CGDisplayStreamFrameAvailableHandler handler = | ||||||
|  |       ^(CGDisplayStreamFrameStatus status, uint64_t display_time, | ||||||
|  |         IOSurfaceRef frame_surface, CGDisplayStreamUpdateRef updateRef) { | ||||||
|  |         if (status == kCGDisplayStreamFrameStatusStopped) return; | ||||||
|  |         // Only pay attention to frame updates. | ||||||
|  |         if (status != kCGDisplayStreamFrameStatusFrameComplete) return; | ||||||
|  |  | ||||||
|  |         // size_t count = 0; | ||||||
|  |         // const CGRect* rects = CGDisplayStreamUpdateGetRects( | ||||||
|  |         //     updateRef, kCGDisplayStreamUpdateDirtyRects, &count); | ||||||
|  |  | ||||||
|  |         // 获取帧数据 | ||||||
|  |         void* frameData = IOSurfaceGetBaseAddressOfPlane(frame_surface, 0); | ||||||
|  |         size_t width = IOSurfaceGetWidthOfPlane(frame_surface, 0); | ||||||
|  |         size_t height = IOSurfaceGetHeightOfPlane(frame_surface, 0); | ||||||
|  |       }; | ||||||
|  |  | ||||||
|  |   CFDictionaryRef properties_dictionary = CFDictionaryCreate( | ||||||
|  |       kCFAllocatorDefault, (const void*[]){kCGDisplayStreamShowCursor}, | ||||||
|  |       (const void*[]){kCFBooleanFalse}, 1, &kCFTypeDictionaryKeyCallBacks, | ||||||
|  |       &kCFTypeDictionaryValueCallBacks); | ||||||
|  |  | ||||||
|  |   CGDisplayStreamRef display_stream = | ||||||
|  |       CGDisplayStreamCreate(display_id, pixel_width, pixel_height, 'BGRA', | ||||||
|  |                             properties_dictionary, handler); | ||||||
|  |  | ||||||
|  |   if (display_stream) { | ||||||
|  |     CGError error = CGDisplayStreamStart(display_stream); | ||||||
|  |     if (error != kCGErrorSuccess) return -1; | ||||||
|  |  | ||||||
|  |     CFRunLoopSourceRef source = CGDisplayStreamGetRunLoopSource(display_stream); | ||||||
|  |     CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopCommonModes); | ||||||
|  |     display_streams_.push_back(display_stream); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   CFRelease(properties_dictionary); | ||||||
|  |  | ||||||
|  |   return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int ScreenCapturerCg::Destroy() { | ||||||
|  |   running_ = false; | ||||||
|  |   return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int ScreenCapturerCg::Start() { | ||||||
|  |   if (_running) { | ||||||
|  |     return 0; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   running_ = true; | ||||||
|  |   capture_thread_ = std::thread([this]() { | ||||||
|  |     while (running_) { | ||||||
|  |       CFRunLoopRun(); | ||||||
|  |     } | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int ScreenCapturerCg::Stop() { | ||||||
|  |   running_ = false; | ||||||
|  |   return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int ScreenCapturerCg::Pause() { return 0; } | ||||||
|  |  | ||||||
|  | int ScreenCapturerCg::Resume() { return 0; } | ||||||
|  |  | ||||||
|  | void ScreenCapturerCg::OnFrame() {} | ||||||
|  |  | ||||||
|  | void ScreenCapturerCg::CleanUp() {} | ||||||
|  |  | ||||||
|  | // | ||||||
|  |  | ||||||
|  | void ScreenCapturerCg::UnregisterRefreshAndMoveHandlers() { | ||||||
|  |   for (CGDisplayStreamRef stream : display_streams_) { | ||||||
|  |     CFRunLoopSourceRef source = CGDisplayStreamGetRunLoopSource(stream); | ||||||
|  |     CFRunLoopRemoveSource(CFRunLoopGetCurrent(), source, kCFRunLoopCommonModes); | ||||||
|  |     CGDisplayStreamStop(stream); | ||||||
|  |     CFRelease(stream); | ||||||
|  |   } | ||||||
|  |   display_streams_.clear(); | ||||||
|  | } | ||||||
| @@ -0,0 +1,56 @@ | |||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * @Author: DI JUNKUN | ||||||
|  |  * @Date: 2024-10-16 | ||||||
|  |  * Copyright (c) 2024 by DI JUNKUN, All Rights Reserved. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #ifndef _SCREEN_CAPTURER_CGD_H_ | ||||||
|  | #define _SCREEN_CAPTURER_CGD_H_ | ||||||
|  |  | ||||||
|  | #include <CoreGraphics/CoreGraphics.h> | ||||||
|  |  | ||||||
|  | #include <atomic> | ||||||
|  | #include <functional> | ||||||
|  | #include <memory> | ||||||
|  | #include <string> | ||||||
|  | #include <thread> | ||||||
|  | #include <vector> | ||||||
|  |  | ||||||
|  | #include "screen_capturer.h" | ||||||
|  |  | ||||||
|  | class ScreenCapturerCg : public ScreenCapturer { | ||||||
|  |  public: | ||||||
|  |   ScreenCapturerCg(); | ||||||
|  |   ~ScreenCapturerCg(); | ||||||
|  |  | ||||||
|  |  public: | ||||||
|  |   virtual int Init(const int fps, cb_desktop_data cb); | ||||||
|  |  | ||||||
|  |   virtual int Destroy(); | ||||||
|  |  | ||||||
|  |   virtual int Start(); | ||||||
|  |  | ||||||
|  |   virtual int Stop(); | ||||||
|  |  | ||||||
|  |   int Pause(); | ||||||
|  |  | ||||||
|  |   int Resume(); | ||||||
|  |  | ||||||
|  |   void OnFrame(); | ||||||
|  |  | ||||||
|  |  protected: | ||||||
|  |   void CleanUp(); | ||||||
|  |  | ||||||
|  |  private: | ||||||
|  |   int _fps; | ||||||
|  |   cb_desktop_data _on_data; | ||||||
|  |  | ||||||
|  |   // thread | ||||||
|  |   std::thread capture_thread_; | ||||||
|  |   std::atomic_bool running_; | ||||||
|  |  | ||||||
|  |  private: | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | #endif | ||||||
| @@ -1,145 +0,0 @@ | |||||||
| #include "screen_capturer_avf.h" |  | ||||||
|  |  | ||||||
| #include <iostream> |  | ||||||
|  |  | ||||||
| #include "log.h" |  | ||||||
|  |  | ||||||
| #define NV12_BUFFER_SIZE 1280 * 720 * 3 / 2 |  | ||||||
| unsigned char nv12_buffer_[NV12_BUFFER_SIZE]; |  | ||||||
|  |  | ||||||
| ScreenCapturerAvf::ScreenCapturerAvf() {} |  | ||||||
|  |  | ||||||
| ScreenCapturerAvf::~ScreenCapturerAvf() { |  | ||||||
|   if (inited_ && capture_thread_->joinable()) { |  | ||||||
|     capture_thread_->join(); |  | ||||||
|     inited_ = false; |  | ||||||
|   } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int ScreenCapturerAvf::Init(const RECORD_DESKTOP_RECT &rect, const int fps, |  | ||||||
|                             cb_desktop_data cb) { |  | ||||||
|   if (cb) { |  | ||||||
|     _on_data = cb; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   av_log_set_level(AV_LOG_QUIET); |  | ||||||
|  |  | ||||||
|   pFormatCtx_ = avformat_alloc_context(); |  | ||||||
|  |  | ||||||
|   avdevice_register_all(); |  | ||||||
|  |  | ||||||
|   // grabbing frame rate |  | ||||||
|   av_dict_set(&options_, "framerate", "60", 0); |  | ||||||
|   av_dict_set(&options_, "pixel_format", "nv12", 0); |  | ||||||
|   // show remote cursor |  | ||||||
|   av_dict_set(&options_, "capture_cursor", "1", 0); |  | ||||||
|   // Make the grabbed area follow the mouse |  | ||||||
|   // av_dict_set(&options_, "follow_mouse", "centered", 0); |  | ||||||
|   // Video frame size. The default is to capture the full screen |  | ||||||
|   // av_dict_set(&options_, "video_size", "1280x720", 0); |  | ||||||
|   ifmt_ = (AVInputFormat *)av_find_input_format("avfoundation"); |  | ||||||
|   if (!ifmt_) { |  | ||||||
|     printf("Couldn't find_input_format\n"); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   // Grab at position 10,20 |  | ||||||
|   if (avformat_open_input(&pFormatCtx_, "Capture screen 0", ifmt_, &options_) != |  | ||||||
|       0) { |  | ||||||
|     printf("Couldn't open input stream.\n"); |  | ||||||
|     return -1; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   if (avformat_find_stream_info(pFormatCtx_, NULL) < 0) { |  | ||||||
|     printf("Couldn't find stream information.\n"); |  | ||||||
|     return -1; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   videoindex_ = -1; |  | ||||||
|   for (i_ = 0; i_ < pFormatCtx_->nb_streams; i_++) |  | ||||||
|     if (pFormatCtx_->streams[i_]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { |  | ||||||
|       videoindex_ = i_; |  | ||||||
|       break; |  | ||||||
|     } |  | ||||||
|   if (videoindex_ == -1) { |  | ||||||
|     printf("Didn't find a video stream.\n"); |  | ||||||
|     return -1; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   pCodecParam_ = pFormatCtx_->streams[videoindex_]->codecpar; |  | ||||||
|  |  | ||||||
|   pCodecCtx_ = avcodec_alloc_context3(NULL); |  | ||||||
|   avcodec_parameters_to_context(pCodecCtx_, pCodecParam_); |  | ||||||
|  |  | ||||||
|   pCodec_ = const_cast<AVCodec *>(avcodec_find_decoder(pCodecCtx_->codec_id)); |  | ||||||
|   if (pCodec_ == NULL) { |  | ||||||
|     printf("Codec not found.\n"); |  | ||||||
|     return -1; |  | ||||||
|   } |  | ||||||
|   if (avcodec_open2(pCodecCtx_, pCodec_, NULL) < 0) { |  | ||||||
|     printf("Could not open codec.\n"); |  | ||||||
|     return -1; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   const int screen_w = pFormatCtx_->streams[videoindex_]->codecpar->width; |  | ||||||
|   const int screen_h = pFormatCtx_->streams[videoindex_]->codecpar->height; |  | ||||||
|  |  | ||||||
|   pFrame_ = av_frame_alloc(); |  | ||||||
|   pFrameNv12_ = av_frame_alloc(); |  | ||||||
|  |  | ||||||
|   pFrame_->width = screen_w; |  | ||||||
|   pFrame_->height = screen_h; |  | ||||||
|   pFrameNv12_->width = 1280; |  | ||||||
|   pFrameNv12_->height = 720; |  | ||||||
|  |  | ||||||
|   packet_ = (AVPacket *)av_malloc(sizeof(AVPacket)); |  | ||||||
|  |  | ||||||
|   img_convert_ctx_ = sws_getContext( |  | ||||||
|       pFrame_->width, pFrame_->height, pCodecCtx_->pix_fmt, pFrameNv12_->width, |  | ||||||
|       pFrameNv12_->height, AV_PIX_FMT_NV12, SWS_BICUBIC, NULL, NULL, NULL); |  | ||||||
|  |  | ||||||
|   inited_ = true; |  | ||||||
|  |  | ||||||
|   return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int ScreenCapturerAvf::Destroy() { return 0; } |  | ||||||
|  |  | ||||||
| int ScreenCapturerAvf::Start() { |  | ||||||
|   capture_thread_.reset(new std::thread([this]() { |  | ||||||
|     while (1) { |  | ||||||
|       if (av_read_frame(pFormatCtx_, packet_) >= 0) { |  | ||||||
|         if (packet_->stream_index == videoindex_) { |  | ||||||
|           avcodec_send_packet(pCodecCtx_, packet_); |  | ||||||
|           av_packet_unref(packet_); |  | ||||||
|           got_picture_ = avcodec_receive_frame(pCodecCtx_, pFrame_); |  | ||||||
|  |  | ||||||
|           if (!got_picture_) { |  | ||||||
|             av_image_fill_arrays(pFrameNv12_->data, pFrameNv12_->linesize, |  | ||||||
|                                  nv12_buffer_, AV_PIX_FMT_NV12, |  | ||||||
|                                  pFrameNv12_->width, pFrameNv12_->height, 1); |  | ||||||
|  |  | ||||||
|             sws_scale(img_convert_ctx_, pFrame_->data, pFrame_->linesize, 0, |  | ||||||
|                       pFrame_->height, pFrameNv12_->data, |  | ||||||
|                       pFrameNv12_->linesize); |  | ||||||
|  |  | ||||||
|             _on_data((unsigned char *)nv12_buffer_, |  | ||||||
|                      pFrameNv12_->width * pFrameNv12_->height * 3 / 2, |  | ||||||
|                      pFrameNv12_->width, pFrameNv12_->height); |  | ||||||
|           } |  | ||||||
|         } |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|   })); |  | ||||||
|  |  | ||||||
|   return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int ScreenCapturerAvf::Stop() { return 0; } |  | ||||||
|  |  | ||||||
| int ScreenCapturerAvf::Pause() { return 0; } |  | ||||||
|  |  | ||||||
| int ScreenCapturerAvf::Resume() { return 0; } |  | ||||||
|  |  | ||||||
| void ScreenCapturerAvf::OnFrame() {} |  | ||||||
|  |  | ||||||
| void ScreenCapturerAvf::CleanUp() {} |  | ||||||
| @@ -0,0 +1,51 @@ | |||||||
|  | #include "screen_capturer_sck.h" | ||||||
|  |  | ||||||
|  | #include "rd_log.h" | ||||||
|  |  | ||||||
|  | ScreenCapturerSck::ScreenCapturerSck() {} | ||||||
|  | ScreenCapturerSck::~ScreenCapturerSck() { | ||||||
|  |   // if (inited_ && capture_thread_.joinable()) { | ||||||
|  |   //   capture_thread_.join(); | ||||||
|  |   //   inited_ = false; | ||||||
|  |   // } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int ScreenCapturerSck::Init(const int fps, cb_desktop_data cb) { | ||||||
|  |   if (cb) { | ||||||
|  |     on_data_ = cb; | ||||||
|  |   } else { | ||||||
|  |     LOG_ERROR("cb is null"); | ||||||
|  |     return -1; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   screen_capturer_sck_impl_ = CreateScreenCapturerSck(); | ||||||
|  |   screen_capturer_sck_impl_->Init(fps, on_data_); | ||||||
|  |  | ||||||
|  |   return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int ScreenCapturerSck::Destroy() { return 0; } | ||||||
|  |  | ||||||
|  | int ScreenCapturerSck::Start() { | ||||||
|  |   // if (running_) { | ||||||
|  |   //   return 0; | ||||||
|  |   // } | ||||||
|  |  | ||||||
|  |   // running_ = true; | ||||||
|  |   // capture_thread_ = std::thread([this]() { | ||||||
|  |   //   while (running_) { | ||||||
|  |   //   } | ||||||
|  |   // }); | ||||||
|  |  | ||||||
|  |   return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int ScreenCapturerSck::Stop() { return 0; } | ||||||
|  |  | ||||||
|  | int ScreenCapturerSck::Pause() { return 0; } | ||||||
|  |  | ||||||
|  | int ScreenCapturerSck::Resume() { return 0; } | ||||||
|  |  | ||||||
|  | void ScreenCapturerSck::OnFrame() {} | ||||||
|  |  | ||||||
|  | void ScreenCapturerSck::CleanUp() {} | ||||||
| @@ -0,0 +1,59 @@ | |||||||
|  | /* | ||||||
|  |  * @Author: DI JUNKUN | ||||||
|  |  * @Date: 2024-10-17 | ||||||
|  |  * Copyright (c) 2024 by DI JUNKUN, All Rights Reserved. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #ifndef _SCREEN_CAPTURER_SCK_H_ | ||||||
|  | #define _SCREEN_CAPTURER_SCK_H_ | ||||||
|  |  | ||||||
|  | #include <atomic> | ||||||
|  | #include <functional> | ||||||
|  | #include <memory> | ||||||
|  | #include <string> | ||||||
|  | #include <thread> | ||||||
|  | #include <vector> | ||||||
|  |  | ||||||
|  | #include "screen_capturer.h" | ||||||
|  |  | ||||||
|  | class ScreenCapturerSck : public ScreenCapturer { | ||||||
|  |  public: | ||||||
|  |   ScreenCapturerSck(); | ||||||
|  |   ~ScreenCapturerSck(); | ||||||
|  |  | ||||||
|  |  public: | ||||||
|  |   virtual int Init(const int fps, cb_desktop_data cb); | ||||||
|  |  | ||||||
|  |   virtual int Destroy(); | ||||||
|  |  | ||||||
|  |   virtual int Start(); | ||||||
|  |  | ||||||
|  |   virtual int Stop(); | ||||||
|  |  | ||||||
|  |   int Pause(); | ||||||
|  |  | ||||||
|  |   int Resume(); | ||||||
|  |  | ||||||
|  |   void OnFrame(); | ||||||
|  |  | ||||||
|  |  protected: | ||||||
|  |   void CleanUp(); | ||||||
|  |  | ||||||
|  |  private: | ||||||
|  |   std::unique_ptr<ScreenCapturer> CreateScreenCapturerSck(); | ||||||
|  |  | ||||||
|  |  private: | ||||||
|  |   int _fps; | ||||||
|  |   cb_desktop_data on_data_; | ||||||
|  |   unsigned char* nv12_frame_ = nullptr; | ||||||
|  |   bool inited_ = false; | ||||||
|  |  | ||||||
|  |   // thread | ||||||
|  |   std::thread capture_thread_; | ||||||
|  |   std::atomic_bool running_; | ||||||
|  |  | ||||||
|  |  private: | ||||||
|  |   std::unique_ptr<ScreenCapturer> screen_capturer_sck_impl_; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | #endif | ||||||
| @@ -0,0 +1,256 @@ | |||||||
|  | /* | ||||||
|  |  *  Copyright (c) 2024 The WebRTC project authors. All Rights Reserved. | ||||||
|  |  * | ||||||
|  |  *  Use of this source code is governed by a BSD-style license | ||||||
|  |  *  that can be found in the LICENSE file in the root of the source | ||||||
|  |  *  tree. An additional intellectual property rights grant can be found | ||||||
|  |  *  in the file PATENTS.  All contributing project authors may | ||||||
|  |  *  be found in the AUTHORS file in the root of the source tree. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #include "screen_capturer_sck.h" | ||||||
|  |  | ||||||
|  | #include "rd_log.h" | ||||||
|  | #include <CoreGraphics/CoreGraphics.h> | ||||||
|  | #include <IOSurface/IOSurface.h> | ||||||
|  | #include <ScreenCaptureKit/ScreenCaptureKit.h> | ||||||
|  | #include <atomic> | ||||||
|  | #include <mutex> | ||||||
|  |  | ||||||
|  | class ScreenCapturerSckImpl; | ||||||
|  |  | ||||||
|  | // The ScreenCaptureKit API was available in macOS 12.3, but full-screen capture | ||||||
|  | // was reported to be broken before macOS 13 - see http://crbug.com/40234870. | ||||||
|  | // Also, the `SCContentFilter` fields `contentRect` and `pointPixelScale` were | ||||||
|  | // introduced in macOS 14. | ||||||
|  | API_AVAILABLE(macos(14.0)) | ||||||
|  | @interface SckHelper : NSObject <SCStreamDelegate, SCStreamOutput> | ||||||
|  |  | ||||||
|  | - (instancetype)initWithCapturer:(ScreenCapturerSckImpl *)capturer; | ||||||
|  |  | ||||||
|  | - (void)onShareableContentCreated:(SCShareableContent *)content; | ||||||
|  |  | ||||||
|  | // Called just before the capturer is destroyed. This avoids a dangling pointer, | ||||||
|  | // and prevents any new calls into a deleted capturer. If any method-call on the | ||||||
|  | // capturer is currently running on a different thread, this blocks until it | ||||||
|  | // completes. | ||||||
|  | - (void)releaseCapturer; | ||||||
|  | @end | ||||||
|  |  | ||||||
|  | class API_AVAILABLE(macos(14.0)) ScreenCapturerSckImpl : public ScreenCapturer { | ||||||
|  | public: | ||||||
|  |   explicit ScreenCapturerSckImpl(); | ||||||
|  |  | ||||||
|  |   ScreenCapturerSckImpl(const ScreenCapturerSckImpl &) = delete; | ||||||
|  |   ScreenCapturerSckImpl &operator=(const ScreenCapturerSckImpl &) = delete; | ||||||
|  |   ~ScreenCapturerSckImpl(); | ||||||
|  |  | ||||||
|  | public: | ||||||
|  |   int Init(const int fps, cb_desktop_data cb); | ||||||
|  |   void OnReceiveContent(SCShareableContent *content); | ||||||
|  |   void OnNewIOSurface(IOSurfaceRef io_surface, CFDictionaryRef attachment); | ||||||
|  |  | ||||||
|  |   virtual int Destroy() { return 0; } | ||||||
|  |  | ||||||
|  |   virtual int Start() { return 0; } | ||||||
|  |  | ||||||
|  |   virtual int Stop() { return 0; } | ||||||
|  |  | ||||||
|  | private: | ||||||
|  |   SckHelper *__strong helper_; | ||||||
|  |   SCStream *__strong stream_; | ||||||
|  |  | ||||||
|  |   cb_desktop_data _on_data; | ||||||
|  |   unsigned char *nv12_frame_ = nullptr; | ||||||
|  |   bool permanent_error_ = false; | ||||||
|  |   CGDirectDisplayID current_display_ = -1; | ||||||
|  |   std::mutex mtx_; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | @implementation SckHelper { | ||||||
|  |   // This lock is to prevent the capturer being destroyed while an instance | ||||||
|  |   // method is still running on another thread. | ||||||
|  |   std::mutex helper_mtx_; | ||||||
|  |   ScreenCapturerSckImpl *_capturer; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | - (instancetype)initWithCapturer:(ScreenCapturerSckImpl *)capturer { | ||||||
|  |   self = [super init]; | ||||||
|  |   if (self) { | ||||||
|  |     _capturer = capturer; | ||||||
|  |   } | ||||||
|  |   return self; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | - (void)onShareableContentCreated:(SCShareableContent *)content { | ||||||
|  |   std::lock_guard<std::mutex> lock(helper_mtx_); | ||||||
|  |   if (_capturer) { | ||||||
|  |     _capturer->OnReceiveContent(content); | ||||||
|  |   } else { | ||||||
|  |     LOG_ERROR("Invalid capturer"); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | - (void)stream:(SCStream *)stream | ||||||
|  |     didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer | ||||||
|  |                    ofType:(SCStreamOutputType)type { | ||||||
|  |   CVPixelBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer); | ||||||
|  |   if (!pixelBuffer) { | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   IOSurfaceRef ioSurface = CVPixelBufferGetIOSurface(pixelBuffer); | ||||||
|  |   if (!ioSurface) { | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   CFArrayRef attachmentsArray = CMSampleBufferGetSampleAttachmentsArray( | ||||||
|  |       sampleBuffer, /*createIfNecessary=*/false); | ||||||
|  |   if (!attachmentsArray || CFArrayGetCount(attachmentsArray) <= 0) { | ||||||
|  |     LOG_ERROR("Discarding frame with no attachments"); | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   CFDictionaryRef attachment = | ||||||
|  |       static_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(attachmentsArray, 0)); | ||||||
|  |  | ||||||
|  |   std::lock_guard<std::mutex> lock(helper_mtx_); | ||||||
|  |   if (_capturer) { | ||||||
|  |     _capturer->OnNewIOSurface(ioSurface, attachment); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | - (void)releaseCapturer { | ||||||
|  |   std::lock_guard<std::mutex> lock(helper_mtx_); | ||||||
|  |   _capturer = nullptr; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @end | ||||||
|  |  | ||||||
|  | ScreenCapturerSckImpl::ScreenCapturerSckImpl() { | ||||||
|  |   helper_ = [[SckHelper alloc] initWithCapturer:this]; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | ScreenCapturerSckImpl::~ScreenCapturerSckImpl() { | ||||||
|  |   [stream_ stopCaptureWithCompletionHandler:nil]; | ||||||
|  |   [helper_ releaseCapturer]; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int ScreenCapturerSckImpl::Init(const int fps, cb_desktop_data cb) { | ||||||
|  |   _on_data = cb; | ||||||
|  |  | ||||||
|  |   SckHelper *local_helper = helper_; | ||||||
|  |   auto handler = ^(SCShareableContent *content, NSError *error) { | ||||||
|  |     [local_helper onShareableContentCreated:content]; | ||||||
|  |   }; | ||||||
|  |  | ||||||
|  |   [SCShareableContent getShareableContentWithCompletionHandler:handler]; | ||||||
|  |  | ||||||
|  |   return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void ScreenCapturerSckImpl::OnReceiveContent(SCShareableContent *content) { | ||||||
|  |   if (!content) { | ||||||
|  |     LOG_ERROR("getShareableContent failed"); | ||||||
|  |     permanent_error_ = true; | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   if (!content.displays.count) { | ||||||
|  |     LOG_ERROR("getShareableContent returned no displays"); | ||||||
|  |     permanent_error_ = true; | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   SCDisplay *captured_display; | ||||||
|  |   { | ||||||
|  |     std::lock_guard<std::mutex> lock(mtx_); | ||||||
|  |     for (SCDisplay *display in content.displays) { | ||||||
|  |       if (current_display_ == display.displayID) { | ||||||
|  |         captured_display = display; | ||||||
|  |         break; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     if (!captured_display) { | ||||||
|  |       if (-1 == current_display_) { | ||||||
|  |         LOG_ERROR("Full screen capture is not supported, falling back to first " | ||||||
|  |                   "display"); | ||||||
|  |       } else { | ||||||
|  |         LOG_ERROR("Display [{}] not found, falling back to first display", | ||||||
|  |                   current_display_); | ||||||
|  |       } | ||||||
|  |       captured_display = content.displays.firstObject; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   SCContentFilter *filter = | ||||||
|  |       [[SCContentFilter alloc] initWithDisplay:captured_display | ||||||
|  |                               excludingWindows:@[]]; | ||||||
|  |   SCStreamConfiguration *config = [[SCStreamConfiguration alloc] init]; | ||||||
|  |   config.pixelFormat = kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange; | ||||||
|  |   config.showsCursor = false; | ||||||
|  |   config.width = filter.contentRect.size.width * filter.pointPixelScale; | ||||||
|  |   config.height = filter.contentRect.size.height * filter.pointPixelScale; | ||||||
|  |   config.captureResolution = SCCaptureResolutionNominal; | ||||||
|  |  | ||||||
|  |   std::lock_guard<std::mutex> lock(mtx_); | ||||||
|  |  | ||||||
|  |   if (stream_) { | ||||||
|  |     LOG_INFO("Updating stream configuration"); | ||||||
|  |     [stream_ updateContentFilter:filter completionHandler:nil]; | ||||||
|  |     [stream_ updateConfiguration:config completionHandler:nil]; | ||||||
|  |   } else { | ||||||
|  |     stream_ = [[SCStream alloc] initWithFilter:filter | ||||||
|  |                                  configuration:config | ||||||
|  |                                       delegate:helper_]; | ||||||
|  |  | ||||||
|  |     // TODO: crbug.com/327458809 - Choose an appropriate sampleHandlerQueue for | ||||||
|  |     // best performance. | ||||||
|  |     NSError *add_stream_output_error; | ||||||
|  |     bool add_stream_output_result = | ||||||
|  |         [stream_ addStreamOutput:helper_ | ||||||
|  |                             type:SCStreamOutputTypeScreen | ||||||
|  |               sampleHandlerQueue:nil | ||||||
|  |                            error:&add_stream_output_error]; | ||||||
|  |     if (!add_stream_output_result) { | ||||||
|  |       stream_ = nil; | ||||||
|  |       LOG_ERROR("addStreamOutput failed"); | ||||||
|  |       permanent_error_ = true; | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     auto handler = ^(NSError *error) { | ||||||
|  |       if (error) { | ||||||
|  |         // It should be safe to access `this` here, because the C++ destructor | ||||||
|  |         // calls stopCaptureWithCompletionHandler on the stream, which cancels | ||||||
|  |         // this handler. | ||||||
|  |         permanent_error_ = true; | ||||||
|  |         LOG_ERROR("startCaptureWithCompletionHandler failed"); | ||||||
|  |       } else { | ||||||
|  |         LOG_INFO("Capture started"); | ||||||
|  |       } | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     [stream_ startCaptureWithCompletionHandler:handler]; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void ScreenCapturerSckImpl::OnNewIOSurface(IOSurfaceRef io_surface, | ||||||
|  |                                            CFDictionaryRef attachment) { | ||||||
|  |   size_t width = IOSurfaceGetWidth(io_surface); | ||||||
|  |   size_t height = IOSurfaceGetHeight(io_surface); | ||||||
|  |  | ||||||
|  |   uint32_t aseed; | ||||||
|  |   IOSurfaceLock(io_surface, kIOSurfaceLockReadOnly, &aseed); | ||||||
|  |  | ||||||
|  |   nv12_frame_ = | ||||||
|  |       static_cast<unsigned char *>(IOSurfaceGetBaseAddress(io_surface)); | ||||||
|  |  | ||||||
|  |   _on_data(nv12_frame_, width * height * 3 / 2, width, height); | ||||||
|  |  | ||||||
|  |   IOSurfaceUnlock(io_surface, kIOSurfaceLockReadOnly, &aseed); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | std::unique_ptr<ScreenCapturer> ScreenCapturerSck::CreateScreenCapturerSck() { | ||||||
|  |   return std::make_unique<ScreenCapturerSckImpl>(); | ||||||
|  | } | ||||||
| @@ -11,20 +11,13 @@ | |||||||
|  |  | ||||||
| class ScreenCapturer { | class ScreenCapturer { | ||||||
|  public: |  public: | ||||||
|   typedef struct { |  | ||||||
|     int left; |  | ||||||
|     int top; |  | ||||||
|     int right; |  | ||||||
|     int bottom; |  | ||||||
|   } RECORD_DESKTOP_RECT; |  | ||||||
|   typedef std::function<void(unsigned char *, int, int, int)> cb_desktop_data; |   typedef std::function<void(unsigned char *, int, int, int)> cb_desktop_data; | ||||||
|  |  | ||||||
|  public: |  public: | ||||||
|   virtual ~ScreenCapturer() {} |   virtual ~ScreenCapturer() {} | ||||||
|  |  | ||||||
|  public: |  public: | ||||||
|   virtual int Init(const RECORD_DESKTOP_RECT &rect, const int fps, |   virtual int Init(const int fps, cb_desktop_data cb) = 0; | ||||||
|                    cb_desktop_data cb) = 0; |  | ||||||
|   virtual int Destroy() = 0; |   virtual int Destroy() = 0; | ||||||
|  |  | ||||||
|   virtual int Start() = 0; |   virtual int Start() = 0; | ||||||
|   | |||||||
| @@ -8,12 +8,12 @@ | |||||||
| #define _SCREEN_CAPTURER_FACTORY_H_ | #define _SCREEN_CAPTURER_FACTORY_H_ | ||||||
|  |  | ||||||
| #ifdef _WIN32 | #ifdef _WIN32 | ||||||
|  |  | ||||||
| #include "screen_capturer_wgc.h" | #include "screen_capturer_wgc.h" | ||||||
| #elif __linux__ | #elif __linux__ | ||||||
| #include "screen_capturer_x11.h" | #include "screen_capturer_x11.h" | ||||||
| #elif __APPLE__ | #elif __APPLE__ | ||||||
| #include "screen_capturer_avf.h" | // #include "screen_capturer_avf.h" | ||||||
|  | #include "screen_capturer_sck.h" | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| class ScreenCapturerFactory { | class ScreenCapturerFactory { | ||||||
| @@ -27,7 +27,8 @@ class ScreenCapturerFactory { | |||||||
| #elif __linux__ | #elif __linux__ | ||||||
|     return new ScreenCapturerX11(); |     return new ScreenCapturerX11(); | ||||||
| #elif __APPLE__ | #elif __APPLE__ | ||||||
|     return new ScreenCapturerAvf(); |     // return new ScreenCapturerAvf(); | ||||||
|  |     return new ScreenCapturerSck(); | ||||||
| #else | #else | ||||||
|     return nullptr; |     return nullptr; | ||||||
| #endif | #endif | ||||||
|   | |||||||
| @@ -62,16 +62,12 @@ bool ScreenCapturerWgc::IsWgcSupported() { | |||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| int ScreenCapturerWgc::Init(const RECORD_DESKTOP_RECT &rect, const int fps, | int ScreenCapturerWgc::Init(const int fps, cb_desktop_data cb) { | ||||||
|                             cb_desktop_data cb) { |  | ||||||
|   int error = 0; |   int error = 0; | ||||||
|   if (_inited == true) return error; |   if (_inited == true) return error; | ||||||
|  |  | ||||||
|   int r = rect.right; |   // nv12_frame_ = new unsigned char[rect.right * rect.bottom * 3 / 2]; | ||||||
|   int b = rect.bottom; |   // nv12_frame_scaled_ = new unsigned char[1280 * 720 * 3 / 2]; | ||||||
|  |  | ||||||
|   nv12_frame_ = new unsigned char[rect.right * rect.bottom * 3 / 2]; |  | ||||||
|   nv12_frame_scaled_ = new unsigned char[1280 * 720 * 3 / 2]; |  | ||||||
|  |  | ||||||
|   _fps = fps; |   _fps = fps; | ||||||
|  |  | ||||||
| @@ -147,14 +143,12 @@ void ConvertABGRtoBGRA(const uint8_t *abgr_data, uint8_t *bgra_data, int width, | |||||||
|                        int height, int abgr_stride, int bgra_stride) { |                        int height, int abgr_stride, int bgra_stride) { | ||||||
|   for (int i = 0; i < height; ++i) { |   for (int i = 0; i < height; ++i) { | ||||||
|     for (int j = 0; j < width; ++j) { |     for (int j = 0; j < width; ++j) { | ||||||
|       // ABGR<47><52>BGRA<52><41><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ӳ<EFBFBD><D3B3> |  | ||||||
|       int abgr_index = (i * abgr_stride + j) * 4; |       int abgr_index = (i * abgr_stride + j) * 4; | ||||||
|       int bgra_index = (i * bgra_stride + j) * 4; |       int bgra_index = (i * bgra_stride + j) * 4; | ||||||
|  |  | ||||||
|       // ֱ<>ӽ<EFBFBD><D3BD><EFBFBD><EFBFBD><EFBFBD>ɫ<EFBFBD>ͺ<EFBFBD>ɫ<EFBFBD><C9AB><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ͬʱ<CDAC><CAB1><EFBFBD><EFBFBD>Alphaͨ<61><CDA8><EFBFBD><EFBFBD><EFBFBD><EFBFBD> |       bgra_data[bgra_index + 0] = abgr_data[abgr_index + 2];  // 蓝色 | ||||||
|       bgra_data[bgra_index + 0] = abgr_data[abgr_index + 2];  // <EFBFBD><EFBFBD>ɫ |       bgra_data[bgra_index + 1] = abgr_data[abgr_index + 1];  // 绿色 | ||||||
|       bgra_data[bgra_index + 1] = abgr_data[abgr_index + 1];  // <EFBFBD><EFBFBD>ɫ |       bgra_data[bgra_index + 2] = abgr_data[abgr_index + 0];  // 红色 | ||||||
|       bgra_data[bgra_index + 2] = abgr_data[abgr_index + 0];  // <20><>ɫ |  | ||||||
|       bgra_data[bgra_index + 3] = abgr_data[abgr_index + 3];  // Alpha |       bgra_data[bgra_index + 3] = abgr_data[abgr_index + 3];  // Alpha | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| @@ -164,11 +158,9 @@ void ConvertBGRAtoABGR(const uint8_t *bgra_data, uint8_t *abgr_data, int width, | |||||||
|                        int height, int bgra_stride, int abgr_stride) { |                        int height, int bgra_stride, int abgr_stride) { | ||||||
|   for (int i = 0; i < height; ++i) { |   for (int i = 0; i < height; ++i) { | ||||||
|     for (int j = 0; j < width; ++j) { |     for (int j = 0; j < width; ++j) { | ||||||
|       // BGRA<52><41>ABGR<47><52><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ӳ<EFBFBD><D3B3> |  | ||||||
|       int bgra_index = (i * bgra_stride + j) * 4; |       int bgra_index = (i * bgra_stride + j) * 4; | ||||||
|       int abgr_index = (i * abgr_stride + j) * 4; |       int abgr_index = (i * abgr_stride + j) * 4; | ||||||
|  |  | ||||||
|       // <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ɫ<EFBFBD><C9AB><EFBFBD><EFBFBD>ɫ<EFBFBD><C9AB><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ͬʱ<CDAC><CAB1><EFBFBD><EFBFBD>Alphaͨ<61><CDA8><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ǰ<EFBFBD><C7B0> |  | ||||||
|       abgr_data[abgr_index + 0] = bgra_data[bgra_index + 3];  // Alpha |       abgr_data[abgr_index + 0] = bgra_data[bgra_index + 3];  // Alpha | ||||||
|       abgr_data[abgr_index + 1] = bgra_data[bgra_index + 0];  // Blue |       abgr_data[abgr_index + 1] = bgra_data[bgra_index + 0];  // Blue | ||||||
|       abgr_data[abgr_index + 2] = bgra_data[bgra_index + 1];  // Green |       abgr_data[abgr_index + 2] = bgra_data[bgra_index + 1];  // Green | ||||||
| @@ -179,22 +171,20 @@ void ConvertBGRAtoABGR(const uint8_t *bgra_data, uint8_t *abgr_data, int width, | |||||||
|  |  | ||||||
| void ScreenCapturerWgc::OnFrame(const WgcSession::wgc_session_frame &frame) { | void ScreenCapturerWgc::OnFrame(const WgcSession::wgc_session_frame &frame) { | ||||||
|   if (_on_data) { |   if (_on_data) { | ||||||
|     int width = 1280; |     // int width = 1280; | ||||||
|     int height = 720; |     // int height = 720; | ||||||
|  |  | ||||||
|  |     if (!nv12_frame_) { | ||||||
|  |       nv12_frame_ = new unsigned char[frame.width * frame.height * 3 / 2]; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     libyuv::ARGBToNV12((const uint8_t *)frame.data, frame.width * 4, |     libyuv::ARGBToNV12((const uint8_t *)frame.data, frame.width * 4, | ||||||
|                        (uint8_t *)nv12_frame_, frame.width, |                        (uint8_t *)nv12_frame_, frame.width, | ||||||
|                        (uint8_t *)(nv12_frame_ + frame.width * frame.height), |                        (uint8_t *)(nv12_frame_ + frame.width * frame.height), | ||||||
|                        frame.width, frame.width, frame.height); |                        frame.width, frame.width, frame.height); | ||||||
|  |  | ||||||
|     libyuv::NV12Scale( |     _on_data(nv12_frame_, frame.width * frame.height * 3 / 2, frame.width, | ||||||
|         (const uint8_t *)nv12_frame_, frame.width, |              frame.height); | ||||||
|         (const uint8_t *)(nv12_frame_ + frame.width * frame.height), |  | ||||||
|         frame.width, frame.width, frame.height, (uint8_t *)nv12_frame_scaled_, |  | ||||||
|         width, (uint8_t *)(nv12_frame_scaled_ + width * height), width, width, |  | ||||||
|         height, libyuv::FilterMode::kFilterLinear); |  | ||||||
|  |  | ||||||
|     _on_data(nv12_frame_scaled_, width * height * 3 / 2, width, height); |  | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -19,8 +19,7 @@ class ScreenCapturerWgc : public ScreenCapturer, | |||||||
|  public: |  public: | ||||||
|   bool IsWgcSupported(); |   bool IsWgcSupported(); | ||||||
|  |  | ||||||
|   virtual int Init(const RECORD_DESKTOP_RECT &rect, const int fps, |   virtual int Init(const int fps, cb_desktop_data cb); | ||||||
|                    cb_desktop_data cb); |  | ||||||
|   virtual int Destroy(); |   virtual int Destroy(); | ||||||
|  |  | ||||||
|   virtual int Start(); |   virtual int Start(); | ||||||
| @@ -45,8 +44,6 @@ class ScreenCapturerWgc : public ScreenCapturer, | |||||||
|  |  | ||||||
|   std::string _device_name; |   std::string _device_name; | ||||||
|  |  | ||||||
|   RECORD_DESKTOP_RECT _rect; |  | ||||||
|  |  | ||||||
|   int _fps; |   int _fps; | ||||||
|  |  | ||||||
|   cb_desktop_data _on_data = nullptr; |   cb_desktop_data _on_data = nullptr; | ||||||
|   | |||||||
| @@ -89,6 +89,8 @@ int WgcSessionImpl::Start() { | |||||||
|  |  | ||||||
|     capture_session_.StartCapture(); |     capture_session_.StartCapture(); | ||||||
|  |  | ||||||
|  |     capture_session_.IsCursorCaptureEnabled(false); | ||||||
|  |  | ||||||
|     error = 0; |     error = 0; | ||||||
|   } catch (winrt::hresult_error) { |   } catch (winrt::hresult_error) { | ||||||
|     std::cout << "AE_WGC_CREATE_CAPTURER_FAILED" << std::endl; |     std::cout << "AE_WGC_CREATE_CAPTURER_FAILED" << std::endl; | ||||||
|   | |||||||
							
								
								
									
										1408
									
								
								src/single_window/IconsFontAwesome6.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1408
									
								
								src/single_window/IconsFontAwesome6.h
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										56
									
								
								src/single_window/about_window.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								src/single_window/about_window.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,56 @@ | |||||||
|  | #include "IconsFontAwesome6.h" | ||||||
|  | #include "layout_style.h" | ||||||
|  | #include "localization.h" | ||||||
|  | #include "rd_log.h" | ||||||
|  | #include "render.h" | ||||||
|  |  | ||||||
|  | int Render::AboutWindow() { | ||||||
|  |   if (show_about_window_) { | ||||||
|  |     const ImGuiViewport *viewport = ImGui::GetMainViewport(); | ||||||
|  |  | ||||||
|  |     ImGui::SetNextWindowPos(ImVec2( | ||||||
|  |         (viewport->WorkSize.x - viewport->WorkPos.x - about_window_width_) / 2, | ||||||
|  |         (viewport->WorkSize.y - viewport->WorkPos.y - about_window_height_) / | ||||||
|  |             2)); | ||||||
|  |  | ||||||
|  |     ImGui::SetNextWindowSize(ImVec2(about_window_width_, about_window_height_)); | ||||||
|  |  | ||||||
|  |     ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(1.0f, 1.0f, 1.0f, 1.0f)); | ||||||
|  |     ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 1.0f); | ||||||
|  |     ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 5.0f); | ||||||
|  |     ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 3.0f); | ||||||
|  |     ImGui::SetWindowFontScale(0.5f); | ||||||
|  |     ImGui::Begin( | ||||||
|  |         localization::about[localization_language_index_].c_str(), nullptr, | ||||||
|  |         ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse | | ||||||
|  |             ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoSavedSettings); | ||||||
|  |     ImGui::SetWindowFontScale(1.0f); | ||||||
|  |     ImGui::SetWindowFontScale(0.5f); | ||||||
|  |  | ||||||
|  |     std::string version; | ||||||
|  | #ifdef RD_VERSION | ||||||
|  |     version = RD_VERSION; | ||||||
|  | #else | ||||||
|  |     version = "Unknown"; | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  |     std::string text = | ||||||
|  |         localization::version[localization_language_index_] + ": " + version; | ||||||
|  |     ImGui::Text("%s", text.c_str()); | ||||||
|  |  | ||||||
|  |     ImGui::SetCursorPosX(about_window_width_ * 0.42f); | ||||||
|  |     ImGui::SetCursorPosY(about_window_height_ * 0.75f); | ||||||
|  |     // OK | ||||||
|  |     if (ImGui::Button(localization::ok[localization_language_index_].c_str())) { | ||||||
|  |       show_about_window_ = false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     ImGui::SetWindowFontScale(1.0f); | ||||||
|  |     ImGui::SetWindowFontScale(0.5f); | ||||||
|  |     ImGui::End(); | ||||||
|  |     ImGui::SetWindowFontScale(1.0f); | ||||||
|  |     ImGui::PopStyleVar(3); | ||||||
|  |     ImGui::PopStyleColor(); | ||||||
|  |   } | ||||||
|  |   return 0; | ||||||
|  | } | ||||||
							
								
								
									
										151
									
								
								src/single_window/connection_status_window.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										151
									
								
								src/single_window/connection_status_window.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,151 @@ | |||||||
|  | #include "layout_style.h" | ||||||
|  | #include "localization.h" | ||||||
|  | #include "rd_log.h" | ||||||
|  | #include "render.h" | ||||||
|  |  | ||||||
|  | int Render::ConnectionStatusWindow() { | ||||||
|  |   if (show_connection_status_window_) { | ||||||
|  |     const ImGuiViewport *viewport = ImGui::GetMainViewport(); | ||||||
|  |  | ||||||
|  |     ImGui::SetNextWindowPos(ImVec2((viewport->WorkSize.x - viewport->WorkPos.x - | ||||||
|  |                                     connection_status_window_width_) / | ||||||
|  |                                        2, | ||||||
|  |                                    (viewport->WorkSize.y - viewport->WorkPos.y - | ||||||
|  |                                     connection_status_window_height_) / | ||||||
|  |                                        2)); | ||||||
|  |  | ||||||
|  |     ImGui::SetNextWindowSize(ImVec2(connection_status_window_width_, | ||||||
|  |                                     connection_status_window_height_)); | ||||||
|  |  | ||||||
|  |     ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 3.0f); | ||||||
|  |  | ||||||
|  |     ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(1.0, 1.0, 1.0, 1.0)); | ||||||
|  |     ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 1.0f); | ||||||
|  |     ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 5.0f); | ||||||
|  |  | ||||||
|  |     ImGui::Begin("ConnectionStatusWindow", nullptr, | ||||||
|  |                  ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse | | ||||||
|  |                      ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar | | ||||||
|  |                      ImGuiWindowFlags_NoSavedSettings); | ||||||
|  |     ImGui::PopStyleVar(2); | ||||||
|  |     ImGui::PopStyleColor(); | ||||||
|  |  | ||||||
|  |     ImGui::SetWindowFontScale(0.5f); | ||||||
|  |     std::string text; | ||||||
|  |  | ||||||
|  |     if (ConnectionStatus::Connecting == connection_status_) { | ||||||
|  |       text = localization::p2p_connecting[localization_language_index_]; | ||||||
|  |       ImGui::SetCursorPosX(connection_status_window_width_ * 3 / 7); | ||||||
|  |       ImGui::SetCursorPosY(connection_status_window_height_ * 2 / 3); | ||||||
|  |     } else if (ConnectionStatus::Connected == connection_status_) { | ||||||
|  |       text = localization::p2p_connected[localization_language_index_]; | ||||||
|  |       // show_connection_status_window_ = false; | ||||||
|  |     } else if (ConnectionStatus::Disconnected == connection_status_) { | ||||||
|  |       text = localization::p2p_disconnected[localization_language_index_]; | ||||||
|  |       ImGui::SetCursorPosX(connection_status_window_width_ * 3 / 7); | ||||||
|  |       ImGui::SetCursorPosY(connection_status_window_height_ * 2 / 3); | ||||||
|  |       // ok | ||||||
|  |       if (ImGui::Button( | ||||||
|  |               localization::ok[localization_language_index_].c_str()) || | ||||||
|  |           ImGui::IsKeyPressed(ImGuiKey_Enter) || | ||||||
|  |           ImGui::IsKeyPressed(ImGuiKey_Escape)) { | ||||||
|  |         show_connection_status_window_ = false; | ||||||
|  |       } | ||||||
|  |     } else if (ConnectionStatus::Failed == connection_status_) { | ||||||
|  |       text = localization::p2p_failed[localization_language_index_]; | ||||||
|  |       ImGui::SetCursorPosX(connection_status_window_width_ * 3 / 7); | ||||||
|  |       ImGui::SetCursorPosY(connection_status_window_height_ * 2 / 3); | ||||||
|  |       // ok | ||||||
|  |       if (ImGui::Button( | ||||||
|  |               localization::ok[localization_language_index_].c_str()) || | ||||||
|  |           ImGui::IsKeyPressed(ImGuiKey_Enter) || | ||||||
|  |           ImGui::IsKeyPressed(ImGuiKey_Escape)) { | ||||||
|  |         show_connection_status_window_ = false; | ||||||
|  |       } | ||||||
|  |     } else if (ConnectionStatus::Closed == connection_status_) { | ||||||
|  |       text = localization::p2p_closed[localization_language_index_]; | ||||||
|  |       ImGui::SetCursorPosX(connection_status_window_width_ * 3 / 7); | ||||||
|  |       ImGui::SetCursorPosY(connection_status_window_height_ * 2 / 3); | ||||||
|  |       // ok | ||||||
|  |       if (ImGui::Button( | ||||||
|  |               localization::ok[localization_language_index_].c_str()) || | ||||||
|  |           ImGui::IsKeyPressed(ImGuiKey_Enter) || | ||||||
|  |           ImGui::IsKeyPressed(ImGuiKey_Escape)) { | ||||||
|  |         show_connection_status_window_ = false; | ||||||
|  |       } | ||||||
|  |     } else if (ConnectionStatus::IncorrectPassword == connection_status_) { | ||||||
|  |       if (!password_validating_) { | ||||||
|  |         if (password_validating_time_ == 1) { | ||||||
|  |           text = localization::input_password[localization_language_index_]; | ||||||
|  |         } else { | ||||||
|  |           text = localization::reinput_password[localization_language_index_]; | ||||||
|  |         } | ||||||
|  |         auto window_width = ImGui::GetWindowSize().x; | ||||||
|  |         auto window_height = ImGui::GetWindowSize().y; | ||||||
|  |         ImGui::SetCursorPosX((window_width - IPUT_WINDOW_WIDTH / 2) * 0.5f); | ||||||
|  |         ImGui::SetCursorPosY(window_height * 0.4f); | ||||||
|  |         ImGui::SetNextItemWidth(IPUT_WINDOW_WIDTH / 2); | ||||||
|  |  | ||||||
|  |         ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f); | ||||||
|  |  | ||||||
|  |         if (focus_on_input_widget_) { | ||||||
|  |           ImGui::SetKeyboardFocusHere(); | ||||||
|  |           focus_on_input_widget_ = false; | ||||||
|  |         } | ||||||
|  |         ImGui::InputText("##password", remote_password_, | ||||||
|  |                          IM_ARRAYSIZE(remote_password_), | ||||||
|  |                          ImGuiInputTextFlags_CharsNoBlank); | ||||||
|  |  | ||||||
|  |         ImGui::PopStyleVar(); | ||||||
|  |  | ||||||
|  |         ImGui::SetCursorPosX(window_width * 0.315f); | ||||||
|  |         ImGui::SetCursorPosY(window_height * 0.75f); | ||||||
|  |         // ok | ||||||
|  |         if (ImGui::Button( | ||||||
|  |                 localization::ok[localization_language_index_].c_str()) || | ||||||
|  |             ImGui::IsKeyPressed(ImGuiKey_Enter)) { | ||||||
|  |           show_connection_status_window_ = true; | ||||||
|  |           password_validating_ = true; | ||||||
|  |           rejoin_ = true; | ||||||
|  |           focus_on_input_widget_ = true; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         ImGui::SameLine(); | ||||||
|  |         // cancel | ||||||
|  |         if (ImGui::Button( | ||||||
|  |                 localization::cancel[localization_language_index_].c_str()) || | ||||||
|  |             ImGui::IsKeyPressed(ImGuiKey_Escape)) { | ||||||
|  |           memset(remote_password_, 0, sizeof(remote_password_)); | ||||||
|  |           show_connection_status_window_ = false; | ||||||
|  |           focus_on_input_widget_ = true; | ||||||
|  |         } | ||||||
|  |       } else if (password_validating_) { | ||||||
|  |         text = localization::validate_password[localization_language_index_]; | ||||||
|  |         ImGui::SetCursorPosX(connection_status_window_width_ * 3 / 7); | ||||||
|  |         ImGui::SetCursorPosY(connection_status_window_height_ * 2 / 3); | ||||||
|  |       } | ||||||
|  |     } else if (ConnectionStatus::NoSuchTransmissionId == connection_status_) { | ||||||
|  |       text = localization::no_such_id[localization_language_index_]; | ||||||
|  |       ImGui::SetCursorPosX(connection_status_window_width_ * 3 / 7); | ||||||
|  |       ImGui::SetCursorPosY(connection_status_window_height_ * 2 / 3); | ||||||
|  |       // ok | ||||||
|  |       if (ImGui::Button( | ||||||
|  |               localization::ok[localization_language_index_].c_str()) || | ||||||
|  |           ImGui::IsKeyPressed(ImGuiKey_Escape)) { | ||||||
|  |         show_connection_status_window_ = 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; | ||||||
|  | } | ||||||
							
								
								
									
										144
									
								
								src/single_window/control_bar.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										144
									
								
								src/single_window/control_bar.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,144 @@ | |||||||
|  | #include "IconsFontAwesome6.h" | ||||||
|  | #include "layout_style.h" | ||||||
|  | #include "localization.h" | ||||||
|  | #include "rd_log.h" | ||||||
|  | #include "render.h" | ||||||
|  |  | ||||||
|  | int Render::ControlBar() { | ||||||
|  |   ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 3.0f); | ||||||
|  |  | ||||||
|  |   if (control_bar_expand_) { | ||||||
|  |     ImGui::SetCursorPosX( | ||||||
|  |         is_control_bar_in_left_ ? (control_window_width_ + 5.0f) : 41.0f); | ||||||
|  |     // mouse control button | ||||||
|  |     ImDrawList* draw_list = ImGui::GetWindowDrawList(); | ||||||
|  |  | ||||||
|  |     if (is_control_bar_in_left_) { | ||||||
|  |       draw_list->AddLine( | ||||||
|  |           ImVec2(ImGui::GetCursorScreenPos().x - 5.0f, | ||||||
|  |                  ImGui::GetCursorScreenPos().y - 7.0f), | ||||||
|  |           ImVec2(ImGui::GetCursorScreenPos().x - 5.0f, | ||||||
|  |                  ImGui::GetCursorScreenPos().y - 7.0f + control_window_height_), | ||||||
|  |           IM_COL32(178, 178, 178, 255), 1.0f); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     float disable_mouse_x = ImGui::GetCursorScreenPos().x + 4.0f; | ||||||
|  |     float disable_mouse_y = ImGui::GetCursorScreenPos().y + 4.0f; | ||||||
|  |     std::string mouse = mouse_control_button_pressed_ ? ICON_FA_COMPUTER_MOUSE | ||||||
|  |                                                       : ICON_FA_COMPUTER_MOUSE; | ||||||
|  |     if (ImGui::Button(mouse.c_str(), ImVec2(25, 25))) { | ||||||
|  |       if (connection_established_) { | ||||||
|  |         control_mouse_ = !control_mouse_; | ||||||
|  |         mouse_control_button_pressed_ = !mouse_control_button_pressed_; | ||||||
|  |         mouse_control_button_label_ = | ||||||
|  |             mouse_control_button_pressed_ | ||||||
|  |                 ? localization::release_mouse[localization_language_index_] | ||||||
|  |                 : localization::control_mouse[localization_language_index_]; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     if (!mouse_control_button_pressed_) { | ||||||
|  |       draw_list->AddLine( | ||||||
|  |           ImVec2(disable_mouse_x, disable_mouse_y), | ||||||
|  |           ImVec2(disable_mouse_x + 16.0f, disable_mouse_y + 14.2f), | ||||||
|  |           IM_COL32(0, 0, 0, 255), 2.0f); | ||||||
|  |       draw_list->AddLine( | ||||||
|  |           ImVec2(disable_mouse_x - 1.2f, disable_mouse_y + 1.2f), | ||||||
|  |           ImVec2(disable_mouse_x + 15.3f, disable_mouse_y + 15.4f), | ||||||
|  |           ImGui::IsItemHovered() ? IM_COL32(66, 150, 250, 255) | ||||||
|  |                                  : IM_COL32(179, 213, 253, 255), | ||||||
|  |           2.0f); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     ImGui::SameLine(); | ||||||
|  |     // audio capture button | ||||||
|  |     float disable_audio_x = ImGui::GetCursorScreenPos().x + 4; | ||||||
|  |     float disable_audio_y = ImGui::GetCursorScreenPos().y + 4.0f; | ||||||
|  |     // std::string audio = audio_capture_button_pressed_ ? ICON_FA_VOLUME_HIGH | ||||||
|  |     //                                                   : ICON_FA_VOLUME_XMARK; | ||||||
|  |     std::string audio = audio_capture_button_pressed_ ? ICON_FA_VOLUME_HIGH | ||||||
|  |                                                       : ICON_FA_VOLUME_HIGH; | ||||||
|  |     if (ImGui::Button(audio.c_str(), ImVec2(25, 25))) { | ||||||
|  |       if (connection_established_) { | ||||||
|  |         audio_capture_ = !audio_capture_; | ||||||
|  |         audio_capture_button_pressed_ = !audio_capture_button_pressed_; | ||||||
|  |         audio_capture_button_label_ = | ||||||
|  |             audio_capture_button_pressed_ | ||||||
|  |                 ? localization::audio_capture[localization_language_index_] | ||||||
|  |                 : localization::mute[localization_language_index_]; | ||||||
|  |  | ||||||
|  |         RemoteAction remote_action; | ||||||
|  |         remote_action.type = ControlType::audio_capture; | ||||||
|  |         remote_action.a = audio_capture_button_pressed_; | ||||||
|  |         SendData(peer_, DATA_TYPE::DATA, (const char*)&remote_action, | ||||||
|  |                  sizeof(remote_action)); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     if (!audio_capture_button_pressed_) { | ||||||
|  |       draw_list->AddLine( | ||||||
|  |           ImVec2(disable_audio_x, disable_audio_y), | ||||||
|  |           ImVec2(disable_audio_x + 16.0f, disable_audio_y + 14.2f), | ||||||
|  |           IM_COL32(0, 0, 0, 255), 2.0f); | ||||||
|  |       draw_list->AddLine( | ||||||
|  |           ImVec2(disable_audio_x - 1.2f, disable_audio_y + 1.2f), | ||||||
|  |           ImVec2(disable_audio_x + 15.3f, disable_audio_y + 15.4f), | ||||||
|  |           ImGui::IsItemHovered() ? IM_COL32(66, 150, 250, 255) | ||||||
|  |                                  : IM_COL32(179, 213, 253, 255), | ||||||
|  |           2.0f); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     ImGui::SameLine(); | ||||||
|  |     // fullscreen button | ||||||
|  |     std::string fullscreen = | ||||||
|  |         fullscreen_button_pressed_ ? ICON_FA_COMPRESS : ICON_FA_EXPAND; | ||||||
|  |     if (ImGui::Button(fullscreen.c_str(), ImVec2(25, 25))) { | ||||||
|  |       fullscreen_button_pressed_ = !fullscreen_button_pressed_; | ||||||
|  |       fullscreen_button_label_ = | ||||||
|  |           fullscreen_button_pressed_ | ||||||
|  |               ? localization::exit_fullscreen[localization_language_index_] | ||||||
|  |               : localization::fullscreen[localization_language_index_]; | ||||||
|  |       if (fullscreen_button_pressed_) { | ||||||
|  |         SDL_SetWindowFullscreen(main_window_, SDL_WINDOW_FULLSCREEN_DESKTOP); | ||||||
|  |       } else { | ||||||
|  |         SDL_SetWindowFullscreen(main_window_, SDL_FALSE); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     ImGui::SameLine(); | ||||||
|  |     // close button | ||||||
|  |     std::string close_button = ICON_FA_XMARK; | ||||||
|  |     if (ImGui::Button(close_button.c_str(), ImVec2(25, 25))) { | ||||||
|  |       SDL_Event event; | ||||||
|  |       event.type = SDL_QUIT; | ||||||
|  |       SDL_PushEvent(&event); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     ImGui::SameLine(); | ||||||
|  |  | ||||||
|  |     if (!is_control_bar_in_left_) { | ||||||
|  |       draw_list->AddLine( | ||||||
|  |           ImVec2(ImGui::GetCursorScreenPos().x - 3.0f, | ||||||
|  |                  ImGui::GetCursorScreenPos().y - 7.0f), | ||||||
|  |           ImVec2(ImGui::GetCursorScreenPos().x - 3.0f, | ||||||
|  |                  ImGui::GetCursorScreenPos().y - 7.0f + control_window_height_), | ||||||
|  |           IM_COL32(178, 178, 178, 255), 1.0f); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   ImGui::SetCursorPosX( | ||||||
|  |       is_control_bar_in_left_ ? (control_window_width_ * 2 - 18.0f) : 3.0f); | ||||||
|  |  | ||||||
|  |   std::string control_bar = | ||||||
|  |       control_bar_expand_ | ||||||
|  |           ? (is_control_bar_in_left_ ? ICON_FA_ANGLE_LEFT : ICON_FA_ANGLE_RIGHT) | ||||||
|  |           : (is_control_bar_in_left_ ? ICON_FA_ANGLE_RIGHT | ||||||
|  |                                      : ICON_FA_ANGLE_LEFT); | ||||||
|  |   if (ImGui::Button(control_bar.c_str(), ImVec2(15, 25))) { | ||||||
|  |     control_bar_expand_ = !control_bar_expand_; | ||||||
|  |     control_bar_button_pressed_time_ = ImGui::GetTime(); | ||||||
|  |     control_window_width_is_changing_ = true; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   ImGui::PopStyleVar(); | ||||||
|  |  | ||||||
|  |   return 0; | ||||||
|  | } | ||||||
							
								
								
									
										150
									
								
								src/single_window/control_window.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										150
									
								
								src/single_window/control_window.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,150 @@ | |||||||
|  | #include "rd_log.h" | ||||||
|  | #include "render.h" | ||||||
|  |  | ||||||
|  | int Render::ControlWindow() { | ||||||
|  |   auto 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; | ||||||
|  |     } else { | ||||||
|  |       control_window_width_ = | ||||||
|  |           control_window_max_width_ - | ||||||
|  |           (control_window_max_width_ - control_window_min_width_) * 4 * | ||||||
|  |               time_duration; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(1, 1, 1, 1)); | ||||||
|  |   ImGui::PushStyleVar(ImGuiStyleVar_WindowMinSize, ImVec2(0, 0)); | ||||||
|  |   ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 10.0f); | ||||||
|  |   ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0); | ||||||
|  |   ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, 10.0f); | ||||||
|  |   ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0)); | ||||||
|  |  | ||||||
|  |   ImGui::SetNextWindowSize( | ||||||
|  |       ImVec2(control_window_width_, control_window_height_), ImGuiCond_Always); | ||||||
|  |   ImGui::SetNextWindowPos(ImVec2(0, title_bar_height_), ImGuiCond_Once); | ||||||
|  |   if (ImGui::IsMouseReleased(ImGuiPopupFlags_MouseButtonLeft) || | ||||||
|  |       control_window_width_is_changing_) { | ||||||
|  |     if (control_winodw_pos_.x <= main_window_width_ / 2) { | ||||||
|  |       int pos_x = 0; | ||||||
|  |       int pos_y = | ||||||
|  |           (control_winodw_pos_.y >= | ||||||
|  |                (fullscreen_button_pressed_ ? 0 : title_bar_height_) && | ||||||
|  |            control_winodw_pos_.y <= | ||||||
|  |                main_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_) | ||||||
|  |                      : (main_window_height_ - control_window_height_)); | ||||||
|  |  | ||||||
|  |       if (control_bar_expand_) { | ||||||
|  |         if (control_window_width_ >= control_window_max_width_) { | ||||||
|  |           control_window_width_ = control_window_max_width_; | ||||||
|  |           control_window_width_is_changing_ = false; | ||||||
|  |         } else { | ||||||
|  |           control_window_width_is_changing_ = true; | ||||||
|  |         } | ||||||
|  |       } else { | ||||||
|  |         if (control_window_width_ <= control_window_min_width_) { | ||||||
|  |           control_window_width_ = control_window_min_width_; | ||||||
|  |           control_window_width_is_changing_ = false; | ||||||
|  |         } else { | ||||||
|  |           control_window_width_is_changing_ = true; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |       ImGui::SetNextWindowPos(ImVec2(pos_x, pos_y), ImGuiCond_Always); | ||||||
|  |       is_control_bar_in_left_ = true; | ||||||
|  |     } else if (control_winodw_pos_.x > main_window_width_ / 2) { | ||||||
|  |       int pos_x = 0; | ||||||
|  |       int pos_y = | ||||||
|  |           (control_winodw_pos_.y >= | ||||||
|  |                (fullscreen_button_pressed_ ? 0 : title_bar_height_) && | ||||||
|  |            control_winodw_pos_.y <= | ||||||
|  |                main_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_) | ||||||
|  |                      : (main_window_height_ - control_window_height_)); | ||||||
|  |  | ||||||
|  |       if (control_bar_expand_) { | ||||||
|  |         if (control_window_width_ >= control_window_max_width_) { | ||||||
|  |           control_window_width_ = control_window_max_width_; | ||||||
|  |           control_window_width_is_changing_ = false; | ||||||
|  |           pos_x = main_window_width_ - control_window_max_width_; | ||||||
|  |         } else { | ||||||
|  |           control_window_width_is_changing_ = true; | ||||||
|  |           pos_x = main_window_width_ - control_window_width_; | ||||||
|  |         } | ||||||
|  |       } else { | ||||||
|  |         if (control_window_width_ <= control_window_min_width_) { | ||||||
|  |           control_window_width_ = control_window_min_width_; | ||||||
|  |           control_window_width_is_changing_ = false; | ||||||
|  |           pos_x = main_window_width_ - control_window_min_width_; | ||||||
|  |         } else { | ||||||
|  |           control_window_width_is_changing_ = true; | ||||||
|  |           pos_x = main_window_width_ - control_window_width_; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |       ImGui::SetNextWindowPos(ImVec2(pos_x, pos_y), ImGuiCond_Always); | ||||||
|  |       is_control_bar_in_left_ = false; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   ImGui::Begin("ControlWindow", nullptr, | ||||||
|  |                ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | | ||||||
|  |                    ImGuiWindowFlags_NoScrollbar | | ||||||
|  |                    ImGuiWindowFlags_NoBringToFrontOnFocus); | ||||||
|  |   ImGui::PopStyleVar(); | ||||||
|  |  | ||||||
|  |   control_winodw_pos_ = ImGui::GetWindowPos(); | ||||||
|  |   ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(1.0f, 1.0f, 1.0f, 1.0f)); | ||||||
|  |   static bool a, b, c, d, e; | ||||||
|  |   ImGui::SetNextWindowPos( | ||||||
|  |       ImVec2(is_control_bar_in_left_ | ||||||
|  |                  ? control_winodw_pos_.x - control_window_width_ | ||||||
|  |                  : control_winodw_pos_.x, | ||||||
|  |              control_winodw_pos_.y), | ||||||
|  |       ImGuiCond_Always); | ||||||
|  |   ImGui::SetWindowFontScale(0.5f); | ||||||
|  |  | ||||||
|  |   ImGui::BeginChild("ControlBar", | ||||||
|  |                     ImVec2(control_window_width_ * 2, control_window_height_), | ||||||
|  |                     ImGuiChildFlags_Border, ImGuiWindowFlags_NoDecoration); | ||||||
|  |   ImGui::SetWindowFontScale(1.0f); | ||||||
|  |   ImGui::PopStyleColor(); | ||||||
|  |  | ||||||
|  |   ControlBar(); | ||||||
|  |   control_bar_hovered_ = ImGui::IsWindowHovered(); | ||||||
|  |  | ||||||
|  |   ImGui::EndChild(); | ||||||
|  |   ImGui::End(); | ||||||
|  |   ImGui::PopStyleVar(4); | ||||||
|  |   ImGui::PopStyleColor(); | ||||||
|  |  | ||||||
|  |   ImGui::SetNextWindowPos( | ||||||
|  |       ImVec2(0, fullscreen_button_pressed_ ? 0 : title_bar_height_), | ||||||
|  |       ImGuiCond_Always); | ||||||
|  |   ImGui::SetNextWindowSize( | ||||||
|  |       ImVec2(main_window_width_, | ||||||
|  |              main_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; | ||||||
|  | } | ||||||
							
								
								
									
										39
									
								
								src/single_window/layout_style.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								src/single_window/layout_style.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,39 @@ | |||||||
|  | /* | ||||||
|  |  * @Author: DI JUNKUN | ||||||
|  |  * @Date: 2024-06-14 | ||||||
|  |  * Copyright (c) 2024 by DI JUNKUN, All Rights Reserved. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #ifndef _LAYOUT_STYLE_H_ | ||||||
|  | #define _LAYOUT_STYLE_H_ | ||||||
|  |  | ||||||
|  | #define MENU_WINDOW_WIDTH_CN 300 | ||||||
|  | #define MENU_WINDOW_HEIGHT_CN 280 | ||||||
|  | #define LOCAL_WINDOW_WIDTH_CN 300 | ||||||
|  | #define LOCAL_WINDOW_HEIGHT_CN 280 | ||||||
|  | #define REMOTE_WINDOW_WIDTH_CN 300 | ||||||
|  | #define REMOTE_WINDOW_HEIGHT_CN 280 | ||||||
|  | #define MENU_WINDOW_WIDTH_EN 190 | ||||||
|  | #define MENU_WINDOW_HEIGHT_EN 245 | ||||||
|  | #define IPUT_WINDOW_WIDTH 160 | ||||||
|  | #define INPUT_WINDOW_PADDING_CN 66 | ||||||
|  | #define INPUT_WINDOW_PADDING_EN 96 | ||||||
|  | #define SETTINGS_WINDOW_WIDTH_CN 181 | ||||||
|  | #define SETTINGS_WINDOW_WIDTH_EN 228 | ||||||
|  | #define SETTINGS_WINDOW_HEIGHT_CN 220 | ||||||
|  | #define SETTINGS_WINDOW_HEIGHT_EN 220 | ||||||
|  | #define LANGUAGE_SELECT_WINDOW_PADDING_CN 100 | ||||||
|  | #define LANGUAGE_SELECT_WINDOW_PADDING_EN 147 | ||||||
|  | #define VIDEO_QUALITY_SELECT_WINDOW_PADDING_CN 100 | ||||||
|  | #define VIDEO_QUALITY_SELECT_WINDOW_PADDING_EN 147 | ||||||
|  | #define VIDEO_ENCODE_FORMAT_SELECT_WINDOW_PADDING_CN 100 | ||||||
|  | #define VIDEO_ENCODE_FORMAT_SELECT_WINDOW_PADDING_EN 147 | ||||||
|  | #define ENABLE_HARDWARE_VIDEO_CODEC_CHECKBOX_PADDING_CN 151 | ||||||
|  | #define ENABLE_HARDWARE_VIDEO_CODEC_CHECKBOX_PADDING_EN 198 | ||||||
|  | #define ENABLE_TURN_CHECKBOX_PADDING_CN 151 | ||||||
|  | #define ENABLE_TURN_CHECKBOX_PADDING_EN 198 | ||||||
|  | #define SETTINGS_SELECT_WINDOW_WIDTH 73 | ||||||
|  | #define SETTINGS_OK_BUTTON_PADDING_CN 55 | ||||||
|  | #define SETTINGS_OK_BUTTON_PADDING_EN 78 | ||||||
|  |  | ||||||
|  | #endif | ||||||
							
								
								
									
										312
									
								
								src/single_window/local_peer_window.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										312
									
								
								src/single_window/local_peer_window.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,312 @@ | |||||||
|  | #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::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::PopStyleColor(); | ||||||
|  |  | ||||||
|  |   ImGui::SetWindowFontScale(1.0f); | ||||||
|  |   ImGui::Text( | ||||||
|  |       "%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::PushStyleVar(ImGuiStyleVar_ChildRounding, 10.0f); | ||||||
|  |     ImGui::BeginChild("LocalDesktopWindow_1", ImVec2(330, 180), | ||||||
|  |                       ImGuiChildFlags_Border, | ||||||
|  |                       ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse | | ||||||
|  |                           ImGuiWindowFlags_NoTitleBar | | ||||||
|  |                           ImGuiWindowFlags_NoBringToFrontOnFocus); | ||||||
|  |     ImGui::PopStyleVar(); | ||||||
|  |     ImGui::PopStyleColor(); | ||||||
|  |     { | ||||||
|  |       ImGui::SetWindowFontScale(0.5f); | ||||||
|  |       ImGui::Text("%s", | ||||||
|  |                   localization::local_id[localization_language_index_].c_str()); | ||||||
|  |  | ||||||
|  |       ImGui::Spacing(); | ||||||
|  |  | ||||||
|  |       ImGui::SetNextItemWidth(IPUT_WINDOW_WIDTH); | ||||||
|  |       ImGui::SetWindowFontScale(1.0f); | ||||||
|  |       ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f); | ||||||
|  |  | ||||||
|  |       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] = ' '; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       ImGui::InputText( | ||||||
|  |           "##local_id", client_id_display, IM_ARRAYSIZE(client_id_display), | ||||||
|  |           ImGuiInputTextFlags_CharsUppercase | ImGuiInputTextFlags_ReadOnly); | ||||||
|  |       ImGui::PopStyleVar(); | ||||||
|  |  | ||||||
|  |       ImGui::SameLine(); | ||||||
|  |       ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0)); | ||||||
|  |       ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0, 0, 0, 0)); | ||||||
|  |       ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0, 0, 0, 0)); | ||||||
|  |       ImGui::SetWindowFontScale(0.5f); | ||||||
|  |  | ||||||
|  |       if (ImGui::Button(ICON_FA_COPY, ImVec2(35, 38))) { | ||||||
|  |         local_id_copied_ = true; | ||||||
|  |         ImGui::SetClipboardText(client_id_); | ||||||
|  |         copy_start_time_ = ImGui::GetTime(); | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       ImGui::PopStyleColor(3); | ||||||
|  |  | ||||||
|  |       auto time_duration = ImGui::GetTime() - copy_start_time_; | ||||||
|  |       if (local_id_copied_ && time_duration < 1.0f) { | ||||||
|  |         const ImGuiViewport *viewport = ImGui::GetMainViewport(); | ||||||
|  |         ImGui::SetNextWindowPos( | ||||||
|  |             ImVec2((viewport->WorkSize.x - viewport->WorkPos.x - | ||||||
|  |                     notification_window_width_) / | ||||||
|  |                        2, | ||||||
|  |                    (viewport->WorkSize.y - viewport->WorkPos.y - | ||||||
|  |                     notification_window_height_) / | ||||||
|  |                        2)); | ||||||
|  |  | ||||||
|  |         ImGui::SetNextWindowSize( | ||||||
|  |             ImVec2(notification_window_width_, notification_window_height_)); | ||||||
|  |         ImGui::PushStyleColor(ImGuiCol_WindowBg, | ||||||
|  |                               ImVec4(1.0, 1.0, 1.0, 1.0 - time_duration)); | ||||||
|  |         ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); | ||||||
|  |         ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 5.0f); | ||||||
|  |         ImGui::Begin("ConnectionStatusWindow", nullptr, | ||||||
|  |                      ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove | | ||||||
|  |                          ImGuiWindowFlags_NoSavedSettings); | ||||||
|  |         ImGui::PopStyleVar(2); | ||||||
|  |         ImGui::PopStyleColor(); | ||||||
|  |  | ||||||
|  |         auto window_width = ImGui::GetWindowSize().x; | ||||||
|  |         auto window_height = ImGui::GetWindowSize().y; | ||||||
|  |         ImGui::SetWindowFontScale(0.8f); | ||||||
|  |         std::string text = localization::local_id_copied_to_clipboard | ||||||
|  |             [localization_language_index_]; | ||||||
|  |         auto text_width = ImGui::CalcTextSize(text.c_str()).x; | ||||||
|  |         ImGui::SetCursorPosX((window_width - text_width) * 0.5f); | ||||||
|  |         ImGui::SetCursorPosY(window_height * 0.5f); | ||||||
|  |         ImGui::PushStyleColor(ImGuiCol_Text, | ||||||
|  |                               ImVec4(0, 0, 0, 1.0 - time_duration)); | ||||||
|  |         ImGui::Text("%s", text.c_str()); | ||||||
|  |         ImGui::PopStyleColor(); | ||||||
|  |         ImGui::SetWindowFontScale(1.0f); | ||||||
|  |  | ||||||
|  |         ImGui::End(); | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       ImGui::SetWindowFontScale(1.0f); | ||||||
|  |  | ||||||
|  |       ImGui::Spacing(); | ||||||
|  |       ImGui::Separator(); | ||||||
|  |       ImGui::Spacing(); | ||||||
|  |  | ||||||
|  |       ImGui::SetWindowFontScale(0.5f); | ||||||
|  |  | ||||||
|  |       ImGui::Text("%s", | ||||||
|  |                   localization::password[localization_language_index_].c_str()); | ||||||
|  |  | ||||||
|  |       ImGui::SetNextItemWidth(IPUT_WINDOW_WIDTH); | ||||||
|  |       ImGui::Spacing(); | ||||||
|  |  | ||||||
|  |       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); | ||||||
|  |  | ||||||
|  |         random_password_.clear(); | ||||||
|  |         for (int i = 0, len = strlen(a); 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_)); | ||||||
|  |           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", | ||||||
|  |           localization::max_password_len[localization_language_index_].c_str(), | ||||||
|  |           password_saved_, IM_ARRAYSIZE(password_saved_), | ||||||
|  |           show_password_ | ||||||
|  |               ? ImGuiInputTextFlags_CharsNoBlank | ImGuiInputTextFlags_ReadOnly | ||||||
|  |               : ImGuiInputTextFlags_CharsNoBlank | | ||||||
|  |                     ImGuiInputTextFlags_Password | | ||||||
|  |                     ImGuiInputTextFlags_ReadOnly); | ||||||
|  |       ImGui::PopStyleVar(); | ||||||
|  |  | ||||||
|  |       ImGui::SameLine(); | ||||||
|  |       ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0)); | ||||||
|  |       ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0, 0, 0, 0)); | ||||||
|  |       ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0, 0, 0, 0)); | ||||||
|  |  | ||||||
|  |       ImGui::SetWindowFontScale(0.5f); | ||||||
|  |       auto l_x = ImGui::GetCursorScreenPos().x; | ||||||
|  |       auto l_y = ImGui::GetCursorScreenPos().y; | ||||||
|  |       if (ImGui::Button(ICON_FA_EYE, ImVec2(22, 38))) { | ||||||
|  |         show_password_ = !show_password_; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       if (!show_password_) { | ||||||
|  |         ImDrawList *draw_list = ImGui::GetWindowDrawList(); | ||||||
|  |         draw_list->AddLine(ImVec2(l_x + 3.0f, l_y + 12.5f), | ||||||
|  |                            ImVec2(l_x + 20.3f, l_y + 26.5f), | ||||||
|  |                            IM_COL32(239, 240, 242, 255), 2.0f); | ||||||
|  |         draw_list->AddLine(ImVec2(l_x + 3.0f, l_y + 11.0f), | ||||||
|  |                            ImVec2(l_x + 20.3f, l_y + 25.0f), | ||||||
|  |                            IM_COL32(0, 0, 0, 255), 1.5f); | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       ImGui::SameLine(); | ||||||
|  |  | ||||||
|  |       if (ImGui::Button( | ||||||
|  |               regenerate_password_ ? ICON_FA_SPINNER : ICON_FA_ARROWS_ROTATE, | ||||||
|  |               ImVec2(22, 38))) { | ||||||
|  |         regenerate_password_ = true; | ||||||
|  |         password_inited_ = false; | ||||||
|  |         regenerate_password_start_time_ = ImGui::GetTime(); | ||||||
|  |         LeaveConnection(peer_, client_id_); | ||||||
|  |         is_create_connection_ = false; | ||||||
|  |       } | ||||||
|  |       if (ImGui::GetTime() - regenerate_password_start_time_ > 0.3f) { | ||||||
|  |         regenerate_password_ = false; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       ImGui::SameLine(); | ||||||
|  |  | ||||||
|  |       if (ImGui::Button(ICON_FA_PEN, ImVec2(22, 38))) { | ||||||
|  |         show_reset_password_window_ = true; | ||||||
|  |       } | ||||||
|  |       ImGui::SetWindowFontScale(1.0f); | ||||||
|  |       ImGui::PopStyleColor(3); | ||||||
|  |  | ||||||
|  |       if (show_reset_password_window_) { | ||||||
|  |         const ImGuiViewport *viewport = ImGui::GetMainViewport(); | ||||||
|  |  | ||||||
|  |         ImGui::SetNextWindowPos( | ||||||
|  |             ImVec2((viewport->WorkSize.x - viewport->WorkPos.x - | ||||||
|  |                     connection_status_window_width_) / | ||||||
|  |                        2, | ||||||
|  |                    (viewport->WorkSize.y - viewport->WorkPos.y - | ||||||
|  |                     connection_status_window_height_) / | ||||||
|  |                        2)); | ||||||
|  |  | ||||||
|  |         ImGui::SetNextWindowSize(ImVec2(connection_status_window_width_, | ||||||
|  |                                         connection_status_window_height_)); | ||||||
|  |  | ||||||
|  |         ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 3.0f); | ||||||
|  |  | ||||||
|  |         ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(1.0, 1.0, 1.0, 1.0)); | ||||||
|  |         ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 1.0f); | ||||||
|  |         ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 5.0f); | ||||||
|  |  | ||||||
|  |         ImGui::Begin("ResetPasswordWindow", nullptr, | ||||||
|  |                      ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse | | ||||||
|  |                          ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar | | ||||||
|  |                          ImGuiWindowFlags_NoSavedSettings); | ||||||
|  |         ImGui::PopStyleVar(2); | ||||||
|  |         ImGui::PopStyleColor(); | ||||||
|  |  | ||||||
|  |         auto window_width = ImGui::GetWindowSize().x; | ||||||
|  |         auto window_height = ImGui::GetWindowSize().y; | ||||||
|  |         std::string text = | ||||||
|  |             localization::new_password[localization_language_index_]; | ||||||
|  |         auto text_width = ImGui::CalcTextSize(text.c_str()).x; | ||||||
|  |         ImGui::SetWindowFontScale(0.5f); | ||||||
|  |         ImGui::SetCursorPosX((window_width - text_width / 2) * 0.5f); | ||||||
|  |         ImGui::SetCursorPosY(window_height * 0.2f); | ||||||
|  |         ImGui::Text("%s", text.c_str()); | ||||||
|  |  | ||||||
|  |         ImGui::SetCursorPosX((window_width - IPUT_WINDOW_WIDTH / 2) * 0.5f); | ||||||
|  |         ImGui::SetCursorPosY(window_height * 0.4f); | ||||||
|  |         ImGui::SetNextItemWidth(IPUT_WINDOW_WIDTH / 2); | ||||||
|  |  | ||||||
|  |         ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f); | ||||||
|  |  | ||||||
|  |         if (focus_on_input_widget_) { | ||||||
|  |           ImGui::SetKeyboardFocusHere(); | ||||||
|  |           focus_on_input_widget_ = false; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         bool enter_pressed = ImGui::InputText( | ||||||
|  |             "##new_password", new_password_, IM_ARRAYSIZE(new_password_), | ||||||
|  |             ImGuiInputTextFlags_CharsNoBlank | | ||||||
|  |                 ImGuiInputTextFlags_EnterReturnsTrue); | ||||||
|  |  | ||||||
|  |         ImGui::PopStyleVar(); | ||||||
|  |  | ||||||
|  |         ImGui::SetCursorPosX(window_width * 0.315f); | ||||||
|  |         ImGui::SetCursorPosY(window_height * 0.75f); | ||||||
|  |  | ||||||
|  |         // OK | ||||||
|  |         if (ImGui::Button( | ||||||
|  |                 localization::ok[localization_language_index_].c_str()) || | ||||||
|  |             enter_pressed) { | ||||||
|  |           if (6 != strlen(new_password_)) { | ||||||
|  |             LOG_ERROR("Invalid password length"); | ||||||
|  |             show_reset_password_window_ = true; | ||||||
|  |             focus_on_input_widget_ = true; | ||||||
|  |           } else { | ||||||
|  |             show_reset_password_window_ = false; | ||||||
|  |             LOG_INFO("Generate new password and save into cache file"); | ||||||
|  |             strncpy(password_saved_, new_password_, sizeof(password_saved_)); | ||||||
|  |             memset(new_password_, 0, sizeof(new_password_)); | ||||||
|  |             SaveSettingsIntoCacheFile(); | ||||||
|  |             LeaveConnection(peer_, client_id_); | ||||||
|  |             is_create_connection_ = false; | ||||||
|  |             focus_on_input_widget_ = true; | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         ImGui::SameLine(); | ||||||
|  |  | ||||||
|  |         if (ImGui::Button( | ||||||
|  |                 localization::cancel[localization_language_index_].c_str())) { | ||||||
|  |           show_reset_password_window_ = false; | ||||||
|  |           focus_on_input_widget_ = true; | ||||||
|  |           memset(new_password_, 0, sizeof(new_password_)); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         ImGui::SetWindowFontScale(1.0f); | ||||||
|  |  | ||||||
|  |         ImGui::End(); | ||||||
|  |         ImGui::PopStyleVar(); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     ImGui::EndChild(); | ||||||
|  |     ImGui::PopStyleVar(); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   ImGui::EndChild(); | ||||||
|  |   ImGui::PopStyleVar(); | ||||||
|  |  | ||||||
|  |   return 0; | ||||||
|  | } | ||||||
							
								
								
									
										15
									
								
								src/single_window/main_window.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								src/single_window/main_window.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | |||||||
|  | #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); | ||||||
|  |  | ||||||
|  |   LocalWindow(); | ||||||
|  |   RemoteWindow(); | ||||||
|  |   StatusBar(); | ||||||
|  |   ConnectionStatusWindow(); | ||||||
|  |  | ||||||
|  |   return 0; | ||||||
|  | } | ||||||
							
								
								
									
										110
									
								
								src/single_window/remote_peer_window.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										110
									
								
								src/single_window/remote_peer_window.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,110 @@ | |||||||
|  | #include "IconsFontAwesome6.h" | ||||||
|  | #include "layout_style.h" | ||||||
|  | #include "localization.h" | ||||||
|  | #include "rd_log.h" | ||||||
|  | #include "render.h" | ||||||
|  |  | ||||||
|  | static int InputTextCallback(ImGuiInputTextCallbackData *data) { | ||||||
|  |   if (data->BufTextLen > 3 && data->Buf[3] != ' ') { | ||||||
|  |     data->InsertChars(3, " "); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   if (data->BufTextLen > 7 && data->Buf[7] != ' ') { | ||||||
|  |     data->InsertChars(7, " "); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int Render::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(¶ms_); | ||||||
|  |             if (peer_reserved_) { | ||||||
|  |               LOG_INFO("Create peer[reserved] instance successful"); | ||||||
|  |               std::string client_id = "C-"; | ||||||
|  |               client_id += client_id_; | ||||||
|  |               Init(peer_reserved_, client_id.c_str()); | ||||||
|  |               LOG_INFO("Peer[reserved] init finish"); | ||||||
|  |             } else { | ||||||
|  |               LOG_INFO("Create peer[reserved] instance failed"); | ||||||
|  |             } | ||||||
|  |           } | ||||||
|  |           ret = JoinConnection(peer_reserved_ ? peer_reserved_ : peer_, | ||||||
|  |                                remote_id_.c_str(), remote_password_); | ||||||
|  |           if (0 == ret) { | ||||||
|  |             is_client_mode_ = true; | ||||||
|  |             rejoin_ = false; | ||||||
|  |           } else { | ||||||
|  |             rejoin_ = true; | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   ImGui::EndChild(); | ||||||
|  |   ImGui::EndChild(); | ||||||
|  |   ImGui::PopStyleVar(); | ||||||
|  |  | ||||||
|  |   return 0; | ||||||
|  | } | ||||||
							
								
								
									
										755
									
								
								src/single_window/render.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										755
									
								
								src/single_window/render.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,755 @@ | |||||||
|  | #include "render.h" | ||||||
|  |  | ||||||
|  | #include <fstream> | ||||||
|  | #include <iostream> | ||||||
|  | #include <string> | ||||||
|  |  | ||||||
|  | #include "IconsFontAwesome6.h" | ||||||
|  | #include "OPPOSans_Regular.h" | ||||||
|  | #include "device_controller_factory.h" | ||||||
|  | #include "fa_regular_400.h" | ||||||
|  | #include "fa_solid_900.h" | ||||||
|  | #include "layout_style.h" | ||||||
|  | #include "localization.h" | ||||||
|  | #include "platform.h" | ||||||
|  | #include "rd_log.h" | ||||||
|  | #include "screen_capturer_factory.h" | ||||||
|  |  | ||||||
|  | // Refresh Event | ||||||
|  | #define REFRESH_EVENT (SDL_USEREVENT + 1) | ||||||
|  | #define NV12_BUFFER_SIZE 1280 * 720 * 3 / 2 | ||||||
|  |  | ||||||
|  | #define MOUSE_GRAB_PADDING 5 | ||||||
|  |  | ||||||
|  | SDL_HitTestResult Render::HitTestCallback(SDL_Window *window, | ||||||
|  |                                           const SDL_Point *area, void *data) { | ||||||
|  |   Render *render = (Render *)data; | ||||||
|  |   if (!render) { | ||||||
|  |     return SDL_HITTEST_NORMAL; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   if (render->fullscreen_button_pressed_) { | ||||||
|  |     return SDL_HITTEST_NORMAL; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   int window_width, window_height; | ||||||
|  |   SDL_GetWindowSize(window, &window_width, &window_height); | ||||||
|  |  | ||||||
|  |   if (area->y < 30 && area->y > MOUSE_GRAB_PADDING && | ||||||
|  |       area->x < window_width - 120 && area->x > MOUSE_GRAB_PADDING) { | ||||||
|  |     return SDL_HITTEST_DRAGGABLE; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   if (!render->streaming_) { | ||||||
|  |     return SDL_HITTEST_NORMAL; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   if (area->y < MOUSE_GRAB_PADDING) { | ||||||
|  |     if (area->x < MOUSE_GRAB_PADDING) { | ||||||
|  |       return SDL_HITTEST_RESIZE_TOPLEFT; | ||||||
|  |     } else if (area->x > window_width - MOUSE_GRAB_PADDING) { | ||||||
|  |       return SDL_HITTEST_RESIZE_TOPRIGHT; | ||||||
|  |     } else { | ||||||
|  |       return SDL_HITTEST_RESIZE_TOP; | ||||||
|  |     } | ||||||
|  |   } else if (area->y > window_height - MOUSE_GRAB_PADDING) { | ||||||
|  |     if (area->x < MOUSE_GRAB_PADDING) { | ||||||
|  |       return SDL_HITTEST_RESIZE_BOTTOMLEFT; | ||||||
|  |     } else if (area->x > window_width - MOUSE_GRAB_PADDING) { | ||||||
|  |       return SDL_HITTEST_RESIZE_BOTTOMRIGHT; | ||||||
|  |     } else { | ||||||
|  |       return SDL_HITTEST_RESIZE_BOTTOM; | ||||||
|  |     } | ||||||
|  |   } else if (area->x < MOUSE_GRAB_PADDING) { | ||||||
|  |     return SDL_HITTEST_RESIZE_LEFT; | ||||||
|  |   } else if (area->x > window_width - MOUSE_GRAB_PADDING) { | ||||||
|  |     return SDL_HITTEST_RESIZE_RIGHT; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   return SDL_HITTEST_NORMAL; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | Render::Render() {} | ||||||
|  |  | ||||||
|  | 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_) { | ||||||
|  |     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_)); | ||||||
|  |   memset(&cd_cache_.password, 0, sizeof(cd_cache_.password)); | ||||||
|  |   strncpy(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_, | ||||||
|  |          sizeof(video_quality_button_value_)); | ||||||
|  |   memcpy(&cd_cache_.video_encode_format, &video_encode_format_button_value_, | ||||||
|  |          sizeof(video_encode_format_button_value_)); | ||||||
|  |   memcpy(&cd_cache_.enable_hardware_video_codec, &enable_hardware_video_codec_, | ||||||
|  |          sizeof(enable_hardware_video_codec_)); | ||||||
|  |   fwrite(&cd_cache_, sizeof(cd_cache_), 1, cd_cache_file_); | ||||||
|  |   fclose(cd_cache_file_); | ||||||
|  |  | ||||||
|  |   config_center_.SetLanguage((ConfigCenter::LANGUAGE)language_button_value_); | ||||||
|  |   config_center_.SetVideoQuality( | ||||||
|  |       (ConfigCenter::VIDEO_QUALITY)video_quality_button_value_); | ||||||
|  |   config_center_.SetVideoEncodeFormat( | ||||||
|  |       (ConfigCenter::VIDEO_ENCODE_FORMAT)video_encode_format_button_value_); | ||||||
|  |   config_center_.SetHardwareVideoCodec(enable_hardware_video_codec_); | ||||||
|  |  | ||||||
|  |   LOG_INFO("Save settings into cache file success"); | ||||||
|  |  | ||||||
|  |   return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int Render::LoadSettingsFromCacheFile() { | ||||||
|  |   std::lock_guard<std::mutex> lock(cd_cache_mutex_); | ||||||
|  |   cd_cache_file_ = fopen("cache.cd", "r+"); | ||||||
|  |   if (!cd_cache_file_) { | ||||||
|  |     LOG_INFO("Init cache file by using default settings"); | ||||||
|  |     memset(password_saved_, 0, sizeof(password_saved_)); | ||||||
|  |     language_button_value_ = 0; | ||||||
|  |     video_quality_button_value_ = 0; | ||||||
|  |     video_encode_format_button_value_ = 1; | ||||||
|  |     enable_hardware_video_codec_ = false; | ||||||
|  |  | ||||||
|  |     config_center_.SetLanguage((ConfigCenter::LANGUAGE)language_button_value_); | ||||||
|  |     config_center_.SetVideoQuality( | ||||||
|  |         (ConfigCenter::VIDEO_QUALITY)video_quality_button_value_); | ||||||
|  |     config_center_.SetVideoEncodeFormat( | ||||||
|  |         (ConfigCenter::VIDEO_ENCODE_FORMAT)video_encode_format_button_value_); | ||||||
|  |     config_center_.SetHardwareVideoCodec(enable_hardware_video_codec_); | ||||||
|  |  | ||||||
|  |     return -1; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   fseek(cd_cache_file_, 0, SEEK_SET); | ||||||
|  |   fread(&cd_cache_, sizeof(cd_cache_), 1, cd_cache_file_); | ||||||
|  |   fclose(cd_cache_file_); | ||||||
|  |  | ||||||
|  |   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_)); | ||||||
|  |   if (0 != strcmp(password_saved_, "") && 7 == sizeof(password_saved_)) { | ||||||
|  |     password_inited_ = true; | ||||||
|  |   } | ||||||
|  |   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; | ||||||
|  |  | ||||||
|  |   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_; | ||||||
|  |  | ||||||
|  |   config_center_.SetLanguage((ConfigCenter::LANGUAGE)language_button_value_); | ||||||
|  |   config_center_.SetVideoQuality( | ||||||
|  |       (ConfigCenter::VIDEO_QUALITY)video_quality_button_value_); | ||||||
|  |   config_center_.SetVideoEncodeFormat( | ||||||
|  |       (ConfigCenter::VIDEO_ENCODE_FORMAT)video_encode_format_button_value_); | ||||||
|  |   config_center_.SetHardwareVideoCodec(enable_hardware_video_codec_); | ||||||
|  |  | ||||||
|  |   LOG_INFO("Load settings from cache file"); | ||||||
|  |  | ||||||
|  |   return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int Render::StartScreenCapture() { | ||||||
|  |   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 { | ||||||
|  |         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.size = size; | ||||||
|  |           frame.width = width; | ||||||
|  |           frame.height = height; | ||||||
|  |  | ||||||
|  |           SendVideoFrame(peer_, &frame); | ||||||
|  |           last_frame_time_ = now_time; | ||||||
|  |         } | ||||||
|  |       }); | ||||||
|  |  | ||||||
|  |   if (0 == screen_capturer_init_ret) { | ||||||
|  |     screen_capturer_->Start(); | ||||||
|  |   } else { | ||||||
|  |     screen_capturer_->Destroy(); | ||||||
|  |     delete screen_capturer_; | ||||||
|  |     screen_capturer_ = nullptr; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int Render::StopScreenCapture() { | ||||||
|  |   if (screen_capturer_) { | ||||||
|  |     LOG_INFO("Stop screen capturer") | ||||||
|  |     screen_capturer_->Stop(); | ||||||
|  |     screen_capturer_->Destroy(); | ||||||
|  |     delete screen_capturer_; | ||||||
|  |     screen_capturer_ = nullptr; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int Render::StartSpeakerCapture() { | ||||||
|  |   if (!speaker_capturer_) { | ||||||
|  |     speaker_capturer_ = (SpeakerCapturer *)speaker_capturer_factory_->Create(); | ||||||
|  |     int speaker_capturer_init_ret = speaker_capturer_->Init( | ||||||
|  |         [this](unsigned char *data, size_t size) -> void { | ||||||
|  |           if (connection_established_) { | ||||||
|  |             SendData(peer_, DATA_TYPE::AUDIO, (const char *)data, size); | ||||||
|  |           } | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |     if (0 != speaker_capturer_init_ret) { | ||||||
|  |       speaker_capturer_->Destroy(); | ||||||
|  |       delete speaker_capturer_; | ||||||
|  |       speaker_capturer_ = nullptr; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   if (speaker_capturer_) { | ||||||
|  |     speaker_capturer_->Start(); | ||||||
|  |   } | ||||||
|  |   return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int Render::StopSpeakerCapture() { | ||||||
|  |   if (speaker_capturer_) { | ||||||
|  |     speaker_capturer_->Stop(); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int Render::StartMouseControl() { | ||||||
|  |   device_controller_factory_ = new DeviceControllerFactory(); | ||||||
|  |   mouse_controller_ = (MouseController *)device_controller_factory_->Create( | ||||||
|  |       DeviceControllerFactory::Device::Mouse); | ||||||
|  |   int mouse_controller_init_ret = | ||||||
|  |       mouse_controller_->Init(screen_width_, screen_height_); | ||||||
|  |   if (0 != mouse_controller_init_ret) { | ||||||
|  |     LOG_INFO("Destroy mouse controller") | ||||||
|  |     mouse_controller_->Destroy(); | ||||||
|  |     mouse_controller_ = nullptr; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int Render::StopMouseControl() { | ||||||
|  |   if (mouse_controller_) { | ||||||
|  |     mouse_controller_->Destroy(); | ||||||
|  |     delete mouse_controller_; | ||||||
|  |     mouse_controller_ = nullptr; | ||||||
|  |   } | ||||||
|  |   return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int Render::CreateConnectionPeer() { | ||||||
|  |   mac_addr_str_ = GetMac(); | ||||||
|  |  | ||||||
|  |   params_.use_cfg_file = false; | ||||||
|  |   params_.signal_server_ip = "150.158.81.30"; | ||||||
|  |   params_.signal_server_port = 9099; | ||||||
|  |   params_.stun_server_ip = "150.158.81.30"; | ||||||
|  |   params_.stun_server_port = 3478; | ||||||
|  |   params_.turn_server_ip = "150.158.81.30"; | ||||||
|  |   params_.turn_server_port = 3478; | ||||||
|  |   params_.turn_server_username = "dijunkun"; | ||||||
|  |   params_.turn_server_password = "dijunkunpw"; | ||||||
|  |   params_.hardware_acceleration = config_center_.IsHardwareVideoCodec(); | ||||||
|  |   params_.av1_encoding = config_center_.GetVideoEncodeFormat() == | ||||||
|  |                                  ConfigCenter::VIDEO_ENCODE_FORMAT::AV1 | ||||||
|  |                              ? true | ||||||
|  |                              : false; | ||||||
|  |   params_.enable_turn = config_center_.IsEnableTurn(); | ||||||
|  |   params_.on_receive_video_buffer = nullptr; | ||||||
|  |   params_.on_receive_audio_buffer = OnReceiveAudioBufferCb; | ||||||
|  |   params_.on_receive_data_buffer = OnReceiveDataBufferCb; | ||||||
|  |  | ||||||
|  |   params_.on_receive_video_frame = OnReceiveVideoBufferCb; | ||||||
|  |  | ||||||
|  |   params_.on_signal_status = OnSignalStatusCb; | ||||||
|  |   params_.on_connection_status = OnConnectionStatusCb; | ||||||
|  |   params_.net_status_report = NetStatusReport; | ||||||
|  |   params_.user_data = this; | ||||||
|  |  | ||||||
|  |   peer_ = CreatePeer(¶ms_); | ||||||
|  |   if (peer_) { | ||||||
|  |     LOG_INFO("[{}] Create peer instance successful", client_id_); | ||||||
|  |     Init(peer_, client_id_); | ||||||
|  |     LOG_INFO("[{}] Peer init finish", client_id_); | ||||||
|  |   } else { | ||||||
|  |     LOG_INFO("Create peer instance failed"); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int Render::AudioDeviceInit() { | ||||||
|  |   // Audio | ||||||
|  |   SDL_AudioSpec want_in, have_in, want_out, have_out; | ||||||
|  |   SDL_zero(want_in); | ||||||
|  |   want_in.freq = 48000; | ||||||
|  |   want_in.format = AUDIO_S16LSB; | ||||||
|  |   want_in.channels = 1; | ||||||
|  |   want_in.samples = 480; | ||||||
|  |   want_in.callback = SdlCaptureAudioIn; | ||||||
|  |   want_in.userdata = this; | ||||||
|  |  | ||||||
|  |   // input_dev_ = SDL_OpenAudioDevice(NULL, 1, &want_in, &have_in, 0); | ||||||
|  |   // if (input_dev_ == 0) { | ||||||
|  |   //   SDL_Log("Failed to open input: %s", SDL_GetError()); | ||||||
|  |   //   // return 1; | ||||||
|  |   // } | ||||||
|  |  | ||||||
|  |   SDL_zero(want_out); | ||||||
|  |   want_out.freq = 48000; | ||||||
|  |   want_out.format = AUDIO_S16LSB; | ||||||
|  |   want_out.channels = 1; | ||||||
|  |   // want_out.silence = 0; | ||||||
|  |   want_out.samples = 480; | ||||||
|  |   want_out.callback = nullptr; | ||||||
|  |   want_out.userdata = this; | ||||||
|  |  | ||||||
|  |   output_dev_ = SDL_OpenAudioDevice(nullptr, 0, &want_out, NULL, 0); | ||||||
|  |   if (output_dev_ == 0) { | ||||||
|  |     SDL_Log("Failed to open input: %s", SDL_GetError()); | ||||||
|  |     // return 1; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   // SDL_PauseAudioDevice(input_dev_, 0); | ||||||
|  |   SDL_PauseAudioDevice(output_dev_, 0); | ||||||
|  |  | ||||||
|  |   return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int Render::AudioDeviceDestroy() { | ||||||
|  |   SDL_CloseAudioDevice(output_dev_); | ||||||
|  |   // SDL_CloseAudioDevice(input_dev_); | ||||||
|  |  | ||||||
|  |   return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int Render::Run() { | ||||||
|  |   LoadSettingsFromCacheFile(); | ||||||
|  |  | ||||||
|  |   localization_language_ = (ConfigCenter::LANGUAGE)language_button_value_; | ||||||
|  |   localization_language_index_ = language_button_value_; | ||||||
|  |  | ||||||
|  |   // Setup SDL | ||||||
|  |   if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER | | ||||||
|  |                SDL_INIT_GAMECONTROLLER) != 0) { | ||||||
|  |     printf("Error: %s\n", SDL_GetError()); | ||||||
|  |     return -1; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   // get screen resolution | ||||||
|  |   SDL_DisplayMode DM; | ||||||
|  |   SDL_GetCurrentDisplayMode(0, &DM); | ||||||
|  |   screen_width_ = DM.w; | ||||||
|  |   screen_height_ = DM.h; | ||||||
|  |  | ||||||
|  |   stream_render_rect_.x = 0; | ||||||
|  |   stream_render_rect_.y = title_bar_height_; | ||||||
|  |   stream_render_rect_.w = main_window_width_; | ||||||
|  |   stream_render_rect_.h = main_window_height_ - title_bar_height_; | ||||||
|  |  | ||||||
|  |   // use linear filtering to render textures otherwise the graphics will be | ||||||
|  |   // blurry | ||||||
|  |   SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1"); | ||||||
|  |  | ||||||
|  |   // Setup Dear ImGui context | ||||||
|  |   IMGUI_CHECKVERSION(); | ||||||
|  |   ImGui::CreateContext(); | ||||||
|  |   ImGuiIO &io = ImGui::GetIO(); | ||||||
|  |   // Master keyboard navigation enable flag. Enable full Tabbing + directional | ||||||
|  |   // arrows + space/enter to activate. | ||||||
|  |   io.ConfigFlags |= | ||||||
|  |       ImGuiConfigFlags_NavEnableKeyboard;  // Enable Keyboard Controls | ||||||
|  |   io.ConfigFlags |= | ||||||
|  |       ImGuiConfigFlags_NavEnableGamepad;  // Enable Gamepad Controls | ||||||
|  |  | ||||||
|  |   // Load Fonts | ||||||
|  |   io.Fonts->AddFontFromMemoryTTF(OPPOSans_Regular_ttf, | ||||||
|  |                                  sizeof(OPPOSans_Regular_ttf), 32.0f, NULL, | ||||||
|  |                                  io.Fonts->GetGlyphRangesChineseFull()); | ||||||
|  |  | ||||||
|  |   ImFontConfig config; | ||||||
|  |   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->Build(); | ||||||
|  |  | ||||||
|  |   // Setup Dear ImGui style | ||||||
|  |   // ImGui::StyleColorsDark(); | ||||||
|  |   ImGui::StyleColorsLight(); | ||||||
|  |  | ||||||
|  |   // init modules | ||||||
|  |   if (!modules_inited_) { | ||||||
|  |     // audio | ||||||
|  |     AudioDeviceInit(); | ||||||
|  |  | ||||||
|  |     // screen capture init | ||||||
|  |     screen_capturer_factory_ = new ScreenCapturerFactory(); | ||||||
|  |  | ||||||
|  |     // speaker capture init | ||||||
|  |     speaker_capturer_factory_ = new SpeakerCapturerFactory(); | ||||||
|  |  | ||||||
|  |     // mouse control | ||||||
|  |     device_controller_factory_ = new DeviceControllerFactory(); | ||||||
|  |  | ||||||
|  |     // RTC | ||||||
|  |     CreateConnectionPeer(); | ||||||
|  |  | ||||||
|  |     modules_inited_ = true; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   // create main window with SDL_Renderer graphics context | ||||||
|  |   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); | ||||||
|  |  | ||||||
|  |   main_renderer_ = SDL_CreateRenderer( | ||||||
|  |       main_window_, -1, SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_ACCELERATED); | ||||||
|  |   if (main_renderer_ == nullptr) { | ||||||
|  |     LOG_ERROR("1 Error creating SDL_Renderer"); | ||||||
|  |     return 0; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   stream_pixformat_ = SDL_PIXELFORMAT_NV12; | ||||||
|  |   stream_texture_ = SDL_CreateTexture(main_renderer_, stream_pixformat_, | ||||||
|  |                                       SDL_TEXTUREACCESS_STREAMING, | ||||||
|  |                                       texture_width_, texture_height_); | ||||||
|  |  | ||||||
|  |   // for window region action | ||||||
|  |   SDL_SetWindowHitTest(main_window_, HitTestCallback, this); | ||||||
|  |  | ||||||
|  |   SDL_GL_GetDrawableSize(main_window_, &main_window_width_real_, | ||||||
|  |                          &main_window_height_real_); | ||||||
|  |   dpi_scaling_w_ = (float)main_window_width_real_ / (float)main_window_width_; | ||||||
|  |   dpi_scaling_h_ = (float)main_window_width_real_ / (float)main_window_width_; | ||||||
|  |   SDL_RenderSetScale(main_renderer_, dpi_scaling_w_, dpi_scaling_h_); | ||||||
|  |   LOG_INFO("Use dpi scaling [{}x{}]", dpi_scaling_w_, dpi_scaling_h_); | ||||||
|  |  | ||||||
|  |   // Setup Platform/Renderer backends | ||||||
|  |   ImGui_ImplSDL2_InitForSDLRenderer(main_window_, main_renderer_); | ||||||
|  |   ImGui_ImplSDLRenderer2_Init(main_renderer_); | ||||||
|  |  | ||||||
|  |   // Main loop | ||||||
|  |   while (!exit_) { | ||||||
|  |     if (!label_inited_ || | ||||||
|  |         localization_language_index_last_ != localization_language_index_) { | ||||||
|  |       connect_button_label_ = | ||||||
|  |           connect_button_pressed_ | ||||||
|  |               ? localization::disconnect[localization_language_index_] | ||||||
|  |               : localization::connect[localization_language_index_]; | ||||||
|  |  | ||||||
|  |       mouse_control_button_label_ = | ||||||
|  |           mouse_control_button_pressed_ | ||||||
|  |               ? localization::release_mouse[localization_language_index_] | ||||||
|  |               : localization::control_mouse[localization_language_index_]; | ||||||
|  |  | ||||||
|  |       audio_capture_button_label_ = | ||||||
|  |           audio_capture_button_pressed_ | ||||||
|  |               ? localization::mute[localization_language_index_] | ||||||
|  |               : localization::audio_capture[localization_language_index_]; | ||||||
|  |  | ||||||
|  |       fullscreen_button_label_ = | ||||||
|  |           fullscreen_button_pressed_ | ||||||
|  |               ? localization::exit_fullscreen[localization_language_index_] | ||||||
|  |               : localization::fullscreen[localization_language_index_]; | ||||||
|  |  | ||||||
|  |       settings_button_label_ = | ||||||
|  |           localization::settings[localization_language_index_]; | ||||||
|  |       label_inited_ = true; | ||||||
|  |       localization_language_index_last_ = localization_language_index_; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Start the Dear ImGui frame | ||||||
|  |     ImGui_ImplSDLRenderer2_NewFrame(); | ||||||
|  |     ImGui_ImplSDL2_NewFrame(); | ||||||
|  |     ImGui::NewFrame(); | ||||||
|  |  | ||||||
|  |     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(main_window_width_, | ||||||
|  |                  (connection_established_ && streaming_ && is_client_mode_) | ||||||
|  |                      ? (fullscreen_button_pressed_ ? 0 : title_bar_height_) | ||||||
|  |                      : main_window_height_default_), | ||||||
|  |           ImGuiCond_Always); | ||||||
|  |       ImGui::Begin("Render", nullptr, | ||||||
|  |                    ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar | | ||||||
|  |                        ImGuiWindowFlags_NoResize | | ||||||
|  |                        ImGuiWindowFlags_NoScrollbar | | ||||||
|  |                        ImGuiWindowFlags_NoBringToFrontOnFocus); | ||||||
|  |       ImGui::PopStyleColor(); | ||||||
|  |  | ||||||
|  |       TitleBar(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (connection_established_ && streaming_ && is_client_mode_) { | ||||||
|  |       if (!resizable_) { | ||||||
|  |         resizable_ = !resizable_; | ||||||
|  |         SDL_SetWindowResizable(main_window_, SDL_TRUE); | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       ControlWindow(); | ||||||
|  |     } else { | ||||||
|  |       if (resizable_) { | ||||||
|  |         resizable_ = !resizable_; | ||||||
|  |         SDL_SetWindowResizable(main_window_, SDL_FALSE); | ||||||
|  |       } | ||||||
|  |       MainWindow(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (!fullscreen_button_pressed_) { | ||||||
|  |       ImGui::End(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // create connection | ||||||
|  |     if (SignalStatus::SignalConnected == signal_status_ && | ||||||
|  |         !is_create_connection_ && password_inited_) { | ||||||
|  |       LOG_INFO("Connected with signal server, create p2p connection"); | ||||||
|  |       is_create_connection_ = | ||||||
|  |           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_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; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     SDL_Event event; | ||||||
|  |     while (SDL_PollEvent(&event)) { | ||||||
|  |       ImGui_ImplSDL2_ProcessEvent(&event); | ||||||
|  |       if (event.type == SDL_QUIT) { | ||||||
|  |         if (streaming_) { | ||||||
|  |           LOG_INFO("Return to main interface"); | ||||||
|  |           LOG_INFO("[{}] Leave connection [{}]", client_id_, remote_id_); | ||||||
|  |           LeaveConnection(peer_reserved_ ? peer_reserved_ : peer_, | ||||||
|  |                           remote_id_.c_str()); | ||||||
|  |           if (peer_reserved_) { | ||||||
|  |             LOG_INFO("Destroy peer[reserved]"); | ||||||
|  |             DestroyPeer(&peer_reserved_); | ||||||
|  |           } | ||||||
|  |  | ||||||
|  |           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_); | ||||||
|  |  | ||||||
|  |           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); | ||||||
|  |  | ||||||
|  |           continue; | ||||||
|  |         } else { | ||||||
|  |           LOG_INFO("Quit program"); | ||||||
|  |           exit_ = true; | ||||||
|  |         } | ||||||
|  |       } else if (event.window.event == SDL_WINDOWEVENT_MAXIMIZED) { | ||||||
|  |       } 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(main_window_, &main_window_width_, | ||||||
|  |                           &main_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 = main_window_width_; | ||||||
|  |         int render_area_height = | ||||||
|  |             main_window_height_ - | ||||||
|  |             (fullscreen_button_pressed_ ? 0 : title_bar_height_); | ||||||
|  |  | ||||||
|  |         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; | ||||||
|  |         } else if (render_area_width > render_area_height * video_ratio) { | ||||||
|  |           stream_render_rect_.x = | ||||||
|  |               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; | ||||||
|  |         } 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; | ||||||
|  |         } | ||||||
|  |       } else if (event.type == SDL_WINDOWEVENT && | ||||||
|  |                  event.window.event == SDL_WINDOWEVENT_CLOSE) { | ||||||
|  |         if (connection_established_) { | ||||||
|  |           continue; | ||||||
|  |         } else { | ||||||
|  |           exit_ = true; | ||||||
|  |         } | ||||||
|  |       } else if (event.type == REFRESH_EVENT) { | ||||||
|  |         if (stream_texture_) | ||||||
|  |           if (video_width_ != texture_width_ || | ||||||
|  |               video_height_ != texture_height_) { | ||||||
|  |             texture_width_ = video_width_; | ||||||
|  |             texture_height_ = video_height_; | ||||||
|  |  | ||||||
|  |             SDL_DestroyTexture(stream_texture_); | ||||||
|  |             stream_texture_ = SDL_CreateTexture( | ||||||
|  |                 main_renderer_, stream_pixformat_, SDL_TEXTUREACCESS_STREAMING, | ||||||
|  |                 texture_width_, texture_height_); | ||||||
|  |           } | ||||||
|  |         SDL_UpdateTexture(stream_texture_, NULL, dst_buffer_, texture_width_); | ||||||
|  |       } else { | ||||||
|  |         if (connection_established_) { | ||||||
|  |           ProcessMouseKeyEven(event); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Rendering | ||||||
|  |     ImGui::Render(); | ||||||
|  |  | ||||||
|  |     SDL_RenderClear(main_renderer_); | ||||||
|  |     if (connection_established_ && received_frame_ && streaming_) { | ||||||
|  |       SDL_RenderCopy(main_renderer_, stream_texture_, NULL, | ||||||
|  |                      &stream_render_rect_); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     ImGui_ImplSDLRenderer2_RenderDrawData(ImGui::GetDrawData(), main_renderer_); | ||||||
|  |     SDL_RenderPresent(main_renderer_); | ||||||
|  |  | ||||||
|  |     // frame_count_++; | ||||||
|  |     // end_time_ = SDL_GetTicks(); | ||||||
|  |     // elapsed_time_ = end_time_ - start_time_; | ||||||
|  |     // if (elapsed_time_ >= 1000) { | ||||||
|  |     //   fps_ = frame_count_ / (elapsed_time_ / 1000); | ||||||
|  |     //   frame_count_ = 0; | ||||||
|  |     //   window_title = "Remote Desk Client FPS [" + std::to_string(fps_) + | ||||||
|  |     //                  "] status [" + connection_status_str_ + "|" + | ||||||
|  |     //                  connection_status_str_ + "]"; | ||||||
|  |     //   // For MacOS, UI frameworks can only be called from the main thread | ||||||
|  |     //   SDL_SetWindowTitle(main_window_, window_title.c_str()); | ||||||
|  |     //   start_time_ = end_time_; | ||||||
|  |     // } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   // Cleanup | ||||||
|  |   if (screen_capturer_) { | ||||||
|  |     screen_capturer_->Destroy(); | ||||||
|  |     delete screen_capturer_; | ||||||
|  |     screen_capturer_ = nullptr; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   if (speaker_capturer_) { | ||||||
|  |     speaker_capturer_->Destroy(); | ||||||
|  |     delete speaker_capturer_; | ||||||
|  |     speaker_capturer_ = nullptr; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   if (mouse_controller_) { | ||||||
|  |     mouse_controller_->Destroy(); | ||||||
|  |     delete mouse_controller_; | ||||||
|  |     mouse_controller_ = nullptr; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   if (screen_capturer_factory_) { | ||||||
|  |     delete screen_capturer_factory_; | ||||||
|  |     screen_capturer_factory_ = nullptr; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   if (speaker_capturer_factory_) { | ||||||
|  |     delete speaker_capturer_factory_; | ||||||
|  |     speaker_capturer_factory_ = nullptr; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   if (device_controller_factory_) { | ||||||
|  |     delete device_controller_factory_; | ||||||
|  |     device_controller_factory_ = nullptr; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   if (peer_) { | ||||||
|  |     LOG_INFO("[{}] Leave connection [{}]", client_id_, client_id_); | ||||||
|  |     LeaveConnection(peer_, client_id_); | ||||||
|  |     is_client_mode_ = false; | ||||||
|  |     LOG_INFO("Destroy peer"); | ||||||
|  |     DestroyPeer(&peer_); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   if (peer_reserved_) { | ||||||
|  |     LOG_INFO("Destroy peer[reserved]"); | ||||||
|  |     DestroyPeer(&peer_reserved_); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   AudioDeviceDestroy(); | ||||||
|  |  | ||||||
|  |   ImGui_ImplSDLRenderer2_Shutdown(); | ||||||
|  |   ImGui_ImplSDL2_Shutdown(); | ||||||
|  |  | ||||||
|  |   SDL_DestroyTexture(stream_texture_); | ||||||
|  |   SDL_DestroyRenderer(main_renderer_); | ||||||
|  |   SDL_DestroyWindow(main_window_); | ||||||
|  |  | ||||||
|  |   SDL_Quit(); | ||||||
|  |  | ||||||
|  |   return 0; | ||||||
|  | } | ||||||
							
								
								
									
										291
									
								
								src/single_window/render.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										291
									
								
								src/single_window/render.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,291 @@ | |||||||
|  | /* | ||||||
|  |  * @Author: DI JUNKUN | ||||||
|  |  * @Date: 2024-05-29 | ||||||
|  |  * Copyright (c) 2024 by DI JUNKUN, All Rights Reserved. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #ifndef _MAIN_WINDOW_H_ | ||||||
|  | #define _MAIN_WINDOW_H_ | ||||||
|  |  | ||||||
|  | #include <SDL.h> | ||||||
|  |  | ||||||
|  | #include <atomic> | ||||||
|  | #include <chrono> | ||||||
|  | #include <string> | ||||||
|  |  | ||||||
|  | #include "../../thirdparty/projectx/src/interface/x.h" | ||||||
|  | #include "config_center.h" | ||||||
|  | #include "device_controller_factory.h" | ||||||
|  | #include "imgui.h" | ||||||
|  | #include "imgui_impl_sdl2.h" | ||||||
|  | #include "imgui_impl_sdlrenderer2.h" | ||||||
|  | #include "screen_capturer_factory.h" | ||||||
|  | #include "speaker_capturer_factory.h" | ||||||
|  |  | ||||||
|  | class Render { | ||||||
|  |  public: | ||||||
|  |   Render(); | ||||||
|  |   ~Render(); | ||||||
|  |  | ||||||
|  |  public: | ||||||
|  |   int Run(); | ||||||
|  |  | ||||||
|  |  private: | ||||||
|  |   int CreateStreamRenderWindow(); | ||||||
|  |   int TitleBar(); | ||||||
|  |   int MainWindow(); | ||||||
|  |   int LocalWindow(); | ||||||
|  |   int RemoteWindow(); | ||||||
|  |   int SettingWindow(); | ||||||
|  |   int ControlWindow(); | ||||||
|  |   int ControlBar(); | ||||||
|  |   int AboutWindow(); | ||||||
|  |   int StatusBar(); | ||||||
|  |   int ConnectionStatusWindow(); | ||||||
|  |  | ||||||
|  |  public: | ||||||
|  |   static void OnReceiveVideoBufferCb(const XVideoFrame *video_frame, | ||||||
|  |                                      const char *user_id, size_t user_id_size, | ||||||
|  |                                      void *user_data); | ||||||
|  |  | ||||||
|  |   static void OnReceiveAudioBufferCb(const char *data, size_t size, | ||||||
|  |                                      const char *user_id, size_t user_id_size, | ||||||
|  |                                      void *user_data); | ||||||
|  |  | ||||||
|  |   static void OnReceiveDataBufferCb(const char *data, size_t size, | ||||||
|  |                                     const char *user_id, size_t user_id_size, | ||||||
|  |                                     void *user_data); | ||||||
|  |  | ||||||
|  |   static void OnSignalStatusCb(SignalStatus status, void *user_data); | ||||||
|  |  | ||||||
|  |   static void OnConnectionStatusCb(ConnectionStatus status, const char *user_id, | ||||||
|  |                                    size_t user_id_size, void *user_data); | ||||||
|  |  | ||||||
|  |   static void NetStatusReport(const char *client_id, size_t client_id_size, | ||||||
|  |                               TraversalMode mode, const unsigned short send, | ||||||
|  |                               const unsigned short receive, void *user_data); | ||||||
|  |  | ||||||
|  |   static SDL_HitTestResult HitTestCallback(SDL_Window *window, | ||||||
|  |                                            const SDL_Point *area, void *data); | ||||||
|  |  | ||||||
|  |  private: | ||||||
|  |   int ProcessMouseKeyEven(SDL_Event &ev); | ||||||
|  |  | ||||||
|  |   static void SdlCaptureAudioIn(void *userdata, Uint8 *stream, int len); | ||||||
|  |   static void SdlCaptureAudioOut(void *userdata, Uint8 *stream, int len); | ||||||
|  |  | ||||||
|  |  private: | ||||||
|  |   int SaveSettingsIntoCacheFile(); | ||||||
|  |   int LoadSettingsFromCacheFile(); | ||||||
|  |  | ||||||
|  |   int StartScreenCapture(); | ||||||
|  |   int StopScreenCapture(); | ||||||
|  |  | ||||||
|  |   int StartSpeakerCapture(); | ||||||
|  |   int StopSpeakerCapture(); | ||||||
|  |  | ||||||
|  |   int StartMouseControl(); | ||||||
|  |   int StopMouseControl(); | ||||||
|  |  | ||||||
|  |   int CreateConnectionPeer(); | ||||||
|  |  | ||||||
|  |   int AudioDeviceInit(); | ||||||
|  |   int AudioDeviceDestroy(); | ||||||
|  |  | ||||||
|  |  private: | ||||||
|  |   typedef struct { | ||||||
|  |     char client_id[10]; | ||||||
|  |     char password[7]; | ||||||
|  |     int language; | ||||||
|  |     int video_quality; | ||||||
|  |     int video_encode_format; | ||||||
|  |     bool enable_hardware_video_codec; | ||||||
|  |   } CDCache; | ||||||
|  |  | ||||||
|  |  private: | ||||||
|  |   FILE *cd_cache_file_ = nullptr; | ||||||
|  |   CDCache cd_cache_; | ||||||
|  |   std::mutex cd_cache_mutex_; | ||||||
|  |  | ||||||
|  |   ConfigCenter config_center_; | ||||||
|  |  | ||||||
|  |   ConfigCenter::LANGUAGE localization_language_ = | ||||||
|  |       ConfigCenter::LANGUAGE::CHINESE; | ||||||
|  |  | ||||||
|  |   int localization_language_index_ = -1; | ||||||
|  |   int localization_language_index_last_ = -1; | ||||||
|  |  | ||||||
|  |   bool modules_inited_ = false; | ||||||
|  |  | ||||||
|  |  private: | ||||||
|  |   std::string window_title = "Remote Desk Client"; | ||||||
|  |   std::string mac_addr_str_ = ""; | ||||||
|  |   std::string connect_button_label_ = "Connect"; | ||||||
|  |   std::string fullscreen_button_label_ = "Fullscreen"; | ||||||
|  |   std::string mouse_control_button_label_ = "Mouse Control"; | ||||||
|  |   std::string audio_capture_button_label_ = "Audio Capture"; | ||||||
|  |   std::string settings_button_label_ = "Setting"; | ||||||
|  |   char input_password_tmp_[7] = ""; | ||||||
|  |   char input_password_[7] = ""; | ||||||
|  |   std::string random_password_ = ""; | ||||||
|  |   char remote_password_[7] = ""; | ||||||
|  |   char new_password_[7] = ""; | ||||||
|  |   char remote_id_display_[12] = ""; | ||||||
|  |   std::string remote_id_ = ""; | ||||||
|  |   char client_password_[20] = ""; | ||||||
|  |  | ||||||
|  |  private: | ||||||
|  |   int title_bar_width_ = 960; | ||||||
|  |   int 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; | ||||||
|  |   int stream_window_width_ = 1280; | ||||||
|  |   int 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; | ||||||
|  |  | ||||||
|  |   int control_bar_pos_x_ = 0; | ||||||
|  |   int control_bar_pos_y_ = 30; | ||||||
|  |  | ||||||
|  |   int main_window_width_real_ = 960; | ||||||
|  |   int main_window_height_real_ = 540; | ||||||
|  |   float dpi_scaling_w_ = 1.0f; | ||||||
|  |   float dpi_scaling_h_ = 1.0f; | ||||||
|  |  | ||||||
|  |   int texture_width_ = 1280; | ||||||
|  |   int texture_height_ = 720; | ||||||
|  |  | ||||||
|  |   int video_width_ = 1280; | ||||||
|  |   int video_height_ = 720; | ||||||
|  |   int video_size_ = 1280 * 720 * 3; | ||||||
|  |  | ||||||
|  |   SDL_Window *main_window_; | ||||||
|  |   SDL_Renderer *main_renderer_ = nullptr; | ||||||
|  |  | ||||||
|  |   // video window | ||||||
|  |   SDL_Texture *stream_texture_ = nullptr; | ||||||
|  |   SDL_Rect stream_render_rect_; | ||||||
|  |   uint32_t stream_pixformat_ = 0; | ||||||
|  |  | ||||||
|  |   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 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; | ||||||
|  |   bool control_mouse_ = false; | ||||||
|  |   bool audio_capture_ = true; | ||||||
|  |   bool local_id_copied_ = false; | ||||||
|  |   bool show_password_ = true; | ||||||
|  |   bool password_inited_ = false; | ||||||
|  |   bool regenerate_password_ = false; | ||||||
|  |   bool show_about_window_ = false; | ||||||
|  |   bool show_connection_status_window_ = false; | ||||||
|  |   bool show_reset_password_window_ = false; | ||||||
|  |   bool focus_on_input_widget_ = true; | ||||||
|  |   bool window_maximized_ = false; | ||||||
|  |   bool streaming_ = false; | ||||||
|  |   bool is_client_mode_ = false; | ||||||
|  |   bool is_control_bar_in_left_ = true; | ||||||
|  |   bool control_window_width_is_changing_ = false; | ||||||
|  |  | ||||||
|  |   double copy_start_time_ = 0; | ||||||
|  |   double regenerate_password_start_time_ = 0; | ||||||
|  |   double control_bar_button_pressed_time_ = 0; | ||||||
|  |  | ||||||
|  |   ImVec2 control_winodw_pos_; | ||||||
|  |  | ||||||
|  |   int fps_ = 0; | ||||||
|  |   uint32_t start_time_; | ||||||
|  |   uint32_t end_time_; | ||||||
|  |   uint32_t elapsed_time_; | ||||||
|  |   uint32_t frame_count_ = 0; | ||||||
|  |  | ||||||
|  |  private: | ||||||
|  |   ConnectionStatus connection_status_ = ConnectionStatus::Closed; | ||||||
|  |   SignalStatus signal_status_ = SignalStatus::SignalClosed; | ||||||
|  |   std::string signal_status_str_ = ""; | ||||||
|  |   std::string connection_status_str_ = ""; | ||||||
|  |   bool signal_connected_ = false; | ||||||
|  |   bool p2p_mode_ = true; | ||||||
|  |  | ||||||
|  |  private: | ||||||
|  |   PeerPtr *peer_ = nullptr; | ||||||
|  |   PeerPtr *peer_reserved_ = nullptr; | ||||||
|  |   Params params_; | ||||||
|  |  | ||||||
|  |  private: | ||||||
|  |   SDL_AudioDeviceID input_dev_; | ||||||
|  |   SDL_AudioDeviceID output_dev_; | ||||||
|  |   unsigned char audio_buffer_[960]; | ||||||
|  |   int audio_len_ = 0; | ||||||
|  |   unsigned char *dst_buffer_ = nullptr; | ||||||
|  |   int dst_buffer_capacity_ = 0; | ||||||
|  |  | ||||||
|  |  private: | ||||||
|  |   ScreenCapturerFactory *screen_capturer_factory_ = nullptr; | ||||||
|  |   ScreenCapturer *screen_capturer_ = nullptr; | ||||||
|  |   SpeakerCapturerFactory *speaker_capturer_factory_ = nullptr; | ||||||
|  |   SpeakerCapturer *speaker_capturer_ = nullptr; | ||||||
|  |   DeviceControllerFactory *device_controller_factory_ = nullptr; | ||||||
|  |   MouseController *mouse_controller_ = nullptr; | ||||||
|  |   uint32_t last_frame_time_; | ||||||
|  |  | ||||||
|  |  private: | ||||||
|  |   char client_id_[10] = ""; | ||||||
|  |   char password_saved_[7] = ""; | ||||||
|  |   int language_button_value_ = 0; | ||||||
|  |   int video_quality_button_value_ = 0; | ||||||
|  |   int video_encode_format_button_value_ = 0; | ||||||
|  |   bool enable_hardware_video_codec_ = false; | ||||||
|  |   bool enable_turn_ = false; | ||||||
|  |  | ||||||
|  |   int language_button_value_last_ = 0; | ||||||
|  |   int video_quality_button_value_last_ = 0; | ||||||
|  |   int video_encode_format_button_value_last_ = 0; | ||||||
|  |   bool enable_hardware_video_codec_last_ = false; | ||||||
|  |   bool enable_turn_last_ = false; | ||||||
|  |  | ||||||
|  |  private: | ||||||
|  |   std::atomic<bool> start_screen_capture_{false}; | ||||||
|  |   std::atomic<bool> start_mouse_control_{false}; | ||||||
|  |   std::atomic<bool> screen_capture_is_started_{false}; | ||||||
|  |   std::atomic<bool> mouse_control_is_started_{false}; | ||||||
|  |  | ||||||
|  |  private: | ||||||
|  |   bool settings_window_pos_reset_ = true; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | #endif | ||||||
							
								
								
									
										330
									
								
								src/single_window/render_callback_func.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										330
									
								
								src/single_window/render_callback_func.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,330 @@ | |||||||
|  | #include "device_controller.h" | ||||||
|  | #include "localization.h" | ||||||
|  | #include "rd_log.h" | ||||||
|  | #include "render.h" | ||||||
|  |  | ||||||
|  | // Refresh Event | ||||||
|  | #define REFRESH_EVENT (SDL_USEREVENT + 1) | ||||||
|  | #define NV12_BUFFER_SIZE 1280 * 720 * 3 / 2 | ||||||
|  |  | ||||||
|  | #ifdef REMOTE_DESK_DEBUG | ||||||
|  | #else | ||||||
|  | #define MOUSE_CONTROL 1 | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | int Render::ProcessMouseKeyEven(SDL_Event &ev) { | ||||||
|  |   if (!control_mouse_ || !connection_established_) { | ||||||
|  |     return 0; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   float ratio_x = (float)video_width_ / (float)stream_render_rect_.w; | ||||||
|  |   float ratio_y = (float)video_height_ / (float)stream_render_rect_.h; | ||||||
|  |  | ||||||
|  |   if (ev.button.x <= stream_render_rect_.x) { | ||||||
|  |     ev.button.x = 0; | ||||||
|  |   } else if (ev.button.x > stream_render_rect_.x && | ||||||
|  |              ev.button.x < stream_render_rect_.x + stream_render_rect_.w) { | ||||||
|  |     ev.button.x -= stream_render_rect_.x; | ||||||
|  |   } else if (ev.button.x >= stream_render_rect_.x + stream_render_rect_.w) { | ||||||
|  |     ev.button.x = stream_render_rect_.w; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   if (ev.button.y <= stream_render_rect_.y) { | ||||||
|  |     ev.button.y = 0; | ||||||
|  |   } else if (ev.button.y > stream_render_rect_.y && | ||||||
|  |              ev.button.y < stream_render_rect_.y + stream_render_rect_.h) { | ||||||
|  |     ev.button.y -= stream_render_rect_.y; | ||||||
|  |   } else if (ev.button.y >= stream_render_rect_.y + stream_render_rect_.h) { | ||||||
|  |     ev.button.y = stream_render_rect_.h; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   RemoteAction remote_action; | ||||||
|  |   remote_action.m.x = (size_t)(ev.button.x * ratio_x); | ||||||
|  |   remote_action.m.y = (size_t)(ev.button.y * ratio_y); | ||||||
|  |  | ||||||
|  |   if (SDL_KEYDOWN == ev.type)  // SDL_KEYUP | ||||||
|  |   { | ||||||
|  |     // printf("SDLK_DOWN: %d\n", SDL_KeyCode(ev.key.keysym.sym)); | ||||||
|  |     if (SDLK_DOWN == ev.key.keysym.sym) { | ||||||
|  |       // printf("SDLK_DOWN  \n"); | ||||||
|  |  | ||||||
|  |     } else if (SDLK_UP == ev.key.keysym.sym) { | ||||||
|  |       // printf("SDLK_UP  \n"); | ||||||
|  |  | ||||||
|  |     } else if (SDLK_LEFT == ev.key.keysym.sym) { | ||||||
|  |       // printf("SDLK_LEFT  \n"); | ||||||
|  |  | ||||||
|  |     } else if (SDLK_RIGHT == ev.key.keysym.sym) { | ||||||
|  |       // printf("SDLK_RIGHT  \n"); | ||||||
|  |     } | ||||||
|  |   } else if (SDL_MOUSEBUTTONDOWN == ev.type) { | ||||||
|  |     remote_action.type = ControlType::mouse; | ||||||
|  |     if (SDL_BUTTON_LEFT == ev.button.button) { | ||||||
|  |       remote_action.m.flag = MouseFlag::left_down; | ||||||
|  |     } else if (SDL_BUTTON_RIGHT == ev.button.button) { | ||||||
|  |       remote_action.m.flag = MouseFlag::right_down; | ||||||
|  |     } | ||||||
|  |     if (control_bar_hovered_) { | ||||||
|  |       remote_action.m.flag = MouseFlag::move; | ||||||
|  |     } | ||||||
|  |     SendData(peer_, DATA_TYPE::DATA, (const char *)&remote_action, | ||||||
|  |              sizeof(remote_action)); | ||||||
|  |   } else if (SDL_MOUSEBUTTONUP == ev.type) { | ||||||
|  |     remote_action.type = ControlType::mouse; | ||||||
|  |     if (SDL_BUTTON_LEFT == ev.button.button) { | ||||||
|  |       remote_action.m.flag = MouseFlag::left_up; | ||||||
|  |     } else if (SDL_BUTTON_RIGHT == ev.button.button) { | ||||||
|  |       remote_action.m.flag = MouseFlag::right_up; | ||||||
|  |     } | ||||||
|  |     if (control_bar_hovered_) { | ||||||
|  |       remote_action.m.flag = MouseFlag::move; | ||||||
|  |     } | ||||||
|  |     SendData(peer_, DATA_TYPE::DATA, (const char *)&remote_action, | ||||||
|  |              sizeof(remote_action)); | ||||||
|  |   } else if (SDL_MOUSEMOTION == ev.type) { | ||||||
|  |     remote_action.type = ControlType::mouse; | ||||||
|  |     remote_action.m.flag = MouseFlag::move; | ||||||
|  |     SendData(peer_, DATA_TYPE::DATA, (const char *)&remote_action, | ||||||
|  |              sizeof(remote_action)); | ||||||
|  |   } else if (SDL_QUIT == ev.type) { | ||||||
|  |     SDL_Event event; | ||||||
|  |     event.type = SDL_QUIT; | ||||||
|  |     SDL_PushEvent(&event); | ||||||
|  |     printf("SDL_QUIT\n"); | ||||||
|  |     return 0; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void Render::SdlCaptureAudioIn(void *userdata, Uint8 *stream, int len) { | ||||||
|  |   Render *render = (Render *)userdata; | ||||||
|  |   if (!render) { | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   if (1) { | ||||||
|  |     if ("Connected" == render->connection_status_str_) { | ||||||
|  |       SendData(render->peer_, DATA_TYPE::AUDIO, (const char *)stream, len); | ||||||
|  |     } | ||||||
|  |   } else { | ||||||
|  |     memcpy(render->audio_buffer_, stream, len); | ||||||
|  |     render->audio_len_ = len; | ||||||
|  |     SDL_Delay(10); | ||||||
|  |     render->audio_buffer_fresh_ = true; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void Render::SdlCaptureAudioOut(void *userdata, Uint8 *stream, int len) { | ||||||
|  |   // Render *render = (Render *)userdata; | ||||||
|  |   // if ("Connected" == render->connection_status_str_) { | ||||||
|  |   //   SendData(render->peer_, DATA_TYPE::AUDIO, (const char *)stream, len); | ||||||
|  |   // } | ||||||
|  |  | ||||||
|  |   // if (!render->audio_buffer_fresh_) { | ||||||
|  |   //   return; | ||||||
|  |   // } | ||||||
|  |  | ||||||
|  |   // SDL_memset(stream, 0, len); | ||||||
|  |  | ||||||
|  |   // if (render->audio_len_ == 0) { | ||||||
|  |   //   return; | ||||||
|  |   // } else { | ||||||
|  |   // } | ||||||
|  |  | ||||||
|  |   // len = (len > render->audio_len_ ? render->audio_len_ : len); | ||||||
|  |   // SDL_MixAudioFormat(stream, render->audio_buffer_, AUDIO_S16LSB, len, | ||||||
|  |   //                    SDL_MIX_MAXVOLUME); | ||||||
|  |   // render->audio_buffer_fresh_ = false; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void Render::OnReceiveVideoBufferCb(const XVideoFrame *video_frame, | ||||||
|  |                                     const char *user_id, size_t user_id_size, | ||||||
|  |                                     void *user_data) { | ||||||
|  |   Render *render = (Render *)user_data; | ||||||
|  |   if (!render) { | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   if (render->connection_established_) { | ||||||
|  |     if (!render->dst_buffer_) { | ||||||
|  |       render->dst_buffer_capacity_ = video_frame->size; | ||||||
|  |       render->dst_buffer_ = new unsigned char[video_frame->size]; | ||||||
|  |       // 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) { | ||||||
|  |       delete render->dst_buffer_; | ||||||
|  |       render->dst_buffer_capacity_ = video_frame->size; | ||||||
|  |       render->dst_buffer_ = new unsigned char[video_frame->size]; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     memcpy(render->dst_buffer_, video_frame->data, video_frame->size); | ||||||
|  |     render->video_width_ = video_frame->width; | ||||||
|  |     render->video_height_ = video_frame->height; | ||||||
|  |     render->video_size_ = video_frame->size; | ||||||
|  |  | ||||||
|  |     SDL_Event event; | ||||||
|  |     event.type = REFRESH_EVENT; | ||||||
|  |     SDL_PushEvent(&event); | ||||||
|  |     render->received_frame_ = true; | ||||||
|  |     render->streaming_ = true; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void Render::OnReceiveAudioBufferCb(const char *data, size_t size, | ||||||
|  |                                     const char *user_id, size_t user_id_size, | ||||||
|  |                                     void *user_data) { | ||||||
|  |   Render *render = (Render *)user_data; | ||||||
|  |   if (!render) { | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   render->audio_buffer_fresh_ = true; | ||||||
|  |   SDL_QueueAudio(render->output_dev_, data, (uint32_t)size); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void Render::OnReceiveDataBufferCb(const char *data, size_t size, | ||||||
|  |                                    const char *user_id, size_t user_id_size, | ||||||
|  |                                    void *user_data) { | ||||||
|  |   Render *render = (Render *)user_data; | ||||||
|  |   if (!render) { | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   std::string user(user_id, user_id_size); | ||||||
|  |   RemoteAction remote_action; | ||||||
|  |   memcpy(&remote_action, data, sizeof(remote_action)); | ||||||
|  |  | ||||||
|  |   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(); | ||||||
|  |     } else { | ||||||
|  |       render->StopSpeakerCapture(); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void Render::OnSignalStatusCb(SignalStatus status, void *user_data) { | ||||||
|  |   Render *render = (Render *)user_data; | ||||||
|  |   if (!render) { | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   render->signal_status_ = status; | ||||||
|  |   if (SignalStatus::SignalConnecting == status) { | ||||||
|  |     render->signal_status_str_ = "SignalConnecting"; | ||||||
|  |     render->signal_connected_ = false; | ||||||
|  |   } else if (SignalStatus::SignalConnected == status) { | ||||||
|  |     render->signal_status_str_ = "SignalConnected"; | ||||||
|  |     render->signal_connected_ = true; | ||||||
|  |   } else if (SignalStatus::SignalFailed == status) { | ||||||
|  |     render->signal_status_str_ = "SignalFailed"; | ||||||
|  |     render->signal_connected_ = false; | ||||||
|  |   } else if (SignalStatus::SignalClosed == status) { | ||||||
|  |     render->signal_status_str_ = "SignalClosed"; | ||||||
|  |     render->signal_connected_ = false; | ||||||
|  |   } else if (SignalStatus::SignalReconnecting == status) { | ||||||
|  |     render->signal_status_str_ = "SignalReconnecting"; | ||||||
|  |     render->signal_connected_ = false; | ||||||
|  |   } else if (SignalStatus::SignalServerClosed == status) { | ||||||
|  |     render->signal_status_str_ = "SignalServerClosed"; | ||||||
|  |     render->signal_connected_ = false; | ||||||
|  |     render->is_create_connection_ = false; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void Render::OnConnectionStatusCb(ConnectionStatus status, const char *user_id, | ||||||
|  |                                   const size_t user_id_size, void *user_data) { | ||||||
|  |   Render *render = (Render *)user_data; | ||||||
|  |   if (!render) { | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   render->connection_status_ = status; | ||||||
|  |   render->show_connection_status_window_ = true; | ||||||
|  |   if (ConnectionStatus::Connecting == status) { | ||||||
|  |     render->connection_status_str_ = "Connecting"; | ||||||
|  |   } else if (ConnectionStatus::Gathering == status) { | ||||||
|  |     render->connection_status_str_ = "Gathering"; | ||||||
|  |   } else if (ConnectionStatus::Connected == status) { | ||||||
|  |     render->connection_status_str_ = "Connected"; | ||||||
|  |     render->connection_established_ = true; | ||||||
|  |     if (render->peer_reserved_ || !render->is_client_mode_) { | ||||||
|  |       render->start_screen_capture_ = true; | ||||||
|  |       render->start_mouse_control_ = true; | ||||||
|  |     } | ||||||
|  |   } else if (ConnectionStatus::Disconnected == status) { | ||||||
|  |     render->connection_status_str_ = "Disconnected"; | ||||||
|  |     render->password_validating_time_ = 0; | ||||||
|  |   } else if (ConnectionStatus::Failed == status) { | ||||||
|  |     render->connection_status_str_ = "Failed"; | ||||||
|  |     render->password_validating_time_ = 0; | ||||||
|  |   } else if (ConnectionStatus::Closed == status) { | ||||||
|  |     render->connection_status_str_ = "Closed"; | ||||||
|  |     render->password_validating_time_ = 0; | ||||||
|  |     render->start_screen_capture_ = false; | ||||||
|  |     render->start_mouse_control_ = false; | ||||||
|  |     render->connection_established_ = false; | ||||||
|  |     render->control_mouse_ = false; | ||||||
|  |     if (render->audio_capture_) { | ||||||
|  |       render->StopSpeakerCapture(); | ||||||
|  |       render->audio_capture_ = false; | ||||||
|  |       render->audio_capture_button_pressed_ = false; | ||||||
|  |     } | ||||||
|  |     render->exit_video_window_ = false; | ||||||
|  |     if (!render->rejoin_) { | ||||||
|  |       memset(render->remote_password_, 0, sizeof(render->remote_password_)); | ||||||
|  |     } | ||||||
|  |     if (render->dst_buffer_) { | ||||||
|  |       memset(render->dst_buffer_, 0, render->dst_buffer_capacity_); | ||||||
|  |       SDL_UpdateTexture(render->stream_texture_, NULL, render->dst_buffer_, | ||||||
|  |                         render->texture_width_); | ||||||
|  |     } | ||||||
|  |   } else if (ConnectionStatus::IncorrectPassword == status) { | ||||||
|  |     render->connection_status_str_ = "Incorrect password"; | ||||||
|  |     render->password_validating_ = false; | ||||||
|  |     render->password_validating_time_++; | ||||||
|  |     if (render->connect_button_pressed_) { | ||||||
|  |       render->connection_established_ = false; | ||||||
|  |       render->connect_button_label_ = | ||||||
|  |           render->connect_button_pressed_ | ||||||
|  |               ? localization::disconnect[render->localization_language_index_] | ||||||
|  |               : localization::connect[render->localization_language_index_]; | ||||||
|  |     } | ||||||
|  |   } else if (ConnectionStatus::NoSuchTransmissionId == status) { | ||||||
|  |     render->connection_status_str_ = "No such transmission id"; | ||||||
|  |     if (render->connect_button_pressed_) { | ||||||
|  |       render->connection_established_ = false; | ||||||
|  |       render->connect_button_label_ = | ||||||
|  |           render->connect_button_pressed_ | ||||||
|  |               ? localization::disconnect[render->localization_language_index_] | ||||||
|  |               : localization::connect[render->localization_language_index_]; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void Render::NetStatusReport(const char *client_id, size_t client_id_size, | ||||||
|  |                              TraversalMode mode, const unsigned short send, | ||||||
|  |                              const unsigned short receive, void *user_data) { | ||||||
|  |   Render *render = (Render *)user_data; | ||||||
|  |   if (!render) { | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   if (0 == strcmp(render->client_id_, "")) { | ||||||
|  |     memset(&render->client_id_, 0, sizeof(render->client_id_)); | ||||||
|  |     strncpy(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)); | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										277
									
								
								src/single_window/setting_window.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										277
									
								
								src/single_window/setting_window.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,277 @@ | |||||||
|  | #include "IconsFontAwesome6.h" | ||||||
|  | #include "layout_style.h" | ||||||
|  | #include "localization.h" | ||||||
|  | #include "rd_log.h" | ||||||
|  | #include "render.h" | ||||||
|  |  | ||||||
|  | int Render::SettingWindow() { | ||||||
|  |   if (show_settings_window_) { | ||||||
|  |     if (settings_window_pos_reset_) { | ||||||
|  |       const ImGuiViewport *viewport = ImGui::GetMainViewport(); | ||||||
|  |       if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) { | ||||||
|  |         ImGui::SetNextWindowPos( | ||||||
|  |             ImVec2((viewport->WorkSize.x - viewport->WorkPos.x - | ||||||
|  |                     SETTINGS_WINDOW_WIDTH_CN) / | ||||||
|  |                        2, | ||||||
|  |                    (viewport->WorkSize.y - viewport->WorkPos.y - | ||||||
|  |                     SETTINGS_WINDOW_HEIGHT_CN) / | ||||||
|  |                        2)); | ||||||
|  |  | ||||||
|  |         ImGui::SetNextWindowSize( | ||||||
|  |             ImVec2(SETTINGS_WINDOW_WIDTH_CN, SETTINGS_WINDOW_HEIGHT_CN)); | ||||||
|  |       } else { | ||||||
|  |         ImGui::SetNextWindowPos( | ||||||
|  |             ImVec2((viewport->WorkSize.x - viewport->WorkPos.x - | ||||||
|  |                     SETTINGS_WINDOW_WIDTH_EN) / | ||||||
|  |                        2, | ||||||
|  |                    (viewport->WorkSize.y - viewport->WorkPos.y - | ||||||
|  |                     SETTINGS_WINDOW_HEIGHT_EN) / | ||||||
|  |                        2)); | ||||||
|  |  | ||||||
|  |         ImGui::SetNextWindowSize( | ||||||
|  |             ImVec2(SETTINGS_WINDOW_WIDTH_EN, SETTINGS_WINDOW_HEIGHT_EN)); | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       settings_window_pos_reset_ = false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Settings | ||||||
|  |     { | ||||||
|  |       ImGui::SetWindowFontScale(0.5f); | ||||||
|  |       ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(1.0f, 1.0f, 1.0f, 1.0f)); | ||||||
|  |       ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 5.0f); | ||||||
|  |       ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 3.0f); | ||||||
|  |  | ||||||
|  |       ImGui::Begin(localization::settings[localization_language_index_].c_str(), | ||||||
|  |                    nullptr, | ||||||
|  |                    ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse | | ||||||
|  |                        ImGuiWindowFlags_NoSavedSettings); | ||||||
|  |       ImGui::SetWindowFontScale(1.0f); | ||||||
|  |       ImGui::SetWindowFontScale(0.5f); | ||||||
|  |       ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f); | ||||||
|  |       { | ||||||
|  |         const char *language_items[] = { | ||||||
|  |             localization::language_zh[localization_language_index_].c_str(), | ||||||
|  |             localization::language_en[localization_language_index_].c_str()}; | ||||||
|  |  | ||||||
|  |         ImGui::SetCursorPosY(32); | ||||||
|  |         ImGui::Text( | ||||||
|  |             "%s", localization::language[localization_language_index_].c_str()); | ||||||
|  |         if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) { | ||||||
|  |           ImGui::SetCursorPosX(LANGUAGE_SELECT_WINDOW_PADDING_CN); | ||||||
|  |         } else { | ||||||
|  |           ImGui::SetCursorPosX(LANGUAGE_SELECT_WINDOW_PADDING_EN); | ||||||
|  |         } | ||||||
|  |         ImGui::SetCursorPosY(30); | ||||||
|  |         ImGui::SetNextItemWidth(SETTINGS_SELECT_WINDOW_WIDTH); | ||||||
|  |  | ||||||
|  |         ImGui::Combo("##language", &language_button_value_, language_items, | ||||||
|  |                      IM_ARRAYSIZE(language_items)); | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       ImGui::Separator(); | ||||||
|  |  | ||||||
|  |       { | ||||||
|  |         const char *video_quality_items[] = { | ||||||
|  |             localization::video_quality_high[localization_language_index_] | ||||||
|  |                 .c_str(), | ||||||
|  |             localization::video_quality_medium[localization_language_index_] | ||||||
|  |                 .c_str(), | ||||||
|  |             localization::video_quality_low[localization_language_index_] | ||||||
|  |                 .c_str()}; | ||||||
|  |  | ||||||
|  |         ImGui::SetCursorPosY(62); | ||||||
|  |         ImGui::Text( | ||||||
|  |             "%s", | ||||||
|  |             localization::video_quality[localization_language_index_].c_str()); | ||||||
|  |  | ||||||
|  |         if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) { | ||||||
|  |           ImGui::SetCursorPosX(VIDEO_QUALITY_SELECT_WINDOW_PADDING_CN); | ||||||
|  |         } else { | ||||||
|  |           ImGui::SetCursorPosX(VIDEO_QUALITY_SELECT_WINDOW_PADDING_EN); | ||||||
|  |         } | ||||||
|  |         ImGui::SetCursorPosY(60); | ||||||
|  |         ImGui::SetNextItemWidth(SETTINGS_SELECT_WINDOW_WIDTH); | ||||||
|  |  | ||||||
|  |         ImGui::Combo("##video_quality", &video_quality_button_value_, | ||||||
|  |                      video_quality_items, IM_ARRAYSIZE(video_quality_items)); | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       ImGui::Separator(); | ||||||
|  |  | ||||||
|  |       { | ||||||
|  |         const char *video_encode_format_items[] = { | ||||||
|  |             localization::av1[localization_language_index_].c_str(), | ||||||
|  |             localization::h264[localization_language_index_].c_str()}; | ||||||
|  |  | ||||||
|  |         ImGui::SetCursorPosY(92); | ||||||
|  |         ImGui::Text( | ||||||
|  |             "%s", | ||||||
|  |             localization::video_encode_format[localization_language_index_] | ||||||
|  |                 .c_str()); | ||||||
|  |  | ||||||
|  |         if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) { | ||||||
|  |           ImGui::SetCursorPosX(VIDEO_ENCODE_FORMAT_SELECT_WINDOW_PADDING_CN); | ||||||
|  |         } else { | ||||||
|  |           ImGui::SetCursorPosX(VIDEO_ENCODE_FORMAT_SELECT_WINDOW_PADDING_EN); | ||||||
|  |         } | ||||||
|  |         ImGui::SetCursorPosY(90); | ||||||
|  |         ImGui::SetNextItemWidth(SETTINGS_SELECT_WINDOW_WIDTH); | ||||||
|  |  | ||||||
|  |         ImGui::Combo( | ||||||
|  |             "##video_encode_format", &video_encode_format_button_value_, | ||||||
|  |             video_encode_format_items, IM_ARRAYSIZE(video_encode_format_items)); | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       ImGui::Separator(); | ||||||
|  |  | ||||||
|  |       { | ||||||
|  |         ImGui::SetCursorPosY(122); | ||||||
|  |         ImGui::Text("%s", localization::enable_hardware_video_codec | ||||||
|  |                               [localization_language_index_] | ||||||
|  |                                   .c_str()); | ||||||
|  |  | ||||||
|  |         if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) { | ||||||
|  |           ImGui::SetCursorPosX(ENABLE_HARDWARE_VIDEO_CODEC_CHECKBOX_PADDING_CN); | ||||||
|  |         } else { | ||||||
|  |           ImGui::SetCursorPosX(ENABLE_HARDWARE_VIDEO_CODEC_CHECKBOX_PADDING_EN); | ||||||
|  |         } | ||||||
|  |         ImGui::SetCursorPosY(120); | ||||||
|  |         ImGui::Checkbox("##enable_hardware_video_codec", | ||||||
|  |                         &enable_hardware_video_codec_); | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       ImGui::Separator(); | ||||||
|  |  | ||||||
|  |       { | ||||||
|  |         ImGui::SetCursorPosY(152); | ||||||
|  |         ImGui::Text( | ||||||
|  |             "%s", | ||||||
|  |             localization::enable_turn[localization_language_index_].c_str()); | ||||||
|  |  | ||||||
|  |         if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) { | ||||||
|  |           ImGui::SetCursorPosX(ENABLE_TURN_CHECKBOX_PADDING_CN); | ||||||
|  |         } else { | ||||||
|  |           ImGui::SetCursorPosX(ENABLE_TURN_CHECKBOX_PADDING_EN); | ||||||
|  |         } | ||||||
|  |         ImGui::SetCursorPosY(150); | ||||||
|  |         ImGui::Checkbox("##enable_turn", &enable_turn_); | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) { | ||||||
|  |         ImGui::SetCursorPosX(SETTINGS_OK_BUTTON_PADDING_CN); | ||||||
|  |       } else { | ||||||
|  |         ImGui::SetCursorPosX(SETTINGS_OK_BUTTON_PADDING_EN); | ||||||
|  |       } | ||||||
|  |       ImGui::SetCursorPosY(190.0f); | ||||||
|  |       ImGui::PopStyleVar(); | ||||||
|  |  | ||||||
|  |       // OK | ||||||
|  |       if (ImGui::Button( | ||||||
|  |               localization::ok[localization_language_index_].c_str())) { | ||||||
|  |         show_settings_window_ = false; | ||||||
|  |  | ||||||
|  |         // Language | ||||||
|  |         if (language_button_value_ == 0) { | ||||||
|  |           config_center_.SetLanguage(ConfigCenter::LANGUAGE::CHINESE); | ||||||
|  |         } else { | ||||||
|  |           config_center_.SetLanguage(ConfigCenter::LANGUAGE::ENGLISH); | ||||||
|  |         } | ||||||
|  |         language_button_value_last_ = language_button_value_; | ||||||
|  |         localization_language_ = (ConfigCenter::LANGUAGE)language_button_value_; | ||||||
|  |         localization_language_index_ = language_button_value_; | ||||||
|  |         LOG_INFO("Set localization language: {}", | ||||||
|  |                  localization_language_index_ == 0 ? "zh" : "en"); | ||||||
|  |  | ||||||
|  |         // Video quality | ||||||
|  |         if (video_quality_button_value_ == 0) { | ||||||
|  |           config_center_.SetVideoQuality(ConfigCenter::VIDEO_QUALITY::HIGH); | ||||||
|  |         } else if (video_quality_button_value_ == 1) { | ||||||
|  |           config_center_.SetVideoQuality(ConfigCenter::VIDEO_QUALITY::MEDIUM); | ||||||
|  |         } else { | ||||||
|  |           config_center_.SetVideoQuality(ConfigCenter::VIDEO_QUALITY::LOW); | ||||||
|  |         } | ||||||
|  |         video_quality_button_value_last_ = video_quality_button_value_; | ||||||
|  |  | ||||||
|  |         // Video encode format | ||||||
|  |         if (video_encode_format_button_value_ == 0) { | ||||||
|  |           config_center_.SetVideoEncodeFormat( | ||||||
|  |               ConfigCenter::VIDEO_ENCODE_FORMAT::AV1); | ||||||
|  |         } else if (video_encode_format_button_value_ == 1) { | ||||||
|  |           config_center_.SetVideoEncodeFormat( | ||||||
|  |               ConfigCenter::VIDEO_ENCODE_FORMAT::H264); | ||||||
|  |         } | ||||||
|  |         video_encode_format_button_value_last_ = | ||||||
|  |             video_encode_format_button_value_; | ||||||
|  |  | ||||||
|  |         // Hardware video codec | ||||||
|  |         if (enable_hardware_video_codec_) { | ||||||
|  |           config_center_.SetHardwareVideoCodec(true); | ||||||
|  |         } else { | ||||||
|  |           config_center_.SetHardwareVideoCodec(false); | ||||||
|  |         } | ||||||
|  |         enable_hardware_video_codec_last_ = enable_hardware_video_codec_; | ||||||
|  |  | ||||||
|  |         // TURN mode | ||||||
|  |         if (enable_turn_) { | ||||||
|  |           config_center_.SetTurn(true); | ||||||
|  |         } else { | ||||||
|  |           config_center_.SetTurn(false); | ||||||
|  |         } | ||||||
|  |         enable_turn_last_ = enable_turn_; | ||||||
|  |  | ||||||
|  |         SaveSettingsIntoCacheFile(); | ||||||
|  |         settings_window_pos_reset_ = true; | ||||||
|  |  | ||||||
|  |         // Recreate peer instance | ||||||
|  |         LoadSettingsFromCacheFile(); | ||||||
|  |  | ||||||
|  |         // Recreate peer instance | ||||||
|  |         { | ||||||
|  |           LOG_INFO("Recreate peer instance"); | ||||||
|  |           DestroyPeer(&peer_); | ||||||
|  |           is_create_connection_ = false; | ||||||
|  |           CreateConnectionPeer(); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       ImGui::SameLine(); | ||||||
|  |       // Cancel | ||||||
|  |       if (ImGui::Button( | ||||||
|  |               localization::cancel[localization_language_index_].c_str())) { | ||||||
|  |         show_settings_window_ = false; | ||||||
|  |         if (language_button_value_ != language_button_value_last_) { | ||||||
|  |           language_button_value_ = language_button_value_last_; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (video_quality_button_value_ != video_quality_button_value_last_) { | ||||||
|  |           video_quality_button_value_ = video_quality_button_value_last_; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (video_encode_format_button_value_ != | ||||||
|  |             video_encode_format_button_value_last_) { | ||||||
|  |           video_encode_format_button_value_ = | ||||||
|  |               video_encode_format_button_value_last_; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (enable_hardware_video_codec_ != enable_hardware_video_codec_last_) { | ||||||
|  |           enable_hardware_video_codec_ = enable_hardware_video_codec_last_; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (enable_turn_ != enable_turn_last_) { | ||||||
|  |           enable_turn_ = enable_turn_last_; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         settings_window_pos_reset_ = true; | ||||||
|  |       } | ||||||
|  |       ImGui::SetWindowFontScale(1.0f); | ||||||
|  |       ImGui::SetWindowFontScale(0.5f); | ||||||
|  |       ImGui::End(); | ||||||
|  |       ImGui::PopStyleVar(2); | ||||||
|  |       ImGui::PopStyleColor(); | ||||||
|  |       ImGui::SetWindowFontScale(1.0f); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   return 0; | ||||||
|  | } | ||||||
							
								
								
									
										39
									
								
								src/single_window/status_bar.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								src/single_window/status_bar.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,39 @@ | |||||||
|  | #include "localization.h" | ||||||
|  | #include "render.h" | ||||||
|  |  | ||||||
|  | int Render::StatusBar() { | ||||||
|  |   ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(1.0f, 1.0f, 1.0f, 1.0f)); | ||||||
|  |   static bool a, b, c, d, e; | ||||||
|  |   ImGui::SetNextWindowPos( | ||||||
|  |       ImVec2(0, main_window_height_default_ - status_bar_height_ - 1), | ||||||
|  |       ImGuiCond_Always); | ||||||
|  |  | ||||||
|  |   ImGui::BeginChild( | ||||||
|  |       "StatusBar", ImVec2(main_window_width_, status_bar_height_ + 1), | ||||||
|  |       ImGuiChildFlags_Border, | ||||||
|  |       ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoBringToFrontOnFocus); | ||||||
|  |  | ||||||
|  |   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); | ||||||
|  |  | ||||||
|  |   ImGui::SetWindowFontScale(0.5f); | ||||||
|  |   draw_list->AddText( | ||||||
|  |       ImVec2(25, main_window_height_default_ - status_bar_height_ + 3.0f), | ||||||
|  |       ImColor(0.0f, 0.0f, 0.0f), | ||||||
|  |       signal_connected_ | ||||||
|  |           ? localization::signal_connected[localization_language_index_].c_str() | ||||||
|  |           : localization::signal_disconnected[localization_language_index_] | ||||||
|  |                 .c_str()); | ||||||
|  |   ImGui::SetWindowFontScale(1.0f); | ||||||
|  |  | ||||||
|  |   ImGui::PopStyleColor(); | ||||||
|  |   ImGui::EndChild(); | ||||||
|  |   return 0; | ||||||
|  | } | ||||||
							
								
								
									
										156
									
								
								src/single_window/title_bar.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										156
									
								
								src/single_window/title_bar.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,156 @@ | |||||||
|  | #include "IconsFontAwesome6.h" | ||||||
|  | #include "localization.h" | ||||||
|  | #include "render.h" | ||||||
|  |  | ||||||
|  | #define BUTTON_PADDING 36.0f | ||||||
|  |  | ||||||
|  | int Render::TitleBar() { | ||||||
|  |   ImGui::PushStyleColor(ImGuiCol_MenuBarBg, ImVec4(1, 1, 1, 0.0f)); | ||||||
|  |   ImGui::SetNextWindowPos(ImVec2(0, 0), ImGuiCond_Always); | ||||||
|  |   ImGui::SetWindowFontScale(0.8f); | ||||||
|  |   ImGui::BeginChild("TitleBar", ImVec2(main_window_width_, title_bar_height_), | ||||||
|  |                     ImGuiChildFlags_None, | ||||||
|  |                     ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoDecoration | | ||||||
|  |                         ImGuiWindowFlags_NoBringToFrontOnFocus); | ||||||
|  |   ImDrawList* draw_list = ImGui::GetWindowDrawList(); | ||||||
|  |  | ||||||
|  |   if (ImGui::BeginMenuBar()) { | ||||||
|  |     ImGui::SetCursorPosX(main_window_width_ - (streaming_ | ||||||
|  |                                                    ? BUTTON_PADDING * 4 - 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_) { | ||||||
|  |       float bar_pos_x = ImGui::GetCursorPosX() + 6; | ||||||
|  |       float bar_pos_y = ImGui::GetCursorPosY() + 15; | ||||||
|  |       std::string menu_button = "    ";  // ICON_FA_BARS; | ||||||
|  |       if (ImGui::BeginMenu(menu_button.c_str())) { | ||||||
|  |         ImGui::SetWindowFontScale(0.5f); | ||||||
|  |         if (ImGui::MenuItem( | ||||||
|  |                 localization::settings[localization_language_index_].c_str())) { | ||||||
|  |           show_settings_window_ = true; | ||||||
|  |         } | ||||||
|  |         if (ImGui::MenuItem( | ||||||
|  |                 localization::about[localization_language_index_].c_str())) { | ||||||
|  |           show_about_window_ = true; | ||||||
|  |         } | ||||||
|  |         ImGui::SetWindowFontScale(1.0f); | ||||||
|  |         ImGui::EndMenu(); | ||||||
|  |       } | ||||||
|  |       float menu_bar_line_size = 15.0f; | ||||||
|  |       draw_list->AddLine(ImVec2(bar_pos_x, bar_pos_y - 6), | ||||||
|  |                          ImVec2(bar_pos_x + menu_bar_line_size, bar_pos_y - 6), | ||||||
|  |                          IM_COL32(0, 0, 0, 255)); | ||||||
|  |       draw_list->AddLine(ImVec2(bar_pos_x, bar_pos_y), | ||||||
|  |                          ImVec2(bar_pos_x + menu_bar_line_size, bar_pos_y), | ||||||
|  |                          IM_COL32(0, 0, 0, 255)); | ||||||
|  |       draw_list->AddLine(ImVec2(bar_pos_x, bar_pos_y + 6), | ||||||
|  |                          ImVec2(bar_pos_x + menu_bar_line_size, bar_pos_y + 6), | ||||||
|  |                          IM_COL32(0, 0, 0, 255)); | ||||||
|  |       ImGui::PopStyleColor(2); | ||||||
|  |  | ||||||
|  |       { | ||||||
|  |         SettingWindow(); | ||||||
|  |         AboutWindow(); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0)); | ||||||
|  |     ImGui::SetCursorPosX(main_window_width_ - (streaming_ | ||||||
|  |                                                    ? BUTTON_PADDING * 3 | ||||||
|  |                                                    : 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)); | ||||||
|  |  | ||||||
|  |     float minimize_pos_x = ImGui::GetCursorPosX() + 12; | ||||||
|  |     float minimize_pos_y = ImGui::GetCursorPosY() + 15; | ||||||
|  |     std::string window_minimize_button = "##minimize";  // ICON_FA_MINUS; | ||||||
|  |     if (ImGui::Button(window_minimize_button.c_str(), | ||||||
|  |                       ImVec2(BUTTON_PADDING, 30))) { | ||||||
|  |       SDL_MinimizeWindow(main_window_); | ||||||
|  |     } | ||||||
|  |     draw_list->AddLine(ImVec2(minimize_pos_x, minimize_pos_y), | ||||||
|  |                        ImVec2(minimize_pos_x + 12, minimize_pos_y), | ||||||
|  |                        IM_COL32(0, 0, 0, 255)); | ||||||
|  |     ImGui::PopStyleColor(2); | ||||||
|  |  | ||||||
|  |     if (streaming_) { | ||||||
|  |       ImGui::SetCursorPosX(main_window_width_ - BUTTON_PADDING * 2); | ||||||
|  |       ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0, 0, 0, 0.1f)); | ||||||
|  |       ImGui::PushStyleColor(ImGuiCol_ButtonActive, | ||||||
|  |                             ImVec4(1.0f, 1.0f, 1.0f, 1.0f)); | ||||||
|  |  | ||||||
|  |       if (window_maximized_) { | ||||||
|  |         float pos_x_top = ImGui::GetCursorPosX() + 11; | ||||||
|  |         float pos_y_top = ImGui::GetCursorPosY() + 11; | ||||||
|  |         float pos_x_bottom = ImGui::GetCursorPosX() + 13; | ||||||
|  |         float pos_y_bottom = ImGui::GetCursorPosY() + 9; | ||||||
|  |         std::string window_restore_button = | ||||||
|  |             "##restore";  // ICON_FA_WINDOW_RESTORE; | ||||||
|  |         if (ImGui::Button(window_restore_button.c_str(), | ||||||
|  |                           ImVec2(BUTTON_PADDING, 30))) { | ||||||
|  |           SDL_RestoreWindow(main_window_); | ||||||
|  |           window_maximized_ = false; | ||||||
|  |         } | ||||||
|  |         draw_list->AddRect(ImVec2(pos_x_top, pos_y_top), | ||||||
|  |                            ImVec2(pos_x_top + 12, pos_y_top + 12), | ||||||
|  |                            IM_COL32(0, 0, 0, 255)); | ||||||
|  |         draw_list->AddRect(ImVec2(pos_x_bottom, pos_y_bottom), | ||||||
|  |                            ImVec2(pos_x_bottom + 12, pos_y_bottom + 12), | ||||||
|  |                            IM_COL32(0, 0, 0, 255)); | ||||||
|  |         draw_list->AddRectFilled(ImVec2(pos_x_top + 1, pos_y_top + 1), | ||||||
|  |                                  ImVec2(pos_x_top + 11, pos_y_top + 11), | ||||||
|  |                                  IM_COL32(255, 255, 255, 255)); | ||||||
|  |       } else { | ||||||
|  |         float maximize_pos_x = ImGui::GetCursorPosX() + 12; | ||||||
|  |         float maximize_pos_y = ImGui::GetCursorPosY() + 10; | ||||||
|  |         std::string window_maximize_button = | ||||||
|  |             "##maximize";  // ICON_FA_SQUARE_FULL; | ||||||
|  |         if (ImGui::Button(window_maximize_button.c_str(), | ||||||
|  |                           ImVec2(BUTTON_PADDING, 30))) { | ||||||
|  |           SDL_MaximizeWindow(main_window_); | ||||||
|  |           window_maximized_ = !window_maximized_; | ||||||
|  |         } | ||||||
|  |         draw_list->AddRect(ImVec2(maximize_pos_x, maximize_pos_y), | ||||||
|  |                            ImVec2(maximize_pos_x + 12, maximize_pos_y + 12), | ||||||
|  |                            IM_COL32(0, 0, 0, 255)); | ||||||
|  |       } | ||||||
|  |       ImGui::PopStyleColor(2); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     ImGui::SetCursorPosX(main_window_width_ - BUTTON_PADDING); | ||||||
|  |     ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(1.0f, 0, 0, 1.0f)); | ||||||
|  |     ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(1.0f, 0, 0, 0.5f)); | ||||||
|  |  | ||||||
|  |     float xmark_pos_x = ImGui::GetCursorPosX() + 18; | ||||||
|  |     float xmark_pos_y = ImGui::GetCursorPosY() + 16; | ||||||
|  |     float xmark_size = 12.0f; | ||||||
|  |     std::string close_button = "##xmark";  // ICON_FA_XMARK; | ||||||
|  |     if (ImGui::Button(close_button.c_str(), ImVec2(BUTTON_PADDING, 30))) { | ||||||
|  |       SDL_Event event; | ||||||
|  |       event.type = SDL_QUIT; | ||||||
|  |       SDL_PushEvent(&event); | ||||||
|  |     } | ||||||
|  |     draw_list->AddLine(ImVec2(xmark_pos_x - xmark_size / 2 - 0.25f, | ||||||
|  |                               xmark_pos_y - xmark_size / 2 + 0.75f), | ||||||
|  |                        ImVec2(xmark_pos_x + xmark_size / 2 - 1.5f, | ||||||
|  |                               xmark_pos_y + xmark_size / 2 - 0.5f), | ||||||
|  |                        IM_COL32(0, 0, 0, 255)); | ||||||
|  |     draw_list->AddLine(ImVec2(xmark_pos_x + xmark_size / 2 - 1.75f, | ||||||
|  |                               xmark_pos_y - xmark_size / 2 + 0.75f), | ||||||
|  |                        ImVec2(xmark_pos_x - xmark_size / 2, | ||||||
|  |                               xmark_pos_y + xmark_size / 2 - 1.0f), | ||||||
|  |                        IM_COL32(0, 0, 0, 255)); | ||||||
|  |  | ||||||
|  |     ImGui::PopStyleColor(2); | ||||||
|  |  | ||||||
|  |     ImGui::PopStyleColor(1); | ||||||
|  |   } | ||||||
|  |   ImGui::SetWindowFontScale(1.0f); | ||||||
|  |  | ||||||
|  |   ImGui::EndChild(); | ||||||
|  |   ImGui::PopStyleColor(2); | ||||||
|  |   return 0; | ||||||
|  | } | ||||||
							
								
								
									
										13
									
								
								src/speaker_capturer/linux/speaker_capturer_linux.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								src/speaker_capturer/linux/speaker_capturer_linux.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | |||||||
|  | #include "speaker_capturer_linux.h" | ||||||
|  |  | ||||||
|  | SpeakerCapturerLinux::SpeakerCapturerLinux() {} | ||||||
|  |  | ||||||
|  | SpeakerCapturerLinux::~SpeakerCapturerLinux() {} | ||||||
|  |  | ||||||
|  | int SpeakerCapturerLinux::Init(speaker_data_cb cb) { return 0; } | ||||||
|  | int SpeakerCapturerLinux::Destroy() { return 0; } | ||||||
|  | int SpeakerCapturerLinux::Start() { return 0; } | ||||||
|  | int SpeakerCapturerLinux::Stop() { return 0; } | ||||||
|  |  | ||||||
|  | int SpeakerCapturerLinux::Pause() { return 0; } | ||||||
|  | int SpeakerCapturerLinux::Resume() { return 0; } | ||||||
							
								
								
									
										38
									
								
								src/speaker_capturer/linux/speaker_capturer_linux.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								src/speaker_capturer/linux/speaker_capturer_linux.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,38 @@ | |||||||
|  | /* | ||||||
|  |  * @Author: DI JUNKUN | ||||||
|  |  * @Date: 2024-08-02 | ||||||
|  |  * Copyright (c) 2024 by DI JUNKUN, All Rights Reserved. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #ifndef _SPEAKER_CAPTURER_LINUX_H_ | ||||||
|  | #define _SPEAKER_CAPTURER_LINUX_H_ | ||||||
|  |  | ||||||
|  | #include <thread> | ||||||
|  | #include <vector> | ||||||
|  |  | ||||||
|  | #include "speaker_capturer.h" | ||||||
|  |  | ||||||
|  | class SpeakerCapturerLinux : public SpeakerCapturer { | ||||||
|  |  public: | ||||||
|  |   SpeakerCapturerLinux(); | ||||||
|  |   ~SpeakerCapturerLinux(); | ||||||
|  |  | ||||||
|  |  public: | ||||||
|  |   virtual int Init(speaker_data_cb cb); | ||||||
|  |   virtual int Destroy(); | ||||||
|  |   virtual int Start(); | ||||||
|  |   virtual int Stop(); | ||||||
|  |  | ||||||
|  |   int Pause(); | ||||||
|  |   int Resume(); | ||||||
|  |  | ||||||
|  |  private: | ||||||
|  |   speaker_data_cb cb_ = nullptr; | ||||||
|  |  | ||||||
|  |  private: | ||||||
|  |   bool inited_ = false; | ||||||
|  |   // thread | ||||||
|  |   std::thread capture_thread_; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | #endif | ||||||
							
								
								
									
										14
									
								
								src/speaker_capturer/macosx/speaker_capturer_macosx.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								src/speaker_capturer/macosx/speaker_capturer_macosx.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | |||||||
|  |  | ||||||
|  | #include "speaker_capturer_macosx.h" | ||||||
|  |  | ||||||
|  | SpeakerCapturerMacosx::SpeakerCapturerMacosx() {} | ||||||
|  |  | ||||||
|  | SpeakerCapturerMacosx::~SpeakerCapturerMacosx() {} | ||||||
|  |  | ||||||
|  | int SpeakerCapturerMacosx::Init(speaker_data_cb cb) { return 0; } | ||||||
|  | int SpeakerCapturerMacosx::Destroy() { return 0; } | ||||||
|  | int SpeakerCapturerMacosx::Start() { return 0; } | ||||||
|  | int SpeakerCapturerMacosx::Stop() { return 0; } | ||||||
|  |  | ||||||
|  | int SpeakerCapturerMacosx::Pause() { return 0; } | ||||||
|  | int SpeakerCapturerMacosx::Resume() { return 0; } | ||||||
							
								
								
									
										38
									
								
								src/speaker_capturer/macosx/speaker_capturer_macosx.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								src/speaker_capturer/macosx/speaker_capturer_macosx.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,38 @@ | |||||||
|  | /* | ||||||
|  |  * @Author: DI JUNKUN | ||||||
|  |  * @Date: 2024-08-02 | ||||||
|  |  * Copyright (c) 2024 by DI JUNKUN, All Rights Reserved. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #ifndef _SPEAKER_CAPTURER_MACOSX_H_ | ||||||
|  | #define _SPEAKER_CAPTURER_MACOSX_H_ | ||||||
|  |  | ||||||
|  | #include <thread> | ||||||
|  | #include <vector> | ||||||
|  |  | ||||||
|  | #include "speaker_capturer.h" | ||||||
|  |  | ||||||
|  | class SpeakerCapturerMacosx : public SpeakerCapturer { | ||||||
|  |  public: | ||||||
|  |   SpeakerCapturerMacosx(); | ||||||
|  |   ~SpeakerCapturerMacosx(); | ||||||
|  |  | ||||||
|  |  public: | ||||||
|  |   virtual int Init(speaker_data_cb cb); | ||||||
|  |   virtual int Destroy(); | ||||||
|  |   virtual int Start(); | ||||||
|  |   virtual int Stop(); | ||||||
|  |  | ||||||
|  |   int Pause(); | ||||||
|  |   int Resume(); | ||||||
|  |  | ||||||
|  |  private: | ||||||
|  |   speaker_data_cb cb_ = nullptr; | ||||||
|  |  | ||||||
|  |  private: | ||||||
|  |   bool inited_ = false; | ||||||
|  |   // thread | ||||||
|  |   std::thread capture_thread_; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | #endif | ||||||
							
								
								
									
										26
									
								
								src/speaker_capturer/speaker_capturer.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								src/speaker_capturer/speaker_capturer.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,26 @@ | |||||||
|  | /* | ||||||
|  |  * @Author: DI JUNKUN | ||||||
|  |  * @Date: 2024-07-22 | ||||||
|  |  * Copyright (c) 2024 by DI JUNKUN, All Rights Reserved. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #ifndef _SPEAKER_CAPTURER_H_ | ||||||
|  | #define _SPEAKER_CAPTURER_H_ | ||||||
|  |  | ||||||
|  | #include <functional> | ||||||
|  |  | ||||||
|  | class SpeakerCapturer { | ||||||
|  |  public: | ||||||
|  |   typedef std::function<void(unsigned char *, size_t)> speaker_data_cb; | ||||||
|  |  | ||||||
|  |  public: | ||||||
|  |   virtual ~SpeakerCapturer() {} | ||||||
|  |  | ||||||
|  |  public: | ||||||
|  |   virtual int Init(speaker_data_cb cb) = 0; | ||||||
|  |   virtual int Destroy() = 0; | ||||||
|  |   virtual int Start() = 0; | ||||||
|  |   virtual int Stop() = 0; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | #endif | ||||||
							
								
								
									
										36
									
								
								src/speaker_capturer/speaker_capturer_factory.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								src/speaker_capturer/speaker_capturer_factory.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,36 @@ | |||||||
|  | /* | ||||||
|  |  * @Author: DI JUNKUN | ||||||
|  |  * @Date: 2024-07-22 | ||||||
|  |  * Copyright (c) 2024 by DI JUNKUN, All Rights Reserved. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #ifndef _SPEAKER_CAPTURER_FACTORY_H_ | ||||||
|  | #define _SPEAKER_CAPTURER_FACTORY_H_ | ||||||
|  |  | ||||||
|  | #ifdef _WIN32 | ||||||
|  | #include "speaker_capturer_wasapi.h" | ||||||
|  | #elif __linux__ | ||||||
|  | #include "speaker_capturer_linux.h" | ||||||
|  | #elif __APPLE__ | ||||||
|  | #include "speaker_capturer_macosx.h" | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | class SpeakerCapturerFactory { | ||||||
|  |  public: | ||||||
|  |   virtual ~SpeakerCapturerFactory() {} | ||||||
|  |  | ||||||
|  |  public: | ||||||
|  |   SpeakerCapturer* Create() { | ||||||
|  | #ifdef _WIN32 | ||||||
|  |     return new SpeakerCapturerWasapi(); | ||||||
|  | #elif __linux__ | ||||||
|  |     return new SpeakerCapturerLinux(); | ||||||
|  | #elif __APPLE__ | ||||||
|  |     return new SpeakerCapturerMacosx(); | ||||||
|  | #else | ||||||
|  |     return nullptr; | ||||||
|  | #endif | ||||||
|  |   } | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | #endif | ||||||
							
								
								
									
										100
									
								
								src/speaker_capturer/windows/speaker_capturer_wasapi.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										100
									
								
								src/speaker_capturer/windows/speaker_capturer_wasapi.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,100 @@ | |||||||
|  | #include "speaker_capturer_wasapi.h" | ||||||
|  |  | ||||||
|  | #include "rd_log.h" | ||||||
|  |  | ||||||
|  | #define MINIAUDIO_IMPLEMENTATION | ||||||
|  | #include "miniaudio.h" | ||||||
|  |  | ||||||
|  | #define SAVE_AUDIO_FILE 0 | ||||||
|  |  | ||||||
|  | static ma_device_config device_config_; | ||||||
|  | static ma_device device_; | ||||||
|  | static ma_format format_ = ma_format_s16; | ||||||
|  | static ma_uint32 sample_rate_ = ma_standard_sample_rate_48000; | ||||||
|  | static ma_uint32 channels_ = 1; | ||||||
|  | static FILE* fp_ = nullptr; | ||||||
|  |  | ||||||
|  | void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, | ||||||
|  |                    ma_uint32 frameCount) { | ||||||
|  |   SpeakerCapturerWasapi* ptr = (SpeakerCapturerWasapi*)pDevice->pUserData; | ||||||
|  |   if (ptr) { | ||||||
|  |     if (SAVE_AUDIO_FILE) { | ||||||
|  |       fwrite(pInput, frameCount * ma_get_bytes_per_frame(format_, channels_), 1, | ||||||
|  |              fp_); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     ptr->GetCallback()((unsigned char*)pInput, | ||||||
|  |                        frameCount * ma_get_bytes_per_frame(format_, channels_)); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   (void)pOutput; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | SpeakerCapturerWasapi::speaker_data_cb SpeakerCapturerWasapi::GetCallback() { | ||||||
|  |   return cb_; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | SpeakerCapturerWasapi::SpeakerCapturerWasapi() {} | ||||||
|  |  | ||||||
|  | SpeakerCapturerWasapi::~SpeakerCapturerWasapi() { | ||||||
|  |   if (SAVE_AUDIO_FILE) { | ||||||
|  |     fclose(fp_); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int SpeakerCapturerWasapi::Init(speaker_data_cb cb) { | ||||||
|  |   if (inited_) { | ||||||
|  |     return 0; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   cb_ = cb; | ||||||
|  |  | ||||||
|  |   if (SAVE_AUDIO_FILE) { | ||||||
|  |     fopen_s(&fp_, "system_audio.pcm", "wb"); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   ma_result result; | ||||||
|  |   ma_backend backends[] = {ma_backend_wasapi}; | ||||||
|  |  | ||||||
|  |   device_config_ = ma_device_config_init(ma_device_type_loopback); | ||||||
|  |   device_config_.capture.pDeviceID = NULL; | ||||||
|  |   device_config_.capture.format = format_; | ||||||
|  |   device_config_.capture.channels = channels_; | ||||||
|  |   device_config_.sampleRate = sample_rate_; | ||||||
|  |   device_config_.dataCallback = data_callback; | ||||||
|  |   device_config_.pUserData = this; | ||||||
|  |  | ||||||
|  |   result = ma_device_init_ex(backends, sizeof(backends) / sizeof(backends[0]), | ||||||
|  |                              NULL, &device_config_, &device_); | ||||||
|  |   if (result != MA_SUCCESS) { | ||||||
|  |     LOG_ERROR("Failed to initialize loopback device"); | ||||||
|  |     return -1; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   inited_ = true; | ||||||
|  |  | ||||||
|  |   return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int SpeakerCapturerWasapi::Start() { | ||||||
|  |   ma_result result = ma_device_start(&device_); | ||||||
|  |   if (result != MA_SUCCESS) { | ||||||
|  |     ma_device_uninit(&device_); | ||||||
|  |     LOG_ERROR("Failed to start device"); | ||||||
|  |     return -1; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int SpeakerCapturerWasapi::Stop() { | ||||||
|  |   ma_device_stop(&device_); | ||||||
|  |   return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int SpeakerCapturerWasapi::Destroy() { | ||||||
|  |   ma_device_uninit(&device_); | ||||||
|  |   return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int SpeakerCapturerWasapi::Pause() { return 0; } | ||||||
							
								
								
									
										35
									
								
								src/speaker_capturer/windows/speaker_capturer_wasapi.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								src/speaker_capturer/windows/speaker_capturer_wasapi.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,35 @@ | |||||||
|  | /* | ||||||
|  |  * @Author: DI JUNKUN | ||||||
|  |  * @Date: 2024-08-15 | ||||||
|  |  * Copyright (c) 2024 by DI JUNKUN, All Rights Reserved. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #ifndef _SPEAKER_CAPTURER_WASAPI_H_ | ||||||
|  | #define _SPEAKER_CAPTURER_WASAPI_H_ | ||||||
|  |  | ||||||
|  | #include "speaker_capturer.h" | ||||||
|  |  | ||||||
|  | class SpeakerCapturerWasapi : public SpeakerCapturer { | ||||||
|  |  public: | ||||||
|  |   SpeakerCapturerWasapi(); | ||||||
|  |   ~SpeakerCapturerWasapi(); | ||||||
|  |  | ||||||
|  |  public: | ||||||
|  |   virtual int Init(speaker_data_cb cb); | ||||||
|  |   virtual int Destroy(); | ||||||
|  |   virtual int Start(); | ||||||
|  |   virtual int Stop(); | ||||||
|  |  | ||||||
|  |   int Pause(); | ||||||
|  |   int Resume(); | ||||||
|  |  | ||||||
|  |   speaker_data_cb GetCallback(); | ||||||
|  |  | ||||||
|  |  private: | ||||||
|  |   speaker_data_cb cb_ = nullptr; | ||||||
|  |  | ||||||
|  |  private: | ||||||
|  |   bool inited_ = false; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | #endif | ||||||
							
								
								
									
										303
									
								
								test/audio_capture/audio_capture_wasapi.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										303
									
								
								test/audio_capture/audio_capture_wasapi.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,303 @@ | |||||||
|  | // MyAudioSink.cpp : 定义控制台应用程序的入口点。 | ||||||
|  | // | ||||||
|  |  | ||||||
|  | // #define  _CRT_SECURE_NO_WARNINGS | ||||||
|  |  | ||||||
|  | #include <Audioclient.h> | ||||||
|  | #include <Devicetopology.h> | ||||||
|  | #include <Endpointvolume.h> | ||||||
|  | #include <Mmdeviceapi.h> | ||||||
|  | #include <tchar.h> | ||||||
|  |  | ||||||
|  | #include <iostream> | ||||||
|  | //----------------------------------------------------------- | ||||||
|  | // Record an audio stream from the default audio capture | ||||||
|  | // device. The RecordAudioStream function allocates a shared | ||||||
|  | // buffer big enough to hold one second of PCM audio data. | ||||||
|  | // The function uses this buffer to stream data from the | ||||||
|  | // capture device. The main loop runs every 1/2 second. | ||||||
|  | //----------------------------------------------------------- | ||||||
|  |  | ||||||
|  | // REFERENCE_TIME time units per second and per millisecond | ||||||
|  | #define REFTIMES_PER_SEC 10000000 | ||||||
|  | #define REFTIMES_PER_MILLISEC 10000 | ||||||
|  |  | ||||||
|  | #define EXIT_ON_ERROR(hres) \ | ||||||
|  |   if (FAILED(hres)) {       \ | ||||||
|  |     goto Exit;              \ | ||||||
|  |   } | ||||||
|  |  | ||||||
|  | #define SAFE_RELEASE(punk) \ | ||||||
|  |   if ((punk) != NULL) {    \ | ||||||
|  |     (punk)->Release();     \ | ||||||
|  |     (punk) = NULL;         \ | ||||||
|  |   } | ||||||
|  |  | ||||||
|  | #define IS_INPUT_DEVICE 0  // 切换输入和输出音频设备 | ||||||
|  |  | ||||||
|  | #define BUFFER_TIME_100NS (5 * 10000000) | ||||||
|  |  | ||||||
|  | const CLSID CLSID_MMDeviceEnumerator = __uuidof(MMDeviceEnumerator); | ||||||
|  | const IID IID_IMMDeviceEnumerator = __uuidof(IMMDeviceEnumerator); | ||||||
|  | const IID IID_IAudioClient = __uuidof(IAudioClient); | ||||||
|  | const IID IID_IAudioCaptureClient = __uuidof(IAudioCaptureClient); | ||||||
|  |  | ||||||
|  | const IID IID_IDeviceTopology = __uuidof(IDeviceTopology); | ||||||
|  | const IID IID_IAudioVolumeLevel = __uuidof(IAudioVolumeLevel); | ||||||
|  | const IID IID_IPart = __uuidof(IPart); | ||||||
|  | const IID IID_IConnector = __uuidof(IConnector); | ||||||
|  | const IID IID_IAudioEndpointVolume = __uuidof(IAudioEndpointVolume); | ||||||
|  |  | ||||||
|  | class MyAudioSink { | ||||||
|  |  public: | ||||||
|  |   // WAVEFORMATEX *pwfx = NULL; | ||||||
|  |   int SetFormat(WAVEFORMATEX *pwfx); | ||||||
|  |  | ||||||
|  |   int CopyData(SHORT *pData, UINT32 numFramesAvailable, BOOL *pbDone); | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | int MyAudioSink::SetFormat(WAVEFORMATEX *pwfx) { | ||||||
|  |   printf("wFormatTag is %x\n", pwfx->wFormatTag); | ||||||
|  |   printf("nChannels is %x\n", pwfx->nChannels); | ||||||
|  |   printf("nSamplesPerSec is %d\n", pwfx->nSamplesPerSec); | ||||||
|  |   printf("nAvgBytesPerSec is %d\n", pwfx->nAvgBytesPerSec); | ||||||
|  |   printf("wBitsPerSample is %d\n", pwfx->wBitsPerSample); | ||||||
|  |  | ||||||
|  |   return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | FILE *fp; | ||||||
|  |  | ||||||
|  | int MyAudioSink::CopyData(SHORT *pData, UINT32 numFramesAvailable, | ||||||
|  |                           BOOL *pbDone) { | ||||||
|  |   if (pData != NULL) { | ||||||
|  |     size_t t = sizeof(SHORT); | ||||||
|  |     for (int i = 0; i < numFramesAvailable / t; i++) { | ||||||
|  |       double dbVal = pData[i]; | ||||||
|  |       pData[i] = dbVal;  // 可以通过不同的分母来控制声音大小 | ||||||
|  |     } | ||||||
|  |     fwrite(pData, numFramesAvailable, 1, fp); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /// pwfx->nSamplesPerSec    = 44100; | ||||||
|  | /// 不支持修改采样率, 看来只能等得到数据之后再 swr 转换了 | ||||||
|  | BOOL AdjustFormatTo16Bits(WAVEFORMATEX *pwfx) { | ||||||
|  |   BOOL bRet(FALSE); | ||||||
|  |  | ||||||
|  |   if (pwfx->wFormatTag == WAVE_FORMAT_IEEE_FLOAT) { | ||||||
|  |     pwfx->wFormatTag = WAVE_FORMAT_PCM; | ||||||
|  |     pwfx->wBitsPerSample = 16; | ||||||
|  |     pwfx->nBlockAlign = pwfx->nChannels * pwfx->wBitsPerSample / 8; | ||||||
|  |     pwfx->nAvgBytesPerSec = pwfx->nBlockAlign * pwfx->nSamplesPerSec; | ||||||
|  |  | ||||||
|  |     bRet = TRUE; | ||||||
|  |   } else if (pwfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE) { | ||||||
|  |     PWAVEFORMATEXTENSIBLE pEx = reinterpret_cast<PWAVEFORMATEXTENSIBLE>(pwfx); | ||||||
|  |     if (IsEqualGUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, pEx->SubFormat)) { | ||||||
|  |       pEx->SubFormat = KSDATAFORMAT_SUBTYPE_PCM; | ||||||
|  |       pEx->Samples.wValidBitsPerSample = 16; | ||||||
|  |       pwfx->wBitsPerSample = 16; | ||||||
|  |       pwfx->nBlockAlign = pwfx->nChannels * pwfx->wBitsPerSample / 8; | ||||||
|  |       pwfx->nAvgBytesPerSec = pwfx->nBlockAlign * pwfx->nSamplesPerSec; | ||||||
|  |  | ||||||
|  |       bRet = TRUE; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   return bRet; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | typedef unsigned long long uint64_t; | ||||||
|  | static bool have_clockfreq = false; | ||||||
|  | static LARGE_INTEGER clock_freq; | ||||||
|  | static inline uint64_t get_clockfreq(void) { | ||||||
|  |   if (!have_clockfreq) QueryPerformanceFrequency(&clock_freq); | ||||||
|  |   return clock_freq.QuadPart; | ||||||
|  | } | ||||||
|  | uint64_t os_gettime_ns(void) { | ||||||
|  |   LARGE_INTEGER current_time; | ||||||
|  |   double time_val; | ||||||
|  |  | ||||||
|  |   QueryPerformanceCounter(¤t_time); | ||||||
|  |   time_val = (double)current_time.QuadPart; | ||||||
|  |   time_val *= 1000000000.0; | ||||||
|  |   time_val /= (double)get_clockfreq(); | ||||||
|  |  | ||||||
|  |   return (uint64_t)time_val; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | HRESULT RecordAudioStream(MyAudioSink *pMySink) { | ||||||
|  |   HRESULT hr; | ||||||
|  |   REFERENCE_TIME hnsActualDuration; | ||||||
|  |   UINT32 bufferFrameCount; | ||||||
|  |   UINT32 numFramesAvailable; | ||||||
|  |   BYTE *pData; | ||||||
|  |   DWORD flags; | ||||||
|  |   REFERENCE_TIME hnsDefaultDevicePeriod(0); | ||||||
|  |  | ||||||
|  |   REFERENCE_TIME hnsRequestedDuration = REFTIMES_PER_SEC; | ||||||
|  |   IMMDeviceEnumerator *pEnumerator = NULL; | ||||||
|  |   IMMDevice *pDevice = NULL; | ||||||
|  |   IAudioClient *pAudioClient = NULL; | ||||||
|  |   IAudioCaptureClient *pCaptureClient = NULL; | ||||||
|  |   WAVEFORMATEX *pwfx = NULL; | ||||||
|  |   UINT32 packetLength = 0; | ||||||
|  |   BOOL bDone = FALSE; | ||||||
|  |   HANDLE hTimerWakeUp = NULL; | ||||||
|  |   UINT64 pos, ts; | ||||||
|  |  | ||||||
|  |   hr = CoCreateInstance(CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, | ||||||
|  |                         IID_IMMDeviceEnumerator, (void **)&pEnumerator); | ||||||
|  |  | ||||||
|  |   EXIT_ON_ERROR(hr) | ||||||
|  |  | ||||||
|  |   if (IS_INPUT_DEVICE) | ||||||
|  |     hr = pEnumerator->GetDefaultAudioEndpoint(eCapture, eCommunications, | ||||||
|  |                                               &pDevice);  // 输入 | ||||||
|  |   else | ||||||
|  |     hr = pEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, | ||||||
|  |                                               &pDevice);  // 输出 | ||||||
|  |  | ||||||
|  |   // wchar_t *w_id; | ||||||
|  |   // os_utf8_to_wcs_ptr(device_id.c_str(), device_id.size(), &w_id); | ||||||
|  |   // hr = pEnumerator->GetDevice(w_id, &pDevice); | ||||||
|  |   // bfree(w_id); | ||||||
|  |  | ||||||
|  |   EXIT_ON_ERROR(hr) | ||||||
|  |  | ||||||
|  |   hr = pDevice->Activate(IID_IAudioClient, CLSCTX_ALL, NULL, | ||||||
|  |                          (void **)&pAudioClient); | ||||||
|  |  | ||||||
|  |   EXIT_ON_ERROR(hr) | ||||||
|  |  | ||||||
|  |   hr = pAudioClient->GetMixFormat(&pwfx); | ||||||
|  |  | ||||||
|  |   EXIT_ON_ERROR(hr) | ||||||
|  |  | ||||||
|  |   // The GetDevicePeriod method retrieves the length of the periodic interval | ||||||
|  |   // separating successive processing passes by the audio engine on the data in | ||||||
|  |   // the endpoint buffer. | ||||||
|  |   hr = pAudioClient->GetDevicePeriod(&hnsDefaultDevicePeriod, NULL); | ||||||
|  |  | ||||||
|  |   EXIT_ON_ERROR(hr) | ||||||
|  |  | ||||||
|  |   AdjustFormatTo16Bits(pwfx); | ||||||
|  |  | ||||||
|  |   // 平时创建定时器使用的是WINAPI  SetTimer,不过该函数一般用于有界面的时候。 | ||||||
|  |   // 无界面的情况下,可以选择微软提供的CreateWaitableTimer和SetWaitableTimer | ||||||
|  |   // API。 | ||||||
|  |   hTimerWakeUp = CreateWaitableTimer(NULL, FALSE, NULL); | ||||||
|  |  | ||||||
|  |   DWORD flag; | ||||||
|  |   if (IS_INPUT_DEVICE) | ||||||
|  |     flag = 0; | ||||||
|  |   else | ||||||
|  |     flag = AUDCLNT_STREAMFLAGS_LOOPBACK; | ||||||
|  |  | ||||||
|  |   if (IS_INPUT_DEVICE) | ||||||
|  |     hr = pAudioClient->Initialize(AUDCLNT_SHAREMODE_SHARED, flag /*0*/, 0, 0, | ||||||
|  |                                   pwfx, NULL);  // 输入 | ||||||
|  |   else | ||||||
|  |     hr = pAudioClient->Initialize(AUDCLNT_SHAREMODE_SHARED, flag /*0*/, 0, 0, | ||||||
|  |                                   pwfx, NULL);  // 输出 | ||||||
|  |  | ||||||
|  |   EXIT_ON_ERROR(hr) | ||||||
|  |  | ||||||
|  |   // Get the size of the allocated buffer. | ||||||
|  |   hr = pAudioClient->GetBufferSize(&bufferFrameCount); | ||||||
|  |   EXIT_ON_ERROR(hr) | ||||||
|  |  | ||||||
|  |   hr = pAudioClient->GetService(IID_IAudioCaptureClient, | ||||||
|  |                                 (void **)&pCaptureClient); | ||||||
|  |  | ||||||
|  |   EXIT_ON_ERROR(hr) | ||||||
|  |  | ||||||
|  |   LARGE_INTEGER liFirstFire; | ||||||
|  |   liFirstFire.QuadPart = | ||||||
|  |       -hnsDefaultDevicePeriod / 2;  // negative means relative time | ||||||
|  |   LONG lTimeBetweenFires = (LONG)hnsDefaultDevicePeriod / 2 / | ||||||
|  |                            (10 * 1000);  // convert to milliseconds | ||||||
|  |  | ||||||
|  |   BOOL bOK = SetWaitableTimer(hTimerWakeUp, &liFirstFire, lTimeBetweenFires, | ||||||
|  |                               NULL, NULL, FALSE); | ||||||
|  |  | ||||||
|  |   // Notify the audio sink which format to use. | ||||||
|  |   hr = pMySink->SetFormat(pwfx); | ||||||
|  |   EXIT_ON_ERROR(hr) | ||||||
|  |  | ||||||
|  |   // Calculate the actual duration of the allocated buffer. | ||||||
|  |   hnsActualDuration = | ||||||
|  |       (double)REFTIMES_PER_SEC * bufferFrameCount / pwfx->nSamplesPerSec; | ||||||
|  |  | ||||||
|  |   /*************************************************************/ | ||||||
|  |   hr = pAudioClient->Start();  // Start recording. | ||||||
|  |   EXIT_ON_ERROR(hr) | ||||||
|  |   HANDLE waitArray[1] = {/*htemp hEventStop,*/ hTimerWakeUp}; | ||||||
|  |  | ||||||
|  |   // Each loop fills about half of the shared buffer. | ||||||
|  |   while (bDone == FALSE) { | ||||||
|  |     // Sleep for half the buffer duration. | ||||||
|  |     // Sleep(hnsActualDuration/REFTIMES_PER_MILLISEC/2);//这句貌似不加也可以 | ||||||
|  |     // WaitForSingleObject(hTimerWakeUp,INFINITE); | ||||||
|  |     int a = sizeof(waitArray); | ||||||
|  |     int aa = sizeof(waitArray[0]); | ||||||
|  |     WaitForMultipleObjects(sizeof(waitArray) / sizeof(waitArray[0]), waitArray, | ||||||
|  |                            FALSE, INFINITE); | ||||||
|  |     // WaitForMultipleObjects(sizeof(waitArray) / sizeof(waitArray[0]), | ||||||
|  |     // waitArray, FALSE, INFINITE); | ||||||
|  |  | ||||||
|  |     hr = pCaptureClient->GetNextPacketSize(&packetLength); | ||||||
|  |     EXIT_ON_ERROR(hr) | ||||||
|  |  | ||||||
|  |     while (packetLength != 0) { | ||||||
|  |       // Get the available data in the shared buffer. | ||||||
|  |       hr = pCaptureClient->GetBuffer(&pData, &numFramesAvailable, &flags, NULL, | ||||||
|  |                                      &ts); | ||||||
|  |       ts = ts * 100; | ||||||
|  |       uint64_t timestamp = | ||||||
|  |           os_gettime_ns();  // ts是设备时间,timestamp是系统时间 | ||||||
|  |       EXIT_ON_ERROR(hr) | ||||||
|  |  | ||||||
|  |       // 位运算,flags的标志符为2(静音状态)时,将pData置为NULL | ||||||
|  |       if (flags & AUDCLNT_BUFFERFLAGS_SILENT) { | ||||||
|  |         pData = NULL;  // Tell CopyData to write silence. | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       // Copy the available capture data to the audio sink. | ||||||
|  |       hr = pMySink->CopyData((SHORT *)pData, | ||||||
|  |                              numFramesAvailable * pwfx->nBlockAlign, &bDone); | ||||||
|  |       EXIT_ON_ERROR(hr) | ||||||
|  |  | ||||||
|  |       hr = pCaptureClient->ReleaseBuffer(numFramesAvailable); | ||||||
|  |       EXIT_ON_ERROR(hr) | ||||||
|  |  | ||||||
|  |       hr = pCaptureClient->GetNextPacketSize(&packetLength); | ||||||
|  |       EXIT_ON_ERROR(hr) | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   hr = pAudioClient->Stop();  // Stop recording. | ||||||
|  |   EXIT_ON_ERROR(hr) | ||||||
|  |  | ||||||
|  | Exit: | ||||||
|  |   CoTaskMemFree(pwfx); | ||||||
|  |   SAFE_RELEASE(pEnumerator) | ||||||
|  |   SAFE_RELEASE(pDevice) | ||||||
|  |   SAFE_RELEASE(pAudioClient) | ||||||
|  |   SAFE_RELEASE(pCaptureClient) | ||||||
|  |  | ||||||
|  |   return hr; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int _tmain(int argc, _TCHAR *argv[]) { | ||||||
|  |   fopen_s(&fp, "record.pcm", "wb"); | ||||||
|  |   CoInitialize(NULL); | ||||||
|  |   MyAudioSink test; | ||||||
|  |  | ||||||
|  |   RecordAudioStream(&test); | ||||||
|  |  | ||||||
|  |   return 0; | ||||||
|  | } | ||||||
							
								
								
									
										96
									
								
								test/audio_capture/miniaudio.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										96
									
								
								test/audio_capture/miniaudio.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,96 @@ | |||||||
|  | /* | ||||||
|  | Demonstrates how to implement loopback recording. | ||||||
|  |  | ||||||
|  | This example simply captures data from your default playback device until you | ||||||
|  | press Enter. The output is saved to the file specified on the command line. | ||||||
|  |  | ||||||
|  | Loopback mode is when you record audio that is played from a given speaker. It | ||||||
|  | is only supported on WASAPI, but can be used indirectly with PulseAudio by | ||||||
|  | choosing the appropriate loopback device after enumeration. | ||||||
|  |  | ||||||
|  | To use loopback mode you just need to set the device type to | ||||||
|  | ma_device_type_loopback and set the capture device config properties. The output | ||||||
|  | buffer in the callback will be null whereas the input buffer will be valid. | ||||||
|  | */ | ||||||
|  | #define MINIAUDIO_IMPLEMENTATION | ||||||
|  | #include "miniaudio.h" | ||||||
|  |  | ||||||
|  | #include <stdio.h> | ||||||
|  | #include <stdlib.h> | ||||||
|  |  | ||||||
|  | FILE* fp; | ||||||
|  |  | ||||||
|  | void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, | ||||||
|  |                    ma_uint32 frameCount) { | ||||||
|  |   // ma_encoder* pEncoder = (ma_encoder*)pDevice->pUserData; | ||||||
|  |   // MA_ASSERT(pEncoder != NULL); | ||||||
|  |  | ||||||
|  |   // ma_encoder_write_pcm_frames(pEncoder, pInput, frameCount, NULL); | ||||||
|  |  | ||||||
|  |   fwrite(pInput, frameCount * ma_get_bytes_per_frame(ma_format_s16, 1), 1, fp); | ||||||
|  |  | ||||||
|  |   (void)pOutput; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int main(int argc, char** argv) { | ||||||
|  |   ma_result result; | ||||||
|  |   ma_encoder_config encoderConfig; | ||||||
|  |   ma_encoder encoder; | ||||||
|  |   ma_device_config deviceConfig; | ||||||
|  |   ma_device device; | ||||||
|  |  | ||||||
|  |   fopen_s(&fp, "miniaudio.pcm", "wb"); | ||||||
|  |  | ||||||
|  |   /* Loopback mode is currently only supported on WASAPI. */ | ||||||
|  |   ma_backend backends[] = {ma_backend_wasapi}; | ||||||
|  |  | ||||||
|  |   // if (argc < 2) { | ||||||
|  |   //   printf("No output file.\n"); | ||||||
|  |   //   return -1; | ||||||
|  |   // } | ||||||
|  |  | ||||||
|  |   // encoderConfig = | ||||||
|  |   //     ma_encoder_config_init(ma_encoding_format_wav, ma_format_s16, 1, | ||||||
|  |   //     48000); | ||||||
|  |  | ||||||
|  |   // if (ma_encoder_init_file(argv[1], &encoderConfig, &encoder) != MA_SUCCESS) | ||||||
|  |   // { | ||||||
|  |   //   printf("Failed to initialize output file.\n"); | ||||||
|  |   //   return -1; | ||||||
|  |   // } | ||||||
|  |  | ||||||
|  |   deviceConfig = ma_device_config_init(ma_device_type_loopback); | ||||||
|  |   deviceConfig.capture.pDeviceID = | ||||||
|  |       NULL; /* Use default device for this example. Set this to the ID of a | ||||||
|  |                _playback_ device if you want to capture from a specific device. | ||||||
|  |              */ | ||||||
|  |   deviceConfig.capture.format = ma_format_s16; | ||||||
|  |   deviceConfig.capture.channels = 1; | ||||||
|  |   deviceConfig.sampleRate = 48000; | ||||||
|  |   deviceConfig.dataCallback = data_callback; | ||||||
|  |   deviceConfig.pUserData = nullptr; | ||||||
|  |  | ||||||
|  |   result = ma_device_init_ex(backends, sizeof(backends) / sizeof(backends[0]), | ||||||
|  |                              NULL, &deviceConfig, &device); | ||||||
|  |   if (result != MA_SUCCESS) { | ||||||
|  |     printf("Failed to initialize loopback device.\n"); | ||||||
|  |     return -2; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   result = ma_device_start(&device); | ||||||
|  |   if (result != MA_SUCCESS) { | ||||||
|  |     ma_device_uninit(&device); | ||||||
|  |     printf("Failed to start device.\n"); | ||||||
|  |     return -3; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   printf("Press Enter to stop recording...\n"); | ||||||
|  |   getchar(); | ||||||
|  |  | ||||||
|  |   fclose(fp); | ||||||
|  |  | ||||||
|  |   ma_device_uninit(&device); | ||||||
|  |   // ma_encoder_uninit(&encoder); | ||||||
|  |  | ||||||
|  |   return 0; | ||||||
|  | } | ||||||
| @@ -228,7 +228,7 @@ int main(int argc, char *argv[]) { | |||||||
|   // Event Loop |   // Event Loop | ||||||
|   SDL_Event event; |   SDL_Event event; | ||||||
|  |  | ||||||
|   last_frame_time = std::chrono::high_resolution_clock::now(); |   last_frame_time = std::chrono::steady_clock::now(); | ||||||
|  |  | ||||||
|   for (;;) { |   for (;;) { | ||||||
|     // Wait |     // Wait | ||||||
| @@ -249,7 +249,7 @@ int main(int argc, char *argv[]) { | |||||||
|           } |           } | ||||||
|           printf("xxxxxxxxxxxxxxxxxxx\n"); |           printf("xxxxxxxxxxxxxxxxxxx\n"); | ||||||
|           if (!got_picture) { |           if (!got_picture) { | ||||||
|             auto now_time = std::chrono::high_resolution_clock::now(); |             auto now_time = std::chrono::steady_clock::now(); | ||||||
|             std::chrono::duration<double> duration = now_time - last_frame_time; |             std::chrono::duration<double> duration = now_time - last_frame_time; | ||||||
|             auto tc = duration.count() * 1000; |             auto tc = duration.count() * 1000; | ||||||
|             printf("duration: %f\n", tc); |             printf("duration: %f\n", tc); | ||||||
|   | |||||||
							
								
								
									
										2
									
								
								thirdparty/projectx
									
									
									
									
										vendored
									
									
								
							
							
								
								
								
								
								
							
						
						
									
										2
									
								
								thirdparty/projectx
									
									
									
									
										vendored
									
									
								
							 Submodule thirdparty/projectx updated: 28fb35faf5...9d2e6f0c2a
									
								
							
							
								
								
									
										73
									
								
								xmake.lua
									
									
									
									
									
								
							
							
						
						
									
										73
									
								
								xmake.lua
									
									
									
									
									
								
							| @@ -1,9 +1,12 @@ | |||||||
| set_project("remote_desk") | set_project("remote_desk") | ||||||
| set_version("0.0.1") |  | ||||||
| set_license("LGPL-3.0") | set_license("LGPL-3.0") | ||||||
|  |  | ||||||
|  | set_version("0.0.1") | ||||||
|  | add_defines("RD_VERSION=\"0.0.1\""); | ||||||
|  |  | ||||||
| add_rules("mode.release", "mode.debug") | add_rules("mode.release", "mode.debug") | ||||||
| set_languages("c++17") | set_languages("c++17") | ||||||
|  | set_encodings("utf-8") | ||||||
|  |  | ||||||
| -- set_policy("build.warning", true) | -- set_policy("build.warning", true) | ||||||
| -- set_warnings("all", "extra") | -- set_warnings("all", "extra") | ||||||
| @@ -14,10 +17,11 @@ if is_mode("debug") then | |||||||
| end | end | ||||||
|  |  | ||||||
| add_requires("spdlog 1.14.1", {system = false}) | add_requires("spdlog 1.14.1", {system = false}) | ||||||
| add_requires("imgui 1.90.6", {configs = {sdl2 = true, sdl2_renderer = true}}) | add_requires("imgui v1.91.0", {configs = {sdl2 = true, sdl2_renderer = true}}) | ||||||
| add_requires("libyuv") | add_requires("miniaudio 0.11.21") | ||||||
|  |  | ||||||
| if is_os("windows") then | if is_os("windows") then | ||||||
|  |     add_requires("libyuv") | ||||||
|     add_links("Shell32", "windowsapp", "dwmapi", "User32", "kernel32", |     add_links("Shell32", "windowsapp", "dwmapi", "User32", "kernel32", | ||||||
|         "SDL2-static", "SDL2main", "gdi32", "winmm", "setupapi", "version", |         "SDL2-static", "SDL2main", "gdi32", "winmm", "setupapi", "version", | ||||||
|         "Imm32", "iphlpapi") |         "Imm32", "iphlpapi") | ||||||
| @@ -37,46 +41,65 @@ elseif is_os("macosx") then | |||||||
|     add_packages("libxcb") |     add_packages("libxcb") | ||||||
|     add_links("SDL2", "SDL2main") |     add_links("SDL2", "SDL2main") | ||||||
|     add_ldflags("-Wl,-ld_classic") |     add_ldflags("-Wl,-ld_classic") | ||||||
|     add_frameworks("OpenGL") |     add_frameworks("OpenGL", "IOSurface", "ScreenCaptureKit") | ||||||
| end | end | ||||||
|  |  | ||||||
| add_packages("spdlog", "imgui") | add_packages("spdlog", "imgui") | ||||||
|  |  | ||||||
| includes("thirdparty") | includes("thirdparty") | ||||||
|  |  | ||||||
| target("log") | target("rd_log") | ||||||
|     set_kind("object") |     set_kind("object") | ||||||
|     add_packages("spdlog") |     add_packages("spdlog") | ||||||
|     add_headerfiles("src/log/log.h") |     add_headerfiles("src/log/rd_log.h") | ||||||
|     add_includedirs("src/log", {public = true}) |     add_includedirs("src/log", {public = true}) | ||||||
|  |  | ||||||
| target("common") | target("common") | ||||||
|     set_kind("object") |     set_kind("object") | ||||||
|     add_deps("log") |     add_deps("rd_log") | ||||||
|     add_files("src/common/*.cpp") |     add_files("src/common/*.cpp") | ||||||
|     add_includedirs("src/common", {public = true}) |     add_includedirs("src/common", {public = true}) | ||||||
|  |  | ||||||
| target("screen_capturer") | target("screen_capturer") | ||||||
|     set_kind("object") |     set_kind("object") | ||||||
|     add_deps("log") |     add_deps("rd_log") | ||||||
|     add_packages("libyuv") |  | ||||||
|     add_includedirs("src/screen_capturer", {public = true}) |     add_includedirs("src/screen_capturer", {public = true}) | ||||||
|     if is_os("windows") then |     if is_os("windows") then | ||||||
|  |         add_packages("libyuv") | ||||||
|         add_files("src/screen_capturer/windows/*.cpp") |         add_files("src/screen_capturer/windows/*.cpp") | ||||||
|         add_includedirs("src/screen_capturer/windows", {public = true}) |         add_includedirs("src/screen_capturer/windows", {public = true}) | ||||||
|     elseif is_os("macosx") then |     elseif is_os("macosx") then | ||||||
|         add_packages("ffmpeg") |         add_packages("ffmpeg") | ||||||
|         add_files("src/screen_capturer/macosx/*.cpp") |         add_files("src/screen_capturer/macosx/avfoundation/*.cpp", | ||||||
|         add_includedirs("src/screen_capturer/macosx", {public = true}) |         "src/screen_capturer/macosx/screen_capturer_kit/*.cpp", | ||||||
|  |         "src/screen_capturer/macosx/screen_capturer_kit/*.mm") | ||||||
|  |         add_includedirs("src/screen_capturer/macosx/avfoundation", | ||||||
|  |         "src/screen_capturer/macosx/screen_capturer_kit", {public = true}) | ||||||
|     elseif is_os("linux") then |     elseif is_os("linux") then | ||||||
|         add_packages("ffmpeg") |         add_packages("ffmpeg") | ||||||
|         add_files("src/screen_capturer/linux/*.cpp") |         add_files("src/screen_capturer/linux/*.cpp") | ||||||
|         add_includedirs("src/screen_capturer/linux", {public = true}) |         add_includedirs("src/screen_capturer/linux", {public = true}) | ||||||
|     end |     end | ||||||
|  |  | ||||||
|  | target("speaker_capturer") | ||||||
|  |     set_kind("object") | ||||||
|  |     add_deps("rd_log") | ||||||
|  |     add_includedirs("src/speaker_capturer", {public = true}) | ||||||
|  |     if is_os("windows") then | ||||||
|  |         add_packages("miniaudio") | ||||||
|  |         add_files("src/speaker_capturer/windows/*.cpp") | ||||||
|  |         add_includedirs("src/speaker_capturer/windows", {public = true}) | ||||||
|  |     elseif is_os("macosx") then | ||||||
|  |         add_files("src/speaker_capturer/macosx/*.cpp") | ||||||
|  |         add_includedirs("src/speaker_capturer/macosx", {public = true}) | ||||||
|  |     elseif is_os("linux") then | ||||||
|  |         add_files("src/speaker_capturer/linux/*.cpp") | ||||||
|  |         add_includedirs("src/speaker_capturer/linux", {public = true}) | ||||||
|  |     end | ||||||
|  |  | ||||||
| target("device_controller") | target("device_controller") | ||||||
|     set_kind("object") |     set_kind("object") | ||||||
|     add_deps("log") |     add_deps("rd_log") | ||||||
|     add_includedirs("src/device_controller", {public = true}) |     add_includedirs("src/device_controller", {public = true}) | ||||||
|     if is_os("windows") then |     if is_os("windows") then | ||||||
|         add_files("src/device_controller/mouse/windows/*.cpp") |         add_files("src/device_controller/mouse/windows/*.cpp") | ||||||
| @@ -91,7 +114,7 @@ target("device_controller") | |||||||
|  |  | ||||||
| target("config_center") | target("config_center") | ||||||
|     set_kind("object") |     set_kind("object") | ||||||
|     add_deps("log") |     add_deps("rd_log") | ||||||
|     add_files("src/config_center/*.cpp") |     add_files("src/config_center/*.cpp") | ||||||
|     add_includedirs("src/config_center", {public = true}) |     add_includedirs("src/config_center", {public = true}) | ||||||
|  |  | ||||||
| @@ -99,27 +122,39 @@ target("localization") | |||||||
|     set_kind("headeronly") |     set_kind("headeronly") | ||||||
|     add_includedirs("src/localization", {public = true}) |     add_includedirs("src/localization", {public = true}) | ||||||
|  |  | ||||||
| target("main_window") | target("single_window") | ||||||
|     set_kind("object") |     set_kind("object") | ||||||
|     add_deps("log", "common", "localization", "config_center", "projectx", "screen_capturer", "device_controller") |     add_deps("rd_log", "common", "localization", "config_center", "projectx", "screen_capturer", "speaker_capturer", "device_controller") | ||||||
|     if is_os("macosx") then |     if is_os("macosx") then | ||||||
|         add_packages("ffmpeg") |         add_packages("ffmpeg") | ||||||
|     elseif is_os("linux") then |     elseif is_os("linux") then | ||||||
|         add_packages("ffmpeg") |         add_packages("ffmpeg") | ||||||
|     end |     end | ||||||
|     add_files("src/main_window/*.cpp") |     add_files("src/single_window/*.cpp") | ||||||
|     add_includedirs("src/main_window", {public = true}) |     add_includedirs("src/single_window", {public = true}) | ||||||
|  |     add_includedirs("fonts", {public = true}) | ||||||
|  |  | ||||||
| target("remote_desk") | target("remote_desk") | ||||||
|     set_kind("binary") |     set_kind("binary") | ||||||
|     add_deps("log", "common", "main_window") |     add_deps("rd_log", "common", "single_window") | ||||||
|     if is_os("macosx") then |     if is_os("windows") then | ||||||
|  |         add_files("icon/app.rc") | ||||||
|  |     elseif is_os("macosx") then | ||||||
|         add_packages("ffmpeg") |         add_packages("ffmpeg") | ||||||
|  |         -- add_rules("xcode.application") | ||||||
|  |         -- add_files("Info.plist") | ||||||
|     elseif is_os("linux") then |     elseif is_os("linux") then | ||||||
|         add_packages("ffmpeg") |         add_packages("ffmpeg") | ||||||
|     end |     end | ||||||
|     add_files("src/gui/main.cpp") |     add_files("src/gui/main.cpp") | ||||||
|  |  | ||||||
|  | -- target("miniaudio_capture") | ||||||
|  | --     set_kind("binary") | ||||||
|  | --     add_packages("miniaudio") | ||||||
|  | --     if is_os("windows") then | ||||||
|  | --         add_files("test/audio_capture/miniaudio.cpp") | ||||||
|  | --     end | ||||||
|  |  | ||||||
| -- target("screen_capturer") | -- target("screen_capturer") | ||||||
| --     set_kind("binary") | --     set_kind("binary") | ||||||
| --     add_packages("sdl2", "imgui",  "ffmpeg", "openh264") | --     add_packages("sdl2", "imgui",  "ffmpeg", "openh264") | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user