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

View File

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

View File

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

View File

@ -5,11 +5,11 @@ export const DISCO_REMOTE_CONTROL_FEATURE
= "http://jitsi.org/meet/remotecontrol"; = "http://jitsi.org/meet/remotecontrol";
/** /**
* Types of remote-control-event events. * Types of remote-control events.
* @readonly * @readonly
* @enum {string} * @enum {string}
*/ */
export const EVENT_TYPES = { export const EVENTS = {
mousemove: "mousemove", mousemove: "mousemove",
mousedown: "mousedown", mousedown: "mousedown",
mouseup: "mouseup", mouseup: "mouseup",
@ -18,10 +18,20 @@ export const EVENT_TYPES = {
keydown: "keydown", keydown: "keydown",
keyup: "keyup", keyup: "keyup",
permissions: "permissions", permissions: "permissions",
start: "start",
stop: "stop", stop: "stop",
supported: "supported" supported: "supported"
}; };
/**
* Types of remote-control requests.
* @readonly
* @enum {string}
*/
export const REQUESTS = {
start: "start"
};
/** /**
* Actions for the remote control permission events. * Actions for the remote control permission events.
* @readonly * @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. * The remote control event.
* @typedef {object} RemoteControlEvent * @typedef {object} RemoteControlEvent
* @property {EVENT_TYPES} type - the type of the event * @property {EVENTS | REQUESTS} type - the type of the message
* @property {int} x - avaibale for type === mousemove only. The new x * @property {number} x - avaibale for type === mousemove only. The new x
* coordinate of the mouse * 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 * coordinate of the mouse and for mousescroll - represents the vertical
* scrolling diff value * 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. * mousedown, mouseup and mousedblclick types.
* @property {KEYS} key - Represents the key related to the event. Supported by * @property {KEYS} key - Represents the key related to the event. Supported by
* keydown and keyup types. * keydown and keyup types.