feat(remotecontrol): multi monitor support

This commit is contained in:
hristoterezov 2017-06-22 16:28:57 -05:00
parent f878f54b4d
commit 814d56c25c
5 changed files with 131 additions and 141 deletions

View File

@ -2118,5 +2118,16 @@ export default {
room.setDisplayName(formattedNickname);
APP.UI.changeDisplayName(this.getMyUserId(), formattedNickname);
}
},
/**
* Returns the desktop sharing source id or undefined if the desktop sharing
* is not active at the moment.
*
* @returns {string|undefined} - The source id. If the track is not desktop
* track or the source id is not available, undefined will be returned.
*/
getDesktopSharingSourceId() {
return localVideo.sourceId;
}
};

View File

@ -4,9 +4,9 @@ import { getLogger } from 'jitsi-meet-logger';
import * as KeyCodes from '../keycode/keycode';
import {
EVENT_TYPES,
EVENTS,
PERMISSIONS_ACTIONS,
REMOTE_CONTROL_EVENT_NAME
REMOTE_CONTROL_MESSAGE_NAME
} from '../../service/remotecontrol/Constants';
import UIEvents from '../../service/UI/UIEvents';
@ -145,8 +145,8 @@ export default class Controller extends RemoteControlParticipant {
APP.conference.addConferenceListener(ConferenceEvents.USER_LEFT,
onUserLeft);
this._requestedParticipant = userId;
this.sendRemoteControlEvent(userId, {
type: EVENT_TYPES.permissions,
this.sendRemoteControlEndpointMessage(userId, {
type: EVENTS.permissions,
action: PERMISSIONS_ACTIONS.request
}, e => {
clearRequest();
@ -167,8 +167,8 @@ export default class Controller extends RemoteControlParticipant {
const userId = participant.getId();
if (this._enabled
&& event.name === REMOTE_CONTROL_EVENT_NAME
&& event.type === EVENT_TYPES.permissions
&& event.name === REMOTE_CONTROL_MESSAGE_NAME
&& event.type === EVENTS.permissions
&& userId === this._requestedParticipant) {
if (event.action !== PERMISSIONS_ACTIONS.grant) {
this._area = undefined;
@ -201,13 +201,13 @@ export default class Controller extends RemoteControlParticipant {
* event.
* @param {Object} event - EndpointMessage event from the data channels.
* @property {string} type - The function process only events with
* name REMOTE_CONTROL_EVENT_NAME.
* name REMOTE_CONTROL_MESSAGE_NAME.
* @returns {void}
*/
_handleRemoteControlStoppedEvent(participant: Object, event: Object) {
if (this._enabled
&& event.name === REMOTE_CONTROL_EVENT_NAME
&& event.type === EVENT_TYPES.stop
&& event.name === REMOTE_CONTROL_MESSAGE_NAME
&& event.type === EVENTS.stop
&& participant.getId() === this._controlledParticipant) {
this._stop();
}
@ -251,8 +251,8 @@ export default class Controller extends RemoteControlParticipant {
// $FlowDisableNextLine: we are sure that this._area is not null.
const position = this._area.position();
this.sendRemoteControlEvent(this._controlledParticipant, {
type: EVENT_TYPES.mousemove,
this.sendRemoteControlEndpointMessage(this._controlledParticipant, {
type: EVENTS.mousemove,
// $FlowDisableNextLine: we are sure that this._area is not null
x: (event.pageX - position.left) / this._area.width(),
@ -264,30 +264,30 @@ export default class Controller extends RemoteControlParticipant {
// $FlowDisableNextLine: we are sure that this._area is not null.
this._area.mousedown(this._onMouseClickHandler.bind(this,
EVENT_TYPES.mousedown));
EVENTS.mousedown));
// $FlowDisableNextLine: we are sure that this._area is not null.
this._area.mouseup(this._onMouseClickHandler.bind(this,
EVENT_TYPES.mouseup));
EVENTS.mouseup));
// $FlowDisableNextLine: we are sure that this._area is not null.
this._area.dblclick(
this._onMouseClickHandler.bind(this, EVENT_TYPES.mousedblclick));
this._onMouseClickHandler.bind(this, EVENTS.mousedblclick));
// $FlowDisableNextLine: we are sure that this._area is not null.
this._area.contextmenu(() => false);
// $FlowDisableNextLine: we are sure that this._area is not null.
this._area[0].onmousewheel = event => {
this.sendRemoteControlEvent(this._controlledParticipant, {
type: EVENT_TYPES.mousescroll,
this.sendRemoteControlEndpointMessage(this._controlledParticipant, {
type: EVENTS.mousescroll,
x: event.deltaX,
y: event.deltaY
});
};
$(window).keydown(this._onKeyPessHandler.bind(this,
EVENT_TYPES.keydown));
$(window).keyup(this._onKeyPessHandler.bind(this, EVENT_TYPES.keyup));
EVENTS.keydown));
$(window).keyup(this._onKeyPessHandler.bind(this, EVENTS.keyup));
}
/**
@ -309,8 +309,8 @@ export default class Controller extends RemoteControlParticipant {
this._stopListener);
APP.conference.removeConferenceListener(ConferenceEvents.USER_LEFT,
this._userLeftListener);
this._controlledParticipant = null;
this.pause();
this._controlledParticipant = null;
this._area = undefined;
APP.UI.messageHandler.openMessageDialog(
'dialog.remoteControlTitle',
@ -330,8 +330,8 @@ export default class Controller extends RemoteControlParticipant {
if (!this._controlledParticipant) {
return;
}
this.sendRemoteControlEvent(this._controlledParticipant, {
type: EVENT_TYPES.stop
this.sendRemoteControlEndpointMessage(this._controlledParticipant, {
type: EVENTS.stop
});
this._stop();
}
@ -383,7 +383,7 @@ export default class Controller extends RemoteControlParticipant {
* @returns {void}
*/
_onMouseClickHandler(type: string, event: Object) {
this.sendRemoteControlEvent(this._controlledParticipant, {
this.sendRemoteControlEndpointMessage(this._controlledParticipant, {
type,
button: event.which
});
@ -416,7 +416,7 @@ export default class Controller extends RemoteControlParticipant {
* @returns {void}
*/
_onKeyPessHandler(type: string, event: Object) {
this.sendRemoteControlEvent(this._controlledParticipant, {
this.sendRemoteControlEndpointMessage(this._controlledParticipant, {
type,
key: getKey(event),
modifiers: getModifiers(event)

View File

@ -8,9 +8,10 @@ import {
} from '../../react/features/remote-control';
import {
DISCO_REMOTE_CONTROL_FEATURE,
EVENT_TYPES,
EVENTS,
PERMISSIONS_ACTIONS,
REMOTE_CONTROL_EVENT_NAME
REMOTE_CONTROL_MESSAGE_NAME,
REQUESTS
} from '../../service/remotecontrol/Constants';
import { getJitsiMeetTransport } from '../transport';
@ -51,14 +52,14 @@ export default class Receiver extends RemoteControlParticipant {
super();
this._controller = null;
this._remoteControlEventsListener
= this._onRemoteControlEvent.bind(this);
= this._onRemoteControlMessage.bind(this);
this._userLeftListener = this._onUserLeft.bind(this);
this._hangupListener = this._onHangup.bind(this);
// We expect here that even if we receive the supported event earlier
// it will be cached and we'll receive it.
transport.on('event', event => {
if (event.name === REMOTE_CONTROL_EVENT_NAME) {
if (event.name === REMOTE_CONTROL_MESSAGE_NAME) {
this._onRemoteControlAPIEvent(event);
return true;
@ -120,12 +121,10 @@ export default class Receiver extends RemoteControlParticipant {
this._controller = null;
APP.conference.removeConferenceListener(ConferenceEvents.USER_LEFT,
this._userLeftListener);
if (this.remoteControlExternalAuth) {
transport.sendEvent({
name: REMOTE_CONTROL_EVENT_NAME,
type: EVENT_TYPES.stop
});
}
transport.sendEvent({
name: REMOTE_CONTROL_MESSAGE_NAME,
type: EVENTS.stop
});
if (!dontShowDialog) {
APP.UI.messageHandler.openMessageDialog(
'dialog.remoteControlTitle',
@ -143,89 +142,46 @@ export default class Receiver extends RemoteControlParticipant {
if (!this._controller) {
return;
}
this.sendRemoteControlEvent(this._controller, {
type: EVENT_TYPES.stop
this.sendRemoteControlEndpointMessage(this._controller, {
type: EVENTS.stop
});
this._stop();
}
/**
* Listens for data channel EndpointMessage events. Handles only events of
* type remote control. Sends "remote-control-event" events to the API
* module.
* Listens for data channel EndpointMessage. Handles only remote control
* messages. Sends the remote control messages to the external app that
* will execute them.
*
* @param {JitsiParticipant} participant - The controller participant.
* @param {Object} event - EndpointMessage event from the data channels.
* @param {string} event.name - The function process only events with
* name REMOTE_CONTROL_EVENT_NAME.
* @param {Object} message - EndpointMessage from the data channels.
* @param {string} message.name - The function processes only messages with
* name REMOTE_CONTROL_MESSAGE_NAME.
* @returns {void}
*/
_onRemoteControlEvent(participant: Object, event: Object) {
if (event.name !== REMOTE_CONTROL_EVENT_NAME) {
_onRemoteControlMessage(participant: Object, message: Object) {
if (message.name !== REMOTE_CONTROL_MESSAGE_NAME) {
return;
}
const remoteControlEvent = Object.assign({}, event);
if (this._enabled) {
if (this._controller === null
&& event.type === EVENT_TYPES.permissions
&& event.action === PERMISSIONS_ACTIONS.request) {
&& message.type === EVENTS.permissions
&& message.action === PERMISSIONS_ACTIONS.request) {
const userId = participant.getId();
if (!config.remoteControlExternalAuth) {
APP.store.dispatch(
openRemoteControlAuthorizationDialog(userId));
return;
APP.store.dispatch(
openRemoteControlAuthorizationDialog(userId));
} else if (this._controller === participant.getId()) {
if (message.type === EVENTS.stop) {
this._stop();
} else { // forward the message
transport.sendEvent(message);
}
// FIXME: Maybe use transport.sendRequest in this case???
remoteControlEvent.userId = userId;
remoteControlEvent.userJID = participant.getJid();
remoteControlEvent.displayName = participant.getDisplayName()
|| interfaceConfig.DEFAULT_REMOTE_DISPLAY_NAME;
remoteControlEvent.screenSharing
= APP.conference.isSharingScreen;
} else if (this._controller !== participant.getId()) {
return;
} else if (event.type === EVENT_TYPES.stop) {
this._stop();
return;
}
transport.sendEvent(remoteControlEvent);
} // else ignore
} else {
logger.log('Remote control event is ignored because remote '
+ 'control is disabled', event);
}
}
/**
* Handles remote control permission events.
*
* @param {string} userId - The user id of the participant related to the
* event.
* @param {PERMISSIONS_ACTIONS} action - The action related to the event.
* @returns {void}
*/
_onRemoteControlPermissionsEvent(userId: string, action: string) {
switch (action) {
case PERMISSIONS_ACTIONS.grant:
this.grant(userId);
break;
case PERMISSIONS_ACTIONS.deny:
this.deny(userId);
break;
case PERMISSIONS_ACTIONS.error:
this.sendRemoteControlEvent(userId, {
type: EVENT_TYPES.permissions,
action
});
break;
default:
// Unknown action. Ignore.
logger.log('Remote control message is ignored because remote '
+ 'control is disabled', message);
}
}
@ -237,8 +193,8 @@ export default class Receiver extends RemoteControlParticipant {
* @returns {void}
*/
deny(userId: string) {
this.sendRemoteControlEvent(userId, {
type: EVENT_TYPES.permissions,
this.sendRemoteControlEndpointMessage(userId, {
type: EVENTS.permissions,
action: PERMISSIONS_ACTIONS.deny
});
}
@ -255,50 +211,63 @@ export default class Receiver extends RemoteControlParticipant {
this._userLeftListener);
this._controller = userId;
logger.log(`Remote control permissions granted to: ${userId}`);
let promise;
if (APP.conference.isSharingScreen) {
this.sendRemoteControlEvent(userId, {
type: EVENT_TYPES.permissions,
action: PERMISSIONS_ACTIONS.grant
});
promise = this._sendStartRequest();
} else {
APP.conference.toggleScreenSharing()
.then(() => {
if (APP.conference.isSharingScreen) {
this.sendRemoteControlEvent(userId, {
type: EVENT_TYPES.permissions,
action: PERMISSIONS_ACTIONS.grant
});
} else {
this.sendRemoteControlEvent(userId, {
type: EVENT_TYPES.permissions,
action: PERMISSIONS_ACTIONS.error
});
}
})
.catch(() => {
this.sendRemoteControlEvent(userId, {
type: EVENT_TYPES.permissions,
action: PERMISSIONS_ACTIONS.error
});
});
promise = APP.conference.toggleScreenSharing()
.then(() => this._sendStartRequest());
}
promise
.then(() =>
this.sendRemoteControlEndpointMessage(userId, {
type: EVENTS.permissions,
action: PERMISSIONS_ACTIONS.grant
})
)
.catch(() => {
this.sendRemoteControlEndpointMessage(userId, {
type: EVENTS.permissions,
action: PERMISSIONS_ACTIONS.error
});
// FIXME: show err msg
this._stop();
});
}
/**
* Sends remote control start request.
*
* @returns {Promise}
*/
_sendStartRequest() {
return transport.sendRequest({
name: REMOTE_CONTROL_MESSAGE_NAME,
type: REQUESTS.start,
sourceId: APP.conference.getDesktopSharingSourceId()
});
}
/**
* Handles remote control events from the external app. Currently only
* events with type = EVENT_TYPES.supported or EVENT_TYPES.permissions.
* events with type EVENTS.supported and EVENTS.stop are
* supported.
*
* @param {RemoteControlEvent} event - The remote control event.
* @returns {void}
*/
_onRemoteControlAPIEvent(event: Object) {
switch (event.type) {
case EVENT_TYPES.permissions:
this._onRemoteControlPermissionsEvent(event.userId, event.action);
break;
case EVENT_TYPES.supported:
case EVENTS.supported:
this._onRemoteControlSupported();
break;
case EVENTS.stop:
this.stop();
break;
}
}

View File

@ -3,7 +3,7 @@
import { getLogger } from 'jitsi-meet-logger';
import {
REMOTE_CONTROL_EVENT_NAME
REMOTE_CONTROL_MESSAGE_NAME
} from '../../service/remotecontrol/Constants';
const logger = getLogger(__filename);
@ -34,14 +34,14 @@ export default class RemoteControlParticipant {
}
/**
* Sends remote control event to other participant trough data channel.
* Sends remote control message to other participant trough data channel.
*
* @param {string} to - The participant who will receive the event.
* @param {RemoteControlEvent} event - The remote control event.
* @param {Function} onDataChannelFail - Handler for data channel failure.
* @returns {void}
*/
sendRemoteControlEvent(
sendRemoteControlEndpointMessage(
to: ?string,
event: Object,
onDataChannelFail: ?Function) {
@ -55,7 +55,7 @@ export default class RemoteControlParticipant {
}
try {
APP.conference.sendEndpointMessage(to, {
name: REMOTE_CONTROL_EVENT_NAME,
name: REMOTE_CONTROL_MESSAGE_NAME,
...event
});
} catch (e) {

View File

@ -5,11 +5,11 @@ export const DISCO_REMOTE_CONTROL_FEATURE
= "http://jitsi.org/meet/remotecontrol";
/**
* Types of remote-control-event events.
* Types of remote-control events.
* @readonly
* @enum {string}
*/
export const EVENT_TYPES = {
export const EVENTS = {
mousemove: "mousemove",
mousedown: "mousedown",
mouseup: "mouseup",
@ -18,10 +18,20 @@ export const EVENT_TYPES = {
keydown: "keydown",
keyup: "keyup",
permissions: "permissions",
start: "start",
stop: "stop",
supported: "supported"
};
/**
* Types of remote-control requests.
* @readonly
* @enum {string}
*/
export const REQUESTS = {
start: "start"
};
/**
* Actions for the remote control permission events.
* @readonly
@ -35,20 +45,20 @@ export const PERMISSIONS_ACTIONS = {
};
/**
* The type of remote control events sent trough the API module.
* The type of remote control messages.
*/
export const REMOTE_CONTROL_EVENT_NAME = "remote-control-event";
export const REMOTE_CONTROL_MESSAGE_NAME = "remote-control";
/**
* The remote control event.
* @typedef {object} RemoteControlEvent
* @property {EVENT_TYPES} type - the type of the event
* @property {int} x - avaibale for type === mousemove only. The new x
* @property {EVENTS | REQUESTS} type - the type of the message
* @property {number} x - avaibale for type === mousemove only. The new x
* coordinate of the mouse
* @property {int} y - For mousemove type - the new y
* @property {number} y - For mousemove type - the new y
* coordinate of the mouse and for mousescroll - represents the vertical
* scrolling diff value
* @property {int} button - 1(left), 2(middle) or 3 (right). Supported by
* @property {number} button - 1(left), 2(middle) or 3 (right). Supported by
* mousedown, mouseup and mousedblclick types.
* @property {KEYS} key - Represents the key related to the event. Supported by
* keydown and keyup types.