2016-12-28 00:46:55 +00:00
|
|
|
/* global $, JitsiMeetJS, APP */
|
2016-12-09 23:15:04 +00:00
|
|
|
import * as KeyCodes from "../keycode/keycode";
|
2016-12-28 00:46:55 +00:00
|
|
|
import {EVENT_TYPES, REMOTE_CONTROL_EVENT_TYPE, PERMISSIONS_ACTIONS}
|
2016-12-20 22:15:13 +00:00
|
|
|
from "../../service/remotecontrol/Constants";
|
2016-12-28 00:46:55 +00:00
|
|
|
import RemoteControlParticipant from "./RemoteControlParticipant";
|
|
|
|
|
|
|
|
const ConferenceEvents = JitsiMeetJS.events.conference;
|
2016-12-09 23:15:04 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Extract the keyboard key from the keyboard event.
|
|
|
|
* @param event {KeyboardEvent} the event.
|
|
|
|
* @returns {KEYS} the key that is pressed or undefined.
|
|
|
|
*/
|
|
|
|
function getKey(event) {
|
|
|
|
return KeyCodes.keyboardEventToKey(event);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Extract the modifiers from the keyboard event.
|
|
|
|
* @param event {KeyboardEvent} the event.
|
|
|
|
* @returns {Array} with possible values: "shift", "control", "alt", "command".
|
|
|
|
*/
|
|
|
|
function getModifiers(event) {
|
|
|
|
let modifiers = [];
|
|
|
|
if(event.shiftKey) {
|
|
|
|
modifiers.push("shift");
|
|
|
|
}
|
|
|
|
|
|
|
|
if(event.ctrlKey) {
|
|
|
|
modifiers.push("control");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if(event.altKey) {
|
|
|
|
modifiers.push("alt");
|
|
|
|
}
|
|
|
|
|
|
|
|
if(event.metaKey) {
|
|
|
|
modifiers.push("command");
|
|
|
|
}
|
|
|
|
|
|
|
|
return modifiers;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This class represents the controller party for a remote controller session.
|
|
|
|
* It listens for mouse and keyboard events and sends them to the receiver
|
|
|
|
* party of the remote control session.
|
|
|
|
*/
|
2016-12-28 00:46:55 +00:00
|
|
|
export default class Controller extends RemoteControlParticipant {
|
2016-12-09 23:15:04 +00:00
|
|
|
/**
|
|
|
|
* Creates new instance.
|
|
|
|
*/
|
2016-12-20 22:15:13 +00:00
|
|
|
constructor() {
|
2016-12-28 00:46:55 +00:00
|
|
|
super();
|
|
|
|
this.controlledParticipant = null;
|
|
|
|
this.requestedParticipant = null;
|
|
|
|
this.stopListener = this._handleRemoteControlStoppedEvent.bind(this);
|
2016-12-20 22:15:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2016-12-28 00:46:55 +00:00
|
|
|
* Requests permissions from the remote control receiver side.
|
|
|
|
* @param {string} userId the user id of the participant that will be
|
|
|
|
* requested.
|
2016-12-20 22:15:13 +00:00
|
|
|
*/
|
2016-12-28 00:46:55 +00:00
|
|
|
requestPermissions(userId) {
|
|
|
|
if(!this.enabled) {
|
|
|
|
return Promise.reject(new Error("Remote control is disabled!"));
|
|
|
|
}
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
let permissionsReplyListener = (participant, event) => {
|
|
|
|
let result = null;
|
|
|
|
try {
|
|
|
|
result = this._handleReply(participant, event);
|
|
|
|
} catch (e) {
|
|
|
|
reject(e);
|
|
|
|
}
|
|
|
|
if(result !== null) {
|
|
|
|
this.requestedParticipant = null;
|
|
|
|
APP.conference.removeConferenceListener(
|
|
|
|
ConferenceEvents.ENDPOINT_MESSAGE_RECEIVED,
|
|
|
|
permissionsReplyListener);
|
|
|
|
resolve(result);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
APP.conference.addConferenceListener(
|
|
|
|
ConferenceEvents.ENDPOINT_MESSAGE_RECEIVED,
|
|
|
|
permissionsReplyListener);
|
|
|
|
this.requestedParticipant = userId;
|
|
|
|
this._sendRemoteControlEvent(userId, {
|
|
|
|
type: EVENT_TYPES.permissions,
|
|
|
|
action: PERMISSIONS_ACTIONS.request
|
|
|
|
}, e => {
|
|
|
|
APP.conference.removeConferenceListener(
|
|
|
|
ConferenceEvents.ENDPOINT_MESSAGE_RECEIVED,
|
|
|
|
permissionsReplyListener);
|
|
|
|
this.requestedParticipant = null;
|
|
|
|
reject(e);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Handles the reply of the permissions request.
|
|
|
|
* @param {JitsiParticipant} participant the participant that has sent the
|
|
|
|
* reply
|
|
|
|
* @param {object} event the remote control event.
|
|
|
|
*/
|
|
|
|
_handleReply(participant, event) {
|
|
|
|
const remoteControlEvent = event.event;
|
|
|
|
const userId = participant.getId();
|
|
|
|
if(this.enabled && event.type === REMOTE_CONTROL_EVENT_TYPE
|
|
|
|
&& remoteControlEvent.type === EVENT_TYPES.permissions
|
|
|
|
&& userId === this.requestedParticipant) {
|
|
|
|
if(remoteControlEvent.action === PERMISSIONS_ACTIONS.grant) {
|
|
|
|
this.controlledParticipant = userId;
|
|
|
|
this._start();
|
|
|
|
return true;
|
|
|
|
} else if(remoteControlEvent.action === PERMISSIONS_ACTIONS.deny) {
|
|
|
|
return false;
|
|
|
|
} else {
|
|
|
|
throw new Error("Unknown reply received!");
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
//different message type or another user -> ignoring the message
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Handles remote control stopped.
|
|
|
|
* @param {JitsiParticipant} participant the participant that has sent the
|
|
|
|
* event
|
|
|
|
* @param {object} event the the remote control event.
|
|
|
|
*/
|
|
|
|
_handleRemoteControlStoppedEvent(participant, event) {
|
|
|
|
if(this.enabled && event.type === REMOTE_CONTROL_EVENT_TYPE
|
|
|
|
&& event.event.type === EVENT_TYPES.stop
|
|
|
|
&& participant.getId() === this.controlledParticipant) {
|
|
|
|
this._stop();
|
|
|
|
}
|
2016-12-20 22:15:13 +00:00
|
|
|
}
|
2016-12-09 23:15:04 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Starts processing the mouse and keyboard events.
|
|
|
|
*/
|
2016-12-28 00:46:55 +00:00
|
|
|
_start() {
|
2016-12-20 22:15:13 +00:00
|
|
|
if(!this.enabled)
|
|
|
|
return;
|
2016-12-28 00:46:55 +00:00
|
|
|
APP.conference.addConferenceListener(
|
|
|
|
ConferenceEvents.ENDPOINT_MESSAGE_RECEIVED,
|
|
|
|
this.stopListener);
|
|
|
|
this.area = $("#largeVideoWrapper");
|
2016-12-09 23:15:04 +00:00
|
|
|
this.area.mousemove(event => {
|
|
|
|
const position = this.area.position();
|
2016-12-28 00:46:55 +00:00
|
|
|
this._sendRemoteControlEvent(this.controlledParticipant, {
|
2016-12-20 22:15:13 +00:00
|
|
|
type: EVENT_TYPES.mousemove,
|
2016-12-09 23:15:04 +00:00
|
|
|
x: (event.pageX - position.left)/this.area.width(),
|
|
|
|
y: (event.pageY - position.top)/this.area.height()
|
|
|
|
});
|
|
|
|
});
|
2016-12-20 22:15:13 +00:00
|
|
|
this.area.mousedown(this._onMouseClickHandler.bind(this,
|
|
|
|
EVENT_TYPES.mousedown));
|
|
|
|
this.area.mouseup(this._onMouseClickHandler.bind(this,
|
|
|
|
EVENT_TYPES.mouseup));
|
2016-12-09 23:15:04 +00:00
|
|
|
this.area.dblclick(
|
2016-12-20 22:15:13 +00:00
|
|
|
this._onMouseClickHandler.bind(this, EVENT_TYPES.mousedblclick));
|
2016-12-09 23:15:04 +00:00
|
|
|
this.area.contextmenu(() => false);
|
|
|
|
this.area[0].onmousewheel = event => {
|
2016-12-28 00:46:55 +00:00
|
|
|
this._sendRemoteControlEvent(this.controlledParticipant, {
|
2016-12-20 22:15:13 +00:00
|
|
|
type: EVENT_TYPES.mousescroll,
|
2016-12-09 23:15:04 +00:00
|
|
|
x: event.deltaX,
|
|
|
|
y: event.deltaY
|
|
|
|
});
|
|
|
|
};
|
2016-12-20 22:15:13 +00:00
|
|
|
$(window).keydown(this._onKeyPessHandler.bind(this,
|
|
|
|
EVENT_TYPES.keydown));
|
|
|
|
$(window).keyup(this._onKeyPessHandler.bind(this, EVENT_TYPES.keyup));
|
2016-12-09 23:15:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Stops processing the mouse and keyboard events.
|
|
|
|
*/
|
2016-12-28 00:46:55 +00:00
|
|
|
_stop() {
|
|
|
|
APP.conference.removeConferenceListener(
|
|
|
|
ConferenceEvents.ENDPOINT_MESSAGE_RECEIVED,
|
|
|
|
this.stopListener);
|
|
|
|
this.controlledParticipant = null;
|
2016-12-09 23:15:04 +00:00
|
|
|
this.area.off( "mousemove" );
|
|
|
|
this.area.off( "mousedown" );
|
|
|
|
this.area.off( "mouseup" );
|
|
|
|
this.area.off( "contextmenu" );
|
|
|
|
this.area.off( "dblclick" );
|
|
|
|
$(window).off( "keydown");
|
|
|
|
$(window).off( "keyup");
|
|
|
|
this.area[0].onmousewheel = undefined;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Handler for mouse click events.
|
|
|
|
* @param {String} type the type of event ("mousedown"/"mouseup")
|
|
|
|
* @param {Event} event the mouse event.
|
|
|
|
*/
|
|
|
|
_onMouseClickHandler(type, event) {
|
2016-12-28 00:46:55 +00:00
|
|
|
this._sendRemoteControlEvent(this.controlledParticipant, {
|
2016-12-09 23:15:04 +00:00
|
|
|
type: type,
|
|
|
|
button: event.which
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Handler for key press events.
|
|
|
|
* @param {String} type the type of event ("keydown"/"keyup")
|
|
|
|
* @param {Event} event the key event.
|
|
|
|
*/
|
|
|
|
_onKeyPessHandler(type, event) {
|
2016-12-28 00:46:55 +00:00
|
|
|
this._sendRemoteControlEvent(this.controlledParticipant, {
|
2016-12-09 23:15:04 +00:00
|
|
|
type: type,
|
|
|
|
key: getKey(event),
|
|
|
|
modifiers: getModifiers(event),
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|