feat(remotecontrol): multi monitor support
This commit is contained in:
parent
f878f54b4d
commit
814d56c25c
|
@ -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;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Reference in New Issue