VulpesOS/gaia/apps/system/js/multi_screen_controller.js
OverSu b38dea0597 Publish Vulpes Preview 15 sources for desktop, Android and Tundra
Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
2026-09-30 23:13:03 +02:00

260 lines
7 KiB
JavaScript
Executable file

/* global ActionMenu, BaseModule, LazyLoader, BroadcastChannel */
'use strict';
(function() {
var MultiScreenController = function() {};
MultiScreenController.SERVICES = [
'chooseDisplay'
];
MultiScreenController.EVENTS = [
'mozChromeEvent'
];
MultiScreenController.SETTINGS = [
'multiscreen.enabled'
];
MultiScreenController.STATES = [
'enabled'
];
BaseModule.create(MultiScreenController, {
name: 'MultiScreenController',
EVENT_PREFIX: 'remote-',
DEBUG: false,
enabled: function() {
return this._enabled;
},
chooseDisplay: function(config) {
this.debug('chooseDisplay is invoked');
if (!this._enabled) {
this.debug('multi-screen is disabled');
return Promise.reject();
}
if (config.isSystemMessage || config.stayBackground) {
this.debug('unsupported config: ' + JSON.stringify(config));
return Promise.reject();
}
return this.queryExternalDisplays()
.then(this.showMenu.bind(this))
.then((displayId) => {
this.debug('chosen display id: ' + displayId);
if (!displayId) {
return Promise.reject();
}
this.postMessage(displayId, 'launch-app', config);
return Promise.resolve(displayId);
});
},
choosePresentationDevice: function() {
this.debug('chooseDisplay for presentation device selection');
var deviceList = [];
return this.queryPresentationDevices()
.then((list) => {
deviceList = list.map((device, idx) => {
return {
name: device.name,
deviceId: device.id,
id: idx,
};
});
return Promise.resolve(deviceList);
})
.then(this.showMenu.bind(this))
.then((displayId) => {
this.debug('chosen display id: ' + displayId);
if (isNaN(displayId)) {
return Promise.reject();
}
return Promise.resolve(deviceList.find((device) => {
return device.id == displayId;
}).deviceId);
});
},
showMenu: function(displays) {
this.debug('showMenu is invoked');
if (this.actionMenu) {
this.debug('actionMenu is busy');
return Promise.reject();
}
if (!displays.length) {
this.debug('no external display so cancel the menu directly');
return Promise.resolve();
}
return new Promise((resolve, reject) => {
LazyLoader.load('js/action_menu.js', () => {
this.actionMenu = new ActionMenu({
successCb: (choice) => {
this.actionMenu = null;
resolve(choice);
},
cancelCb: () => {
this.actionMenu = null;
resolve();
}
});
this.actionMenu.show(displays.map(function(display) {
return {
label: display.name,
value: display.id
};
}), 'multiscreen-pick');
});
});
},
queryExternalDisplays: function() {
this.debug('queryExternalDisplays is invoked');
if (this.queryPromiseCallback) {
this.debug('there\'s a pending query');
return Promise.reject();
}
return new Promise((resolve, reject) => {
this.debug('sending mozContentEvent');
this.queryPromiseCallback = {
resolve: resolve,
reject: reject
};
window.dispatchEvent(new CustomEvent('mozContentEvent', {
detail: {
type: 'get-display-list'
}
}));
});
},
queryPresentationDevices: function() {
this.debug('queryPresentationDevices is invoked');
return navigator.mozPresentationDeviceInfo.getAll();
},
postMessage: function(target, type, detail) {
this.debug('broadcast message to #' + target + ': ' +
type + ', ' + JSON.stringify(detail));
this.broadcastChannel.postMessage({
target: target,
type: type,
detail: detail
});
},
_start: function() {
this._enabled = false;
this.actionMenu = null;
this.queryPromiseCallback = null;
this.broadcastChannel = new BroadcastChannel('multiscreen');
this.broadcastChannel.addEventListener('message', this);
},
_stop: function() {
this._enabled = false;
this.broadcastChannel.close();
this.broadcastChannel = null;
if (this.queryPromiseCallback) {
this.queryPromiseCallback.reject('module has been stoped');
this.queryPromiseCallback = null;
}
if (this.actionMenu) {
this.actionMenu.hide();
if (this.actionMenu.oncancel) {
this.actionMenu.oncancel();
}
this.actionMenu = null;
}
},
_handle_mozChromeEvent: function(evt) {
var detail = evt.detail;
switch (detail.type) {
case 'presentation-select-device':
this._presentationDeviceSelectionHandler(detail);
break;
case 'get-display-list-success':
case 'get-display-list-error':
this._displayListResultHandler(detail);
break;
}
},
_presentationDeviceSelectionHandler: function(detail) {
this.debug('handle presentation-select-device event');
this.choosePresentationDevice(null)
.then((deviceId) => {
window.dispatchEvent(new CustomEvent('mozContentEvent', {
detail: {
'type': 'presentation-select-result',
'deviceId': deviceId,
'id': detail.id,
}
}));
}).catch(() => {
window.dispatchEvent(new CustomEvent('mozContentEvent', {
detail: {
'type': 'presentation-select-deny',
'id': detail.id
}
}));
});
},
_displayListResultHandler: function(detail) {
if (!this.queryPromiseCallback) {
return;
}
switch (detail.type) {
case 'get-display-list-success':
this.queryPromiseCallback.resolve(detail.display);
break;
case 'get-display-list-error':
this.queryPromiseCallback.reject(detail.error);
break;
default:
return;
}
this.queryPromiseCallback = null;
this.debug('got mozChromeEvent: ' + JSON.stringify(detail));
},
_handle_message: function(evt) {
var data = evt.data;
if (data.target !== undefined) {
return;
}
this.debug('got message from #' + data.source + ': ' +
data.type + ', ' + JSON.stringify(data.detail));
switch(data.type) {
case 'launch-app-success':
case 'launch-app-error':
this.publish(data.type, {
displayId: data.source,
config: data.detail.config,
reason: data.detail.reason
});
break;
}
},
'_observe_multiscreen.enabled': function(value) {
if (value == this._enabled) {
return;
}
this._enabled = value;
}
});
}());