mirror of
				https://github.com/gyurix1968/guacamole-client.git
				synced 2025-10-31 00:53:21 +00:00 
			
		
		
		
	GUACAMOLE-1204: Add support for multi-touch events.
This commit is contained in:
		| @@ -364,6 +364,39 @@ Guacamole.Client = function(tunnel) { | ||||
|         tunnel.sendMessage("mouse", Math.floor(x), Math.floor(y), buttonMask); | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
|      * Sends a touch event having the properties provided by the given touch | ||||
|      * state. | ||||
|      * | ||||
|      * @param {Guacamole.Touch.State} touchState | ||||
|      *     The state of the touch contact to send in the touch event. | ||||
|      * | ||||
|      * @param {Boolean} [applyDisplayScale=false] | ||||
|      *     Whether the provided touch state uses local display units, rather | ||||
|      *     than remote display units, and should be scaled to match the | ||||
|      *     {@link Guacamole.Display}. | ||||
|      */ | ||||
|     this.sendTouchState = function sendTouchState(touchState, applyDisplayScale) { | ||||
|  | ||||
|         // Do not send requests if not connected | ||||
|         if (!isConnected()) | ||||
|             return; | ||||
|  | ||||
|         var x = touchState.x; | ||||
|         var y = touchState.y; | ||||
|  | ||||
|         // Translate for display units if requested | ||||
|         if (applyDisplayScale) { | ||||
|             x /= display.getScale(); | ||||
|             y /= display.getScale(); | ||||
|         } | ||||
|  | ||||
|         tunnel.sendMessage('touch', touchState.id, Math.floor(x), Math.floor(y), | ||||
|             Math.floor(touchState.radiusX), Math.floor(touchState.radiusY), | ||||
|             touchState.angle, touchState.force); | ||||
|  | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
|      * Allocates an available stream index and creates a new | ||||
|      * Guacamole.OutputStream using that index, associating the resulting | ||||
| @@ -663,6 +696,20 @@ Guacamole.Client = function(tunnel) { | ||||
|      */ | ||||
|     this.onvideo = null; | ||||
|  | ||||
|     /** | ||||
|      * Fired when the remote client is explicitly declaring the level of | ||||
|      * multi-touch support provided by a particular display layer. | ||||
|      * | ||||
|      * @event | ||||
|      * @param {Guacamole.Display.VisibleLayer} layer | ||||
|      *     The layer whose multi-touch support level is being declared. | ||||
|      * | ||||
|      * @param {Number} touches | ||||
|      *     The maximum number of simultaneous touches supported by the given | ||||
|      *     layer, where 0 indicates that touch events are not supported at all. | ||||
|      */ | ||||
|     this.onmultitouch = null; | ||||
|  | ||||
|     /** | ||||
|      * Fired when the current value of a connection parameter is being exposed | ||||
|      * by the server. | ||||
| @@ -839,6 +886,14 @@ Guacamole.Client = function(tunnel) { | ||||
|  | ||||
|         "miter-limit": function(layer, value) { | ||||
|             display.setMiterLimit(layer, parseFloat(value)); | ||||
|         }, | ||||
|  | ||||
|         "multi-touch" : function layerSupportsMultiTouch(layer, value) { | ||||
|  | ||||
|             // Process "multi-touch" property only for true visible layers (not off-screen buffers) | ||||
|             if (guac_client.onmultitouch && layer instanceof Guacamole.Display.VisibleLayer) | ||||
|                 guac_client.onmultitouch(layer, parseInt(value)); | ||||
|  | ||||
|         } | ||||
|  | ||||
|     }; | ||||
|   | ||||
							
								
								
									
										280
									
								
								guacamole-common-js/src/main/webapp/modules/Touch.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										280
									
								
								guacamole-common-js/src/main/webapp/modules/Touch.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,280 @@ | ||||
| /* | ||||
|  * Licensed to the Apache Software Foundation (ASF) under one | ||||
|  * or more contributor license agreements.  See the NOTICE file | ||||
|  * distributed with this work for additional information | ||||
|  * regarding copyright ownership.  The ASF licenses this file | ||||
|  * to you under the Apache License, Version 2.0 (the | ||||
|  * "License"); you may not use this file except in compliance | ||||
|  * with the License.  You may obtain a copy of the License at | ||||
|  * | ||||
|  *   http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, | ||||
|  * software distributed under the License is distributed on an | ||||
|  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||||
|  * KIND, either express or implied.  See the License for the | ||||
|  * specific language governing permissions and limitations | ||||
|  * under the License. | ||||
|  */ | ||||
|  | ||||
| var Guacamole = Guacamole || {}; | ||||
|  | ||||
| /** | ||||
|  * Provides cross-browser multi-touch events for a given element. The events of | ||||
|  * the given element are automatically populated with handlers that translate | ||||
|  * touch events into a non-browser-specific event provided by the | ||||
|  * Guacamole.Touch instance. | ||||
|  *  | ||||
|  * @constructor | ||||
|  * @augments Guacamole.Event.Target | ||||
|  * @param {Element} element | ||||
|  *     The Element to use to provide touch events. | ||||
|  */ | ||||
| Guacamole.Touch = function Touch(element) { | ||||
|  | ||||
|     Guacamole.Event.Target.call(this); | ||||
|  | ||||
|     /** | ||||
|      * Reference to this Guacamole.Touch. | ||||
|      * | ||||
|      * @private | ||||
|      * @type {Guacamole.Touch} | ||||
|      */ | ||||
|     var guacTouch = this; | ||||
|  | ||||
|     /** | ||||
|      * The default X/Y radius of each touch if the device or browser does not | ||||
|      * expose the size of the contact area. | ||||
|      * | ||||
|      * @private | ||||
|      * @constant | ||||
|      * @type {Number} | ||||
|      */ | ||||
|     var DEFAULT_CONTACT_RADIUS = Math.floor(16 * window.devicePixelRatio); | ||||
|  | ||||
|     /** | ||||
|      * The set of all active touches, stored by their unique identifiers. | ||||
|      * | ||||
|      * @type {Object.<Number, Guacamole.Touch.State>} | ||||
|      */ | ||||
|     this.touches = {}; | ||||
|  | ||||
|     /** | ||||
|      * The number of active touches currently stored within | ||||
|      * {@link Guacamole.Touch#touches touches}. | ||||
|      */ | ||||
|     this.activeTouches = 0; | ||||
|  | ||||
|     /** | ||||
|      * Fired whenever a new touch contact is initiated on the element | ||||
|      * associated with this Guacamole.Touch. | ||||
|      *  | ||||
|      * @event Guacamole.Touch#touchstart | ||||
|      * @param {Guacamole.Touch.Event} event | ||||
|      *     A {@link Guacamole.Touch.Event} object representing the "touchstart" | ||||
|      *     event. | ||||
|      */ | ||||
|  | ||||
|     /** | ||||
|      * Fired whenever an established touch contact moves within the element | ||||
|      * associated with this Guacamole.Touch. | ||||
|      *  | ||||
|      * @event Guacamole.Touch#touchmove | ||||
|      * @param {Guacamole.Touch.Event} event | ||||
|      *     A {@link Guacamole.Touch.Event} object representing the "touchmove" | ||||
|      *     event. | ||||
|      */ | ||||
|  | ||||
|     /** | ||||
|      * Fired whenever an established touch contact is lifted from the element | ||||
|      * associated with this Guacamole.Touch. | ||||
|      *  | ||||
|      * @event Guacamole.Touch#touchend | ||||
|      * @param {Guacamole.Touch.Event} event | ||||
|      *     A {@link Guacamole.Touch.Event} object representing the "touchend" | ||||
|      *     event. | ||||
|      */ | ||||
|  | ||||
|     element.addEventListener('touchstart', function touchstart(e) { | ||||
|  | ||||
|         // Fire "ontouchstart" events for all new touches | ||||
|         for (var i = 0; i < e.changedTouches.length; i++) { | ||||
|  | ||||
|             var changedTouch = e.changedTouches[i]; | ||||
|             var identifier = changedTouch.identifier; | ||||
|  | ||||
|             // Ignore duplicated touches | ||||
|             if (guacTouch.touches[identifier]) | ||||
|                 continue; | ||||
|  | ||||
|             var touch = guacTouch.touches[identifier] = new Guacamole.Touch.State({ | ||||
|                 id      : identifier, | ||||
|                 radiusX : changedTouch.radiusX || DEFAULT_CONTACT_RADIUS, | ||||
|                 radiusY : changedTouch.radiusY || DEFAULT_CONTACT_RADIUS, | ||||
|                 angle   : changedTouch.angle || 0.0, | ||||
|                 force   : changedTouch.force || 1.0 /* Within JavaScript changedTouch events, a force of 0.0 indicates the device does not support reporting changedTouch force */ | ||||
|             }); | ||||
|  | ||||
|             guacTouch.activeTouches++; | ||||
|  | ||||
|             touch.fromClientPosition(element, changedTouch.clientX, changedTouch.clientY); | ||||
|             guacTouch.dispatch(new Guacamole.Touch.Event('touchmove', e, touch)); | ||||
|  | ||||
|         } | ||||
|  | ||||
|     }, false); | ||||
|  | ||||
|     element.addEventListener('touchmove', function touchstart(e) { | ||||
|  | ||||
|         // Fire "ontouchmove" events for all updated touches | ||||
|         for (var i = 0; i < e.changedTouches.length; i++) { | ||||
|  | ||||
|             var changedTouch = e.changedTouches[i]; | ||||
|             var identifier = changedTouch.identifier; | ||||
|  | ||||
|             // Ignore any unrecognized touches | ||||
|             var touch = guacTouch.touches[identifier]; | ||||
|             if (!touch) | ||||
|                 continue; | ||||
|  | ||||
|             // Update force only if supported by browser (otherwise, assume | ||||
|             // force is unchanged) | ||||
|             if (changedTouch.force) | ||||
|                 touch.force = changedTouch.force; | ||||
|  | ||||
|             // Update touch area, if supported by browser and device | ||||
|             touch.angle = changedTouch.angle || 0.0; | ||||
|             touch.radiusX = changedTouch.radiusX || DEFAULT_CONTACT_RADIUS; | ||||
|             touch.radiusY = changedTouch.radiusY || DEFAULT_CONTACT_RADIUS; | ||||
|  | ||||
|             // Update with any change in position | ||||
|             touch.fromClientPosition(element, changedTouch.clientX, changedTouch.clientY); | ||||
|             guacTouch.dispatch(new Guacamole.Touch.Event('touchmove', e, touch)); | ||||
|  | ||||
|         } | ||||
|  | ||||
|     }, false); | ||||
|  | ||||
|     element.addEventListener('touchend', function touchstart(e) { | ||||
|  | ||||
|         // Fire "ontouchend" events for all updated touches | ||||
|         for (var i = 0; i < e.changedTouches.length; i++) { | ||||
|  | ||||
|             var changedTouch = e.changedTouches[i]; | ||||
|             var identifier = changedTouch.identifier; | ||||
|  | ||||
|             // Ignore any unrecognized touches | ||||
|             var touch = guacTouch.touches[identifier]; | ||||
|             if (!touch) | ||||
|                 continue; | ||||
|  | ||||
|             // Stop tracking this particular touch | ||||
|             delete guacTouch.touches[identifier]; | ||||
|             guacTouch.activeTouches--; | ||||
|  | ||||
|             // Touch has ended | ||||
|             touch.force = 0.0; | ||||
|  | ||||
|             // Update with final position | ||||
|             touch.fromClientPosition(element, changedTouch.clientX, changedTouch.clientY); | ||||
|             guacTouch.dispatch(new Guacamole.Touch.Event('touchend', e, touch)); | ||||
|  | ||||
|         } | ||||
|  | ||||
|     }, false); | ||||
|  | ||||
| }; | ||||
|  | ||||
| /** | ||||
|  * The current state of a touch contact. | ||||
|  * | ||||
|  * @constructor | ||||
|  * @augments Guacamole.Position | ||||
|  * @param {Guacamole.Touch.State|Object} [template={}] | ||||
|  *     The object whose properties should be copied within the new | ||||
|  *     Guacamole.Touch.State. | ||||
|  */ | ||||
| Guacamole.Touch.State = function State(template) { | ||||
|  | ||||
|     template = template || {}; | ||||
|  | ||||
|     Guacamole.Position.call(this, template); | ||||
|  | ||||
|     /** | ||||
|      * An arbitrary integer ID which uniquely identifies this contact relative | ||||
|      * to other active contacts. | ||||
|      * | ||||
|      * @type {Number} | ||||
|      * @default 0 | ||||
|      */ | ||||
|     this.id = template.id || 0; | ||||
|  | ||||
|     /** | ||||
|      * The Y radius of the ellipse covering the general area of the touch | ||||
|      * contact, in pixels. | ||||
|      * | ||||
|      * @type {Number} | ||||
|      * @default 0 | ||||
|      */ | ||||
|     this.radiusX = template.radiusX || 0; | ||||
|  | ||||
|     /** | ||||
|      * The X radius of the ellipse covering the general area of the touch | ||||
|      * contact, in pixels. | ||||
|      * | ||||
|      * @type {Number} | ||||
|      * @default 0 | ||||
|      */ | ||||
|     this.radiusY = template.radiusY || 0; | ||||
|  | ||||
|     /** | ||||
|      * The rough angle of clockwise rotation of the general area of the touch | ||||
|      * contact, in degrees. | ||||
|      * | ||||
|      * @type {Number} | ||||
|      * @default 0.0 | ||||
|      */ | ||||
|     this.angle = template.angle || 0.0; | ||||
|  | ||||
|     /** | ||||
|      * The relative force exerted by the touch contact, where 0 is no force | ||||
|      * (the touch has been lifted) and 1 is maximum force (the maximum amount | ||||
|      * of force representable by the device). | ||||
|      * | ||||
|      * @type {Number} | ||||
|      * @default 1.0 | ||||
|      */ | ||||
|     this.force = template.force || 1.0; | ||||
|  | ||||
| }; | ||||
|  | ||||
| /** | ||||
|  * An event which represents a change in state of a single touch contact, | ||||
|  * including the creation or removal of that contact. If multiple contacts are | ||||
|  * involved in a touch interaction, each contact will be associated with its | ||||
|  * own event. | ||||
|  * | ||||
|  * @constructor | ||||
|  * @augments Guacamole.Event.DOMEvent | ||||
|  * @param {String} type | ||||
|  *     The name of the touch event type. Possible values are "touchstart", | ||||
|  *     "touchmove", and "touchend". | ||||
|  * | ||||
|  * @param {TouchEvent} event | ||||
|  *     The DOM touch event that produced this Guacamole.Touch.Event. | ||||
|  * | ||||
|  * @param {Guacamole.Touch.State} state | ||||
|  *     The state of the touch contact associated with this event. | ||||
|  */ | ||||
| Guacamole.Touch.Event = function TouchEvent(type, event, state) { | ||||
|  | ||||
|     Guacamole.Event.DOMEvent.call(this, type, [ event ]); | ||||
|  | ||||
|     /** | ||||
|      * The state of the touch contact associated with this event. | ||||
|      * | ||||
|      * @type{Guacamole.Touch.State} | ||||
|      */ | ||||
|     this.state = state; | ||||
|  | ||||
| }; | ||||
		Reference in New Issue
	
	Block a user