23
23
angular . module ( 'client' ) . controller ( 'secondaryMonitorController' , [ '$scope' , '$injector' , '$routeParams' ,
24
24
function clientController ( $scope , $injector , $routeParams ) {
25
25
26
- // Required types
27
- const ClipboardData = $injector . get ( 'ClipboardData' ) ;
28
-
29
26
// Required services
30
- const $window = $injector . get ( '$window' ) ;
31
- const clipboardService = $injector . get ( 'clipboardService ' ) ;
32
- const guacFullscreen = $injector . get ( 'guacFullscreen ' ) ;
27
+ const $window = $injector . get ( '$window' ) ;
28
+ const guacFullscreen = $injector . get ( 'guacFullscreen ' ) ;
29
+ const guacManageMonitor = $injector . get ( 'guacManageMonitor ' ) ;
33
30
34
- // ID of this monitor
31
+ /**
32
+ * ID of this monitor.
33
+ *
34
+ * @type {!String }
35
+ */
35
36
const monitorId = $routeParams . id ;
36
37
37
- // Broadcast channel
38
- const broadcast = new BroadcastChannel ( 'guac_monitors' ) ;
39
-
40
- // Latest mouse state
41
- const mouseState = { } ;
42
-
43
- // Display size in pixels and position of the monitor
44
- let displayWidth = 0 ;
45
- let displayHeight = 0 ;
46
- let monitorPosition = 0 ;
47
- let monitorsCount = 0 ;
48
- let currentScaling = 1 ;
49
-
50
38
/**
51
39
* In order to open the guacamole menu, we need to hit ctrl-alt-shift. There are
52
40
* several possible keysysms for each key.
@@ -57,262 +45,19 @@ angular.module('client').controller('secondaryMonitorController', ['$scope', '$i
57
45
CTRL_KEYS = { 0xFFE3 : true , 0xFFE4 : true } ,
58
46
MENU_KEYS = angular . extend ( { } , SHIFT_KEYS , ALT_KEYS , CTRL_KEYS ) ;
59
47
60
- // Instantiate client, using an HTTP tunnel for communications.
61
- const client = new Guacamole . Client (
62
- new Guacamole . HTTPTunnel ( "tunnel" )
63
- ) ;
64
-
65
- const display = client . getDisplay ( ) ;
66
- let displayContainer ;
67
-
68
- setTimeout ( function ( ) {
69
- displayContainer = document . querySelector ( '.display' )
70
-
71
- // Remove any existing display
72
- displayContainer . innerHTML = "" ;
73
-
74
- // Add display element
75
- displayContainer . appendChild ( display . getElement ( ) ) ;
76
-
77
- // Ready for resize
78
- pushBroadcastMessage ( 'resize' , true ) ;
79
- } , 1000 ) ;
80
-
81
- /**
82
- * Adjust the display scaling according to the window size.
83
- */
84
- $scope . scaleDisplay = function scaleDisplay ( ) {
85
-
86
- // Calculate required scaling factor
87
- const scaleX = $window . innerWidth / displayWidth ;
88
- const scaleY = $window . innerHeight / displayHeight ;
89
-
90
- // Use the lowest scaling to avoid acreen overflow
91
- if ( scaleX <= scaleY )
92
- currentScaling = scaleX ;
93
- else
94
- currentScaling = scaleY ;
95
-
96
- display . scale ( currentScaling ) ;
97
- } ;
98
-
99
- // Send monitor-close event to broadcast channel on window unload
100
- $window . addEventListener ( 'unload' , function unloadWindow ( ) {
101
- pushBroadcastMessage ( 'monitorClose' , monitorId ) ;
102
- } ) ;
103
-
104
- // Mouse and keyboard
105
- const mouse = new Guacamole . Mouse ( client . getDisplay ( ) . getElement ( ) ) ;
106
- const keyboard = new Guacamole . Keyboard ( document ) ;
107
-
108
- // Move mouse on screen and send mouse events to main window
109
- mouse . onEach ( [ 'mousedown' , 'mouseup' , 'mousemove' ] , function sendMouseEvent ( e ) {
110
-
111
- // Ensure software cursor is shown
112
- display . showCursor ( true ) ;
113
-
114
- // Update client-side cursor
115
- display . moveCursor (
116
- Math . floor ( e . state . x / currentScaling ) ,
117
- Math . floor ( e . state . y / currentScaling )
118
- ) ;
119
-
120
- // Limit mouse move events to reduce latency
121
- if ( mouseState . lastPush && Date . now ( ) - mouseState . lastPush < 100
122
- && mouseState . down === e . state . down
123
- && mouseState . up === e . state . up
124
- && mouseState . left === e . state . left
125
- && mouseState . middle === e . state . middle
126
- && mouseState . right === e . state . right )
127
- return ;
128
-
129
- // Click on actual display instead of the first
130
- const displayOffset = displayWidth * monitorPosition ;
131
-
132
- // Convert mouse state to serializable object
133
- mouseState . down = e . state . down ;
134
- mouseState . up = e . state . up ;
135
- mouseState . left = e . state . left ;
136
- mouseState . middle = e . state . middle ;
137
- mouseState . right = e . state . right ;
138
- mouseState . x = e . state . x / currentScaling + displayOffset ;
139
- mouseState . y = e . state . y / currentScaling ;
140
- mouseState . lastPush = Date . now ( ) ;
141
-
142
- // Send mouse state to main window
143
- pushBroadcastMessage ( 'mouseState' , mouseState ) ;
144
- } ) ;
145
-
146
- // Hide software cursor when mouse leaves display
147
- mouse . on ( 'mouseout' , function ( ) {
148
- if ( ! display ) return ;
149
- display . showCursor ( false ) ;
150
- } ) ;
151
-
152
- // Handle any received clipboard data
153
- client . onclipboard = function clientClipboardReceived ( stream , mimetype ) {
154
-
155
- let reader ;
156
-
157
- // If the received data is text, read it as a simple string
158
- if ( / ^ t e x t \/ / . exec ( mimetype ) ) {
159
-
160
- reader = new Guacamole . StringReader ( stream ) ;
161
-
162
- // Assemble received data into a single string
163
- let data = '' ;
164
- reader . ontext = function textReceived ( text ) {
165
- data += text ;
166
- } ;
167
-
168
- // Set clipboard contents once stream is finished
169
- reader . onend = function textComplete ( ) {
170
- clipboardService . setClipboard ( new ClipboardData ( {
171
- source : 'secondaryMonitor' ,
172
- type : mimetype ,
173
- data : data
174
- } ) ) [ 'catch' ] ( angular . noop ) ;
175
- } ;
176
-
177
- }
178
-
179
- // Otherwise read the clipboard data as a Blob
180
- else {
181
- reader = new Guacamole . BlobReader ( stream , mimetype ) ;
182
- reader . onend = function blobComplete ( ) {
183
- clipboardService . setClipboard ( new ClipboardData ( {
184
- source : 'secondaryMonitor' ,
185
- type : mimetype ,
186
- data : reader . getBlob ( )
187
- } ) ) [ 'catch' ] ( angular . noop ) ;
188
- } ;
189
- }
190
-
191
- } ;
192
-
193
- // Send keydown events to main window
194
- keyboard . onkeydown = function ( keysym ) {
195
- pushBroadcastMessage ( 'keydown' , keysym ) ;
196
- } ;
197
-
198
- // Send keyup events to main window
199
- keyboard . onkeyup = function ( keysym ) {
200
- pushBroadcastMessage ( 'keyup' , keysym ) ;
201
- } ;
202
-
203
- /**
204
- * Push broadcast message containing instructions that allows additional
205
- * monitor windows to draw display, resize window and more.
206
- *
207
- * @param {!string } type
208
- * The type of message (ex: handler, fullscreen, resize)
209
- *
210
- * @param {* } content
211
- * The content of the message, can contain any type of serializable
212
- * content.
213
- */
214
- function pushBroadcastMessage ( type , content ) {
215
- const message = {
216
- [ type ] : content
217
- } ;
218
-
219
- broadcast . postMessage ( message ) ;
220
- } ;
221
-
222
- /**
223
- * Handle messages sent by main window in guac_monitors channel. These
224
- * messages contain instructions to draw the screen, resize window, or
225
- * request full screen mode.
226
- *
227
- * @param {Event } e
228
- * Received message event from guac_monitors channel.
229
- */
230
- broadcast . onmessage = function broadcastMessage ( message ) {
231
-
232
- // Run the client handler to draw display
233
- if ( message . data . handler )
234
- client . runHandler ( message . data . handler . opcode ,
235
- message . data . handler . parameters ) ;
236
-
237
- if ( message . data . monitorsInfos ) {
238
-
239
- const monitorsInfos = message . data . monitorsInfos ;
240
-
241
- // Store new monitor count and position
242
- monitorsCount = monitorsInfos . count ;
243
- monitorPosition = monitorsInfos . map [ monitorId ] ;
244
-
245
- // Set the monitor count in display
246
- display . updateMonitors ( monitorsCount ) ;
247
-
248
- }
249
-
250
- // Resize display and window with parameters sent by guacd in the size handler
251
- if ( message . data . handler && message . data . handler . opcode === 'size' ) {
48
+ guacManageMonitor . init ( "secondary" ) ;
49
+ guacManageMonitor . monitorAttributes . monitorId = monitorId ;
252
50
253
- const parameters = message . data . handler . parameters ;
254
- const default_layer = 0 ;
255
- const layer = parseInt ( parameters [ 0 ] ) ;
256
-
257
- // Ignore other layers (ex: mouse) that can have other size
258
- if ( layer !== default_layer )
259
- return ;
260
-
261
- // Set the new display size
262
- displayWidth = parseInt ( parameters [ 1 ] ) / monitorsCount ;
263
- displayHeight = parseInt ( parameters [ 2 ] ) ;
264
-
265
- // Translate all draw actions on X to draw the current display
266
- // instead of the first
267
- client . offsetX = displayWidth * monitorPosition ;
268
-
269
- // Get unusable window height and width (ex: titlebar)
270
- const windowUnusableHeight = $window . outerHeight - $window . innerHeight ;
271
- const windowUnusableWidth = $window . outerWidth - $window . innerWidth ;
272
-
273
- // Remove scrollbars
274
- document . querySelector ( '.client-main' ) . style . overflow = 'hidden' ;
275
-
276
- // Resize window to the display size
277
- $window . resizeTo (
278
- displayWidth + windowUnusableWidth ,
279
- displayHeight + windowUnusableHeight
280
- ) ;
281
-
282
- // Adjust scaling to new size
283
- $scope . scaleDisplay ( ) ;
284
-
285
- }
286
-
287
- // Full screen mode instructions
288
- if ( message . data . fullscreen ) {
289
-
290
- // setFullscreenMode require explicit user action
291
- if ( message . data . fullscreen !== false )
292
- openConsentButton ( ) ;
293
-
294
- // Close fullscreen mode instantly
295
- else
296
- guacFullscreen . setFullscreenMode ( message . data . fullscreen ) ;
297
-
298
- }
299
-
300
- } ;
301
-
302
- /**
303
- * Add button to request user consent before enabling fullscreen mode to
304
- * comply with the setFullscreenMode requirements that require explicit
305
- * user action. The button is removed after a few seconds if the user does
306
- * not click on it.
307
- */
308
- function openConsentButton ( ) {
51
+ guacManageMonitor . openConsentButton = function openConsentButton ( ) {
309
52
310
53
// Show button
311
54
$scope . showFullscreenConsent = true ;
55
+ $scope . $apply ( ) ;
312
56
313
57
// Auto hide button after delay
314
58
setTimeout ( function ( ) {
315
59
$scope . showFullscreenConsent = false ;
60
+ $scope . $apply ( ) ;
316
61
} , 10000 ) ;
317
62
318
63
} ;
@@ -367,11 +112,16 @@ angular.module('client').controller('secondaryMonitorController', ['$scope', '$i
367
112
368
113
// Toggle the menu
369
114
$scope . $apply ( function ( ) {
370
- pushBroadcastMessage ( 'guacMenu' , true ) ;
115
+ guacManageMonitor . pushBroadcastMessage ( 'guacMenu' , true ) ;
371
116
} ) ;
372
117
373
118
}
374
119
375
120
} ) ;
376
121
122
+ // Send monitor-close event to broadcast channel on window unload
123
+ $window . addEventListener ( 'unload' , function unloadWindow ( ) {
124
+ guacManageMonitor . pushBroadcastMessage ( 'monitorClose' , monitorId ) ;
125
+ } ) ;
126
+
377
127
} ] ) ;
0 commit comments