├── .appveyor.yml
├── .clang-format
├── .gitattributes
├── .gitignore
├── .travis.yml
├── CMakeLists-ExternalProjects.txt
├── CMakeLists.txt
├── LICENSE
├── README.md
├── include
├── Logging.h
├── RAT.h
└── cxxopts.hpp
├── modules
└── FindLIBJPEGTURBO.cmake
├── src
├── ClientDriver.cpp
├── Desktop_Server.ico
├── ServerDriver.cpp
├── client
│ ├── .angular-cli.json
│ ├── .editorconfig
│ ├── .vscode
│ │ ├── 0.010801647396913694.bat
│ │ └── 0.8898083114717834.bat
│ ├── CMakeLists.txt
│ ├── dummy.cpp
│ ├── main.ts
│ ├── package.json
│ ├── postcss.config.js
│ ├── src
│ │ ├── app
│ │ │ ├── app.component.css
│ │ │ ├── app.component.html
│ │ │ ├── app.component.ts
│ │ │ ├── app.module.ts
│ │ │ ├── connect.dialog
│ │ │ │ ├── connect.dialog.html
│ │ │ │ └── connect.dialog.ts
│ │ │ ├── lib
│ │ │ │ ├── input_lite.ts
│ │ │ │ └── rat_lite.ts
│ │ │ ├── material.module.ts
│ │ │ ├── models
│ │ │ │ └── connect.model.ts
│ │ │ ├── monitorcanvas
│ │ │ │ ├── monitorcanvas.component.html
│ │ │ │ └── monitorcanvas.component.ts
│ │ │ ├── options.dialog
│ │ │ │ ├── monitorstowatch.component.html
│ │ │ │ ├── monitorstowatch.component.ts
│ │ │ │ ├── options.dialog.html
│ │ │ │ └── options.dialog.ts
│ │ │ └── validators
│ │ │ │ └── numericonly.ts
│ │ ├── environments
│ │ │ ├── environment.prod.ts
│ │ │ └── environment.ts
│ │ ├── favicon.ico
│ │ ├── index.html
│ │ ├── main.ts
│ │ ├── polyfills.ts
│ │ ├── styles.scss
│ │ ├── test.ts
│ │ ├── tsconfig.app.json
│ │ └── typings.d.ts
│ ├── tsconfig.json
│ └── webpack.config.js
└── server
│ ├── CMakeLists.txt
│ ├── Server.cpp
│ ├── Server.h
│ ├── ServerFunctions.cpp
│ ├── ServerFunctions.h
│ ├── main.cpp
│ └── windows
│ ├── RAT_Server.rc
│ └── resource.h
└── test
├── AboutKeyAndCert.txt
├── CMakeLists.txt
├── main.cpp
├── private.key
└── public.crt
/.appveyor.yml:
--------------------------------------------------------------------------------
1 | image: Visual Studio 2017
2 |
3 | configuration:
4 | - Debug
5 |
6 | clone_folder: c:\projects\proj
7 |
8 | platform:
9 | - x86
10 |
11 | install:
12 | - ps: If ($env:Platform -Match "x86"){ $env:CMAKE_ARCH="" } Else { $env:CMAKE_ARCH=" Win64"}
13 | - mkdir C:\projects\deps
14 | - cd C:\projects\deps
15 | - set CMAKE_URL="https://cmake.org/files/v3.10/cmake-3.10.0-win64-x64.zip"
16 | - appveyor DownloadFile %CMAKE_URL% -FileName cmake.zip
17 | - 7z x cmake.zip -oC:\projects\deps > nul
18 | - move C:\projects\deps\cmake-* C:\projects\deps\cmake # Move to a version-agnostic directory
19 | - set PATH=C:\projects\deps\cmake\bin;%PATH%
20 | - cmake --version
21 | - ps: Install-Product node 8.8.1
22 | - cd c:\projects
23 | - mkdir build
24 | - cd build
25 | - git clone https://github.com/Microsoft/vcpkg
26 | - cd vcpkg
27 | - powershell -exec bypass scripts\bootstrap.ps1
28 | - .\vcpkg integrate install
29 | - .\vcpkg install zlib:x86-windows-static libjpeg-turbo:x86-windows-static openssl:x86-windows-static zlib openssl libjpeg-turbo
30 | - npm install -g typescript electron @angular/cli@latest
31 |
32 | build_script:
33 | - cd c:\projects\proj
34 | - mkdir build
35 | - cd build
36 | - cmake -DBUILD_SHARED_LIBS=OFF -DVCPKG_TARGET_TRIPLET=x86-windows-static "-DCMAKE_TOOLCHAIN_FILE=c:\projects\build\vcpkg\scripts\buildsystems\vcpkg.cmake" ..
37 | - cmake --build .
38 | - ctest -V -C Debug
39 | - cmake -DBUILD_SHARED_LIBS=ON "-DCMAKE_TOOLCHAIN_FILE=c:\projects\build\vcpkg\scripts\buildsystems\vcpkg.cmake" ..
40 | - cmake --build .
41 | - ctest -V -C Debug
42 |
43 |
--------------------------------------------------------------------------------
/.clang-format:
--------------------------------------------------------------------------------
1 | BasedOnStyle: LLVM
2 | Language: Cpp
3 | IndentWidth: 4
4 | TabWidth: 8
5 | UseTab: Never
6 | BreakBeforeBraces: Stroustrup
7 | NamespaceIndentation: Inner
8 | ColumnLimit: 150
9 |
10 | Language: JavaScript
11 | IndentWidth: 4
12 | TabWidth: 8
13 | UseTab: Never
14 | ColumnLimit: 150
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
4 | # Custom for Visual Studio
5 | *.cs diff=csharp
6 |
7 | # Standard to msysgit
8 | *.doc diff=astextplain
9 | *.DOC diff=astextplain
10 | *.docx diff=astextplain
11 | *.DOCX diff=astextplain
12 | *.dot diff=astextplain
13 | *.DOT diff=astextplain
14 | *.pdf diff=astextplain
15 | *.PDF diff=astextplain
16 | *.rtf diff=astextplain
17 | *.RTF diff=astextplain
18 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # User-specific files
5 | *.suo
6 | *.user
7 | *.userosscache
8 | *.sln.docstates
9 | *.opendb
10 |
11 | # Build results
12 | [Dd]ebug/
13 | [Dd]ebugPublic/
14 | [Rr]elease/
15 | [Rr]eleases/
16 | x64/
17 | x86/
18 | build/
19 | bld/
20 | libs/
21 | [Bb]in/
22 | [Oo]bj/
23 | .workspace/
24 | .gradle/
25 | [Jj]ni[Ll]ibs/
26 | .vs/
27 | *.js
28 | !postcss.config.js
29 | !webpack.config.js
30 | .codelite/
31 | .build-*/
32 | *.tlog
33 | # Roslyn cache directories
34 | *.ide/
35 | src/client/dist/
36 |
37 | # MSTest test Results
38 | [Tt]est[Rr]esult*/
39 | [Bb]uild[Ll]og.*
40 |
41 | #NUNIT
42 | *.VisualState.xml
43 | TestResult.xml
44 |
45 | # Build Results of an ATL Project
46 | [Dd]ebugPS/
47 | [Rr]eleasePS/
48 | dlldata.c
49 |
50 | *.so
51 | *.a
52 | *.db
53 | *_i.c
54 | *_p.c
55 | *_i.h
56 | *.ilk
57 | *.meta
58 | *.obj
59 | *.pch
60 | *.pdb
61 | *.pgc
62 | *.pgd
63 | *.rsp
64 | *.sbr
65 | *.tlb
66 | *.tli
67 | *.tlh
68 | *.tmp
69 | *.tmp_proj
70 | *.log
71 | *.vspscc
72 | *.vssscc
73 | .builds
74 | *.pidb
75 | *.svclog
76 | *.scc
77 | *.mk
78 |
79 | # Chutzpah Test files
80 | _Chutzpah*
81 |
82 | # Visual C++ cache files
83 | ipch/
84 | *.aps
85 | *.ncb
86 | *.opensdf
87 | *.sdf
88 | *.cachefile
89 |
90 | # Visual Studio profiler
91 | *.psess
92 | *.vsp
93 | *.vspx
94 |
95 | # TFS 2012 Local Workspace
96 | $tf/
97 |
98 | # Guidance Automation Toolkit
99 | *.gpState
100 |
101 | # ReSharper is a .NET coding add-in
102 | _ReSharper*/
103 | *.[Rr]e[Ss]harper
104 | *.DotSettings.user
105 |
106 | # JustCode is a .NET coding addin-in
107 | .JustCode
108 |
109 | # TeamCity is a build add-in
110 | _TeamCity*
111 |
112 | # DotCover is a Code Coverage Tool
113 | *.dotCover
114 |
115 | # NCrunch
116 | _NCrunch_*
117 | .*crunch*.local.xml
118 |
119 | # MightyMoose
120 | *.mm.*
121 | AutoTest.Net/
122 |
123 | # Web workbench (sass)
124 | .sass-cache/
125 |
126 | # Installshield output folder
127 | [Ee]xpress/
128 |
129 | # DocProject is a documentation generator add-in
130 | DocProject/buildhelp/
131 | DocProject/Help/*.HxT
132 | DocProject/Help/*.HxC
133 | DocProject/Help/*.hhc
134 | DocProject/Help/*.hhk
135 | DocProject/Help/*.hhp
136 | DocProject/Help/Html2
137 | DocProject/Help/html
138 |
139 | # Click-Once directory
140 | publish/
141 |
142 | # Publish Web Output
143 | *.[Pp]ublish.xml
144 | *.azurePubxml
145 | # TODO: Comment the next line if you want to checkin your web deploy settings
146 | # but database connection strings (with potential passwords) will be unencrypted
147 | *.pubxml
148 | *.publishproj
149 |
150 | # NuGet Packages
151 | *.nupkg
152 | # The packages folder can be ignored because of Package Restore
153 | **/packages/*
154 | # except build/, which is used as an MSBuild target.
155 | !**/packages/build/
156 | # If using the old MSBuild-Integrated Package Restore, uncomment this:
157 | #!**/packages/repositories.config
158 |
159 | # Windows Azure Build Output
160 | csx/
161 | *.build.csdef
162 |
163 | # Windows Store app package directory
164 | AppPackages/
165 |
166 | # Others
167 | sql/
168 | *.Cache
169 | ClientBin/
170 | [Ss]tyle[Cc]op.*
171 | ~$*
172 | *~
173 | *.dbmdl
174 | *.dbproj.schemaview
175 | *.pfx
176 | *.publishsettings
177 | node_modules/
178 |
179 | # RIA/Silverlight projects
180 | Generated_Code/
181 |
182 | # Backup & report files from converting an old project file
183 | # to a newer Visual Studio version. Backup files are not needed,
184 | # because we have git ;-)
185 | _UpgradeReport_Files/
186 | Backup*/
187 | UpgradeLog*.XML
188 | UpgradeLog*.htm
189 |
190 | # SQL Server files
191 | *.mdf
192 | *.ldf
193 |
194 | # Business Intelligence projects
195 | *.rdl.data
196 | *.bim.layout
197 | *.bim_*.settings
198 |
199 | # Microsoft Fakes
200 | FakesAssemblies/
201 |
202 | # =========================
203 | # Operating System Files
204 | # =========================
205 |
206 | # OSX
207 | # =========================
208 |
209 | .DS_Store
210 | .AppleDouble
211 | .LSOverride
212 |
213 | # Thumbnails
214 | ._*
215 |
216 | # Files that might appear on external disk
217 | .Spotlight-V100
218 | .Trashes
219 |
220 | # Directories potentially created on remote AFP share
221 | .AppleDB
222 | .AppleDesktop
223 | Network Trash Folder
224 | Temporary Items
225 | .apdisk
226 |
227 | # Windows
228 | # =========================
229 |
230 | # Windows image file caches
231 | Thumbs.db
232 | ehthumbs.db
233 |
234 | # Folder config file
235 | Desktop.ini
236 |
237 | # Recycle Bin used on file shares
238 | $RECYCLE.BIN/
239 |
240 | # Windows Installer files
241 | *.cab
242 | *.msi
243 | *.msm
244 | *.msp
245 |
246 | # Windows shortcuts
247 | *.lnk
248 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language : cpp
2 |
3 | matrix:
4 | include:
5 | - os: osx
6 | osx_image: xcode9.1
7 | compiler: clang
8 | - os: linux
9 | env: CLANG_VERSION=5.0 BUILD_TYPE=Debug
10 | addons: &clang5
11 | apt:
12 | packages:
13 | - g++-6
14 | - clang-5.0
15 | - libx11-dev
16 | - libxfixes-dev
17 | - libxtst-dev
18 | - libxext-dev
19 | - libxinerama-dev
20 | - nasm
21 | sources:
22 | - ubuntu-toolchain-r-test
23 | - llvm-toolchain-precise
24 |
25 | before_install:
26 | - if [[ -n "$CLANG_VERSION" ]]; then export CXX=clang++-$CLANG_VERSION CC=clang-$CLANG_VERSION; fi
27 | - |
28 | if [ "${TRAVIS_OS_NAME}" == "linux" ]; then
29 | CMAKE_URL="https://cmake.org/files/v3.9/cmake-3.9.6-Linux-x86_64.tar.gz"
30 | mkdir -p ${TRAVIS_BUILD_DIR}/deps/cmake && travis_retry wget --no-check-certificate --quiet -O - "${CMAKE_URL}" | tar --strip-components=1 -xz -C ${TRAVIS_BUILD_DIR}/deps/cmake
31 | export PATH="${TRAVIS_BUILD_DIR}/deps/cmake/bin:${PATH}";
32 | else
33 | brew update && brew upgrade cmake || brew install cmake;
34 | brew install openssl zlib libjpeg-turbo;
35 | fi
36 | - npm install -g typescript electron @angular/cli@latest;
37 |
38 | script:
39 | - mkdir build
40 | - cd build
41 | - if [ "${TRAVIS_OS_NAME}" = "osx" ]; then
42 | cmake -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DBUILD_SHARED_LIBS=ON -DCMAKE_CXX_COMPILER="$CXX" -DCMAKE_C_COMPILER="$CC" -DOPENSSL_ROOT_DIR=/usr/local/opt/openssl .. && make;
43 | else
44 | cmake -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DBUILD_SHARED_LIBS=ON -DCMAKE_CXX_COMPILER="$CXX" -DCMAKE_C_COMPILER="$CC" .. && make;
45 | ctest -V -C Debug;
46 | cmake -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DBUILD_SHARED_LIBS=OFF -DCMAKE_CXX_COMPILER="$CXX" -DCMAKE_C_COMPILER="$CC" .. && make;
47 | ctest -V -C Debug;
48 | fi
49 |
50 | notifications:
51 | email: false
52 |
53 |
--------------------------------------------------------------------------------
/CMakeLists-ExternalProjects.txt:
--------------------------------------------------------------------------------
1 | include(ExternalProject)
2 |
3 | ExternalProject_Add(
4 | websocket_lite
5 |
6 | GIT_REPOSITORY "https://github.com/smasherprog/websocket_lite.git"
7 | GIT_TAG "master"
8 |
9 | UPDATE_COMMAND ""
10 | PATCH_COMMAND ""
11 |
12 | SOURCE_DIR "${CMAKE_BINARY_DIR}/libs/websocket_lite"
13 | CMAKE_ARGS -DBUILD_SHARED_LIBS=${BUILD_SHARED_LIBS} -DOPENSSL_ROOT_DIR=${OPENSSL_ROOT_DIR} -DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE} -DCMAKE_INSTALL_PREFIX=${CMAKE_BINARY_DIR}/bin
14 |
15 | TEST_COMMAND ""
16 | )
17 |
18 | ExternalProject_Add(
19 | input_lite
20 |
21 | GIT_REPOSITORY "https://github.com/smasherprog/input_lite.git"
22 | GIT_TAG "3.0.0"
23 |
24 | UPDATE_COMMAND ""
25 | PATCH_COMMAND ""
26 |
27 | SOURCE_DIR "${CMAKE_BINARY_DIR}/libs/input_lite"
28 | CMAKE_ARGS -DBUILD_SHARED_LIBS=${BUILD_SHARED_LIBS} -DCMAKE_INSTALL_PREFIX=${CMAKE_BINARY_DIR}/bin
29 |
30 | TEST_COMMAND ""
31 | )
32 |
33 | ExternalProject_Add(
34 | clipboard_lite
35 |
36 | GIT_REPOSITORY "https://github.com/smasherprog/clipboard_lite.git"
37 | GIT_TAG "3.4"
38 |
39 | UPDATE_COMMAND ""
40 | PATCH_COMMAND ""
41 |
42 | SOURCE_DIR "${CMAKE_BINARY_DIR}/libs/clipboard_lite"
43 | CMAKE_ARGS -DBUILD_SHARED_LIBS=${BUILD_SHARED_LIBS} -DCMAKE_INSTALL_PREFIX=${CMAKE_BINARY_DIR}/bin
44 |
45 | TEST_COMMAND ""
46 | )
47 |
48 | ExternalProject_Add(
49 | screen_capture_lite
50 |
51 | GIT_REPOSITORY "https://github.com/smasherprog/screen_capture_lite.git"
52 | GIT_TAG "15.0.1"
53 |
54 | UPDATE_COMMAND ""
55 | PATCH_COMMAND ""
56 |
57 | SOURCE_DIR "${CMAKE_BINARY_DIR}/libs/screen_capture_lite"
58 | CMAKE_ARGS -DBUILD_SHARED_LIBS=${BUILD_SHARED_LIBS} -DCMAKE_INSTALL_PREFIX=${CMAKE_BINARY_DIR}/bin
59 |
60 | TEST_COMMAND ""
61 | )
62 |
63 |
64 |
--------------------------------------------------------------------------------
/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.8)
2 | project(rat_lite)
3 |
4 | set(CMAKE_CXX_STANDARD 17)
5 | set(CMAKE_CXX_STANDARD_REQUIRED ON)
6 | set(CMAKE_CXX_EXTENSIONS OFF)
7 | set(BUILD_EXAMPLE ON)
8 | option(BUILD_SHARED_LIBS "Build shared library" OFF)
9 | set (CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/modules")
10 |
11 | include(CMakeLists-ExternalProjects.txt)
12 |
13 | if(WIN32)
14 | set(REMOTE_ACCESS_PLATFORM_INC
15 | include/windows
16 | )
17 | add_definitions(-D_CRT_SECURE_NO_WARNINGS -DNOMINMAX)
18 | elseif(APPLE)
19 | set(REMOTE_ACCESS_PLATFORM_INC
20 | include/ios
21 | )
22 | else()
23 | set(REMOTE_ACCESS_PLATFORM_INC
24 | include/linux
25 | )
26 | endif()
27 |
28 | if(MSVC)
29 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4")
30 | else()
31 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -pedantic")
32 | endif()
33 |
34 | find_package(ZLIB REQUIRED)
35 | find_package(OpenSSL REQUIRED)
36 | find_package(LIBJPEGTURBO REQUIRED)
37 |
38 | set(COMMON_INCLUDE_DIRS
39 | include
40 | ${CMAKE_BINARY_DIR}/bin/include
41 | ${OPENSSL_INCLUDE_DIR}
42 | ${ZLIB_INCLUDE_DIRS}
43 | ${LIBJPEGTURBO_INCLUDE_DIRS}
44 | )
45 | include_directories(
46 | ${COMMON_INCLUDE_DIRS}
47 | )
48 |
49 | add_library(${PROJECT_NAME}
50 | include/RAT.h
51 | src/ClientDriver.cpp
52 | src/ServerDriver.cpp
53 | )
54 | add_dependencies(${PROJECT_NAME} websocket_lite input_lite clipboard_lite screen_capture_lite)
55 | if(WIN32)
56 | target_link_libraries(${PROJECT_NAME} Crypt32 Dwmapi)
57 | elseif(APPLE)
58 | find_library(corefoundation_lib CoreFoundation)
59 | find_library(cocoa_lib Cocoa)
60 | find_package(Threads REQUIRED)
61 | target_link_libraries(${PROJECT_NAME} ${CMAKE_THREAD_LIBS_INIT} ${corefoundation_lib} ${cocoa_lib})
62 | else()
63 | find_package(X11 REQUIRED)
64 | find_package(Threads REQUIRED)
65 | target_link_libraries(${PROJECT_NAME} ${CMAKE_THREAD_LIBS_INIT} dl ${X11_LIBRARIES} ${X11_Xfixes_LIB} ${X11_XTest_LIB} ${X11_Xinerama_LIB})
66 | endif()
67 |
68 | if(${BUILD_SHARED_LIBS})
69 | set_target_properties(${PROJECT_NAME} PROPERTIES DEFINE_SYMBOL RAT_LITE_DLL)
70 | endif()
71 | if(WIN32)
72 | set(LIBEXTENSION .lib)
73 | else()
74 | if(${BUILD_SHARED_LIBS})
75 | set(LIBEXTENSION ${CMAKE_SHARED_LIBRARY_SUFFIX})
76 | else()
77 | set(LIBEXTENSION ${CMAKE_STATIC_LIBRARY_SUFFIX})
78 | endif()
79 | endif()
80 | if(${BUILD_SHARED_LIBS})
81 | target_link_libraries(${PROJECT_NAME}
82 | ${CMAKE_BINARY_DIR}/bin/lib/${CMAKE_SHARED_LIBRARY_PREFIX}websocket_lite${LIBEXTENSION}
83 | ${CMAKE_BINARY_DIR}/bin/lib/${CMAKE_SHARED_LIBRARY_PREFIX}input_lite${LIBEXTENSION}
84 | ${CMAKE_BINARY_DIR}/bin/lib/${CMAKE_SHARED_LIBRARY_PREFIX}clipboard_lite${LIBEXTENSION}
85 | ${CMAKE_BINARY_DIR}/bin/lib/${CMAKE_SHARED_LIBRARY_PREFIX}screen_capture_lite${LIBEXTENSION}
86 | ${OPENSSL_LIBRARIES}
87 | ${ZLIB_LIBRARIES}
88 | ${LIBJPEGTURBO_LIBRARIES}
89 | )
90 | endif()
91 |
92 | install (TARGETS ${PROJECT_NAME}
93 | RUNTIME DESTINATION bin
94 | ARCHIVE DESTINATION lib
95 | LIBRARY DESTINATION lib
96 | )
97 |
98 | install (FILES
99 | include/RAT.h
100 | include/Logging.h
101 | DESTINATION include
102 | )
103 |
104 | if(BUILD_EXAMPLE)
105 | add_subdirectory(src/server)
106 | add_subdirectory(src/client)
107 | endif()
108 |
109 | enable_testing()
110 | add_subdirectory(test)
111 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Scott
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## [Getting Started](https://github.com/smasherprog/rat_lite/wiki)
2 | ### I am not updating this regularly, if anyone wants to take over, please issue PR
3 | ### All development happens in the master branch. To get stable releases, use tags by going to the release section.
4 |
Linux/Mac 
5 |
Windows 
6 |
7 |
Windows Connecting to Mac
8 |
9 |
10 | Mac Connecting to Windows
11 |
12 |
13 | Windows Connecting to Android
14 |
15 |
16 |
17 | Windows Connecting to Linux
18 |
19 |
20 | Linux Connecting to Windows
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/include/Logging.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 | #include
3 | #include
4 | #include
5 |
6 | #if __ANDROID__
7 |
8 | #define APPNAME "RAT"
9 | #include
10 |
11 | #endif
12 |
13 | namespace SL {
14 | namespace RAT_Lite {
15 |
16 | enum Logging_Levels { Debug_log_level, ERROR_log_level, FATAL_log_level, INFO_log_level, WARN_log_level };
17 | const std::string Logging_level_Names[] = {"DEBUG", "ERROR", "FATAL", "INFO", "WARN"};
18 | inline void Log(Logging_Levels level, const char *file, int line, const char *func, std::ostringstream &data)
19 | {
20 | #if __ANDROID__
21 |
22 | std::ostringstream buffer;
23 | buffer << Logging_level_Names[level] << ": FILE: " << file << " Line: " << line << " Func: " << func << " Msg: " << data.str();
24 | auto msg = buffer.str();
25 | switch (level) {
26 | case (Debug_log_level):
27 | __android_log_print(ANDROID_LOG_DEBUG, APPNAME, "%s", msg.c_str());
28 | break;
29 | case (ERROR_log_level):
30 | __android_log_print(ANDROID_LOG_ERROR, APPNAME, "%s", msg.c_str());
31 | break;
32 | case (FATAL_log_level):
33 | __android_log_print(ANDROID_LOG_FATAL, APPNAME, "%s", msg.c_str());
34 | break;
35 | case (INFO_log_level):
36 | __android_log_print(ANDROID_LOG_INFO, APPNAME, "%s", msg.c_str());
37 | break;
38 | case (WARN_log_level):
39 | __android_log_print(ANDROID_LOG_WARN, APPNAME, "%s", msg.c_str());
40 | break;
41 | default:
42 | __android_log_print(ANDROID_LOG_INFO, APPNAME, "%s", msg.c_str());
43 | }
44 | #else
45 | std::cout << Logging_level_Names[level] << ": FILE: " << file << " Line: " << line << " Func: " << func << " Msg: " << data.str()
46 | << std::endl;
47 |
48 | #endif
49 | }
50 |
51 | } // namespace RAT_Lite
52 | } // namespace SL
53 | #define UNUSED(x) (void)(x)
54 | #define S(x) #x
55 | #define S_(x) S(x)
56 | // Usage SL_RAT_LOG(SL::RAT::Logging_Levels::Debug_log_level, "Message goes here "<< 56 <<" Any Valid cout stuff works");
57 |
58 | #define SL_RAT_LOG(level, msg) \
59 | { \
60 | \
61 | std::ostringstream buffersl134nonesd; \
62 | \
63 | buffersl134nonesd \
64 | << msg; \
65 | \
66 | SL::RAT_Lite::Log(level, __FILE__, __LINE__, __func__, buffersl134nonesd); \
67 | \
68 | }
69 |
--------------------------------------------------------------------------------
/include/RAT.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 | #include "Input_Lite.h"
3 | #include "ScreenCapture.h"
4 | #include "WS_Lite.h"
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 |
11 | #if defined(WINDOWS) || defined(WIN32)
12 | #if defined(RAT_LITE_DLL)
13 | #define RAT_LITE_EXTERN __declspec(dllexport)
14 | #else
15 | #define RAT_LITE_EXTERN
16 | #endif
17 | #else
18 | #define RAT_LITE_EXTERN
19 | #endif
20 |
21 | namespace SL {
22 | namespace RAT_Lite {
23 |
24 | const int PixelStride = 4;
25 | struct Point {
26 | Point() : X(0), Y(0) {}
27 | Point(const Point &p) : Point(p.X, p.Y) {}
28 | Point(int x, int y) : X(x), Y(y) {}
29 | int X, Y;
30 | };
31 | typedef Point Size;
32 | class Rect {
33 |
34 | public:
35 | Rect() : Height(0), Width(0) {}
36 | Rect(const Rect &rect) : Rect(rect.Origin, rect.Height, rect.Width) {}
37 | Rect(Point origin, int height, int width) : Origin(origin), Height(height), Width(width) {}
38 | auto Center() const { return Point(Origin.X + (Width / 2), Origin.Y + (Height / 2)); }
39 |
40 | Point Origin;
41 | int Height, Width;
42 | auto top() const { return Origin.Y; }
43 | auto bottom() const { return Origin.Y + Height; }
44 | void bottom(int b) { Height = b - Origin.Y; }
45 | auto left() const { return Origin.X; }
46 | auto right() const { return Origin.X + Width; }
47 | void right(int r) { Width = r - Origin.X; }
48 | auto Contains(const Point &p) const
49 | {
50 | if (p.X < left())
51 | return false;
52 | if (p.Y < top())
53 | return false;
54 | if (p.X >= right())
55 | return false;
56 | if (p.Y >= bottom())
57 | return false;
58 | return true;
59 | }
60 | void Expand_To_Include(const Point &p)
61 | {
62 | if (p.X <= left())
63 | Origin.X = p.X;
64 | if (p.Y <= top())
65 | Origin.Y = p.Y;
66 | if (right() <= p.X)
67 | Width = p.X - left();
68 | if (bottom() <= p.Y)
69 | Height = p.Y - top();
70 | }
71 | };
72 | inline auto operator==(const Point &p1, const Point &p2) { return p1.X == p2.X && p1.Y == p2.Y; }
73 | inline auto operator!=(const Point &p1, const Point &p2) { return !(p1 == p2); }
74 | inline auto operator==(const Rect &p1, const Rect &p2) { return p1.Origin == p2.Origin && p1.Height == p2.Height && p1.Width == p2.Width; }
75 | inline auto SquaredDistance(const Point &p1, const Point &p2)
76 | {
77 | auto dx = abs(p1.X - p2.X);
78 | auto dy = abs(p1.Y - p2.Y);
79 | return dx * dx + dy * dy;
80 | }
81 | inline auto SquaredDistance(const Point &p, const Rect &r)
82 | {
83 | auto cx = std::max(std::min(p.X, r.right()), r.left());
84 | auto cy = std::max(std::min(p.Y, r.bottom()), r.top());
85 | return SquaredDistance(Point(cx, cy), p);
86 | }
87 | inline auto Distance(const Point &p1, const Point &p2) { return sqrt(SquaredDistance(p1, p2)); }
88 |
89 | inline auto Distance(const Point &p, const Rect &r) { return Distance(p, r.Center()); }
90 | struct Image {
91 | Image() {}
92 | Image(const Rect &r, const unsigned char *d, const size_t &l) : Rect_(r), Data(d), Length(l) {}
93 | Rect Rect_;
94 | const unsigned char *Data = nullptr;
95 | size_t Length = 0;
96 | };
97 |
98 | enum Server_Status { SERVER_RUNNING, SERVER_STOPPING, SERVER_STOPPED };
99 | enum class ImageEncoding : unsigned char { COLOR, GRAYSCALE };
100 | enum class ClipboardSharing : unsigned char { NOT_SHARED, SHARED };
101 | enum class PACKET_TYPES : unsigned int {
102 | INVALID,
103 | HTTP_MSG,
104 | ONMONITORSCHANGED,
105 | ONFRAMECHANGED,
106 | ONNEWFRAME,
107 | ONMOUSEIMAGECHANGED,
108 | ONMOUSEPOSITIONCHANGED,
109 | ONKEYUP,
110 | ONKEYDOWN,
111 | ONMOUSEUP,
112 | ONMOUSEDOWN,
113 | ONMOUSESCROLL,
114 | ONCLIPBOARDTEXTCHANGED,
115 | ONCLIENTSETTINGSCHANGED,
116 | // use LAST_PACKET_TYPE as the starting point of your custom packet types. Everything before this is used internally by the library
117 | LAST_PACKET_TYPE
118 | };
119 |
120 | struct ClientSettings {
121 | ClipboardSharing ShareClip = ClipboardSharing::NOT_SHARED;
122 | int ImageCompressionSetting = 70;
123 | ImageEncoding EncodeImagesAsGrayScale = ImageEncoding::COLOR;
124 | std::vector MonitorsToWatch;
125 | };
126 | template class INetworkHandlers {
127 | public:
128 | virtual ~INetworkHandlers() {}
129 |
130 | virtual std::shared_ptr onConnection(const std::function)> &callback) = 0;
131 | virtual std::shared_ptr
132 | onMessage(const std::function &socket, const WS_LITE::WSMessage)> &callback) = 0;
133 | virtual std::shared_ptr onDisconnection(
134 | const std::function &socket, unsigned short code, const std::string)> &callback) = 0;
135 | };
136 | class RAT_LITE_EXTERN IConfig {
137 | public:
138 | virtual ~IConfig() {}
139 | virtual void ShareClipboard(ClipboardSharing share) = 0;
140 | virtual ClipboardSharing ShareClipboard() const = 0;
141 | };
142 | class RAT_LITE_EXTERN IServerDriver : public IConfig {
143 | public:
144 | virtual ~IServerDriver() {}
145 |
146 | virtual void MaxConnections(int maxconnections) = 0;
147 | virtual int MaxConnections() const = 0;
148 | virtual size_t MemoryUsed() const = 0;
149 | virtual WS_LITE::WSMessage PrepareMonitorsChanged(const std::vector &monitors) = 0;
150 | // imagecompression is [0, 100] = [WORST, BEST] Better compression also takes more time .. 70 seems to work well
151 | virtual WS_LITE::WSMessage PrepareFrameChanged(const Screen_Capture::Image &image, const Screen_Capture::Monitor &monitor,
152 | int imagecompression = 70, bool usegrayscale = false) = 0;
153 | // imagecompression is [0, 100] = [WORST, BEST]
154 | virtual WS_LITE::WSMessage PrepareNewFrame(const Screen_Capture::Image &image, const Screen_Capture::Monitor &monitor,
155 | int imagecompression = 70, bool usegrayscale = false) = 0;
156 | virtual WS_LITE::WSMessage PrepareMouseImageChanged(const Screen_Capture::Image &image) = 0;
157 | virtual WS_LITE::WSMessage PrepareMousePositionChanged(const SL::Screen_Capture::Point &pos) = 0;
158 | virtual WS_LITE::WSMessage PrepareClipboardChanged(const std::string &text) = 0;
159 | };
160 |
161 | class RAT_LITE_EXTERN IServerDriverConfiguration : public INetworkHandlers {
162 | public:
163 | virtual ~IServerDriverConfiguration() {}
164 | // events raised from the server
165 | virtual std::shared_ptr
166 | onKeyUp(const std::function &socket, Input_Lite::KeyCodes key)> &callback) = 0;
167 | virtual std::shared_ptr
168 | onKeyDown(const std::function &socket, Input_Lite::KeyCodes key)> &callback) = 0;
169 | virtual std::shared_ptr
170 | onMouseUp(const std::function &socket, Input_Lite::MouseButtons button)> &callback) = 0;
171 | virtual std::shared_ptr
172 | onMouseDown(const std::function &socket, Input_Lite::MouseButtons button)> &callback) = 0;
173 | virtual std::shared_ptr
174 | onMouseScroll(const std::function &socket, int offset)> &callback) = 0;
175 | virtual std::shared_ptr
176 | onMousePosition(const std::function &socket, const Point &pos)> &callback) = 0;
177 | virtual std::shared_ptr onClientSettingsChanged(
178 | const std::function &socket, const ClientSettings &settings)> &callback) = 0;
179 |
180 | virtual std::shared_ptr onClipboardChanged(const std::function &callback) = 0;
181 | virtual std::shared_ptr Build(const std::shared_ptr &wslistenerconfig) = 0;
182 | };
183 |
184 | class RAT_LITE_EXTERN IClientDriver : public IConfig {
185 | public:
186 | virtual ~IClientDriver() {}
187 |
188 | virtual void SendKeyUp(SL::Input_Lite::KeyCodes key) = 0;
189 | virtual void SendKeyDown(SL::Input_Lite::KeyCodes key) = 0;
190 | virtual void SendMouseUp(const Input_Lite::MouseButtons button) = 0;
191 | virtual void SendMouseDown(const Input_Lite::MouseButtons button) = 0;
192 | virtual void SendMouseScroll(int offset) = 0;
193 | virtual void SendMousePosition(const Point &pos) = 0;
194 | virtual void SendClipboardChanged(const std::string &text) = 0;
195 | };
196 |
197 | class RAT_LITE_EXTERN IClientDriverConfiguration : public INetworkHandlers {
198 | public:
199 | virtual ~IClientDriverConfiguration() {}
200 | virtual std::shared_ptr
201 | onMonitorsChanged(const std::function &)> &callback) = 0;
202 | virtual std::shared_ptr
203 | onFrameChanged(const std::function &callback) = 0;
204 | virtual std::shared_ptr
205 | onNewFrame(const std::function &callback) = 0;
206 | virtual std::shared_ptr onMouseImageChanged(const std::function &callback) = 0;
207 | virtual std::shared_ptr onMousePositionChanged(const std::function &callback) = 0;
208 | virtual std::shared_ptr onClipboardChanged(const std::function &callback) = 0;
209 | virtual std::shared_ptr Build(const std::shared_ptr &wsclientconfig) = 0;
210 | };
211 |
212 | std::shared_ptr RAT_LITE_EXTERN CreateClientDriverConfiguration();
213 | std::shared_ptr RAT_LITE_EXTERN CreateServerDriverConfiguration();
214 |
215 | } // namespace RAT_Lite
216 | } // namespace SL
--------------------------------------------------------------------------------
/modules/FindLIBJPEGTURBO.cmake:
--------------------------------------------------------------------------------
1 | include (FindPackageHandleStandardArgs)
2 |
3 | find_path(LIBJPEGTURBO_INCLUDE_DIRS turbojpeg.h
4 | PATHS /usr/local/opt/jpeg-turbo/include
5 | /usr/include
6 | )
7 | set(JPEG_NAMES jpeg turbojpeg)
8 |
9 | FOREACH(LIB ${JPEG_NAMES})
10 | FIND_LIBRARY(FOUND_LIB_${LIB} ${LIB}
11 | PATHS /usr/local/opt/jpeg-turbo/lib
12 | /opt/libjpeg-turbo/lib64
13 | /opt/libjpeg-turbo/lib
14 | )
15 | if(FOUND_LIB_${LIB})
16 | LIST(APPEND LIBJPEGTURBO_LIBRARIES ${FOUND_LIB_${LIB}})
17 | endif()
18 |
19 | ENDFOREACH(LIB)
20 |
21 | FIND_PACKAGE_HANDLE_STANDARD_ARGS(LIBJPEGTURBO DEFAULT_MSG LIBJPEGTURBO_LIBRARIES LIBJPEGTURBO_INCLUDE_DIRS)
22 |
23 | mark_as_advanced(LIBJPEGTURBO_LIBRARIES LIBJPEGTURBO_INCLUDE_DIRS)
--------------------------------------------------------------------------------
/src/ClientDriver.cpp:
--------------------------------------------------------------------------------
1 | #include "RAT.h"
2 | #include "turbojpeg.h"
3 |
4 | #include "Input_Lite.h"
5 | #include "Logging.h"
6 | #include "ScreenCapture.h"
7 | #include "WS_Lite.h"
8 |
9 | #include
10 | #include
11 |
12 | namespace SL {
13 | namespace RAT_Lite {
14 |
15 | class ClientDriver : public IClientDriver {
16 |
17 | Point LastMousePosition_;
18 | std::shared_ptr Socket_;
19 |
20 | std::mutex outputbufferLock;
21 | std::vector outputbuffer;
22 | std::vector Monitors;
23 |
24 | void MouseImageChanged(const std::shared_ptr &socket, const unsigned char *data, size_t len)
25 | {
26 | if (!onMouseImageChanged)
27 | return;
28 | if (len >= sizeof(Rect)) {
29 | Image img(*reinterpret_cast(data), data + sizeof(Rect), len - sizeof(Rect));
30 | if (len >= sizeof(Rect) + (img.Rect_.Width * img.Rect_.Height * PixelStride)) {
31 | return onMouseImageChanged(img);
32 | }
33 | }
34 | socket->close(1000, "Received invalid lenght on onMouseImageChanged");
35 | }
36 | void MousePositionChanged(const std::shared_ptr &socket, const unsigned char *data, size_t len)
37 | {
38 | if (!onMousePositionChanged)
39 | return;
40 | if (len == sizeof(SL::Screen_Capture::Point)) {
41 | return onMousePositionChanged(*reinterpret_cast(data));
42 | }
43 | socket->close(1000, "Received invalid lenght on onMousePositionChanged");
44 | }
45 |
46 | void MonitorsChanged(const std::shared_ptr &socket, const unsigned char *data, size_t len)
47 | {
48 | if (!onMonitorsChanged)
49 | return;
50 | auto num = len / sizeof(Screen_Capture::Monitor);
51 | Monitors.clear();
52 | if (len == num * sizeof(Screen_Capture::Monitor) && num < 8) {
53 | for (decltype(num) i = 0; i < num; i++) {
54 | Monitors.push_back(*(Screen_Capture::Monitor *)data);
55 | data += sizeof(Screen_Capture::Monitor);
56 | }
57 | return onMonitorsChanged(Monitors);
58 | }
59 | else if (len == 0) {
60 | // it is possible to have no monitors.. shouldnt disconnect in that case
61 | return onMonitorsChanged(Monitors);
62 | }
63 | socket->close(1000, "Invalid Monitor Count");
64 | }
65 | void ClipboardTextChanged(const unsigned char *data, size_t len)
66 | {
67 | if (ShareClip == ClipboardSharing::NOT_SHARED || !onClipboardChanged)
68 | return;
69 | if (len < 1024 * 100) { // 100K max
70 | std::string str(reinterpret_cast(data), len);
71 | onClipboardChanged(str);
72 | }
73 | }
74 | template void Frame(const unsigned char *data, size_t len, CALLBACKTYPE &&cb)
75 | {
76 | int monitor_id = 0;
77 | if (len < sizeof(Rect) + sizeof(monitor_id)) {
78 | return Socket_->close(1000, "Invalid length on onFrameChanged");
79 | }
80 |
81 | auto jpegDecompressor = tjInitDecompress();
82 | int jpegSubsamp(0), outwidth(0), outheight(0);
83 |
84 | auto src = (unsigned char *)data;
85 | memcpy(&monitor_id, src, sizeof(monitor_id));
86 | src += sizeof(monitor_id);
87 | Rect rect;
88 | memcpy(&rect, src, sizeof(rect));
89 | src += sizeof(rect);
90 |
91 | auto monitor = std::find_if(begin(Monitors), end(Monitors), [monitor_id](const auto &m) { return m.Id == monitor_id; });
92 | if (monitor == end(Monitors)) {
93 | SL_RAT_LOG(RAT_Lite::Logging_Levels::INFO_log_level, "Monitor Id doesnt exist!");
94 | return;
95 | }
96 | len -= sizeof(Rect) + sizeof(monitor_id);
97 |
98 | if (tjDecompressHeader2(jpegDecompressor, src, static_cast(len), &outwidth, &outheight, &jpegSubsamp) == -1) {
99 | SL_RAT_LOG(RAT_Lite::Logging_Levels::ERROR_log_level, tjGetErrorStr());
100 | }
101 | std::lock_guard lock(outputbufferLock);
102 | outputbuffer.reserve(outwidth * outheight * PixelStride);
103 |
104 | if (tjDecompress2(jpegDecompressor, src, static_cast(len), (unsigned char *)outputbuffer.data(), outwidth, 0, outheight,
105 | TJPF_RGBX, 2048 | TJFLAG_NOREALLOC) == -1) {
106 | SL_RAT_LOG(RAT_Lite::Logging_Levels::ERROR_log_level, tjGetErrorStr());
107 | }
108 | Image img(rect, outputbuffer.data(), outwidth * outheight * PixelStride);
109 |
110 | assert(outwidth == img.Rect_.Width && outheight == img.Rect_.Height);
111 | cb(img, *monitor);
112 | tjDestroy(jpegDecompressor);
113 | }
114 |
115 | public:
116 | std::function &)> onMonitorsChanged;
117 | std::function onFrameChanged;
118 | std::function onNewFrame;
119 | std::function onMouseImageChanged;
120 | std::function onMousePositionChanged;
121 | std::function onClipboardChanged;
122 | std::function)> onConnection;
123 | std::function &socket, const WS_LITE::WSMessage)> onMessage;
124 | std::function &socket, unsigned short code, const std::string)> onDisconnection;
125 | ClipboardSharing ShareClip = ClipboardSharing::NOT_SHARED;
126 |
127 | ClientDriver() {}
128 | virtual ~ClientDriver() {}
129 | void Build(const std::shared_ptr &wsclientconfig)
130 | {
131 |
132 | wsclientconfig
133 | ->onConnection([&](const std::shared_ptr &socket, const SL::WS_LITE::HttpHeader &header) {
134 | SL_RAT_LOG(RAT_Lite::Logging_Levels::INFO_log_level, "onConnection ");
135 | Socket_ = socket;
136 | if (onConnection)
137 | onConnection(socket);
138 | UNUSED(header);
139 | })
140 | ->onDisconnection([&](const std::shared_ptr &socket, unsigned short code, const std::string &msg) {
141 | SL_RAT_LOG(RAT_Lite::Logging_Levels::INFO_log_level, "onDisconnection ");
142 | Socket_.reset();
143 | if (onDisconnection)
144 | onDisconnection(socket, code, msg);
145 | })
146 | ->onMessage([&](const std::shared_ptr &socket, const SL::WS_LITE::WSMessage &message) {
147 | auto p = PACKET_TYPES::INVALID;
148 | assert(message.len >= sizeof(p));
149 |
150 | p = *reinterpret_cast(message.data);
151 | const auto datastart = message.data + sizeof(p);
152 | size_t datasize = message.len - sizeof(p);
153 | switch (p) {
154 | case PACKET_TYPES::ONMONITORSCHANGED:
155 | MonitorsChanged(socket, datastart, datasize);
156 | break;
157 | case PACKET_TYPES::ONFRAMECHANGED:
158 | if (onFrameChanged) {
159 | Frame(datastart, datasize, [&](const auto &img, const auto &monitor) { onFrameChanged(img, monitor); });
160 | }
161 | break;
162 | case PACKET_TYPES::ONNEWFRAME:
163 | if (onNewFrame) {
164 | Frame(datastart, datasize, [&](const auto &img, const auto &monitor) { onNewFrame(img, monitor); });
165 | }
166 | break;
167 | case PACKET_TYPES::ONMOUSEIMAGECHANGED:
168 | MouseImageChanged(socket, datastart, datasize);
169 | break;
170 | case PACKET_TYPES::ONMOUSEPOSITIONCHANGED:
171 | MousePositionChanged(socket, datastart, datasize);
172 | break;
173 | case PACKET_TYPES::ONCLIPBOARDTEXTCHANGED:
174 | ClipboardTextChanged(datastart, datasize);
175 | break;
176 | default:
177 | if (onMessage)
178 | onMessage(socket, message); // pass up the chain
179 | break;
180 | }
181 |
182 | });
183 | }
184 | virtual void ShareClipboard(ClipboardSharing share) override { ShareClip = share; }
185 | virtual ClipboardSharing ShareClipboard() const override { return ShareClip; }
186 | template void SendStruct_Impl(STRUCT key, PACKET_TYPES ptype)
187 | {
188 | if (!Socket_) {
189 | SL_RAT_LOG(RAT_Lite::Logging_Levels::INFO_log_level, "SendKey called on a socket that is not open yet");
190 | return;
191 | }
192 | const auto size = sizeof(ptype) + sizeof(key);
193 | auto ptr(std::shared_ptr(new unsigned char[size], [](auto *p) { delete[] p; }));
194 | *reinterpret_cast(ptr.get()) = ptype;
195 | memcpy(ptr.get() + sizeof(ptype), &key, sizeof(key));
196 |
197 | SL::WS_LITE::WSMessage buf;
198 | buf.code = WS_LITE::OpCode::BINARY;
199 | buf.Buffer = ptr;
200 | buf.len = size;
201 | buf.data = ptr.get();
202 | Socket_->send(buf, SL::WS_LITE::CompressionOptions::NO_COMPRESSION);
203 | }
204 | virtual void SendClipboardChanged(const std::string &text) override
205 | {
206 | if (!Socket_) {
207 | SL_RAT_LOG(RAT_Lite::Logging_Levels::INFO_log_level, "SendClipboardText called on a socket that is not open yet");
208 | return;
209 | }
210 | if (Socket_->is_loopback())
211 | return; // dont send clipboard info to ourselfs as it will cause a loop
212 |
213 | auto ptype = PACKET_TYPES::ONCLIPBOARDTEXTCHANGED;
214 | auto size = sizeof(ptype) + text.size();
215 | auto ptr(std::shared_ptr(new unsigned char[size], [](auto *p) { delete[] p; }));
216 | *reinterpret_cast(ptr.get()) = ptype;
217 | memcpy(ptr.get() + sizeof(ptype), text.data(), text.size());
218 |
219 | SL::WS_LITE::WSMessage buf;
220 | buf.code = WS_LITE::OpCode::BINARY;
221 | buf.Buffer = ptr;
222 | buf.len = size;
223 | buf.data = ptr.get();
224 | Socket_->send(buf, SL::WS_LITE::CompressionOptions::NO_COMPRESSION);
225 | }
226 | virtual void SendKeyUp(Input_Lite::KeyCodes key) override { SendStruct_Impl(key, PACKET_TYPES::ONKEYUP); }
227 | virtual void SendKeyDown(Input_Lite::KeyCodes key) override { SendStruct_Impl(key, PACKET_TYPES::ONKEYDOWN); }
228 | virtual void SendMouseUp(const Input_Lite::MouseButtons button) override { SendStruct_Impl(button, PACKET_TYPES::ONMOUSEUP); }
229 | virtual void SendMouseDown(const Input_Lite::MouseButtons button) override { SendStruct_Impl(button, PACKET_TYPES::ONMOUSEDOWN); }
230 | virtual void SendMouseScroll(int offset) override { SendStruct_Impl(offset, PACKET_TYPES::ONMOUSESCROLL); }
231 | virtual void SendMousePosition(const Point &pos) override { SendStruct_Impl(pos, PACKET_TYPES::ONMOUSEPOSITIONCHANGED); }
232 | };
233 |
234 | class ClientDriverConfiguration : public IClientDriverConfiguration {
235 | std::shared_ptr ClientDriverImpl;
236 |
237 | public:
238 | ClientDriverConfiguration(const std::shared_ptr &c) : ClientDriverImpl(c) {}
239 | virtual ~ClientDriverConfiguration() {}
240 | virtual std::shared_ptr
241 | onMonitorsChanged(const std::function &)> &callback) override
242 | {
243 | assert(!ClientDriverImpl->onMonitorsChanged);
244 | ClientDriverImpl->onMonitorsChanged = callback;
245 | return std::make_shared(ClientDriverImpl);
246 | }
247 | virtual std::shared_ptr
248 | onFrameChanged(const std::function &callback) override
249 | {
250 | assert(!ClientDriverImpl->onFrameChanged);
251 | ClientDriverImpl->onFrameChanged = callback;
252 | return std::make_shared(ClientDriverImpl);
253 | }
254 | virtual std::shared_ptr
255 | onNewFrame(const std::function &callback) override
256 | {
257 | assert(!ClientDriverImpl->onNewFrame);
258 | ClientDriverImpl->onNewFrame = callback;
259 | return std::make_shared(ClientDriverImpl);
260 | }
261 | virtual std::shared_ptr onMouseImageChanged(const std::function &callback) override
262 | {
263 | assert(!ClientDriverImpl->onMouseImageChanged);
264 | ClientDriverImpl->onMouseImageChanged = callback;
265 | return std::make_shared(ClientDriverImpl);
266 | }
267 | virtual std::shared_ptr onMousePositionChanged(const std::function &callback) override
268 | {
269 | assert(!ClientDriverImpl->onMousePositionChanged);
270 | ClientDriverImpl->onMousePositionChanged = callback;
271 | return std::make_shared(ClientDriverImpl);
272 | }
273 | virtual std::shared_ptr onClipboardChanged(const std::function &callback) override
274 | {
275 | assert(!ClientDriverImpl->onClipboardChanged);
276 | ClientDriverImpl->onClipboardChanged = callback;
277 | return std::make_shared(ClientDriverImpl);
278 | }
279 |
280 | virtual std::shared_ptr
281 | onConnection(const std::function)> &callback) override
282 | {
283 | assert(!ClientDriverImpl->onConnection);
284 | ClientDriverImpl->onConnection = callback;
285 | return std::make_shared(ClientDriverImpl);
286 | }
287 | virtual std::shared_ptr
288 | onMessage(const std::function &socket, const WS_LITE::WSMessage)> &callback) override
289 | {
290 | assert(!ClientDriverImpl->onMessage);
291 | ClientDriverImpl->onMessage = callback;
292 | return std::make_shared(ClientDriverImpl);
293 | }
294 | virtual std::shared_ptr onDisconnection(
295 | const std::function &socket, unsigned short code, const std::string)> &callback)
296 | override
297 | {
298 | assert(!ClientDriverImpl->onDisconnection);
299 | ClientDriverImpl->onDisconnection = callback;
300 | return std::make_shared(ClientDriverImpl);
301 | }
302 | virtual std::shared_ptr Build(const std::shared_ptr &wsclientconfig) override
303 | {
304 | ClientDriverImpl->Build(wsclientconfig);
305 | return ClientDriverImpl;
306 | }
307 | };
308 |
309 | std::shared_ptr CreateClientDriverConfiguration()
310 | {
311 | return std::make_shared(std::make_shared());
312 | }
313 |
314 | } // namespace RAT_Lite
315 | } // namespace SL
316 |
--------------------------------------------------------------------------------
/src/Desktop_Server.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smasherprog/rat_lite/34bc5f9b6e2a5fd44e8223a88566726246bb00a8/src/Desktop_Server.ico
--------------------------------------------------------------------------------
/src/ServerDriver.cpp:
--------------------------------------------------------------------------------
1 | #include "Input_Lite.h"
2 | #include "Logging.h"
3 | #include "RAT.h"
4 | #include "ScreenCapture.h"
5 | #include "WS_Lite.h"
6 | #include "turbojpeg.h"
7 |
8 | #include
9 | #include
10 |
11 | namespace SL {
12 | namespace RAT_Lite {
13 |
14 | class ServerDriver : public IServerDriver {
15 |
16 | public:
17 | ServerDriver() { MemoryInUse = 0; }
18 | virtual ~ServerDriver() {}
19 | std::atomic ClientCount;
20 | ClipboardSharing ShareClip = ClipboardSharing::NOT_SHARED;
21 | int MaxNumConnections = 10;
22 | std::atomic MemoryInUse;
23 | std::function &socket, Input_Lite::KeyCodes key)> onKeyUp;
24 | std::function &socket, Input_Lite::KeyCodes key)> onKeyDown;
25 | std::function &socket, Input_Lite::MouseButtons button)> onMouseUp;
26 | std::function &socket, Input_Lite::MouseButtons button)> onMouseDown;
27 | std::function &socket, const ClientSettings &settings)> onClientSettingsChanged;
28 | std::function &socket, int offset)> onMouseScroll;
29 | std::function &socket, const Point &pos)> onMousePosition;
30 | std::function onClipboardChanged;
31 |
32 | std::function)> onConnection;
33 | std::function &socket, const WS_LITE::WSMessage)> onMessage;
34 | std::function &socket, unsigned short code, const std::string)> onDisconnection;
35 |
36 | std::shared_mutex MonitorsLock;
37 | std::vector Monitors;
38 |
39 | virtual void ShareClipboard(ClipboardSharing share) override { ShareClip = share; }
40 | virtual ClipboardSharing ShareClipboard() const override { return ShareClip; }
41 | virtual void MaxConnections(int maxconnections) override
42 | {
43 | assert(maxconnections >= 0);
44 | MaxNumConnections = maxconnections;
45 | }
46 | virtual int MaxConnections() const override { return MaxNumConnections; }
47 | virtual size_t MemoryUsed() const override { return MemoryInUse; }
48 |
49 | void KeyUp(const std::shared_ptr &socket, const unsigned char *data, size_t len)
50 | {
51 | if (!onKeyUp)
52 | return;
53 | if (len == sizeof(Input_Lite::KeyCodes)) {
54 | onKeyUp(socket, *reinterpret_cast(data));
55 | }
56 | else {
57 | return socket->close(1000, "Received invalid onKeyUp Event");
58 | }
59 | }
60 | void KeyDown(const std::shared_ptr &socket, const unsigned char *data, size_t len)
61 | {
62 | if (!onKeyDown)
63 | return;
64 | if (len == sizeof(Input_Lite::KeyCodes)) {
65 | onKeyDown(socket, *reinterpret_cast(data));
66 | }
67 | else {
68 | return socket->close(1000, "Received invalid onKeyDown Event");
69 | }
70 | }
71 | void MouseUp(const std::shared_ptr &socket, const unsigned char *data, size_t len)
72 | {
73 | if (!onMouseUp)
74 | return;
75 | if (len == sizeof(Input_Lite::MouseButtons)) {
76 | return onMouseUp(socket, *reinterpret_cast(data));
77 | }
78 | socket->close(1000, "Received invalid onMouseUp Event");
79 | }
80 |
81 | void MouseDown(const std::shared_ptr &socket, const unsigned char *data, size_t len)
82 | {
83 | if (!onMouseDown)
84 | return;
85 | if (len == sizeof(Input_Lite::MouseButtons)) {
86 | return onMouseDown(socket, *reinterpret_cast(data));
87 | }
88 | socket->close(1000, "Received invalid onMouseDown Event");
89 | }
90 | void MousePosition(const std::shared_ptr &socket, const unsigned char *data, size_t len)
91 | {
92 | if (!onMousePosition)
93 | return;
94 | if (len == sizeof(Point)) {
95 | auto p = *reinterpret_cast(data);
96 | return onMousePosition(socket, p);
97 | }
98 | socket->close(1000, "Received invalid onMouseDown Event");
99 | }
100 | void MouseScroll(const std::shared_ptr &socket, const unsigned char *data, size_t len)
101 | {
102 | if (!onMouseScroll)
103 | return;
104 | if (len == sizeof(int)) {
105 | return onMouseScroll(socket, *reinterpret_cast(data));
106 | }
107 | socket->close(1000, "Received invalid onMouseScroll Event");
108 | }
109 | void ClientSettingsChanged(const std::shared_ptr &socket, const unsigned char *data, size_t len)
110 | {
111 | if (!onClientSettingsChanged)
112 | return;
113 | ClientSettings c;
114 | auto beginsize = sizeof(c.ShareClip) + sizeof(c.ImageCompressionSetting) + sizeof(c.EncodeImagesAsGrayScale);
115 | auto remainingdata = len - beginsize;
116 | if (len >= (beginsize + sizeof(int)) && // min size received at least 1 monitor
117 | (remainingdata % sizeof(int) == 0)) { // the remaining bytes are divisible by sizeof int
118 | auto start = data;
119 | // need to do a manualy copy because of byte packing
120 | memcpy(&c.ShareClip, start, sizeof(c.ShareClip));
121 | start += sizeof(c.ShareClip);
122 | memcpy(&c.ImageCompressionSetting, start, sizeof(c.ImageCompressionSetting));
123 | start += sizeof(c.ImageCompressionSetting);
124 | memcpy(&c.EncodeImagesAsGrayScale, start, sizeof(c.EncodeImagesAsGrayScale));
125 | start += sizeof(c.EncodeImagesAsGrayScale);
126 | auto begin = reinterpret_cast(start);
127 | auto end = reinterpret_cast(start + remainingdata + sizeof(int));
128 | std::shared_lock lock(MonitorsLock);
129 | for (auto b = begin; b < end; b++) {
130 | auto found = std::find_if(std::begin(Monitors), std::end(Monitors), [b](auto &mon) { return mon.Id == *b; });
131 | if (found != std::end(Monitors)) {
132 | c.MonitorsToWatch.push_back(*found);
133 | }
134 | }
135 | return onClientSettingsChanged(socket, c);
136 | }
137 | socket->close(1000, "Received invalid onClientSettingsChanged Event");
138 | }
139 | void ClipboardChanged(const unsigned char *data, size_t len)
140 | {
141 | if (!onClipboardChanged || ShareClip == ClipboardSharing::NOT_SHARED)
142 | return;
143 | if (len < 1024 * 100) { // 100K max
144 | std::string str(reinterpret_cast(data), len);
145 | return onClipboardChanged(str);
146 | }
147 | }
148 |
149 | void Build(const std::shared_ptr &wslistenerconfig)
150 | {
151 |
152 | ClientCount = 0;
153 | wslistenerconfig
154 | ->onConnection([&](const std::shared_ptr &socket, const SL::WS_LITE::HttpHeader &header) {
155 | UNUSED(header);
156 | if (MaxNumConnections > 0 && ClientCount + 1 > MaxNumConnections) {
157 | socket->close(1000, "Closing due to max number of connections!");
158 | }
159 | else {
160 | if (onConnection)
161 | onConnection(socket);
162 | ClientCount += 1;
163 | }
164 | })
165 | ->onDisconnection([&](const std::shared_ptr &socket, unsigned short code, const std::string &msg) {
166 | SL_RAT_LOG(RAT_Lite::Logging_Levels::INFO_log_level, "onDisconnection ");
167 | ClientCount -= 1;
168 | if (onDisconnection)
169 | onDisconnection(socket, code, msg);
170 | })
171 | ->onMessage([&](const std::shared_ptr &socket, const WS_LITE::WSMessage &message) {
172 |
173 | auto p = PACKET_TYPES::INVALID;
174 | assert(message.len >= sizeof(p));
175 |
176 | p = *reinterpret_cast(message.data);
177 | auto datastart = message.data + sizeof(p);
178 | auto datasize = message.len - sizeof(p);
179 |
180 | switch (p) {
181 | case PACKET_TYPES::ONKEYDOWN:
182 | KeyDown(socket, datastart, datasize);
183 | break;
184 | case PACKET_TYPES::ONKEYUP:
185 | KeyUp(socket, datastart, datasize);
186 | break;
187 | case PACKET_TYPES::ONMOUSEUP:
188 | MouseUp(socket, datastart, datasize);
189 | break;
190 | case PACKET_TYPES::ONMOUSEDOWN:
191 | MouseDown(socket, datastart, datasize);
192 | break;
193 | case PACKET_TYPES::ONMOUSEPOSITIONCHANGED:
194 | MousePosition(socket, datastart, datasize);
195 | break;
196 | case PACKET_TYPES::ONCLIENTSETTINGSCHANGED:
197 | ClientSettingsChanged(socket, datastart, datasize);
198 | break;
199 | case PACKET_TYPES::ONMOUSESCROLL:
200 | MouseScroll(socket, datastart, datasize);
201 | break;
202 | case PACKET_TYPES::ONCLIPBOARDTEXTCHANGED:
203 | ClipboardChanged(datastart, datasize);
204 | break;
205 | default:
206 | if (onMessage)
207 | onMessage(socket, message);
208 | break;
209 | }
210 | });
211 | }
212 |
213 | WS_LITE::WSMessage PrepareImage(const Screen_Capture::Image &img, int imagecompression, bool usegrayscale,
214 | const Screen_Capture::Monitor &monitor, PACKET_TYPES p)
215 | {
216 | Rect r(Point(img.Bounds.left, img.Bounds.top), Height(img), Width(img));
217 | auto set = usegrayscale ? TJSAMP_GRAY : TJSAMP_420;
218 | unsigned long maxsize =
219 | tjBufSize(Screen_Capture::Width(img), Screen_Capture::Height(img), set) + sizeof(r) + sizeof(p) + sizeof(monitor.Id);
220 | auto jpegCompressor = tjInitCompress();
221 | MemoryInUse += maxsize;
222 | auto buffer = std::shared_ptr(new unsigned char[maxsize], [maxsize, this](auto *p) {
223 | delete[] p;
224 | MemoryInUse -= maxsize;
225 | });
226 | auto dst = (unsigned char *)buffer.get();
227 | memcpy(dst, &p, sizeof(p));
228 | dst += sizeof(p);
229 | memcpy(dst, &monitor.Id, sizeof(monitor.Id));
230 | dst += sizeof(monitor.Id);
231 | memcpy(dst, &r, sizeof(r));
232 | dst += sizeof(r);
233 | auto srcbuffer = std::make_unique(RowStride(img) * Height(img));
234 | Screen_Capture::Extract(img, srcbuffer.get(), RowStride(img) * Height(img));
235 | auto srcbuf = (unsigned char *)srcbuffer.get();
236 | auto colorencoding = TJPF_BGRX;
237 | auto outjpegsize = maxsize;
238 |
239 | if (tjCompress2(jpegCompressor, srcbuf, r.Width, 0, r.Height, colorencoding, &dst, &outjpegsize, set, imagecompression,
240 | TJFLAG_FASTDCT | TJFLAG_NOREALLOC) == -1) {
241 | SL_RAT_LOG(RAT_Lite::Logging_Levels::ERROR_log_level, tjGetErrorStr());
242 | }
243 | // std::cout << "Sending " << r << std::endl;
244 | auto finalsize = sizeof(p) + sizeof(r) + sizeof(monitor.Id) + outjpegsize; // adjust the correct size
245 | tjDestroy(jpegCompressor);
246 | return WS_LITE::WSMessage{buffer.get(), finalsize, WS_LITE::OpCode::BINARY, buffer};
247 | }
248 |
249 | virtual WS_LITE::WSMessage PrepareMonitorsChanged(const std::vector &monitors) override
250 | {
251 | {
252 | std::unique_lock lock(MonitorsLock);
253 | Monitors = monitors;
254 | }
255 | auto p = static_cast(PACKET_TYPES::ONMONITORSCHANGED);
256 | const auto finalsize = (monitors.size() * sizeof(Screen_Capture::Monitor)) + sizeof(p);
257 |
258 | MemoryInUse += finalsize;
259 | auto buffer = std::shared_ptr(new unsigned char[finalsize], [finalsize, this](auto *p) {
260 | delete[] p;
261 | MemoryInUse -= finalsize;
262 | });
263 |
264 | auto buf = buffer.get();
265 | memcpy(buf, &p, sizeof(p));
266 | buf += sizeof(p);
267 | for (auto &a : monitors) {
268 | memcpy(buf, &a, sizeof(a));
269 | buf += sizeof(Screen_Capture::Monitor);
270 | }
271 | return WS_LITE::WSMessage{buffer.get(), finalsize, WS_LITE::OpCode::BINARY, buffer};
272 | }
273 | virtual WS_LITE::WSMessage PrepareFrameChanged(const Screen_Capture::Image &image, const Screen_Capture::Monitor &monitor,
274 | int imagecompression, bool usegrayscale) override
275 | {
276 | assert(imagecompression > 0 && imagecompression <= 100);
277 | return PrepareImage(image, imagecompression, usegrayscale, monitor, PACKET_TYPES::ONFRAMECHANGED);
278 | }
279 | virtual WS_LITE::WSMessage PrepareNewFrame(const Screen_Capture::Image &image, const Screen_Capture::Monitor &monitor, int imagecompression,
280 | bool usegrayscale) override
281 | {
282 | assert(imagecompression > 0 && imagecompression <= 100);
283 | return PrepareImage(image, imagecompression, usegrayscale, monitor, PACKET_TYPES::ONNEWFRAME);
284 | }
285 | virtual WS_LITE::WSMessage PrepareMouseImageChanged(const Screen_Capture::Image &image) override
286 | {
287 | Rect r(Point(0, 0), Height(image), Width(image));
288 | auto p = static_cast(PACKET_TYPES::ONMOUSEIMAGECHANGED);
289 | auto finalsize = (Screen_Capture::RowStride(image) * Screen_Capture::Height(image)) + sizeof(p) + sizeof(r);
290 | MemoryInUse += finalsize;
291 | auto buffer = std::shared_ptr(new unsigned char[finalsize], [finalsize, this](auto *p) {
292 | delete[] p;
293 | MemoryInUse -= finalsize;
294 | });
295 | auto dst = buffer.get();
296 | memcpy(dst, &p, sizeof(p));
297 | dst += sizeof(p);
298 | memcpy(dst, &r, sizeof(r));
299 | dst += sizeof(r);
300 | Screen_Capture::Extract(image, (unsigned char *)dst, Screen_Capture::RowStride(image) * Screen_Capture::Height(image));
301 | return WS_LITE::WSMessage{buffer.get(), finalsize, WS_LITE::OpCode::BINARY, buffer};
302 | }
303 | virtual WS_LITE::WSMessage PrepareMousePositionChanged(const SL::Screen_Capture::Point &pos) override
304 | {
305 | auto p = static_cast(PACKET_TYPES::ONMOUSEPOSITIONCHANGED);
306 | const auto finalsize = sizeof(pos) + sizeof(p);
307 | MemoryInUse += finalsize;
308 | auto buffer = std::shared_ptr(new unsigned char[finalsize], [finalsize, this](auto *p) {
309 | delete[] p;
310 | MemoryInUse -= finalsize;
311 | });
312 | memcpy(buffer.get(), &p, sizeof(p));
313 | memcpy(buffer.get() + sizeof(p), &pos, sizeof(pos));
314 | return WS_LITE::WSMessage{buffer.get(), finalsize, WS_LITE::OpCode::BINARY, buffer};
315 | }
316 | virtual WS_LITE::WSMessage PrepareClipboardChanged(const std::string &text) override
317 | {
318 | auto p = static_cast(PACKET_TYPES::ONCLIPBOARDTEXTCHANGED);
319 | auto finalsize = text.size() + sizeof(p);
320 | MemoryInUse += finalsize;
321 | auto buffer = std::shared_ptr(new unsigned char[finalsize], [finalsize, this](auto *p) {
322 | delete[] p;
323 | MemoryInUse -= finalsize;
324 | });
325 | memcpy(buffer.get(), &p, sizeof(p));
326 | memcpy(buffer.get() + sizeof(p), text.data(), text.size());
327 | return WS_LITE::WSMessage{buffer.get(), finalsize, WS_LITE::OpCode::BINARY, buffer};
328 | }
329 | };
330 | class ServerDriverConfiguration : public IServerDriverConfiguration {
331 | std::shared_ptr ServerDriver_;
332 |
333 | public:
334 | ServerDriverConfiguration(const std::shared_ptr &c) : ServerDriver_(c) {}
335 | virtual ~ServerDriverConfiguration() {}
336 | virtual std::shared_ptr
337 | onKeyUp(const std::function &socket, Input_Lite::KeyCodes key)> &callback) override
338 | {
339 | assert(!ServerDriver_->onKeyUp);
340 | ServerDriver_->onKeyUp = callback;
341 | return std::make_shared(ServerDriver_);
342 | }
343 | virtual std::shared_ptr
344 | onKeyDown(const std::function &socket, Input_Lite::KeyCodes key)> &callback) override
345 | {
346 | assert(!ServerDriver_->onKeyDown);
347 | ServerDriver_->onKeyDown = callback;
348 | return std::make_shared(ServerDriver_);
349 | }
350 | virtual std::shared_ptr
351 | onMouseUp(const std::function &socket, Input_Lite::MouseButtons button)> &callback) override
352 | {
353 | assert(!ServerDriver_->onMouseUp);
354 | ServerDriver_->onMouseUp = callback;
355 | return std::make_shared(ServerDriver_);
356 | }
357 | virtual std::shared_ptr
358 | onMouseDown(const std::function &socket, Input_Lite::MouseButtons button)> &callback) override
359 | {
360 | assert(!ServerDriver_->onMouseDown);
361 | ServerDriver_->onMouseDown = callback;
362 | return std::make_shared(ServerDriver_);
363 | }
364 | virtual std::shared_ptr
365 | onMouseScroll(const std::function &socket, int offset)> &callback) override
366 | {
367 | assert(!ServerDriver_->onMouseScroll);
368 | ServerDriver_->onMouseScroll = callback;
369 | return std::make_shared(ServerDriver_);
370 | }
371 | virtual std::shared_ptr
372 | onMousePosition(const std::function &socket, const Point &pos)> &callback) override
373 | {
374 | assert(!ServerDriver_->onMousePosition);
375 | ServerDriver_->onMousePosition = callback;
376 | return std::make_shared(ServerDriver_);
377 | }
378 | virtual std::shared_ptr onClipboardChanged(const std::function &callback) override
379 | {
380 | assert(!ServerDriver_->onClipboardChanged);
381 | ServerDriver_->onClipboardChanged = callback;
382 | return std::make_shared(ServerDriver_);
383 | }
384 | virtual std::shared_ptr
385 | onConnection(const std::function)> &callback) override
386 | {
387 | assert(!ServerDriver_->onConnection);
388 | ServerDriver_->onConnection = callback;
389 | return std::make_shared(ServerDriver_);
390 | }
391 | virtual std::shared_ptr
392 | onMessage(const std::function &socket, const WS_LITE::WSMessage)> &callback) override
393 | {
394 | assert(!ServerDriver_->onMessage);
395 | ServerDriver_->onMessage = callback;
396 | return std::make_shared(ServerDriver_);
397 | }
398 | virtual std::shared_ptr onDisconnection(
399 | const std::function &socket, unsigned short code, const std::string)> &callback)
400 | override
401 | {
402 | assert(!ServerDriver_->onDisconnection);
403 | ServerDriver_->onDisconnection = callback;
404 | return std::make_shared(ServerDriver_);
405 | }
406 | virtual std::shared_ptr onClientSettingsChanged(
407 | const std::function &socket, const ClientSettings &settings)> &callback) override
408 | {
409 | assert(!ServerDriver_->onClientSettingsChanged);
410 | ServerDriver_->onClientSettingsChanged = callback;
411 | return std::make_shared(ServerDriver_);
412 | }
413 | virtual std::shared_ptr Build(const std::shared_ptr &wslistenerconfig) override
414 | {
415 | ServerDriver_->Build(wslistenerconfig);
416 | return ServerDriver_;
417 | }
418 | };
419 |
420 | std::shared_ptr CreateServerDriverConfiguration()
421 | {
422 | return std::make_shared(std::make_shared());
423 | }
424 |
425 | } // namespace RAT_Lite
426 | } // namespace SL
427 |
--------------------------------------------------------------------------------
/src/client/.angular-cli.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
3 | "project": {
4 | "name": "ratclient",
5 | "ejected": true
6 | },
7 | "apps": [
8 | {
9 | "root": "src",
10 | "outDir": "dist",
11 | "assets": [
12 | "assets",
13 | "favicon.ico"
14 | ],
15 | "index": "index.html",
16 | "main": "main.ts",
17 | "polyfills": "polyfills.ts",
18 | "test": "test.ts",
19 | "tsconfig": "tsconfig.app.json",
20 | "prefix": "app",
21 | "styles": [
22 | "styles.scss"
23 | ],
24 | "scripts": [],
25 | "environmentSource": "environments/environment.ts",
26 | "environments": {
27 | "dev": "environments/environment.ts",
28 | "prod": "environments/environment.prod.ts"
29 | }
30 | }
31 | ],
32 | "defaults": {
33 | "styleExt": "css",
34 | "component": {}
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/client/.editorconfig:
--------------------------------------------------------------------------------
1 | # Editor configuration, see http://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | charset = utf-8
6 | indent_style = space
7 | indent_size = 2
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
11 | [*.md]
12 | max_line_length = off
13 | trim_trailing_whitespace = false
14 |
--------------------------------------------------------------------------------
/src/client/.vscode/0.010801647396913694.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 | call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\Common7\Tools\VsDevCmd.bat" -no_logo -arch=x86 || exit
3 | echo CL := %CL%
4 | echo _CL_ := %_CL_%
5 | echo INCLUDE := %INCLUDE%
6 | echo LIBPATH := %LIBPATH%
7 | echo LINK := %LINK%
8 | echo _LINK_ := %_LINK_%
9 | echo LIB := %LIB%
10 | echo PATH := %PATH%
11 | echo TMP := %TMP%
12 | echo FRAMEWORKDIR := %FRAMEWORKDIR%
13 | echo FRAMEWORKDIR64 := %FRAMEWORKDIR64%
14 | echo FRAMEWORKVERSION := %FRAMEWORKVERSION%
15 | echo FRAMEWORKVERSION64 := %FRAMEWORKVERSION64%
16 | echo UCRTCONTEXTROOT := %UCRTCONTEXTROOT%
17 | echo UCRTVERSION := %UCRTVERSION%
18 | echo UNIVERSALCRTSDKDIR := %UNIVERSALCRTSDKDIR%
19 | echo VCINSTALLDIR := %VCINSTALLDIR%
20 | echo VCTARGETSPATH := %VCTARGETSPATH%
21 | echo WINDOWSLIBPATH := %WINDOWSLIBPATH%
22 | echo WINDOWSSDKDIR := %WINDOWSSDKDIR%
23 | echo WINDOWSSDKLIBVERSION := %WINDOWSSDKLIBVERSION%
24 | echo WINDOWSSDKVERSION := %WINDOWSSDKVERSION%
--------------------------------------------------------------------------------
/src/client/.vscode/0.8898083114717834.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 | call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\Common7\Tools\VsDevCmd.bat" -no_logo -arch=amd64 || exit
3 | echo CL := %CL%
4 | echo _CL_ := %_CL_%
5 | echo INCLUDE := %INCLUDE%
6 | echo LIBPATH := %LIBPATH%
7 | echo LINK := %LINK%
8 | echo _LINK_ := %_LINK_%
9 | echo LIB := %LIB%
10 | echo PATH := %PATH%
11 | echo TMP := %TMP%
12 | echo FRAMEWORKDIR := %FRAMEWORKDIR%
13 | echo FRAMEWORKDIR64 := %FRAMEWORKDIR64%
14 | echo FRAMEWORKVERSION := %FRAMEWORKVERSION%
15 | echo FRAMEWORKVERSION64 := %FRAMEWORKVERSION64%
16 | echo UCRTCONTEXTROOT := %UCRTCONTEXTROOT%
17 | echo UCRTVERSION := %UCRTVERSION%
18 | echo UNIVERSALCRTSDKDIR := %UNIVERSALCRTSDKDIR%
19 | echo VCINSTALLDIR := %VCINSTALLDIR%
20 | echo VCTARGETSPATH := %VCTARGETSPATH%
21 | echo WINDOWSLIBPATH := %WINDOWSLIBPATH%
22 | echo WINDOWSSDKDIR := %WINDOWSSDKDIR%
23 | echo WINDOWSSDKLIBVERSION := %WINDOWSSDKLIBVERSION%
24 | echo WINDOWSSDKVERSION := %WINDOWSSDKVERSION%
--------------------------------------------------------------------------------
/src/client/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | project(rat_client)
2 |
3 | add_library(${PROJECT_NAME}
4 | dummy.cpp
5 | )
6 | add_custom_command(
7 | TARGET rat_client
8 | POST_BUILD
9 | WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
10 | COMMAND npm install
11 | )
12 |
--------------------------------------------------------------------------------
/src/client/dummy.cpp:
--------------------------------------------------------------------------------
1 | // Dummy file to get cmake to generate a project!!
--------------------------------------------------------------------------------
/src/client/main.ts:
--------------------------------------------------------------------------------
1 | const { app, BrowserWindow } = require('electron')
2 | const path = require('path')
3 | const url = require('url')
4 |
5 |
6 | let win, serve;
7 | const args = process.argv.slice(1);
8 | serve = args.some(val => val === '--serve');
9 |
10 | if (serve) {
11 | require('electron-reload')(__dirname, {
12 | });
13 | }
14 | function createWindow() {
15 | // Create the browser window.
16 | win = new BrowserWindow({ width: 800, height: 600 })
17 |
18 | // and load the index.html of the app.
19 | win.loadURL(url.format({
20 | pathname: path.join(__dirname, 'index.html'),
21 | protocol: 'file:',
22 | slashes: true
23 | }))
24 |
25 | // Open the DevTools.
26 | if (serve) {
27 | win.webContents.openDevTools();
28 | }
29 |
30 | // Emitted when the window is closed.
31 | win.on('closed', () => {
32 | // Dereference the window object, usually you would store windows
33 | // in an array if your app supports multi windows, this is the time
34 | // when you should delete the corresponding element.
35 | win = null
36 | })
37 | }
38 |
39 | // This method will be called when Electron has finished
40 | // initialization and is ready to create browser windows.
41 | // Some APIs can only be used after this event occurs.
42 | app.on('ready', createWindow)
43 |
44 | // Quit when all windows are closed.
45 | app.on('window-all-closed', () => {
46 | // On macOS it is common for applications and their menu bar
47 | // to stay active until the user quits explicitly with Cmd + Q
48 | if (process.platform !== 'darwin') {
49 | app.quit()
50 | }
51 | })
52 |
53 | app.on('activate', () => {
54 | // On macOS it's common to re-create a window in the app when the
55 | // dock icon is clicked and there are no other windows open.
56 | if (win === null) {
57 | createWindow()
58 | }
59 | })
60 |
--------------------------------------------------------------------------------
/src/client/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ratclient",
3 | "version": "0.0.0",
4 | "license": "MIT",
5 | "private": true,
6 | "main": "main.js",
7 | "scripts": {
8 | "start": "npm-run-all --parallel webpack:watch electron:serve",
9 | "webpack:watch": "webpack --watch",
10 | "start:web": "webpack-dev-server --content-base . --port 4200 --inline",
11 | "build:electron:main": "tsc main.ts --outDir dist && copyfiles package.json dist && cd dist && npm install --prod && cd ..",
12 | "build": "webpack --display-error-details && npm run build:electron:main",
13 | "build:prod": "cross-env NODE_ENV=production npm run build",
14 | "electron:serve": "npm run build:electron:main && electron ./dist --serve",
15 | "electron:test": "electron ./dist",
16 | "electron:dev": "npm run build && electron ./dist",
17 | "electron:prod": "npm run build:prod && electron ./dist",
18 | "electron:linux": "npm run build:prod && node package.js --asar --platform=linux --arch=x64",
19 | "electron:windows": "npm run build:prod && node package.js --asar --platform=win32 --arch=ia32",
20 | "electron:mac": "npm run build:prod && node package.js --asar --platform=darwin --arch=x64"
21 | },
22 | "dependencies": {
23 | "@angular/animations": "5.0.1",
24 | "@angular/cdk": "5.0.0-rc0",
25 | "@angular/common": "5.0.1",
26 | "@angular/compiler": "5.0.1",
27 | "@angular/core": "5.0.1",
28 | "@angular/forms": "5.0.1",
29 | "@angular/http": "5.0.1",
30 | "@angular/material": "5.0.0-rc0",
31 | "@angular/platform-browser": "5.0.1",
32 | "@angular/platform-browser-dynamic": "5.0.1",
33 | "@angular/router": "5.0.1",
34 | "core-js": "^2.4.1",
35 | "rxjs": "^5.5.2",
36 | "zone.js": "^0.8.14"
37 | },
38 | "devDependencies": {
39 | "@angular/cli": "^1.5.0",
40 | "@angular/compiler-cli": "5.0.1",
41 | "@types/core-js": "0.9.36",
42 | "@types/jasmine": "2.5.54",
43 | "@types/node": "7.0.7",
44 | "autoprefixer": "7.1.4",
45 | "codelyzer": "3.2.0",
46 | "copyfiles": "1.2.0",
47 | "cross-env": "5.0.5",
48 | "css-loader": "0.28.7",
49 | "cssnano": "3.10.0",
50 | "electron": "1.7.11",
51 | "electron-packager": "9.1.0",
52 | "electron-reload": "1.2.1",
53 | "exports-loader": "0.6.4",
54 | "file-loader": "0.11.2",
55 | "html-loader": "0.5.1",
56 | "istanbul-instrumenter-loader": "3.0.0",
57 | "jasmine-core": "2.8.0",
58 | "jasmine-spec-reporter": "4.2.1",
59 | "json-loader": "0.5.7",
60 | "less-loader": "4.0.5",
61 | "minimist": "1.2.0",
62 | "mkdirp": "0.5.1",
63 | "npm-run-all": "^4.1.1",
64 | "postcss-loader": "2.0.6",
65 | "postcss-url": "7.1.2",
66 | "protractor": "5.1.2",
67 | "raw-loader": "0.5.1",
68 | "sass-loader": "6.0.6",
69 | "script-loader": "0.7.1",
70 | "source-map-loader": "0.2.1",
71 | "style-loader": "0.18.2",
72 | "stylus-loader": "3.0.1",
73 | "ts-node": "3.3.0",
74 | "tslint": "5.7.0",
75 | "typescript": "2.5.2",
76 | "url-loader": "^0.6.2",
77 | "webdriver-manager": "12.0.6",
78 | "webpack": "~3.8.1",
79 | "webpack-dev-server": "~2.9.3"
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/src/client/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {};
--------------------------------------------------------------------------------
/src/client/src/app/app.component.css:
--------------------------------------------------------------------------------
1 | .OptionsButtonHidden{
2 | position:relative;
3 | width:200px;
4 | margin:auto;
5 | z-index:50;
6 | }
7 | .connect-spinner {
8 | width:320px;
9 | margin:auto;
10 | }
--------------------------------------------------------------------------------
/src/client/src/app/app.component.html:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
Connecting to {{ConnectedTo}}
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/src/client/src/app/app.component.ts:
--------------------------------------------------------------------------------
1 | import { ConvertToKeyCode } from './lib/input_lite';
2 | import { window } from 'rxjs/operators';
3 | import { MonitorsCanvasComponent } from './monitorcanvas/monitorcanvas.component';
4 | import { OptionsDialog } from './options.dialog/options.dialog';
5 | import {Component, ElementRef, HostListener, OnInit, ViewChild, QueryList, ViewChildren} from '@angular/core';
6 | import {MatDialog} from '@angular/material';
7 |
8 | import {ConnectDialog} from './connect.dialog/connect.dialog';
9 | import {
10 | ClientSettings,
11 | CreateClientDriverConfiguration,
12 | IClientDriver,
13 | Monitor,
14 | Point,
15 | Rect,
16 | WSMessage,
17 | } from './lib/rat_lite';
18 | import {ConnectModel} from './models/connect.model';
19 |
20 | @Component({selector : 'app-root', templateUrl : './app.component.html', styleUrls : [ './app.component.css' ]})
21 | export class AppComponent implements OnInit {
22 | @ViewChild('HTMLCanvasMouseImage_') HTMLCanvasMouseImage_: ElementRef;
23 | @ViewChildren('HTMLCanvasScreenImage_') MonitorCanvas: QueryList;
24 |
25 | ScaleImage_ = false;
26 | ClientDriver_: IClientDriver;
27 | Socket_: WebSocket;
28 | Monitors = new Array();
29 | MouseOverMonitor: Monitor;
30 | ClientSettings_ : ClientSettings;
31 | Cursor_: ImageData;
32 | width = '1000px';
33 | ConnectedTo = '';
34 |
35 | constructor(public dialog: MatDialog) {}
36 | public ngOnInit(): void
37 | {
38 | setTimeout(() => { this.OpenDialog(); }, 100);
39 |
40 | }
41 | public Disconnect(): void
42 | {
43 | if (this.Socket_) {
44 | this.Socket_.close(1000);
45 | }
46 | }
47 | public toggleoptions(): void{
48 | this.dialog.open(OptionsDialog, {
49 | data:
50 | {
51 | ClientSettings:this.ClientSettings_,
52 | Monitors: this.Monitors
53 | }
54 | }).afterClosed().subscribe((a: ClientSettings)=>{
55 | if(a){
56 | this.ClientSettings_ = a;
57 | this.ClientDriver_.SendClientSettingsChanged(a);
58 | }
59 | });
60 | }
61 | public overmonitor(mon: Monitor): void{
62 | this.MouseOverMonitor=mon;
63 | console.log('overmonitor');
64 | }
65 | private humanFileSize(bytes: number) : string {
66 | var thresh = 1024;
67 | if(Math.abs(bytes) < thresh) {
68 | return bytes + ' B';
69 | }
70 | var units =['kB','MB','GB','TB','PB','EB','ZB','YB'] ;
71 | var u = -1;
72 | do {
73 | bytes /= thresh;
74 | ++u;
75 | } while(Math.abs(bytes) >= thresh && u < units.length - 1);
76 | return bytes.toFixed(1)+' '+units[u];
77 | }
78 | public OpenDialog(): void
79 | {
80 | this.dialog.open(ConnectDialog, {disableClose : true}).afterClosed().subscribe((a: ConnectModel) => {
81 | if (a) {
82 | this.Socket_ = new WebSocket(a.Protocol + "://" + a.Host + ":" + a.Port);
83 | this.Socket_.binaryType = 'arraybuffer';
84 | this.ConnectedTo = a.Protocol + "://" + a.Host + ":" + a.Port;
85 | this.ClientDriver_ =
86 | CreateClientDriverConfiguration()
87 | .onConnection((ws: WebSocket, ev: Event) => {
88 | console.log('onConnection');
89 | this.ClientSettings_ = new ClientSettings();
90 | })
91 | .onBytesPerSecond((bytespersecond: number)=>{
92 | document.title = 'Connected to '+ this.ConnectedTo + ' ' + this.humanFileSize(bytespersecond) + '/sec';
93 | })
94 | .onMessage((ws: WebSocket, message: WSMessage) => { console.log('onMessage length:' + message.data.byteLength); })
95 | .onDisconnection((ws: WebSocket, code: number, message: string) => {
96 | console.log('onDisconnection ' + code + " "+ message);
97 | this.ConnectedTo ='';
98 | this.Cursor_ = null;
99 | this.ScaleImage_ = false;
100 | this.Monitors = new Array();
101 | this.ClientDriver_ = null;
102 | this.Socket_ = null;
103 | this.MouseOverMonitor=null;
104 | this.ClientSettings_ = new ClientSettings();
105 | this.OpenDialog();
106 | })
107 | .onClipboardChanged((clipstring: string) => { console.log('onClipboardChanged: ' + clipstring); })
108 | .onFrameChanged((image: HTMLImageElement, monitor: Monitor, rect: Rect) => {
109 | this.MonitorCanvas.forEach((a: MonitorsCanvasComponent)=>{
110 | a.onFrameChanged(image, monitor, rect);
111 | });
112 | var totalwidth=0;
113 | this.MonitorCanvas.forEach((a: MonitorsCanvasComponent)=>{ totalwidth += (a.getScalingFactor() * a.Monitor.Width); });
114 | this.width = totalwidth.toString() + 'px';
115 | })
116 | .onMonitorsChanged((monitors: Monitor[]) => {
117 | if(this.ClientSettings_.MonitorsToWatch.length ==0){
118 | this.ClientSettings_.MonitorsToWatch = monitors;
119 | }
120 | this.Monitors = monitors;
121 | })
122 | .onMouseImageChanged((image: ImageData) => {
123 | this.Cursor_ = image;
124 | if (this.HTMLCanvasMouseImage_ && this.HTMLCanvasMouseImage_.nativeElement ) {
125 | this.HTMLCanvasMouseImage_.nativeElement.getContext("2d").putImageData(this.Cursor_, 0, 0);
126 | }
127 | })
128 | .onMousePositionChanged((pos: Point) => {
129 | if (this.HTMLCanvasMouseImage_ && this.HTMLCanvasMouseImage_.nativeElement ) {
130 | this.HTMLCanvasMouseImage_.nativeElement.style.top = pos.Y + "px";
131 | this.HTMLCanvasMouseImage_.nativeElement.style.left = pos.X + "px";
132 | }
133 | })
134 | .onNewFrame((image: HTMLImageElement, monitor: Monitor, rect: Rect) => {
135 | this.MonitorCanvas.forEach((a: MonitorsCanvasComponent)=>{
136 | a.onNewFrame(image, monitor, rect);
137 | });
138 | })
139 | .Build(this.Socket_);
140 |
141 | this.Socket_.onerror = (ev: Event) => { console.log(ev); };
142 | }
143 | else {
144 | this.OpenDialog();
145 | }
146 | });
147 | }
148 | @HostListener('document:keydown', [ '$event' ])
149 | onkeydown(ev: KeyboardEvent)
150 | {
151 | if (this.ClientDriver_ && this.Socket_.readyState === this.Socket_.OPEN) {
152 | this.ClientDriver_.SendKeyDown(ConvertToKeyCode(ev));
153 | }
154 | }
155 | @HostListener('document:keyup', [ '$event' ])
156 | onkeyup(ev: KeyboardEvent)
157 | {
158 | if (this.ClientDriver_ && this.Socket_.readyState === this.Socket_.OPEN) {
159 | this.ClientDriver_.SendKeyUp(ConvertToKeyCode(ev));
160 | }
161 | }
162 | @HostListener('mousewheel', [ '$event' ])
163 | onwheel(ev: WheelEvent)
164 | {
165 | if (this.ClientDriver_ && this.Socket_.readyState === this.Socket_.OPEN) {
166 | this.ClientDriver_.SendMouseScroll(ev.deltaY < 0 ? -1 : 1);
167 | }
168 | }
169 | @HostListener('mousemove', [ '$event' ])
170 | onmove(ev: WheelEvent)
171 | {
172 | if (this.ClientDriver_ && this.Socket_.readyState === this.Socket_.OPEN && this.MouseOverMonitor) {
173 | let scaling = this.MouseOverMonitor.Scaling;
174 | let point = {Y : ev.pageY * scaling, X : ev.pageX* scaling} as Point;
175 | this.ClientDriver_.SendMousePosition(point);
176 | }
177 | }
178 | @HostListener('mouseup', [ '$event' ])
179 | onmouseup(ev: MouseEvent)
180 | {
181 | if (this.ClientDriver_ && this.Socket_.readyState === this.Socket_.OPEN) {
182 | this.ClientDriver_.SendMouseUp(ev.button);
183 | }
184 | }
185 | @HostListener('onmousedownheel', [ '$event' ])
186 | onmousedown(ev: MouseEvent)
187 | {
188 | if (this.ClientDriver_ && this.Socket_.readyState === this.Socket_.OPEN) {
189 | this.ClientDriver_.SendMouseDown(ev.button);
190 | }
191 | }
192 | @HostListener('mousedown', [ '$event' ])
193 | mousedown(ev: MouseEvent)
194 | {
195 | if (this.ClientDriver_ && this.Socket_.readyState === this.Socket_.OPEN) {
196 | this.ClientDriver_.SendMouseDown(ev.button);
197 | }
198 | }
199 | }
200 |
--------------------------------------------------------------------------------
/src/client/src/app/app.module.ts:
--------------------------------------------------------------------------------
1 | import { OptionsDialog } from './options.dialog/options.dialog';
2 | import { NoopAnimationsModule } from '@angular/platform-browser/animations';
3 | import { MaterialModule } from './material.module';
4 | import { BrowserModule } from '@angular/platform-browser';
5 | import { NgModule } from '@angular/core';
6 |
7 | import { AppComponent } from './app.component';
8 | import { ConnectDialog } from './connect.dialog/connect.dialog';
9 | import { ReactiveFormsModule, FormsModule } from '@angular/forms';
10 | import { MonitorsToWatchComponent } from './options.dialog/monitorstowatch.component';
11 | import { MonitorsCanvasComponent } from './monitorcanvas/monitorcanvas.component';
12 |
13 | @NgModule({
14 | declarations: [
15 | AppComponent,
16 | ConnectDialog,
17 | OptionsDialog,
18 | MonitorsToWatchComponent,
19 | MonitorsCanvasComponent
20 | ],
21 | imports: [
22 | BrowserModule,
23 | MaterialModule,
24 | NoopAnimationsModule,
25 | ReactiveFormsModule,
26 | FormsModule
27 | ],
28 | providers: [],
29 | entryComponents:[
30 | ConnectDialog,
31 | OptionsDialog
32 | ],
33 | bootstrap: [AppComponent]
34 | })
35 | export class AppModule { }
36 |
--------------------------------------------------------------------------------
/src/client/src/app/connect.dialog/connect.dialog.html:
--------------------------------------------------------------------------------
1 | Connect to Host
2 |
3 |
36 |
--------------------------------------------------------------------------------
/src/client/src/app/connect.dialog/connect.dialog.ts:
--------------------------------------------------------------------------------
1 | import {Component, Inject, OnInit} from '@angular/core';
2 | import {FormBuilder, FormGroup, Validators} from '@angular/forms';
3 | import {MatDialogRef} from '@angular/material';
4 |
5 | import {ValidateNumericOnly} from '../validators/numericonly';
6 | import { ConnectModel } from '../models/connect.model';
7 |
8 | @Component({templateUrl : './connect.dialog.html'})
9 | export class ConnectDialog implements OnInit {
10 | public f: FormGroup;
11 | submitting=false;
12 | public Protocols = ['ws'
13 | // , 'wss'
14 | ];
15 | constructor(public dialogRef: MatDialogRef, private fb: FormBuilder) {}
16 | public ngOnInit(): void
17 | {
18 | var defaltproto = 'ws';
19 | //if (window.location.protocol == "https:") {
20 | // defaltproto= "wss";
21 | // }
22 | this.f = this.fb.group({
23 | 'Protocol' : [
24 | defaltproto, [ Validators.required ]
25 | ],
26 | 'Host' : [
27 | 'localhost', [ Validators.required, Validators.minLength(2) ]
28 | ],
29 | 'Port' :
30 | [ '6001', [ Validators.required, Validators.minLength(2), ValidateNumericOnly ] ]
31 | });
32 | }
33 | public onSubmit(): void {
34 | if(!this.submitting && this.f.valid){
35 | this.submitting = true;
36 | this.dialogRef.close(this.f.value as ConnectModel);
37 | }
38 | }
39 | }
--------------------------------------------------------------------------------
/src/client/src/app/lib/input_lite.ts:
--------------------------------------------------------------------------------
1 |
2 |
3 | export enum KeyCodes {
4 | KEY_A = 4,
5 | KEY_B = 5,
6 | KEY_C = 6,
7 | KEY_D = 7,
8 | KEY_E = 8,
9 | KEY_F = 9,
10 | KEY_G = 10,
11 | KEY_H = 11,
12 | KEY_I = 12,
13 | KEY_J = 13,
14 | KEY_K = 14,
15 | KEY_L = 15,
16 | KEY_M = 16,
17 | KEY_N = 17,
18 | KEY_O = 18,
19 | KEY_P = 19,
20 | KEY_Q = 20,
21 | KEY_R = 21,
22 | KEY_S = 22,
23 | KEY_T = 23,
24 | KEY_U = 24,
25 | KEY_V = 25,
26 | KEY_W = 26,
27 | KEY_X = 27,
28 | KEY_Y = 28,
29 | KEY_Z = 29,
30 | KEY_1 = 30,
31 | KEY_2 = 31,
32 | KEY_3 = 32,
33 | KEY_4 = 33,
34 | KEY_5 = 34,
35 | KEY_6 = 35,
36 | KEY_7 = 36,
37 | KEY_8 = 37,
38 | KEY_9 = 38,
39 | KEY_0 = 39,
40 | KEY_Enter = 40,
41 | KEY_Escape = 41,
42 | KEY_Backspace = 42,
43 | KEY_Tab = 43,
44 | KEY_Space = 44,
45 | KEY_Minus = 45,
46 | KEY_Equals = 46,
47 | KEY_LeftBracket = 47,
48 | KEY_RightBracket = 48,
49 | KEY_Backslash = 49,
50 | KEY_Semicolon = 51,
51 | KEY_Quote = 52,
52 | KEY_Grave = 53,
53 | KEY_Comma = 54,
54 | KEY_Period = 55,
55 | KEY_Slash = 56,
56 | KEY_CapsLock = 57,
57 | KEY_F1 = 58,
58 | KEY_F2 = 59,
59 | KEY_F3 = 60,
60 | KEY_F4 = 61,
61 | KEY_F5 = 62,
62 | KEY_F6 = 63,
63 | KEY_F7 = 64,
64 | KEY_F8 = 65,
65 | KEY_F9 = 66,
66 | KEY_F10 = 67,
67 | KEY_F11 = 68,
68 | KEY_F12 = 69,
69 | KEY_PrintScreen = 70,
70 | KEY_ScrollLock = 71,
71 | KEY_Pause = 72,
72 | KEY_Insert = 73,
73 | KEY_Home = 74,
74 | KEY_PageUp = 75,
75 | KEY_Delete = 76,
76 | KEY_End = 77,
77 | KEY_PageDown = 78,
78 | KEY_Right = 79,
79 | KEY_Left = 80,
80 | KEY_Down = 81,
81 | KEY_Up = 82,
82 | KP_NumLock = 83,
83 | KP_Divide = 84,
84 | KP_Multiply = 85,
85 | KP_Subtract = 86,
86 | KP_Add = 87,
87 | KP_Enter = 88,
88 | KP_1 = 89,
89 | KP_2 = 90,
90 | KP_3 = 91,
91 | KP_4 = 92,
92 | KP_5 = 93,
93 | KP_6 = 94,
94 | KP_7 = 95,
95 | KP_8 = 96,
96 | KP_9 = 97,
97 | KP_0 = 98,
98 | KP_Point = 99,
99 | KEY_NonUSBackslash = 100,
100 | KP_Equals = 103,
101 | KEY_F13 = 104,
102 | KEY_F14 = 105,
103 | KEY_F15 = 106,
104 | KEY_F16 = 107,
105 | KEY_F17 = 108,
106 | KEY_F18 = 109,
107 | KEY_F19 = 110,
108 | KEY_F20 = 111,
109 | KEY_F21 = 112,
110 | KEY_F22 = 113,
111 | KEY_F23 = 114,
112 | KEY_F24 = 115,
113 | KEY_Help = 117,
114 | KEY_Menu = 118,
115 | KEY_LeftControl = 224,
116 | KEY_LeftShift = 225,
117 | KEY_LeftAlt = 226,
118 | KEY_LeftMeta = 227,
119 | KEY_RightControl = 228,
120 | KEY_RightShift = 229,
121 | KEY_RightAlt = 230,
122 | KEY_RightMeta = 231,
123 | INVALID = 255
124 | };
125 |
126 | export function ConvertToKeyCode(key: KeyboardEvent): KeyCodes {
127 |
128 | switch (key.key) {
129 | case "0":
130 | return KeyCodes.KEY_0;
131 | case "1":
132 | case "2":
133 | case "3":
134 | case "4":
135 | case "5":
136 | case "6":
137 | case "7":
138 | case "8":
139 | case "9":
140 | return KeyCodes.KEY_1 + (key.key.charCodeAt(0) - 1);
141 | case "A":
142 | case "B":
143 | case "C":
144 | case "D":
145 | case "E":
146 | case "F":
147 | case "G":
148 | case "H":
149 | case "I":
150 | case "J":
151 | case "K":
152 | case "L":
153 | case "M":
154 | case "N":
155 | case "O":
156 | case "P":
157 | case "Q":
158 | case "R":
159 | case "S":
160 | case "T":
161 | case "U":
162 | case "V":
163 | case "W":
164 | case "X":
165 | case "Y":
166 | case "Z":
167 | return KeyCodes.KEY_A + (+key.key - +"A");
168 | case "a":
169 | case "b":
170 | case "c":
171 | case "d":
172 | case "e":
173 | case "f":
174 | case "g":
175 | case "h":
176 | case "i":
177 | case "j":
178 | case "k":
179 | case "l":
180 | case "m":
181 | case "n":
182 | case "o":
183 | case "p":
184 | case "q":
185 | case "r":
186 | case "s":
187 | case "t":
188 | case "u":
189 | case "v":
190 | case "w":
191 | case "x":
192 | case "y":
193 | case "z":
194 | return KeyCodes.KEY_A + (key.key.charCodeAt(0) - "a".charCodeAt(0));
195 | case "Enter":
196 | return KeyCodes.KEY_Enter;
197 | case "Escape":
198 | return KeyCodes.KEY_Escape;
199 | case "Backspace":
200 | return KeyCodes.KEY_Backspace;
201 | case "Tab":
202 | return KeyCodes.KEY_Tab;
203 | case " ":
204 | return KeyCodes.KEY_Space;
205 | case "-":
206 | return KeyCodes.KEY_Minus;
207 | case "=":
208 | return KeyCodes.KEY_Equals; // this is correct and not a mistype
209 | case "[":
210 | return KeyCodes.KEY_LeftBracket;
211 | case "[":
212 | return KeyCodes.KEY_RightBracket;
213 | case "\\":
214 | return KeyCodes.KEY_Backslash;
215 | case ";":
216 | return KeyCodes.KEY_Semicolon;
217 | case "'":
218 | return KeyCodes.KEY_Quote;
219 | case "`":
220 | return KeyCodes.KEY_Grave;
221 | case ",":
222 | return KeyCodes.KEY_Comma;
223 | case ".":
224 | return KeyCodes.KEY_Period;
225 | case "/":
226 | return KeyCodes.KEY_Slash;
227 | case "CapsLock":
228 | return KeyCodes.KEY_CapsLock;
229 | case "F1":
230 | return KeyCodes.KEY_F1;
231 | case "F2":
232 | return KeyCodes.KEY_F2;
233 | case "F3":
234 | return KeyCodes.KEY_F3;
235 | case "F4":
236 | return KeyCodes.KEY_F4;
237 | case "F5":
238 | return KeyCodes.KEY_F5;
239 | case "F6":
240 | return KeyCodes.KEY_F6;
241 | case "F7":
242 | return KeyCodes.KEY_F7;
243 | case "F8":
244 | return KeyCodes.KEY_F8;
245 | case "F9":
246 | return KeyCodes.KEY_F9;
247 | case "F10":
248 | return KeyCodes.KEY_F10;
249 | case "F11":
250 | return KeyCodes.KEY_F11;
251 | case "F12":
252 | return KeyCodes.KEY_F12;
253 | case "F13":
254 | return KeyCodes.KEY_F13;
255 | case "F14":
256 | return KeyCodes.KEY_F14;
257 | case "F15":
258 | return KeyCodes.KEY_F15;
259 | case "F16":
260 | return KeyCodes.KEY_F1;
261 | case "F17":
262 | return KeyCodes.KEY_F1;
263 | case "F18":
264 | return KeyCodes.KEY_F1;
265 | case "F19":
266 | return KeyCodes.KEY_F1;
267 | case "F20":
268 | return KeyCodes.KEY_F1;
269 | case "Print":
270 | return KeyCodes.KEY_PrintScreen;
271 | case "ScrollLock":
272 | return KeyCodes.KEY_ScrollLock;
273 | case "Pause":
274 | return KeyCodes.KEY_Pause;
275 | case "Insert":
276 | return KeyCodes.KEY_Insert;
277 | case "Home":
278 | return KeyCodes.KEY_Home;
279 | case "PageUp":
280 | return KeyCodes.KEY_PageUp;
281 | case "Delete":
282 | return KeyCodes.KEY_Delete;
283 | case "End":
284 | return KeyCodes.KEY_End;
285 | case "PageDown":
286 | return KeyCodes.KEY_PageDown;
287 | case "ArrowRight":
288 | return KeyCodes.KEY_Right;
289 | case "ArrowLeft":
290 | return KeyCodes.KEY_Left;
291 | case "ArrowDown":
292 | return KeyCodes.KEY_Down;
293 | case "ArrowUp":
294 | return KeyCodes.KEY_Up;
295 | case "NumLock":
296 | return KeyCodes.KP_NumLock;
297 | case "/":
298 | return KeyCodes.KP_Divide;
299 | case "*":
300 | return KeyCodes.KP_Multiply;
301 | case "-":
302 | return KeyCodes.KP_Subtract;
303 | case "+":
304 | return KeyCodes.KP_Add;
305 | case ".":
306 | return KeyCodes.KP_Point;
307 | case "Help":
308 | return KeyCodes.KEY_Help;
309 | case "Alt":
310 | return KeyCodes.KEY_Menu;
311 | case "Control":
312 | return KeyCodes.KEY_LeftControl;
313 | case "Shift":
314 | return KeyCodes.KEY_LeftShift;
315 | case "Meta":
316 | return KeyCodes.KEY_LeftMeta;
317 | default:
318 | return KeyCodes.INVALID;
319 | }
320 |
321 | }
322 |
323 | export enum MouseButtons { LEFT, MIDDLE, RIGHT };
324 |
325 | export class KeyEvent {
326 | Pressed: boolean;
327 | Key: KeyCodes;
328 | };
329 | export class MouseButtonEvent {
330 | Pressed: boolean;
331 | Button: MouseButtons;
332 | };
333 | export class MouseScrollEvent {
334 | Offset = 0;
335 | };
336 | export class MousePositionOffsetEvent {
337 | X = 0;
338 | Y = 0;
339 | };
340 | export class MousePositionAbsoluteEvent {
341 | X = 0;
342 | Y = 0;
343 | };
--------------------------------------------------------------------------------
/src/client/src/app/lib/rat_lite.ts:
--------------------------------------------------------------------------------
1 | import { KeyCodes, MouseButtons } from './input_lite';
2 | export enum OpCode {
3 | CONTINUATION = 0,
4 | TEXT = 1,
5 | BINARY = 2,
6 | CLOSE = 8,
7 | PING = 9,
8 | PONG = 10,
9 | INVALID = 255
10 | }
11 | ;
12 |
13 | export enum ImageEncoding {
14 | COLOR,
15 | GRAYSCALE
16 | }
17 | ;
18 | export enum ClipboardSharing {
19 | NOT_SHARED,
20 | SHARED
21 | }
22 | ;
23 | export class WSMessage {
24 | data: DataView;
25 | code: OpCode;
26 | };
27 | export class Point {
28 | X: number;
29 | Y: number;
30 | };
31 | export class Rect {
32 | Origin: Point;
33 | Height: number;
34 | Width: number;
35 | };
36 | export class Monitor {
37 | Id: number;
38 | Index: number;
39 | Height: number;
40 | Width: number;
41 | // Offsets are the number of pixels that a monitor can be from the origin. For example, users can shuffle their
42 | // monitors around so this affects their offset.
43 | OffsetX: number;
44 | OffsetY: number;
45 | Name: string;
46 | Scaling: number;
47 | };
48 | export class ClientSettings {
49 | ShareClip = ClipboardSharing.NOT_SHARED;
50 | ImageCompressionSetting = 70;
51 | EncodeImagesAsGrayScale = ImageEncoding.COLOR;
52 | MonitorsToWatch = new Array();
53 | };
54 | export enum PACKET_TYPES {
55 | INVALID,
56 | HTTP_MSG,
57 | ONMONITORSCHANGED,
58 | ONFRAMECHANGED,
59 | ONNEWFRAME,
60 | ONMOUSEIMAGECHANGED,
61 | ONMOUSEPOSITIONCHANGED,
62 | ONKEYUP,
63 | ONKEYDOWN,
64 | ONMOUSEUP,
65 | ONMOUSEDOWN,
66 | ONMOUSESCROLL,
67 | ONCLIPBOARDTEXTCHANGED,
68 | ONCLIENTSETTINGSCHANGED,
69 | // use LAST_PACKET_TYPE as the starting point of your custom packet types. Everything before this is used internally by the library
70 | LAST_PACKET_TYPE
71 | }
72 | ;
73 |
74 | export class IClientDriver {
75 | protected ShareClip = false;
76 | protected Monitors = new Array();
77 | protected WebSocket_: WebSocket;
78 |
79 | protected onConnection_: (ws: WebSocket, ev: Event) => void;
80 | protected onMessage_: (ws: WebSocket, message: WSMessage) => void;
81 | protected onDisconnection_: (ws: WebSocket, code: number, message: string) => void;
82 | protected onMonitorsChanged_: (monitors: Monitor[]) => void;
83 | protected onFrameChanged_: (image: HTMLImageElement, monitor: Monitor, rect: Rect) => void;
84 | protected onNewFrame_: (image: HTMLImageElement, monitor: Monitor, rect: Rect) => void;
85 | protected onMouseImageChanged_: (image: ImageData) => void;
86 | protected onMousePositionChanged_: (point: Point) => void;
87 | protected onClipboardChanged_: (clipstring: string) => void;
88 | protected onBytesPerSecondChanged: (bytespersecond: number) => void;
89 |
90 | protected ConnectedToSelf_ = false;
91 | protected BytesPerSecond = 0;
92 | protected SecondTimer = 0;
93 |
94 | setShareClipboard(share: boolean): void { this.ShareClip = share; }
95 | getShareClipboard(): boolean { return this.ShareClip; }
96 | SendKeyUp(key: KeyCodes): void
97 | {
98 | if (this.ConnectedToSelf_)
99 | return;
100 | var data = new Uint8Array(4 + 1);
101 | var dataview = new DataView(data.buffer);
102 | dataview.setUint32(0, PACKET_TYPES.ONKEYUP, true);
103 | dataview.setUint8(4, key);
104 | this.WebSocket_.send(data.buffer);
105 | }
106 | SendKeyDown(key: KeyCodes): void
107 | {
108 | if (this.ConnectedToSelf_)
109 | return;
110 | var data = new Uint8Array(4 + 1);
111 | var dataview = new DataView(data.buffer);
112 | dataview.setUint32(0, PACKET_TYPES.ONKEYDOWN, true);
113 | dataview.setUint8(4, key);
114 | this.WebSocket_.send(data.buffer);
115 | }
116 | SendMouseUp(button: MouseButtons): void
117 | {
118 | if (this.ConnectedToSelf_)
119 | return;
120 | var data = new Uint8Array(4 + 1);
121 | var dataview = new DataView(data.buffer);
122 | dataview.setUint32(0, PACKET_TYPES.ONMOUSEUP, true);
123 | dataview.setUint8(4, button);
124 | this.WebSocket_.send(data.buffer);
125 | }
126 | SendMouseDown(button: MouseButtons): void
127 | {
128 | if (this.ConnectedToSelf_)
129 | return;
130 | var data = new Uint8Array(4 + 1);
131 | var dataview = new DataView(data.buffer);
132 | dataview.setUint32(0, PACKET_TYPES.ONMOUSEDOWN, true);
133 | dataview.setUint8(4, button);
134 | this.WebSocket_.send(data.buffer);
135 | }
136 | SendMouseScroll(offset: number): void
137 | {
138 | if (this.ConnectedToSelf_)
139 | return;
140 | var data = new Uint8Array(4 + 4);
141 | var dataview = new DataView(data.buffer);
142 | dataview.setUint32(0, PACKET_TYPES.ONMOUSESCROLL, true);
143 | dataview.setUint32(4, offset, true);
144 | this.WebSocket_.send(data.buffer);
145 | }
146 | SendMousePosition(pos: Point): void
147 | {
148 | if (this.ConnectedToSelf_)
149 | return;
150 | var data = new Uint8Array(4 + 8);
151 | var dataview = new DataView(data.buffer);
152 | dataview.setUint32(0, PACKET_TYPES.ONMOUSEPOSITIONCHANGED, true);
153 | dataview.setInt32(4, pos.X, true);
154 | dataview.setInt32(8, pos.Y, true);
155 | this.WebSocket_.send(data.buffer);
156 | }
157 | SendClipboardChanged(text: string): void
158 | {
159 | if (this.ConnectedToSelf_)
160 | return;
161 | var data = new Uint8Array(4 + text.length);
162 | var dataview = new DataView(data.buffer);
163 | dataview.setUint32(0, PACKET_TYPES.ONMOUSESCROLL, true);
164 | for (var i = 0; i < text.length; i++) {
165 | data[4 + i] = text.charCodeAt(0);
166 | }
167 | this.WebSocket_.send(data.buffer);
168 | }
169 | SendClientSettingsChanged(clientsettings: ClientSettings): void
170 | {
171 | if (!clientsettings || !clientsettings.MonitorsToWatch || clientsettings.MonitorsToWatch.length <= 0)
172 | return;
173 | var beginsize = 1 + 4 + 1;
174 | var data = new Uint8Array(4 + beginsize + (4 * clientsettings.MonitorsToWatch.length));
175 | var dataview = new DataView(data.buffer);
176 | var offset = 0;
177 | dataview.setUint32(offset, PACKET_TYPES.ONCLIENTSETTINGSCHANGED, true);
178 | offset += 4;
179 | dataview.setUint8(offset, clientsettings.ShareClip);
180 | offset += 1;
181 | dataview.setInt32(offset, clientsettings.ImageCompressionSetting, true);
182 | offset += 4;
183 | dataview.setUint8(offset, clientsettings.EncodeImagesAsGrayScale);
184 | offset += 1;
185 | for (var i = 0; i < clientsettings.MonitorsToWatch.length; i++) {
186 | dataview.setInt32(offset, clientsettings.MonitorsToWatch[i].Id, true);
187 | offset += 4;
188 | }
189 | this.WebSocket_.send(data.buffer);
190 | }
191 | };
192 |
193 | export class IClientDriverConfiguration extends IClientDriver {
194 | onBytesPerSecond(callback: (bytespersecond: number) => void): IClientDriverConfiguration
195 | {
196 | this.onBytesPerSecondChanged = callback;
197 | return this;
198 | }
199 | onConnection(callback: (ws: WebSocket, ev: Event) => void): IClientDriverConfiguration
200 | {
201 | this.onConnection_ = callback;
202 | return this;
203 | }
204 | onMessage(callback: (ws: WebSocket, message: WSMessage) => void): IClientDriverConfiguration
205 | {
206 | this.onMessage_ = callback;
207 | return this;
208 | }
209 | onDisconnection(callback: (ws: WebSocket, code: number, message: string) => void): IClientDriverConfiguration
210 | {
211 | this.onDisconnection_ = callback;
212 | return this;
213 | }
214 | onMonitorsChanged(callback: (monitors: Monitor[]) => void): IClientDriverConfiguration
215 | {
216 | this.onMonitorsChanged_ = callback;
217 | return this;
218 | }
219 | onFrameChanged(callback: (image: HTMLImageElement, monitor: Monitor, rect: Rect) => void): IClientDriverConfiguration
220 | {
221 | this.onFrameChanged_ = callback;
222 | return this;
223 | }
224 | onNewFrame(callback: (image: HTMLImageElement, monitor: Monitor, rect: Rect) => void): IClientDriverConfiguration
225 | {
226 | this.onNewFrame_ = callback;
227 | return this;
228 | }
229 | onMouseImageChanged(callback: (image: ImageData) => void): IClientDriverConfiguration
230 | {
231 | this.onMouseImageChanged_ = callback;
232 | return this;
233 | }
234 | onMousePositionChanged(callback: (point: Point) => void): IClientDriverConfiguration
235 | {
236 | this.onMousePositionChanged_ = callback;
237 | return this;
238 | }
239 | onClipboardChanged(callback: (clipstring: string) => void): IClientDriverConfiguration
240 | {
241 | this.onClipboardChanged_ = callback;
242 | return this;
243 | }
244 | private _arrayBufferToBase64(buffer: Uint8Array): string
245 | {
246 | var binary = '';
247 | for (var i = 0; i < buffer.byteLength; i++) {
248 | binary += String.fromCharCode(buffer[i]);
249 | }
250 | return window.btoa(binary);
251 | }
252 | private MonitorsChanged(ws: WebSocket, dataview: DataView)
253 | {
254 |
255 | if (!this.onMonitorsChanged_)
256 | return;
257 | let sizeofmonitor = 7 * 4 + 128;
258 | let num = dataview.byteLength / sizeofmonitor;
259 |
260 | if (dataview.byteLength == num * sizeofmonitor && num < 8) {
261 | this.Monitors = new Array();
262 | let currentoffset = 0;
263 | for (var i = 0; i < num; i++) {
264 | currentoffset = i * sizeofmonitor;
265 | var name = '';
266 | for (var j = 0, strLen = 128; j < strLen; j++) {
267 | var char = String.fromCharCode(dataview.getUint8((24 + j) + currentoffset));
268 | if (char == '\0') {
269 | break;
270 | }
271 | name += char;
272 | }
273 | this.Monitors.push({
274 | Id : dataview.getInt32(0 + currentoffset, true),
275 | Index : dataview.getInt32(4 + currentoffset, true),
276 | Height : dataview.getInt32(8 + currentoffset, true),
277 | Width : dataview.getInt32(12 + currentoffset, true),
278 | OffsetX : dataview.getInt32(16 + currentoffset, true),
279 | OffsetY : dataview.getInt32(20 + currentoffset, true),
280 | Name : name,
281 | Scaling : dataview.getFloat32(24 + 128 + currentoffset, true)
282 | });
283 | }
284 | return this.onMonitorsChanged_(this.Monitors);
285 | }
286 | else if (dataview.byteLength == 0) {
287 | // it is possible to have no monitors.. shouldnt disconnect in that case
288 | return this.onMonitorsChanged_(this.Monitors);
289 | }
290 | if (this.onDisconnection_) {
291 | this.onDisconnection_(ws, 1000, "Invalid Monitor Count");
292 | }
293 | ws.close(1000, "Invalid Monitor Count");
294 | }
295 |
296 | private Frame(ws: WebSocket, dataview: DataView, callback: (image: HTMLImageElement, monitor: Monitor, rect: Rect) => void)
297 | {
298 |
299 | if (dataview.byteLength >= 4 * 4 + 4) {
300 | var monitorid = dataview.getInt32(0, true);
301 | var rect = {
302 | Origin : {X : dataview.getInt32(4, true), Y : dataview.getInt32(8, true)},
303 | Height : dataview.getInt32(12, true),
304 | Width : dataview.getInt32(16, true)
305 | };
306 |
307 | var foundmonitor = this.Monitors.filter(a => a.Id == monitorid);
308 |
309 | if (foundmonitor.length > 0) {
310 | var i = new Image();
311 | i.src = "data:image/jpeg;base64," + this._arrayBufferToBase64(new Uint8Array(dataview.buffer, 20 + dataview.byteOffset));
312 | i.onload = (ev: Event) => { callback(i, foundmonitor[0], rect); };
313 | i.onerror = (ev: Event) => { console.log(ev); };
314 | i.oninvalid = (ev: Event) => { console.log(ev); };
315 | }
316 | return;
317 | }
318 | if (this.onDisconnection_) {
319 | this.onDisconnection_(ws, 1000, "Received invalid lenght on onMouseImageChanged");
320 | }
321 | ws.close(1000, "Received invalid lenght on onMouseImageChanged");
322 | }
323 |
324 | private MouseImageChanged(ws: WebSocket, dataview: DataView)
325 | {
326 | if (!this.onMouseImageChanged_)
327 | return;
328 | if (dataview.byteLength >= 4 * 4) {
329 | var rect = {
330 | Origin : {X : dataview.getInt32(0, true), Y : dataview.getInt32(4, true)},
331 | Height : dataview.getInt32(8, true),
332 | Width : dataview.getInt32(12, true)
333 | };
334 |
335 | var canvas = document.createElement('canvas');
336 | var imageData = canvas.getContext('2d').createImageData(rect.Width, rect.Height);
337 | for (var i = 16; i < dataview.byteLength; i++) {
338 | imageData.data[i] = dataview[i];
339 | }
340 | if (dataview.byteLength >= 4 * 4 + (rect.Width * rect.Height * 4)) {
341 | return this.onMouseImageChanged_(imageData);
342 | }
343 | }
344 | if (this.onDisconnection_) {
345 | this.onDisconnection_(ws, 1000, "Received invalid lenght on onMouseImageChanged");
346 | }
347 | ws.close(1000, "Received invalid lenght on onMouseImageChanged");
348 | }
349 | private MousePositionChanged(ws: WebSocket, dataview: DataView)
350 | {
351 | if (!this.onMousePositionChanged_)
352 | return;
353 | if (dataview.byteLength == 8) {
354 | var p = {X : dataview.getInt32(0, true), Y : dataview.getInt32(4, true)};
355 | return this.onMousePositionChanged_(p);
356 | }
357 | if (this.onDisconnection_) {
358 | this.onDisconnection_(ws, 1000, "Received invalid lenght on onMousePositionChanged");
359 | }
360 | ws.close(1000, "Received invalid lenght on onMousePositionChanged");
361 | }
362 | private ClipboardTextChanged(dataview: DataView)
363 | {
364 | if (!this.ShareClip || !this.onClipboardChanged_)
365 | return;
366 | if (dataview.byteLength < 1024 * 100) { // 100K max
367 | var text = '';
368 | for (var i = 0, strLen = 128; i < strLen; i++) {
369 | text += String.fromCharCode.apply(dataview.getUint8(20 + i));
370 | }
371 | this.onClipboardChanged_(text);
372 | }
373 | }
374 | Build(ws: WebSocket): IClientDriver
375 | {
376 | this.WebSocket_ = ws;
377 | var self = this;
378 | ws.binaryType = 'arraybuffer';
379 | this.ConnectedToSelf_ = ws.url.toLowerCase().indexOf('127.0.0.1') != -1 || ws.url.toLowerCase().indexOf('localhost') != -1 ||
380 | ws.url.toLowerCase().indexOf('::1') != -1;
381 | this.SecondTimer = performance.now();
382 | ws.onopen = (ev: Event) => {
383 | console.log('onopen');
384 | if (self.onConnection_) {
385 | self.onConnection_(ws, ev);
386 | }
387 | };
388 | ws.onclose = (ev: CloseEvent) => {
389 | console.log('onclose');
390 | if (self.onDisconnection_) {
391 | self.onDisconnection_(ws, ev.code, ev.reason);
392 | }
393 | };
394 | ws.onmessage = (ev: MessageEvent) => {
395 |
396 | var t0 = performance.now();
397 | if (t0 - this.SecondTimer > 1000) {
398 | this.onBytesPerSecondChanged(this.BytesPerSecond);
399 | this.SecondTimer = t0;
400 | this.BytesPerSecond = 0;
401 | }
402 | var data = new DataView(ev.data);
403 | this.BytesPerSecond += data.byteLength;
404 | var packettype = data.getInt32(0, true);
405 | var self = this;
406 | // console.log('received: ' + packettype);
407 | switch (packettype) {
408 | case PACKET_TYPES.ONMONITORSCHANGED:
409 | this.MonitorsChanged(ws, new DataView(ev.data, 4));
410 | break;
411 | case PACKET_TYPES.ONFRAMECHANGED:
412 | if (this.onFrameChanged_) {
413 | this.Frame(ws, new DataView(ev.data, 4), this.onFrameChanged_);
414 | }
415 | break;
416 | case PACKET_TYPES.ONNEWFRAME:
417 | if (this.onNewFrame_) {
418 | this.Frame(ws, new DataView(ev.data, 4), this.onNewFrame_);
419 | }
420 | break;
421 | case PACKET_TYPES.ONMOUSEIMAGECHANGED:
422 | this.MouseImageChanged(ws, new DataView(ev.data, 4));
423 | break;
424 | case PACKET_TYPES.ONMOUSEPOSITIONCHANGED:
425 | this.MousePositionChanged(ws, new DataView(ev.data, 4));
426 | break;
427 | case PACKET_TYPES.ONCLIPBOARDTEXTCHANGED:
428 | this.ClipboardTextChanged(new DataView(ev.data, 4));
429 | break;
430 | default:
431 | if (this.onMessage_) {
432 | var r = new WSMessage();
433 | r.data = new DataView(ev.data, 4);
434 | if (ev.data instanceof ArrayBuffer) {
435 | r.code = OpCode.BINARY;
436 | }
437 | else if (typeof ev.data === "string") {
438 | r.code = OpCode.TEXT;
439 | }
440 | this.onMessage_(ws, r); // pass up the chain
441 | }
442 | break;
443 | }
444 | var t1 = performance.now();
445 | // console.log("took " + (t1 - t0) + " milliseconds to process the receive loop");
446 | };
447 | return this;
448 | }
449 | };
450 | export function CreateClientDriverConfiguration(): IClientDriverConfiguration { return new IClientDriverConfiguration(); }
451 |
--------------------------------------------------------------------------------
/src/client/src/app/material.module.ts:
--------------------------------------------------------------------------------
1 | import {MatButtonModule, MatCheckboxModule, MatDialogModule, MatInputModule, MatSelectModule, MatSidenavModule, MatListModule, MatProgressSpinnerModule} from '@angular/material';
2 | import { NgModule } from '@angular/core';
3 |
4 | @NgModule({
5 | imports: [
6 | MatButtonModule,
7 | MatCheckboxModule,
8 | MatDialogModule,
9 | MatInputModule,
10 | MatSelectModule,
11 | MatSidenavModule,
12 | MatListModule,
13 | MatProgressSpinnerModule
14 | ],
15 | exports: [
16 | MatButtonModule,
17 | MatCheckboxModule,
18 | MatDialogModule,
19 | MatInputModule,
20 | MatSelectModule,
21 | MatSidenavModule,
22 | MatListModule,
23 | MatProgressSpinnerModule
24 | ],
25 | })
26 | export class MaterialModule { }
--------------------------------------------------------------------------------
/src/client/src/app/models/connect.model.ts:
--------------------------------------------------------------------------------
1 | import { ClientSettings, Monitor } from '../lib/rat_lite';
2 | export class ConnectModel{
3 | public Protocol: string;
4 | public Host: string;
5 | public Port: number;
6 | }
7 | export class OptionsModel{
8 | public ClientSettings: ClientSettings;
9 | public Monitors : Monitor[];
10 | }
--------------------------------------------------------------------------------
/src/client/src/app/monitorcanvas/monitorcanvas.component.html:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/src/client/src/app/monitorcanvas/monitorcanvas.component.ts:
--------------------------------------------------------------------------------
1 | import {Component, ElementRef, Input, OnInit, ViewChild} from "@angular/core";
2 |
3 | import {Monitor, Rect} from "../lib/rat_lite";
4 |
5 | @Component({selector : 'monitor-canvas', templateUrl : './monitorcanvas.component.html'})
6 | export class MonitorsCanvasComponent implements OnInit {
7 | @ViewChild('thiscanvas') ThisCanvas: ElementRef;
8 | @Input() Monitor: Monitor;
9 |
10 | ngOnInit(): void
11 | {
12 | if (this.ThisCanvas && this.ThisCanvas.nativeElement ) {
13 | this.ThisCanvas.nativeElement.width = this.getScalingFactor()*this.Monitor.Width;
14 | this.ThisCanvas.nativeElement.height =this.getScalingFactor()* this.Monitor.Height;
15 | }
16 | }
17 | public getScalingFactor(): number{
18 | return (1 / this.Monitor.Scaling);
19 | }
20 | public onFrameChanged(image: HTMLImageElement, monitor: Monitor, rect: Rect): void
21 | {
22 | if (this.Monitor && this.ThisCanvas && this.ThisCanvas.nativeElement && this.Monitor.Id == monitor.Id) {
23 | this.ThisCanvas.nativeElement.getContext("2d").drawImage(image,this.getScalingFactor()* rect.Origin.X, this.getScalingFactor()*rect.Origin.Y, this.getScalingFactor()*image.width, this.getScalingFactor()*image.height);
24 | }
25 | }
26 | public onNewFrame(image: HTMLImageElement, monitor: Monitor, rect: Rect): void
27 | {
28 | if (this.Monitor && this.ThisCanvas && this.ThisCanvas.nativeElement && this.Monitor.Id == monitor.Id) {
29 | this.ThisCanvas.nativeElement.getContext("2d").drawImage(image, 0, 0, this.getScalingFactor()*image.width, this.getScalingFactor()*image.height);
30 | }
31 | }
32 | }
--------------------------------------------------------------------------------
/src/client/src/app/options.dialog/monitorstowatch.component.html:
--------------------------------------------------------------------------------
1 | {{warningtext}}
2 |
3 |
4 | {{monitor.Name}} {{monitor.Width}}x{{monitor.Height}}
5 |
6 |
--------------------------------------------------------------------------------
/src/client/src/app/options.dialog/monitorstowatch.component.ts:
--------------------------------------------------------------------------------
1 | import {Component, Inject, Input, OnInit} from '@angular/core';
2 | import {AbstractControl, FormArray, FormBuilder, FormControl, FormGroup, Validators} from '@angular/forms';
3 | import {MAT_DIALOG_DATA, MatCheckboxChange} from '@angular/material';
4 |
5 | import {ClientSettings, Monitor} from '../lib/rat_lite';
6 |
7 | @Component({selector : 'monitors-towatch', templateUrl : './monitorstowatch.component.html'})
8 | export class MonitorsToWatchComponent implements OnInit {
9 | @Input() f: FormArray;
10 | @Input() MonitorsToWatch: Monitor[];
11 | @Input() Monitors: Monitor[];
12 | warningtext = null;
13 | constructor(private fb: FormBuilder) {}
14 | public ngOnInit(): void
15 | {
16 | this.MonitorsToWatch.forEach((a: Monitor) => { this.f.push(new FormControl(a.Id)); });
17 | }
18 | public isChecked(mon: Monitor): boolean
19 | {
20 | var found = this.f.controls.find((a: AbstractControl) => { return a.value == mon.Id; });
21 | if (found) {
22 | return true;
23 | }
24 | else {
25 | return false;
26 | }
27 | }
28 | public checked(changedevent: MatCheckboxChange, mon: Monitor): void
29 | {
30 | this.warningtext = null;
31 | if (changedevent.checked) {
32 | var found = this.f.controls.findIndex((a: AbstractControl) => { return a.value == mon.Id; });
33 | if (found == -1) {
34 | this.f.push(new FormControl(mon.Id));
35 | this.f.setErrors(null);
36 | }
37 | }
38 | else {
39 | var found = this.f.controls.findIndex((a: AbstractControl) => { return a.value == mon.Id; });
40 | if (found != -1) {
41 | this.f.removeAt(found);
42 | }
43 | if (this.f.controls.length ==0) {
44 | this.warningtext = "You must have at least one monitor selected!";
45 | this.f.setErrors({onemonitorneeded : true});
46 | }
47 | else {
48 | this.f.setErrors(null);
49 | }
50 | }
51 | }
52 | }
--------------------------------------------------------------------------------
/src/client/src/app/options.dialog/options.dialog.html:
--------------------------------------------------------------------------------
1 | Options
2 |
3 |
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/src/client/src/app/options.dialog/options.dialog.ts:
--------------------------------------------------------------------------------
1 | import { ClientSettings, ClipboardSharing, ImageEncoding, Monitor } from '../lib/rat_lite';
2 | import { Component, Inject, OnInit } from '@angular/core';
3 | import { FormBuilder, FormGroup, Validators, FormArray } from '@angular/forms';
4 | import { MatDialogRef } from '@angular/material';
5 | import { MAT_DIALOG_DATA } from '@angular/material';
6 |
7 | import { OptionsModel } from '../models/connect.model';
8 |
9 | @Component({templateUrl : './options.dialog.html'})
10 | export class OptionsDialog implements OnInit {
11 | public f: FormGroup;
12 | submitting = false;
13 |
14 | public CompressionOptions = [ 30, 40, 50, 60, 70, 80, 90, 100 ];
15 | constructor(public dialogRef: MatDialogRef, private fb: FormBuilder, @Inject(MAT_DIALOG_DATA) public data: OptionsModel) {}
16 | public ngOnInit(): void
17 | {
18 | this.f = this.fb.group({
19 | ShareClip : [ this.data.ClientSettings.ShareClip, [ Validators.required ] ],
20 | ImageCompressionSetting : [ this.data.ClientSettings.ImageCompressionSetting, [ Validators.required, Validators.min(30), Validators.max(100) ] ],
21 | EncodeImagesAsGrayScale : [ this.data.ClientSettings.EncodeImagesAsGrayScale, [ Validators.required ] ],
22 | MonitorsToWatch: new FormArray([])
23 | });
24 | }
25 | public onSubmit(): void
26 | {
27 | if (!this.submitting && this.f.valid) {
28 | this.submitting = true;
29 | var nc = new ClientSettings();
30 | nc.EncodeImagesAsGrayScale = this.f.value.EncodeImagesAsGrayScale ? ImageEncoding.GRAYSCALE : ImageEncoding.COLOR;
31 | nc.ImageCompressionSetting = this.f.value.ImageCompressionSetting;
32 | nc.ShareClip = this.f.value.ShareClip ? ClipboardSharing.SHARED : ClipboardSharing.NOT_SHARED;
33 | this.f.value.MonitorsToWatch.forEach((monitorid: number) => {
34 | var found = this.data.Monitors.find((m: Monitor)=>{ return m.Id == monitorid; });
35 | if(found){
36 | nc.MonitorsToWatch.push(found);
37 | }
38 | });
39 | console.log(nc.MonitorsToWatch);
40 | this.dialogRef.close(nc);
41 | }
42 | }
43 | public cancel(): void{
44 | this.dialogRef.close(null);
45 | }
46 | }
--------------------------------------------------------------------------------
/src/client/src/app/validators/numericonly.ts:
--------------------------------------------------------------------------------
1 | import {FormControl} from '@angular/forms';
2 | export function ValidateNumericOnly(control: FormControl)
3 | {
4 | if (!+control.value) {
5 | return {ValidateNumericOnly : true};
6 | }
7 | return null;
8 | }
--------------------------------------------------------------------------------
/src/client/src/environments/environment.prod.ts:
--------------------------------------------------------------------------------
1 | export const environment = {
2 | production: true
3 | };
4 |
--------------------------------------------------------------------------------
/src/client/src/environments/environment.ts:
--------------------------------------------------------------------------------
1 | // The file contents for the current environment will overwrite these during build.
2 | // The build system defaults to the dev environment which uses `environment.ts`, but if you do
3 | // `ng build --env=prod` then `environment.prod.ts` will be used instead.
4 | // The list of which env maps to which file can be found in `.angular-cli.json`.
5 |
6 | export const environment = {
7 | production: false
8 | };
9 |
--------------------------------------------------------------------------------
/src/client/src/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smasherprog/rat_lite/34bc5f9b6e2a5fd44e8223a88566726246bb00a8/src/client/src/favicon.ico
--------------------------------------------------------------------------------
/src/client/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Ratclient
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/src/client/src/main.ts:
--------------------------------------------------------------------------------
1 | import { enableProdMode } from '@angular/core';
2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
3 |
4 | import { AppModule } from './app/app.module';
5 | import { environment } from './environments/environment';
6 |
7 | if (environment.production) {
8 | enableProdMode();
9 | }
10 |
11 | platformBrowserDynamic().bootstrapModule(AppModule)
12 | .catch(err => console.log(err));
13 |
--------------------------------------------------------------------------------
/src/client/src/polyfills.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * This file includes polyfills needed by Angular and is loaded before the app.
3 | * You can add your own extra polyfills to this file.
4 | *
5 | * This file is divided into 2 sections:
6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main
8 | * file.
9 | *
10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that
11 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
12 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
13 | *
14 | * Learn more in https://angular.io/docs/ts/latest/guide/browser-support.html
15 | */
16 |
17 | /***************************************************************************************************
18 | * BROWSER POLYFILLS
19 | */
20 |
21 | /** IE9, IE10 and IE11 requires all of the following polyfills. **/
22 | // import 'core-js/es6/symbol';
23 | // import 'core-js/es6/object';
24 | // import 'core-js/es6/function';
25 | // import 'core-js/es6/parse-int';
26 | // import 'core-js/es6/parse-float';
27 | // import 'core-js/es6/number';
28 | // import 'core-js/es6/math';
29 | // import 'core-js/es6/string';
30 | // import 'core-js/es6/date';
31 | // import 'core-js/es6/array';
32 | // import 'core-js/es6/regexp';
33 | // import 'core-js/es6/map';
34 | // import 'core-js/es6/weak-map';
35 | // import 'core-js/es6/set';
36 |
37 | /** IE10 and IE11 requires the following for NgClass support on SVG elements */
38 | // import 'classlist.js'; // Run `npm install --save classlist.js`.
39 |
40 | /** Evergreen browsers require these. **/
41 | import 'core-js/es6/reflect';
42 | import 'core-js/es7/reflect';
43 |
44 |
45 | /**
46 | * Required to support Web Animations `@angular/platform-browser/animations`.
47 | * Needed for: All but Chrome, Firefox and Opera. http://caniuse.com/#feat=web-animation
48 | **/
49 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`.
50 |
51 |
52 |
53 | /***************************************************************************************************
54 | * Zone JS is required by Angular itself.
55 | */
56 | import 'zone.js/dist/zone'; // Included with Angular CLI.
57 |
58 |
59 |
60 | /***************************************************************************************************
61 | * APPLICATION IMPORTS
62 | */
63 |
64 | /**
65 | * Date, currency, decimal and percent pipes.
66 | * Needed for: All but Chrome, Firefox, Edge, IE11 and Safari 10
67 | */
68 | // import 'intl'; // Run `npm install --save intl`.
69 | /**
70 | * Need to import at least one locale-data with intl.
71 | */
72 | // import 'intl/locale-data/jsonp/en';
73 |
--------------------------------------------------------------------------------
/src/client/src/styles.scss:
--------------------------------------------------------------------------------
1 | /* You can add global styles to this file, and also import other style files */
2 | @import "~@angular/material/prebuilt-themes/indigo-pink.css";
3 |
--------------------------------------------------------------------------------
/src/client/src/test.ts:
--------------------------------------------------------------------------------
1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files
2 |
3 | import 'zone.js/dist/long-stack-trace-zone';
4 | import 'zone.js/dist/proxy.js';
5 | import 'zone.js/dist/sync-test';
6 | import 'zone.js/dist/jasmine-patch';
7 | import 'zone.js/dist/async-test';
8 | import 'zone.js/dist/fake-async-test';
9 | import { getTestBed } from '@angular/core/testing';
10 | import {
11 | BrowserDynamicTestingModule,
12 | platformBrowserDynamicTesting
13 | } from '@angular/platform-browser-dynamic/testing';
14 |
15 | // Unfortunately there's no typing for the `__karma__` variable. Just declare it as any.
16 | declare const __karma__: any;
17 | declare const require: any;
18 |
19 | // Prevent Karma from running prematurely.
20 | __karma__.loaded = function () {};
21 |
22 | // First, initialize the Angular testing environment.
23 | getTestBed().initTestEnvironment(
24 | BrowserDynamicTestingModule,
25 | platformBrowserDynamicTesting()
26 | );
27 | // Then we find all the tests.
28 | const context = require.context('./', true, /\.spec\.ts$/);
29 | // And load the modules.
30 | context.keys().map(context);
31 | // Finally, start Karma to run the tests.
32 | __karma__.start();
33 |
--------------------------------------------------------------------------------
/src/client/src/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../out-tsc/app",
5 | "baseUrl": "./",
6 | "module": "es2015",
7 | "types": []
8 | },
9 | "exclude": [
10 | "test.ts",
11 | "**/*.spec.ts"
12 | ]
13 | }
14 |
--------------------------------------------------------------------------------
/src/client/src/typings.d.ts:
--------------------------------------------------------------------------------
1 | /* SystemJS module definition */
2 | declare var module: NodeModule;
3 | interface NodeModule {
4 | id: string;
5 | }
6 |
--------------------------------------------------------------------------------
/src/client/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compileOnSave": false,
3 | "compilerOptions": {
4 | "outDir": "./dist/out-tsc",
5 | "sourceMap": true,
6 | "declaration": false,
7 | "moduleResolution": "node",
8 | "emitDecoratorMetadata": true,
9 | "experimentalDecorators": true,
10 | "target": "es5",
11 | "typeRoots": [
12 | "node_modules/@types"
13 | ],
14 | "lib": [
15 | "es2017",
16 | "dom"
17 | ]
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/client/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const webpack = require('webpack');
3 | const ProgressPlugin = require('webpack/lib/ProgressPlugin');
4 | const HtmlWebpackPlugin = require('html-webpack-plugin');
5 | const ExtractTextPlugin = require('extract-text-webpack-plugin');
6 | const autoprefixer = require('autoprefixer');
7 | const postcssUrl = require('postcss-url');
8 | const ConcatPlugin = require('webpack-concat-plugin');
9 |
10 | const { NoEmitOnErrorsPlugin, LoaderOptionsPlugin, DefinePlugin, HashedModuleIdsPlugin } = require('webpack');
11 | const { GlobCopyWebpackPlugin, BaseHrefWebpackPlugin, InsertConcatAssetsWebpackPlugin } = require('@angular/cli/plugins/webpack');
12 | const { CommonsChunkPlugin, UglifyJsPlugin } = require('webpack').optimize;
13 | const { AotPlugin } = require('@ngtools/webpack');
14 |
15 | const nodeModules = path.join(process.cwd(), 'node_modules');
16 | const entryPoints = ["inline", "polyfills", "sw-register", "styles", "vendor", "main"];
17 | const baseHref = "";
18 | const deployUrl = "";
19 |
20 | const isProd = (process.env.NODE_ENV === 'production');
21 |
22 | //add all external css to be added in our index.html--> like as if it's .angular-cli.json
23 | const styles = [
24 | "./src/styles.scss"
25 | ];
26 |
27 | //we add all our external scripts we want to load externally, like inserting in our index.html --> like as if it's .angular-cli.json
28 | const scripts = [
29 | ];
30 |
31 | //create file path for each , so we use for our excludes and includes where needed
32 | let style_paths = styles.map(style_src => path.join(process.cwd(), style_src));
33 |
34 | function getPlugins() {
35 | var plugins = [];
36 |
37 | // Always expose NODE_ENV to webpack, you can now use `process.env.NODE_ENV`
38 | // inside your code for any environment checks; UglifyJS will automatically
39 | // drop any unreachable code.
40 | plugins.push(new DefinePlugin({
41 | "process.env.NODE_ENV": "\"production\""
42 | }));
43 |
44 | plugins.push(new NoEmitOnErrorsPlugin());
45 |
46 | if(scripts.length > 0){
47 | plugins.push(new ConcatPlugin({
48 | "uglify": false,
49 | "sourceMap": true,
50 | "name": "scripts",
51 | "fileName": "[name].bundle.js",
52 | "filesToConcat": scripts
53 | }));
54 | plugins.push(new InsertConcatAssetsWebpackPlugin([
55 | "scripts"
56 | ]));
57 | }
58 |
59 | plugins.push(new GlobCopyWebpackPlugin({
60 | "patterns": [
61 | "assets",
62 | "favicon.ico"
63 | ],
64 | "globOptions": {
65 | "cwd": process.cwd() + "/src",
66 | "dot": true,
67 | "ignore": "**/.gitkeep"
68 | }
69 | }));
70 |
71 | plugins.push(new ProgressPlugin());
72 |
73 | plugins.push(new HtmlWebpackPlugin({
74 | "template": "./src/index.html",
75 | "filename": "./index.html",
76 | "hash": false,
77 | "inject": true,
78 | "compile": true,
79 | "favicon": false,
80 | "minify": false,
81 | "cache": true,
82 | "showErrors": true,
83 | "chunks": "all",
84 | "excludeChunks": [],
85 | "title": "Webpack App",
86 | "xhtml": true,
87 | "chunksSortMode": function sort(left, right) {
88 | let leftIndex = entryPoints.indexOf(left.names[0]);
89 | let rightindex = entryPoints.indexOf(right.names[0]);
90 | if (leftIndex > rightindex) {
91 | return 1;
92 | }
93 | else if (leftIndex < rightindex) {
94 | return -1;
95 | }
96 | else {
97 | return 0;
98 | }
99 | }
100 | }));
101 |
102 | plugins.push(new BaseHrefWebpackPlugin({}));
103 |
104 | plugins.push(new CommonsChunkPlugin({
105 | "name": "inline",
106 | "minChunks": null
107 | }));
108 |
109 | plugins.push(new CommonsChunkPlugin({
110 | "name": "vendor",
111 | "minChunks": (module) => module.resource && module.resource.startsWith(nodeModules),
112 | "chunks": [
113 | "main"
114 | ]
115 | }));
116 |
117 | plugins.push(new ExtractTextPlugin({
118 | "filename": "[name].bundle.css",
119 | "disable": true
120 | }));
121 |
122 | plugins.push(new LoaderOptionsPlugin({
123 | "sourceMap": false,
124 | "options": {
125 | "postcss": [
126 | autoprefixer(),
127 | postcssUrl({
128 | "url": (obj) => {
129 | // Only convert root relative URLs, which CSS-Loader won't process into require().
130 | if (!obj.url.startsWith('/') || obj.url.startsWith('//')) {
131 | return obj.url;
132 | }
133 | if (deployUrl.match(/:\/\//)) {
134 | // If deployUrl contains a scheme, ignore baseHref use deployUrl as is.
135 | return `${deployUrl.replace(/\/$/, '')}${obj.url}`;
136 | }
137 | else if (baseHref.match(/:\/\//)) {
138 | // If baseHref contains a scheme, include it as is.
139 | return baseHref.replace(/\/$/, '') +
140 | `/${deployUrl}/${obj.url}`.replace(/\/\/+/g, '/');
141 | }
142 | else {
143 | // Join together base-href, deploy-url and the original URL.
144 | // Also dedupe multiple slashes into single ones.
145 | return `/${baseHref}/${deployUrl}/${obj.url}`.replace(/\/\/+/g, '/');
146 | }
147 | }
148 | })
149 | ],
150 | "sassLoader": {
151 | "sourceMap": false,
152 | "includePaths": []
153 | },
154 | "lessLoader": {
155 | "sourceMap": false
156 | },
157 | "context": ""
158 | }
159 | }));
160 |
161 | if (isProd) {
162 | plugins.push(new HashedModuleIdsPlugin({
163 | "hashFunction": "md5",
164 | "hashDigest": "base64",
165 | "hashDigestLength": 4
166 | }));
167 |
168 | plugins.push(new AotPlugin({
169 | "mainPath": "main.ts",
170 | "hostReplacementPaths": {
171 | "environments/environment.ts": "environments/environment.prod.ts"
172 | },
173 | "exclude": [],
174 | "tsConfigPath": "src/tsconfig.app.json"
175 | }));
176 |
177 | plugins.push(new UglifyJsPlugin({
178 | "mangle": {
179 | "screw_ie8": true
180 | },
181 | "compress": {
182 | "screw_ie8": true,
183 | "warnings": false
184 | },
185 | "sourceMap": false
186 | }));
187 |
188 | } else {
189 | plugins.push(new AotPlugin({
190 | "mainPath": "main.ts",
191 | "hostReplacementPaths": {
192 | "environments/environment.ts": "environments/environment.ts"
193 | },
194 | "exclude": [],
195 | "tsConfigPath": "src/tsconfig.app.json",
196 | "skipCodeGeneration": true
197 | }));
198 | }
199 |
200 | return plugins;
201 | }
202 |
203 | module.exports = {
204 | "devtool": "source-map",
205 | "externals": {
206 | "electron": "require('electron')",
207 | "buffer": "require('buffer')",
208 | "child_process": "require('child_process')",
209 | "crypto": "require('crypto')",
210 | "events": "require('events')",
211 | "fs": "require('fs')",
212 | "http": "require('http')",
213 | "https": "require('https')",
214 | "assert": "require('assert')",
215 | "dns": "require('dns')",
216 | "net": "require('net')",
217 | "os": "require('os')",
218 | "path": "require('path')",
219 | "querystring": "require('querystring')",
220 | "readline": "require('readline')",
221 | "repl": "require('repl')",
222 | "stream": "require('stream')",
223 | "string_decoder": "require('string_decoder')",
224 | "url": "require('url')",
225 | "util": "require('util')",
226 | "zlib": "require('zlib')"
227 | },
228 | "resolve": {
229 | "extensions": [
230 | ".ts",
231 | ".js",
232 | ".scss",
233 | ".json"
234 | ],
235 | "aliasFields": [],
236 | "alias": { // WORKAROUND See. angular-cli/issues/5433
237 | "environments": isProd ? path.resolve(__dirname, 'src/environments/environment.prod.ts') : path.resolve(__dirname, 'src/environments/environment.ts')
238 | },
239 | "modules": [
240 | "./node_modules"
241 | ]
242 | },
243 | "resolveLoader": {
244 | "modules": [
245 | "./node_modules"
246 | ]
247 | },
248 | "entry": {
249 | "main": [
250 | "./src/main.ts"
251 | ],
252 | "polyfills": [
253 | "./src/polyfills.ts"
254 | ],
255 | "styles": styles
256 | },
257 | "output": {
258 | "path": path.join(process.cwd(), "dist"),
259 | "filename": "[name].bundle.js",
260 | "chunkFilename": "[id].chunk.js"
261 | },
262 | "module": {
263 | "rules": [
264 | {
265 | "enforce": "pre",
266 | "test": /\.(js|ts)$/,
267 | "loader": "source-map-loader",
268 | "exclude": [
269 | /\/node_modules\//,
270 | path.join(__dirname, 'node_modules', '@angular/compiler')
271 | ]
272 | },
273 | {
274 | "test": /\.html$/,
275 | "loader": "html-loader"
276 | },
277 | {
278 | "test": /\.(eot|svg)$/,
279 | "loader": "file-loader?name=[name].[hash:20].[ext]"
280 | },
281 | {
282 | "test": /\.(jpg|png|gif|otf|ttf|woff|woff2|cur|ani)$/,
283 | "loader": "url-loader?name=[name].[hash:20].[ext]&limit=10000"
284 | },
285 | {
286 | "exclude": style_paths,
287 | "test": /\.css$/,
288 | "loaders": [
289 | "exports-loader?module.exports.toString()",
290 | "css-loader?{\"sourceMap\":false,\"importLoaders\":1}",
291 | "postcss-loader"
292 | ]
293 | },
294 | {
295 | "exclude": style_paths,
296 | "test": /\.scss$|\.sass$/,
297 | "loaders": [
298 | "exports-loader?module.exports.toString()",
299 | "css-loader?{\"sourceMap\":false,\"importLoaders\":1}",
300 | "postcss-loader",
301 | "sass-loader"
302 | ]
303 | },
304 | {
305 | "exclude": style_paths,
306 | "test": /\.less$/,
307 | "loaders": [
308 | "exports-loader?module.exports.toString()",
309 | "css-loader?{\"sourceMap\":false,\"importLoaders\":1}",
310 | "postcss-loader",
311 | "less-loader"
312 | ]
313 | },
314 | {
315 | "exclude": style_paths,
316 | "test": /\.styl$/,
317 | "loaders": [
318 | "exports-loader?module.exports.toString()",
319 | "css-loader?{\"sourceMap\":false,\"importLoaders\":1}",
320 | "postcss-loader",
321 | "stylus-loader?{\"sourceMap\":false,\"paths\":[]}"
322 | ]
323 | },
324 | {
325 | "include": style_paths,
326 | "test": /\.css$/,
327 | "loaders": ExtractTextPlugin.extract({
328 | "use": [
329 | "css-loader?{\"sourceMap\":false,\"importLoaders\":1}",
330 | "postcss-loader"
331 | ],
332 | "fallback": "style-loader",
333 | "publicPath": ""
334 | })
335 | },
336 | {
337 | "include": style_paths,
338 | "test": /\.scss$|\.sass$/,
339 | "loaders": ExtractTextPlugin.extract({
340 | "use": [
341 | "css-loader?{\"sourceMap\":false,\"importLoaders\":1}",
342 | "postcss-loader",
343 | "sass-loader"
344 | ],
345 | "fallback": "style-loader",
346 | "publicPath": ""
347 | })
348 | },
349 | {
350 | "include":style_paths,
351 | "test": /\.less$/,
352 | "loaders": ExtractTextPlugin.extract({
353 | "use": [
354 | "css-loader?{\"sourceMap\":false,\"importLoaders\":1}",
355 | "postcss-loader",
356 | "less-loader"
357 | ],
358 | "fallback": "style-loader",
359 | "publicPath": ""
360 | })
361 | },
362 | {
363 | "include": style_paths,
364 | "test": /\.styl$/,
365 | "loaders": ExtractTextPlugin.extract({
366 | "use": [
367 | "css-loader?{\"sourceMap\":false,\"importLoaders\":1}",
368 | "postcss-loader",
369 | "stylus-loader?{\"sourceMap\":false,\"paths\":[]}"
370 | ],
371 | "fallback": "style-loader",
372 | "publicPath": ""
373 | })
374 | },
375 | {
376 | "test": /\.ts$/,
377 | "loader": "@ngtools/webpack"
378 | }
379 | ]
380 | },
381 | "plugins": getPlugins(),
382 | "node": {
383 | fs: "empty",
384 | global: true,
385 | crypto: "empty",
386 | tls: "empty",
387 | net: "empty",
388 | process: true,
389 | module: false,
390 | clearImmediate: false,
391 | setImmediate: false,
392 | __dirname: false,
393 | __filename: false
394 | }
395 | };
--------------------------------------------------------------------------------
/src/server/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | project(rat_server)
2 |
3 | if(WIN32)
4 | set(${PROJECT_NAME}_PLATFORM_SRC
5 | windows/resource.h
6 | windows/RAT_Server.rc
7 | ../Desktop_Server.ico
8 | )
9 | add_definitions(-D_CRT_SECURE_NO_WARNINGS -DNOMINMAX)
10 | elseif(APPLE)
11 |
12 | else()
13 |
14 | endif()
15 |
16 | add_definitions(
17 | -DTEST_CERTIFICATE_PRIVATE_PATH="${CMAKE_CURRENT_SOURCE_DIR}/../../Test/private.key"
18 | -DTEST_CERTIFICATE_PUBLIC_PATH="${CMAKE_CURRENT_SOURCE_DIR}/../../Test/public.crt"
19 | -DTEST_CERTIFICATE_PRIVATE_PASSWORD="Test pass"
20 | )
21 | find_package(ZLIB REQUIRED)
22 | find_package(OpenSSL REQUIRED)
23 | find_package(LIBJPEGTURBO REQUIRED)
24 |
25 | include_directories(
26 | ${COMMON_INCLUDE_DIRS}
27 | ${CMAKE_CURRENT_SOURCE_DIR}
28 | ${LIBJPEGTURBO_INCLUDE_DIRS}
29 | )
30 | add_executable(${PROJECT_NAME}
31 | Server.h
32 | main.cpp
33 | Server.cpp
34 | ServerFunctions.h
35 | ServerFunctions.cpp
36 | ${${PROJECT_NAME}_PLATFORM_SRC}
37 | )
38 |
39 | if(WIN32)
40 | target_link_libraries(${PROJECT_NAME} Crypt32 Dwmapi)
41 | elseif(APPLE)
42 | find_library(corefoundation_lib CoreFoundation)
43 | find_library(cocoa_lib Cocoa)
44 | find_package(Threads REQUIRED)
45 | target_link_libraries(${PROJECT_NAME} ${CMAKE_THREAD_LIBS_INIT} ${corefoundation_lib} ${cocoa_lib})
46 | else()
47 | find_package(X11 REQUIRED)
48 | find_package(Threads REQUIRED)
49 | target_link_libraries(${PROJECT_NAME} ${CMAKE_THREAD_LIBS_INIT} dl ${X11_LIBRARIES} ${X11_Xfixes_LIB} ${X11_XTest_LIB} ${X11_Xinerama_LIB})
50 | endif()
51 |
52 | if(WIN32)
53 | set(LIBEXTENSION .lib)
54 | else()
55 | if(${BUILD_SHARED_LIBS})
56 | set(LIBEXTENSION ${CMAKE_SHARED_LIBRARY_SUFFIX})
57 | else()
58 | set(LIBEXTENSION ${CMAKE_STATIC_LIBRARY_SUFFIX})
59 | endif()
60 | endif()
61 |
62 | target_link_libraries(${PROJECT_NAME}
63 | rat_lite
64 | ${CMAKE_BINARY_DIR}/bin/lib/${CMAKE_SHARED_LIBRARY_PREFIX}websocket_lite${LIBEXTENSION}
65 | ${CMAKE_BINARY_DIR}/bin/lib/${CMAKE_SHARED_LIBRARY_PREFIX}input_lite${LIBEXTENSION}
66 | ${CMAKE_BINARY_DIR}/bin/lib/${CMAKE_SHARED_LIBRARY_PREFIX}clipboard_lite${LIBEXTENSION}
67 | ${CMAKE_BINARY_DIR}/bin/lib/${CMAKE_SHARED_LIBRARY_PREFIX}screen_capture_lite${LIBEXTENSION}
68 | ${OPENSSL_LIBRARIES}
69 | ${ZLIB_LIBRARIES}
70 | ${LIBJPEGTURBO_LIBRARIES}
71 | )
72 |
73 | if(${BUILD_SHARED_LIBS})
74 | set_target_properties(${PROJECT_NAME} PROPERTIES DEFINE_SYMBOL RAT_LITE_DLL)
75 | set(librarylocation bin)
76 | if(APPLE)
77 | set(librarylocation lib)
78 | endif()
79 |
80 | add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
81 | COMMAND ${CMAKE_COMMAND} -E copy_if_different
82 | ${CMAKE_BINARY_DIR}/bin/${librarylocation}/${CMAKE_SHARED_LIBRARY_PREFIX}websocket_lite${CMAKE_SHARED_LIBRARY_SUFFIX}
83 | $)
84 | add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
85 | COMMAND ${CMAKE_COMMAND} -E copy_if_different
86 | ${CMAKE_BINARY_DIR}/bin/${librarylocation}/${CMAKE_SHARED_LIBRARY_PREFIX}input_lite${CMAKE_SHARED_LIBRARY_SUFFIX}
87 | $)
88 | add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
89 | COMMAND ${CMAKE_COMMAND} -E copy_if_different
90 | ${CMAKE_BINARY_DIR}/bin/${librarylocation}/${CMAKE_SHARED_LIBRARY_PREFIX}clipboard_lite${CMAKE_SHARED_LIBRARY_SUFFIX}
91 | $)
92 | add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
93 | COMMAND ${CMAKE_COMMAND} -E copy_if_different
94 | ${CMAKE_BINARY_DIR}/bin/${librarylocation}/${CMAKE_SHARED_LIBRARY_PREFIX}screen_capture_lite${CMAKE_SHARED_LIBRARY_SUFFIX}
95 | $)
96 | add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
97 | COMMAND ${CMAKE_COMMAND} -E copy_if_different
98 | $
99 | $)
100 | endif()
101 |
--------------------------------------------------------------------------------
/src/server/Server.cpp:
--------------------------------------------------------------------------------
1 | #include "Server.h"
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 |
10 | #include "Clipboard_Lite.h"
11 | #include "Logging.h"
12 | #include "RAT.h"
13 | #include "ScreenCapture.h"
14 |
15 | #include "WS_Lite.h"
16 |
17 | #include "ServerFunctions.h"
18 |
19 | using namespace std::chrono_literals;
20 |
21 | namespace SL {
22 | namespace RAT_Server {
23 | class ServerImpl {
24 | public:
25 | std::shared_ptr ScreenCaptureManager_;
26 | std::shared_ptr Clipboard_;
27 | std::shared_ptr IServerDriver_;
28 |
29 | RAT_Lite::Server_Status Status_ = RAT_Lite::Server_Status::SERVER_STOPPED;
30 | std::shared_mutex ClientsLock;
31 | std::vector> Clients;
32 | std::vector AllMonitors;
33 |
34 | RAT_Lite::ClipboardSharing ShareClip = RAT_Lite::ClipboardSharing::NOT_SHARED;
35 | int ImageCompressionSettingRequested = 70;
36 | int ImageCompressionSettingActual = 70;
37 | int MouseCaptureRate = 50;
38 | int ScreenImageCaptureRateRequested = 100;
39 | int ScreenImageCaptureRateActual = 100;
40 |
41 | bool IgnoreIncomingKeyboardEvents = false;
42 | bool IgnoreIncomingMouseEvents = false;
43 | RAT_Lite::ImageEncoding EncodeImagesAsGrayScale = RAT_Lite::ImageEncoding::COLOR;
44 | size_t MaxMemoryUsed = 1024 * 1024 * 100;
45 |
46 | ServerImpl()
47 | {
48 | Status_ = RAT_Lite::Server_Status::SERVER_STOPPED;
49 | Clipboard_ = Clipboard_Lite::CreateClipboard()
50 | ->onText([&](const std::string &text) {
51 | if (ShareClip == RAT_Lite::ClipboardSharing::SHARED && IServerDriver_) {
52 | SendtoAll(IServerDriver_->PrepareClipboardChanged(text));
53 | }
54 | })
55 | ->run();
56 |
57 | ScreenCaptureManager_ =
58 | Screen_Capture::CreateCaptureConfiguration([&]() {
59 | AllMonitors = Screen_Capture::GetMonitors();
60 | if (!IServerDriver_)
61 | return AllMonitors;
62 | SendtoAll(IServerDriver_->PrepareMonitorsChanged(AllMonitors));
63 | // add everyone to the list!
64 |
65 | std::unique_lock lock(ClientsLock);
66 | onGetMonitors(Clients, AllMonitors);
67 | return AllMonitors;
68 | })
69 | ->onNewFrame([&](const SL::Screen_Capture::Image &img, const SL::Screen_Capture::Monitor &monitor) {
70 | if (!IServerDriver_)
71 | return;
72 | decltype(Clients) clients;
73 | clients.reserve(Clients.size());
74 | {
75 | std::unique_lock lock(ClientsLock);
76 | for (auto &a : Clients) {
77 | auto found =
78 | std::find_if(begin(a->MonitorsNeeded), end(a->MonitorsNeeded), [&monitor](auto m) { return monitor.Id == m.Id; });
79 | if (found != end(a->MonitorsNeeded)) {
80 | clients.push_back(a);
81 | a->MonitorsNeeded.erase(found);
82 | }
83 | }
84 | }
85 | for (auto &a : clients) {
86 | auto msg = IServerDriver_->PrepareNewFrame(img, monitor, ImageCompressionSettingActual,
87 | EncodeImagesAsGrayScale == RAT_Lite::ImageEncoding::GRAYSCALE ||
88 | a->EncodeImagesAsGrayScale == RAT_Lite::ImageEncoding::GRAYSCALE);
89 | a->Socket->send(msg, SL::WS_LITE::CompressionOptions::NO_COMPRESSION);
90 | }
91 | })
92 | ->onFrameChanged([&](const SL::Screen_Capture::Image &img, const SL::Screen_Capture::Monitor &monitor) {
93 | if (!IServerDriver_)
94 | return;
95 |
96 | decltype(Clients) clients;
97 | clients.reserve(Clients.size());
98 | {
99 | std::shared_lock lock(ClientsLock);
100 | for (auto &a : Clients) {
101 | auto found = std::find_if(begin(a->MonitorsToWatch), end(a->MonitorsToWatch),
102 | [&monitor](auto m) { return monitor.Id == m.Id; });
103 | if (found != end(a->MonitorsToWatch)) {
104 | clients.push_back(a);
105 | }
106 | }
107 | }
108 |
109 | auto newscreencapturerate = GetNewScreenCaptureRate(IServerDriver_->MemoryUsed(), MaxMemoryUsed, ScreenImageCaptureRateActual,
110 | ScreenImageCaptureRateRequested);
111 | if (newscreencapturerate != ScreenImageCaptureRateActual) {
112 | ScreenImageCaptureRateActual = newscreencapturerate;
113 | ScreenCaptureManager_->setFrameChangeInterval(std::chrono::milliseconds(newscreencapturerate));
114 | }
115 | ImageCompressionSettingActual = GetNewImageCompression(IServerDriver_->MemoryUsed(), MaxMemoryUsed,
116 | ImageCompressionSettingActual, ImageCompressionSettingRequested);
117 | for (auto &a : clients) {
118 | auto msg = IServerDriver_->PrepareFrameChanged(img, monitor, ImageCompressionSettingActual,
119 | EncodeImagesAsGrayScale == RAT_Lite::ImageEncoding::GRAYSCALE ||
120 | a->EncodeImagesAsGrayScale == RAT_Lite::ImageEncoding::GRAYSCALE);
121 | a->Socket->send(msg, SL::WS_LITE::CompressionOptions::NO_COMPRESSION);
122 | }
123 |
124 | })
125 | ->onMouseChanged([&](const SL::Screen_Capture::Image *img, const SL::Screen_Capture::Point &point) {
126 | if (!IServerDriver_)
127 | return;
128 | if (img) {
129 | SendtoAll(IServerDriver_->PrepareMouseImageChanged(*img));
130 | }
131 | SendtoAll(IServerDriver_->PrepareMousePositionChanged(point));
132 | })
133 | ->start_capturing();
134 |
135 | ScreenCaptureManager_->setMouseChangeInterval(std::chrono::milliseconds(MouseCaptureRate));
136 | ScreenCaptureManager_->setFrameChangeInterval(std::chrono::milliseconds(ScreenImageCaptureRateActual));
137 | ScreenCaptureManager_->pause();
138 | }
139 |
140 | virtual ~ServerImpl()
141 | {
142 | ScreenCaptureManager_.reset();
143 | Clipboard_.reset(); // make sure to prevent race conditions
144 | Status_ = RAT_Lite::Server_Status::SERVER_STOPPED;
145 | }
146 | void SendtoAll(const WS_LITE::WSMessage &msg)
147 | {
148 | std::shared_lock lock(ClientsLock);
149 | for (const auto &a : Clients) {
150 | a->Socket->send(msg, SL::WS_LITE::CompressionOptions::NO_COMPRESSION);
151 | }
152 | }
153 | void Run(unsigned short port, std::string PasswordToPrivateKey, std::string PathTo_Private_Key, std::string PathTo_Public_Certficate)
154 | {
155 | Status_ = RAT_Lite::Server_Status::SERVER_RUNNING;
156 |
157 | auto clientctx = SL::WS_LITE::CreateContext(SL::WS_LITE::ThreadCount(1))
158 |
159 | ->NoTLS()
160 | /*
161 | ->UseTLS(
162 | [&](SL::WS_LITE::ITLSContext *context) {
163 | context->set_options(SL::WS_LITE::options::default_workarounds |
164 | SL::WS_LITE::options::no_sslv2 | SL::WS_LITE::options::no_sslv3 | SL::WS_LITE::options::single_dh_use);
165 | std::error_code ec;
166 |
167 | context->set_password_callback(
168 | [PasswordToPrivateKey](std::size_t s, SL::WS_LITE::password_purpose p) {
169 | return PasswordToPrivateKey; }, ec); if (ec) { std::cout << "set_password_callback failed: " << ec.message();
170 | ec.clear();
171 | }
172 | context->use_certificate_chain_file(PathTo_Public_Certficate, ec);
173 | if (ec) {
174 | std::cout << "use_certificate_chain_file failed: " << ec.message();
175 | ec.clear();
176 | }
177 | context->set_default_verify_paths(ec);
178 | if (ec) {
179 | std::cout << "set_default_verify_paths failed: " << ec.message();
180 | ec.clear();
181 | }
182 | context->use_private_key_file(std::string(PathTo_Private_Key),
183 | SL::WS_LITE::file_format::pem, ec); if (ec) { std::cout << "use_private_key_file failed: " << ec.message();
184 | ec.clear();
185 | }
186 | },
187 | SL::WS_LITE::method::tlsv11_server)*/
188 |
189 | ->CreateListener(port);
190 | IServerDriver_ =
191 | RAT_Lite::CreateServerDriverConfiguration()
192 | ->onConnection([&](const std::shared_ptr &socket) {
193 | auto c = std::make_shared();
194 | c->Socket = socket;
195 | auto m = Screen_Capture::GetMonitors();
196 | c->MonitorsNeeded = m;
197 | c->MonitorsToWatch = m;
198 | socket->send(IServerDriver_->PrepareMonitorsChanged(m), SL::WS_LITE::CompressionOptions::NO_COMPRESSION);
199 | std::unique_lock lock(ClientsLock);
200 | Clients.push_back(c);
201 | ScreenCaptureManager_->resume();
202 | })
203 | ->onMessage([&](const std::shared_ptr &socket, const WS_LITE::WSMessage &msg) {
204 | UNUSED(socket);
205 | UNUSED(msg);
206 | })
207 | ->onDisconnection([&](const std::shared_ptr &socket, unsigned short code, const std::string &msg) {
208 | std::unique_lock lock(ClientsLock);
209 | Clients.erase(std::remove_if(std::begin(Clients), std::end(Clients), [&](auto &s) { return s->Socket == socket; }),
210 | std::end(Clients));
211 | if (Clients.empty()) {
212 | // make sure to stop capturing if isnt needed
213 | ScreenCaptureManager_->pause();
214 | }
215 | UNUSED(code);
216 | UNUSED(msg);
217 | })
218 | ->onKeyUp([&](const std::shared_ptr &socket, Input_Lite::KeyCodes key) {
219 | onKeyUp(IgnoreIncomingKeyboardEvents, socket, key);
220 | })
221 | ->onKeyDown([&](const std::shared_ptr &socket, Input_Lite::KeyCodes key) {
222 | onKeyDown(IgnoreIncomingKeyboardEvents, socket, key);
223 | })
224 | ->onMouseUp([&](const std::shared_ptr &socket, Input_Lite::MouseButtons button) {
225 | onMouseUp(IgnoreIncomingMouseEvents, socket, button);
226 | })
227 | ->onMouseDown([&](const std::shared_ptr &socket, Input_Lite::MouseButtons button) {
228 | onMouseDown(IgnoreIncomingMouseEvents, socket, button);
229 | })
230 | ->onMouseScroll([&](const std::shared_ptr