GUACAMOLE-1904: Merge additional AngularJS events reporting client-specific mouse/touch interaction.

diff --git a/guacamole/src/main/frontend/src/app/client/directives/guacClient.js b/guacamole/src/main/frontend/src/app/client/directives/guacClient.js
index fb88f99..18a1b08 100644
--- a/guacamole/src/main/frontend/src/app/client/directives/guacClient.js
+++ b/guacamole/src/main/frontend/src/app/client/directives/guacClient.js
@@ -54,6 +54,7 @@
         const ManagedClient = $injector.get('ManagedClient');
             
         // Required services
+        const $rootScope = $injector.get('$rootScope');
         const $window = $injector.get('$window');
             
         /**
@@ -198,6 +199,28 @@
         };
 
         /**
+         * Return the name of the angular event associated with the provided
+         * mouse event.
+         *
+         * @param {Guacamole.Mouse.MouseEvent} event
+         *     The mouse event to determine an angular event name for.
+         *
+         * @returns
+         *     The name of the angular event associated with the provided
+         *     mouse event.
+         */
+        const getMouseEventName = event => {
+            switch (event.type) {
+                case 'mousedown':
+                    return 'guacClientMouseDown';
+                case 'mouseup':
+                    return 'guacClientMouseUp';
+                default:
+                    return 'guacClientMouseMove';
+            }
+        };
+
+        /**
          * Handles a mouse event originating from the user's actual mouse.
          * This differs from handleEmulatedMouseEvent() in that the
          * software mouse cursor must be shown only if the user's browser
@@ -220,6 +243,9 @@
             display.showCursor(!localCursor);
             client.sendMouseState(event.state, true);
 
+            // Broadcast the mouse event
+            $rootScope.$broadcast(getMouseEventName(event), event, client);
+
         };
 
         /**
@@ -248,6 +274,31 @@
             scrollToMouse(event.state);
             client.sendMouseState(event.state, true);
 
+            // Broadcast the mouse event
+            $rootScope.$broadcast(getMouseEventName(event), event, client);
+
+        };
+
+        /**
+         * Return the name of the angular event associated with the provided
+         * touch event.
+         *
+         * @param {Guacamole.Touch.TouchEvent} event
+         *     The touch event to determine an angular event name for.
+         *
+         * @returns
+         *     The name of the angular event associated with the provided
+         *     touch event.
+         */
+        const getTouchEventName = event => {
+            switch (event.type) {
+                case 'touchstart':
+                    return 'guacClientTouchStart';
+                case 'touchend':
+                    return 'guacClientTouchEnd';
+                default:
+                    return 'guacClientTouchMove';
+            }
         };
 
         /**
@@ -269,6 +320,9 @@
             display.showCursor(false);
             client.sendTouchState(event.state, true);
 
+            // Broadcast the touch event
+            $rootScope.$broadcast(getTouchEventName(event), event, client);
+
         };
 
         // Attach any given managed client