master
js 259 lines 10.9 KB
Raw
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 window.addEventListener('blur', function () {
145 if (!self.socket || !self.activeSession) return;
146 // Release modifiers so they can't get stuck on the remote after an Alt+Tab (issue #330).
147 var mods = [42, 54, 29, 57373, 56, 57400, 57435, 57436];
148 for (var i = 0; i < mods.length; i++) { self.socket.send(JSON.stringify(['scancode', mods[i], false])); }
149 });
150
151 return this;
152 },
153 /**
154 * disconnect
155 */
156 disconnect: function () {
157 if (this.socket) { this.socket.close(); }
158 },
159 /**
160 * connect
161 * @param ip {string} ip target for rdp
162 * @param domain {string} microsoft domain
163 * @param username {string} session username
164 * @param password {string} session password
165 * @param next {function} asynchrone end callback
166 */
167 connect : function (ip, domain, username, password, options, next) {
168 // Start connection
169 var self = this;
170 this.socket = new WebSocket('wss://' + window.location.host + '/mstscrelay.ashx');
171 this.socket.binaryType = 'arraybuffer';
172 this.socket.onopen = function () {
173 //console.log("WS-OPEN");
174 self.socket.send(JSON.stringify(['infos', {
175 ip: ip,
176 port: 3389,
177 screen: {
178 width: self.canvas.width,
179 height: self.canvas.height
180 },
181 domain: domain,
182 username: username,
183 password: password,
184 options: options,
185 locale: Mstsc.locale()
186 }]));
187 self.prevClipboardText = null;
188 self.clipboardReadTimer = setInterval(function(){
189 if(navigator.clipboard.readText != null){
190 if (Mstsc.browser() == 'firefox') return; // this is needed because firefox pops up a PASTE option every second which is annoying
191 navigator.clipboard.readText()
192 .then(function(data){
193 if(data != self.prevClipboard){
194 self.prevClipboard = data;
195 if (self.socket) { self.socket.send(JSON.stringify(['clipboard', data])); }
196 }
197 })
198 .catch(function(){ });
199 }
200 }, 1000);
201 };
202 this.socket.onmessage = function (evt) {
203 if (typeof evt.data == 'string') {
204 // This is a JSON text string, parse it.
205 var msg = JSON.parse(evt.data);
206 switch (msg[0]) {
207 case 'rdp-connect': {
208 //console.log('[mstsc.js] connected');
209 self.activeSession = true;
210 break;
211 }
212 case 'rdp-bitmap': {
213 if (self.bitmapData == null) break;
214 var bitmap = msg[1];
215 bitmap.data = self.bitmapData; // Use the binary data that was sent earlier.
216 delete self.bitmapData;
217 //console.log('[mstsc.js] bitmap update bpp : ' + bitmap.bitsPerPixel);
218 self.render.update(bitmap);
219 break;
220 }
221 case 'rdp-close': {
222 //console.log('[mstsc.js] close');
223 self.activeSession = false;
224 next(null);
225 break;
226 }
227 case 'rdp-error': {
228 var err = msg[1];
229 console.log('[mstsc.js] error : ' + err.code + '(' + err.message + ')');
230 self.activeSession = false;
231 next(err);
232 break;
233 }
234 case 'rdp-clipboard': {
235 if ((msg[1] != null) && (navigator.clipboard.writeText != null)) {
236 navigator.clipboard.writeText(msg[1]) // Put remote clipboard data into our clipboard
237 .then(function() { })
238 .catch(function(err) { console.log('clipboard.writeText Error', err); });
239 }
240 break;
241 }
242 }
243 } else {
244 // This is binary bitmap data, store it.
245 self.bitmapData = evt.data;
246 }
247 };
248 this.socket.onclose = function () {
249 //console.log("WS-CLOSE");
250 self.activeSession = false;
251 clearInterval(self.clipboardReadTimer);
252 self.prevClipboardText = null;
253 next(null);
254 };
255 }
256 }
257
258 MstscClient = { create : function (canvas) { return new Client(canvas); } }
259 })();