client.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. /*
  2. * Copyright (c) 2015 Sylvain Peyrefitte
  3. *
  4. * This file is part of mstsc.js.
  5. *
  6. * mstsc.js is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  18. */
  19. /*
  20. * added get clipboard from remote RDP - Simon Smith 2024
  21. * added set clipboard to remote RDP - Simon Smith 2024
  22. */
  23. (function() {
  24. /**
  25. * Mouse button mapping
  26. * @param button {integer} client button number
  27. */
  28. function mouseButtonMap(button) {
  29. switch(button) {
  30. case 0: return 1;
  31. case 2: return 2;
  32. default: return 0;
  33. }
  34. };
  35. /**
  36. * Mstsc client
  37. * Input client connection (mouse and keyboard)
  38. * bitmap processing
  39. * @param canvas {canvas} rendering element
  40. */
  41. function Client(canvas) {
  42. this.canvas = canvas;
  43. // create renderer
  44. this.render = new Mstsc.Canvas.create(this.canvas);
  45. this.socket = null;
  46. this.activeSession = false;
  47. this.mouseNagleTimer = null;
  48. this.mouseNagleData = null;
  49. this.install();
  50. }
  51. /*
  52. obj.mNagleTimer = setTimeout(function () {
  53. obj.send(String.fromCharCode(5, obj.buttonmask) + ShortToStr(obj.mx) + ShortToStr(obj.my));
  54. obj.mNagleTimer = null;
  55. }, 50);
  56. */
  57. Client.prototype = {
  58. install : function () {
  59. var self = this;
  60. // Bind mouse move event
  61. this.canvas.addEventListener('mousemove', function (e) {
  62. if (!self.socket || !self.activeSession) return;
  63. var rect = e.target.getBoundingClientRect();
  64. self.mouseNagleData = ['mouse', e.clientX - rect.left, e.clientY - rect.top, 0, false];
  65. if (self.mouseNagleTimer == null) {
  66. //console.log('sending', self.mouseNagleData);
  67. self.mouseNagleTimer = setTimeout(function () { self.socket.send(JSON.stringify(self.mouseNagleData)); self.mouseNagleTimer = null; }, 50);
  68. }
  69. //self.socket.send(JSON.stringify(this.mouseNagleData));
  70. e.preventDefault();
  71. return false;
  72. });
  73. this.canvas.addEventListener('mousedown', function (e) {
  74. if (!self.socket || !self.activeSession) return;
  75. if (self.mouseNagleTimer != null) { clearTimeout(self.mouseNagleTimer); self.mouseNagleTimer = null; }
  76. var rect = e.target.getBoundingClientRect();
  77. self.socket.send(JSON.stringify(['mouse', e.clientX - rect.left, e.clientY - rect.top, mouseButtonMap(e.button), true]));
  78. e.preventDefault();
  79. return false;
  80. });
  81. this.canvas.addEventListener('mouseup', function (e) {
  82. if (!self.socket || !self.activeSession) return;
  83. if (self.mouseNagleTimer != null) { clearTimeout(self.mouseNagleTimer); self.mouseNagleTimer = null; }
  84. var rect = e.target.getBoundingClientRect();
  85. self.socket.send(JSON.stringify(['mouse', e.clientX - rect.left, e.clientY - rect.top, mouseButtonMap(e.button), false]));
  86. e.preventDefault();
  87. return false;
  88. });
  89. this.canvas.addEventListener('contextmenu', function (e) {
  90. if (!self.socket || !self.activeSession) return;
  91. if (self.mouseNagleTimer != null) { clearTimeout(self.mouseNagleTimer); self.mouseNagleTimer = null; }
  92. var rect = e.target.getBoundingClientRect();
  93. self.socket.send(JSON.stringify(['mouse', e.clientX - rect.left, e.clientY - rect.top, mouseButtonMap(e.button), false]));
  94. e.preventDefault();
  95. return false;
  96. });
  97. this.canvas.addEventListener('DOMMouseScroll', function (e) {
  98. if (!self.socket || !self.activeSession) return;
  99. if (self.mouseNagleTimer != null) { clearTimeout(self.mouseNagleTimer); self.mouseNagleTimer = null; }
  100. var isHorizontal = false;
  101. var delta = e.detail;
  102. //var step = Math.round(Math.abs(delta) * 15 / 8);
  103. //var step = Math.abs(e.detail);
  104. var step = 128;
  105. //console.log('DOMMouseScroll', delta, step, e.detail);
  106. var rect = e.target.getBoundingClientRect();
  107. self.socket.send(JSON.stringify(['wheel', e.clientX - rect.left, e.clientY - rect.top, step, delta > 0, isHorizontal]));
  108. e.preventDefault();
  109. return false;
  110. });
  111. this.canvas.addEventListener('mousewheel', function (e) {
  112. if (!self.socket || !self.activeSession) return;
  113. if (self.mouseNagleTimer != null) { clearTimeout(self.mouseNagleTimer); self.mouseNagleTimer = null; }
  114. var isHorizontal = Math.abs(e.deltaX) > Math.abs(e.deltaY);
  115. var delta = isHorizontal?e.deltaX:e.deltaY;
  116. //var step = Math.round(Math.abs(delta) * 15 / 8);
  117. var step = 128;
  118. //console.log('mousewheel', delta, step, e);
  119. var rect = e.target.getBoundingClientRect();
  120. self.socket.send(JSON.stringify(['wheel', e.clientX - rect.left, e.clientY - rect.top, step, delta > 0, isHorizontal]));
  121. e.preventDefault();
  122. return false;
  123. });
  124. // Bind keyboard event
  125. window.addEventListener('keydown', function (e) {
  126. if (!self.socket || !self.activeSession) return;
  127. self.socket.send(JSON.stringify(['scancode', Mstsc.scancode(e), true]));
  128. e.preventDefault();
  129. return false;
  130. });
  131. window.addEventListener('keyup', function (e) {
  132. if (!self.socket || !self.activeSession) return;
  133. self.socket.send(JSON.stringify(['scancode', Mstsc.scancode(e), false]));
  134. e.preventDefault();
  135. return false;
  136. });
  137. return this;
  138. },
  139. /**
  140. * disconnect
  141. */
  142. disconnect: function () {
  143. if (this.socket) { this.socket.close(); }
  144. },
  145. /**
  146. * connect
  147. * @param ip {string} ip target for rdp
  148. * @param domain {string} microsoft domain
  149. * @param username {string} session username
  150. * @param password {string} session password
  151. * @param next {function} asynchrone end callback
  152. */
  153. connect : function (ip, domain, username, password, options, next) {
  154. // Start connection
  155. var self = this;
  156. this.socket = new WebSocket('wss://' + window.location.host + '/mstscrelay.ashx');
  157. this.socket.binaryType = 'arraybuffer';
  158. this.socket.onopen = function () {
  159. //console.log("WS-OPEN");
  160. self.socket.send(JSON.stringify(['infos', {
  161. ip: ip,
  162. port: 3389,
  163. screen: {
  164. width: self.canvas.width,
  165. height: self.canvas.height
  166. },
  167. domain: domain,
  168. username: username,
  169. password: password,
  170. options: options,
  171. locale: Mstsc.locale()
  172. }]));
  173. self.prevClipboardText = null;
  174. self.clipboardReadTimer = setInterval(function(){
  175. if(navigator.clipboard.readText != null){
  176. if (Mstsc.browser() == 'firefox') return; // this is needed because firefox pops up a PASTE option every second which is annoying
  177. navigator.clipboard.readText()
  178. .then(function(data){
  179. if(data != self.prevClipboard){
  180. self.prevClipboard = data;
  181. if (self.socket) { self.socket.send(JSON.stringify(['clipboard', data])); }
  182. }
  183. })
  184. .catch(function(){ });
  185. }
  186. }, 1000);
  187. };
  188. this.socket.onmessage = function (evt) {
  189. if (typeof evt.data == 'string') {
  190. // This is a JSON text string, parse it.
  191. var msg = JSON.parse(evt.data);
  192. switch (msg[0]) {
  193. case 'rdp-connect': {
  194. //console.log('[mstsc.js] connected');
  195. self.activeSession = true;
  196. break;
  197. }
  198. case 'rdp-bitmap': {
  199. if (self.bitmapData == null) break;
  200. var bitmap = msg[1];
  201. bitmap.data = self.bitmapData; // Use the binary data that was sent earlier.
  202. delete self.bitmapData;
  203. //console.log('[mstsc.js] bitmap update bpp : ' + bitmap.bitsPerPixel);
  204. self.render.update(bitmap);
  205. break;
  206. }
  207. case 'rdp-close': {
  208. //console.log('[mstsc.js] close');
  209. self.activeSession = false;
  210. next(null);
  211. break;
  212. }
  213. case 'rdp-error': {
  214. var err = msg[1];
  215. console.log('[mstsc.js] error : ' + err.code + '(' + err.message + ')');
  216. self.activeSession = false;
  217. next(err);
  218. break;
  219. }
  220. case 'rdp-clipboard': {
  221. if ((msg[1] != null) && (navigator.clipboard.writeText != null)) {
  222. navigator.clipboard.writeText(msg[1]) // Put remote clipboard data into our clipboard
  223. .then(function() { })
  224. .catch(function(err) { console.log('clipboard.writeText Error', err); });
  225. }
  226. break;
  227. }
  228. }
  229. } else {
  230. // This is binary bitmap data, store it.
  231. self.bitmapData = evt.data;
  232. }
  233. };
  234. this.socket.onclose = function () {
  235. //console.log("WS-CLOSE");
  236. self.activeSession = false;
  237. clearInterval(self.clipboardReadTimer);
  238. self.prevClipboardText = null;
  239. next(null);
  240. };
  241. }
  242. }
  243. MstscClient = { create : function (canvas) { return new Client(canvas); } }
  244. })();