EverydayTech Platform - Developer Reference
Complete Source Code Documentation - All Applications
Loading...
Searching...
No Matches
client.js
Go to the documentation of this file.
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
24(function() {
25 /**
26 * Mouse button mapping
27 * @param button {integer} client button number
28 */
29 function mouseButtonMap(button) {
30 switch(button) {
31 case 0: return 1;
32 case 2: return 2;
33 default: return 0;
34 }
35 };
36
37 /**
38 * Mstsc client
39 * Input client connection (mouse and keyboard)
40 * bitmap processing
41 * @param canvas {canvas} rendering element
42 */
43 function Client(canvas) {
44 this.canvas = canvas;
45 // create renderer
46 this.render = new Mstsc.Canvas.create(this.canvas);
47 this.socket = null;
48 this.activeSession = false;
49 this.mouseNagleTimer = null;
50 this.mouseNagleData = null;
51 this.install();
52 }
53
54 /*
55 obj.mNagleTimer = setTimeout(function () {
56 obj.send(String.fromCharCode(5, obj.buttonmask) + ShortToStr(obj.mx) + ShortToStr(obj.my));
57 obj.mNagleTimer = null;
58 }, 50);
59 */
60
61 Client.prototype = {
62 install : function () {
63 var self = this;
64
65 // Bind mouse move event
66 this.canvas.addEventListener('mousemove', function (e) {
67 if (!self.socket || !self.activeSession) return;
68 var rect = e.target.getBoundingClientRect();
69 self.mouseNagleData = ['mouse', e.clientX - rect.left, e.clientY - rect.top, 0, false];
70 if (self.mouseNagleTimer == null) {
71 //console.log('sending', self.mouseNagleData);
72 self.mouseNagleTimer = setTimeout(function () { self.socket.send(JSON.stringify(self.mouseNagleData)); self.mouseNagleTimer = null; }, 50);
73 }
74 //self.socket.send(JSON.stringify(this.mouseNagleData));
75 e.preventDefault();
76 return false;
77 });
78 this.canvas.addEventListener('mousedown', function (e) {
79 if (!self.socket || !self.activeSession) return;
80 if (self.mouseNagleTimer != null) { clearTimeout(self.mouseNagleTimer); self.mouseNagleTimer = null; }
81 var rect = e.target.getBoundingClientRect();
82 self.socket.send(JSON.stringify(['mouse', e.clientX - rect.left, e.clientY - rect.top, mouseButtonMap(e.button), true]));
83 e.preventDefault();
84 return false;
85 });
86 this.canvas.addEventListener('mouseup', function (e) {
87 if (!self.socket || !self.activeSession) return;
88 if (self.mouseNagleTimer != null) { clearTimeout(self.mouseNagleTimer); self.mouseNagleTimer = null; }
89 var rect = e.target.getBoundingClientRect();
90 self.socket.send(JSON.stringify(['mouse', e.clientX - rect.left, e.clientY - rect.top, mouseButtonMap(e.button), false]));
91 e.preventDefault();
92 return false;
93 });
94 this.canvas.addEventListener('contextmenu', function (e) {
95 if (!self.socket || !self.activeSession) return;
96 if (self.mouseNagleTimer != null) { clearTimeout(self.mouseNagleTimer); self.mouseNagleTimer = null; }
97 var rect = e.target.getBoundingClientRect();
98 self.socket.send(JSON.stringify(['mouse', e.clientX - rect.left, e.clientY - rect.top, mouseButtonMap(e.button), false]));
99 e.preventDefault();
100 return false;
101 });
102 this.canvas.addEventListener('DOMMouseScroll', function (e) {
103 if (!self.socket || !self.activeSession) return;
104 if (self.mouseNagleTimer != null) { clearTimeout(self.mouseNagleTimer); self.mouseNagleTimer = null; }
105 var isHorizontal = false;
106 var delta = e.detail;
107 //var step = Math.round(Math.abs(delta) * 15 / 8);
108 //var step = Math.abs(e.detail);
109 var step = 128;
110 //console.log('DOMMouseScroll', delta, step, e.detail);
111 var rect = e.target.getBoundingClientRect();
112 self.socket.send(JSON.stringify(['wheel', e.clientX - rect.left, e.clientY - rect.top, step, delta > 0, isHorizontal]));
113 e.preventDefault();
114 return false;
115 });
116 this.canvas.addEventListener('mousewheel', function (e) {
117 if (!self.socket || !self.activeSession) return;
118 if (self.mouseNagleTimer != null) { clearTimeout(self.mouseNagleTimer); self.mouseNagleTimer = null; }
119 var isHorizontal = Math.abs(e.deltaX) > Math.abs(e.deltaY);
120 var delta = isHorizontal?e.deltaX:e.deltaY;
121 //var step = Math.round(Math.abs(delta) * 15 / 8);
122 var step = 128;
123 //console.log('mousewheel', delta, step, e);
124 var rect = e.target.getBoundingClientRect();
125 self.socket.send(JSON.stringify(['wheel', e.clientX - rect.left, e.clientY - rect.top, step, delta > 0, isHorizontal]));
126 e.preventDefault();
127 return false;
128 });
129
130 // Bind keyboard event
131 window.addEventListener('keydown', function (e) {
132 if (!self.socket || !self.activeSession) return;
133 self.socket.send(JSON.stringify(['scancode', Mstsc.scancode(e), true]));
134 e.preventDefault();
135 return false;
136 });
137 window.addEventListener('keyup', function (e) {
138 if (!self.socket || !self.activeSession) return;
139 self.socket.send(JSON.stringify(['scancode', Mstsc.scancode(e), false]));
140 e.preventDefault();
141 return false;
142 });
143
144 return this;
145 },
146 /**
147 * disconnect
148 */
149 disconnect: function () {
150 if (this.socket) { this.socket.close(); }
151 },
152 /**
153 * connect
154 * @param ip {string} ip target for rdp
155 * @param domain {string} microsoft domain
156 * @param username {string} session username
157 * @param password {string} session password
158 * @param next {function} asynchrone end callback
159 */
160 connect : function (ip, domain, username, password, options, next) {
161 // Start connection
162 var self = this;
163 this.socket = new WebSocket('wss://' + window.location.host + '/mstscrelay.ashx');
164 this.socket.binaryType = 'arraybuffer';
165 this.socket.onopen = function () {
166 //console.log("WS-OPEN");
167 self.socket.send(JSON.stringify(['infos', {
168 ip: ip,
169 port: 3389,
170 screen: {
171 width: self.canvas.width,
172 height: self.canvas.height
173 },
174 domain: domain,
175 username: username,
176 password: password,
177 options: options,
178 locale: Mstsc.locale()
179 }]));
180 self.prevClipboardText = null;
181 self.clipboardReadTimer = setInterval(function(){
182 if(navigator.clipboard.readText != null){
183 if (Mstsc.browser() == 'firefox') return; // this is needed because firefox pops up a PASTE option every second which is annoying
184 navigator.clipboard.readText()
185 .then(function(data){
186 if(data != self.prevClipboard){
187 self.prevClipboard = data;
188 if (self.socket) { self.socket.send(JSON.stringify(['clipboard', data])); }
189 }
190 })
191 .catch(function(){ });
192 }
193 }, 1000);
194 };
195 this.socket.onmessage = function (evt) {
196 if (typeof evt.data == 'string') {
197 // This is a JSON text string, parse it.
198 var msg = JSON.parse(evt.data);
199 switch (msg[0]) {
200 case 'rdp-connect': {
201 //console.log('[mstsc.js] connected');
202 self.activeSession = true;
203 break;
204 }
205 case 'rdp-bitmap': {
206 if (self.bitmapData == null) break;
207 var bitmap = msg[1];
208 bitmap.data = self.bitmapData; // Use the binary data that was sent earlier.
209 delete self.bitmapData;
210 //console.log('[mstsc.js] bitmap update bpp : ' + bitmap.bitsPerPixel);
211 self.render.update(bitmap);
212 break;
213 }
214 case 'rdp-close': {
215 //console.log('[mstsc.js] close');
216 self.activeSession = false;
217 next(null);
218 break;
219 }
220 case 'rdp-error': {
221 var err = msg[1];
222 console.log('[mstsc.js] error : ' + err.code + '(' + err.message + ')');
223 self.activeSession = false;
224 next(err);
225 break;
226 }
227 case 'rdp-clipboard': {
228 if ((msg[1] != null) && (navigator.clipboard.writeText != null)) {
229 navigator.clipboard.writeText(msg[1]) // Put remote clipboard data into our clipboard
230 .then(function() { })
231 .catch(function(err) { console.log('clipboard.writeText Error', err); });
232 }
233 break;
234 }
235 }
236 } else {
237 // This is binary bitmap data, store it.
238 self.bitmapData = evt.data;
239 }
240 };
241 this.socket.onclose = function () {
242 //console.log("WS-CLOSE");
243 self.activeSession = false;
244 clearInterval(self.clipboardReadTimer);
245 self.prevClipboardText = null;
246 next(null);
247 };
248 }
249 }
250
251 MstscClient = { create : function (canvas) { return new Client(canvas); } }
252})();