Skip to content

Commit ce5e393

Browse files
committed
[wip] feat(screensharing): use setDisplayMediaRequestHandler
Signed-off-by: Grigorii K. Shartsev <[email protected]>
1 parent 306ebcc commit ce5e393

File tree

6 files changed

+64
-6
lines changed

6 files changed

+64
-6
lines changed

src/main.js

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
const path = require('node:path')
77
const { spawn } = require('node:child_process')
8-
const { app, dialog, ipcMain, desktopCapturer, systemPreferences, shell } = require('electron')
8+
const { app, dialog, ipcMain, desktopCapturer, systemPreferences, shell, session } = require('electron')
99
const { setupMenu } = require('./app/app.menu.js')
1010
const { setupReleaseNotificationScheduler } = require('./app/githubReleaseNotification.service.js')
1111
const { enableWebRequestInterceptor, disableWebRequestInterceptor } = require('./app/webRequestInterceptor.js')
@@ -80,6 +80,7 @@ ipcMain.on('app:relaunch', () => {
8080
})
8181
ipcMain.handle('app:config:get', (event, key) => getAppConfig(key))
8282
ipcMain.handle('app:config:set', (event, key, value) => setAppConfig(key, value))
83+
8384
ipcMain.handle('app:getDesktopCapturerSources', async () => {
8485
// macOS 10.15 Catalina or higher requires consent for screen access
8586
if (isMac && systemPreferences.getMediaAccessStatus('screen') !== 'granted') {
@@ -140,6 +141,35 @@ app.whenReady().then(async () => {
140141
console.log()
141142
}
142143

144+
session.defaultSession.setDisplayMediaRequestHandler(async (request, callback) => {
145+
if (isMac && systemPreferences.getMediaAccessStatus('screen') !== 'granted') {
146+
// Open System Preferences to allow screen recording
147+
await shell.openExternal('x-apple.systempreferences:com.apple.preference.security?Privacy_ScreenCapture')
148+
// We cannot detect that the user has granted access, so return no sources
149+
// The user will have to try again after granting access
150+
return null
151+
}
152+
153+
// We cannot show live previews on Wayland, so we show thumbnails
154+
const thumbnailWidth = isWayland ? 320 : 0
155+
156+
const sources = await desktopCapturer.getSources({
157+
types: ['screen', 'window'],
158+
fetchWindowIcons: true,
159+
thumbnailSize: {
160+
width: thumbnailWidth,
161+
height: thumbnailWidth * 9 / 16,
162+
},
163+
})
164+
165+
sources.map((source) => ({
166+
id: source.id,
167+
name: source.name,
168+
icon: source.appIcon && !source.appIcon.isEmpty() ? source.appIcon.toDataURL() : null,
169+
thumbnail: source.thumbnail && !source.thumbnail.isEmpty() ? source.thumbnail.toDataURL() : null,
170+
}))
171+
}, { useSystemPicker: true })
172+
143173
// TODO: add windows manager
144174
/**
145175
* @type {import('electron').BrowserWindow}

src/preload.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,9 @@ const TALK_DESKTOP = {
165165
* @return {Promise<void>}
166166
*/
167167
showUpgrade: () => ipcRenderer.invoke('upgrade:show'),
168+
169+
onPromptDesktopCaptureSource: (callback) => ipcRenderer.on('talk:onPromptDesktopCaptureSource', (_event, sources) => callback(sources)),
170+
desktopCaptureSourceSelected: (source) => ipcRenderer.send('talk:desktopCaptureSourceSelected', source),
168171
}
169172

170173
// Set global window.TALK_DESKTOP

src/talk/renderer/AppGetDesktopMediaSource.vue

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,13 @@ import { ref } from 'vue'
88
99
import DesktopMediaSourceDialog from './components/DesktopMediaSourceDialog.vue'
1010
11+
const props = defineProps({
12+
sources: {
13+
type: Array,
14+
required: true,
15+
},
16+
})
17+
1118
const showDialog = ref(false)
1219
1320
let promiseWithResolvers = null
@@ -36,5 +43,8 @@ defineExpose({ promptDesktopMediaSource })
3643
</script>
3744

3845
<template>
39-
<DesktopMediaSourceDialog v-if="showDialog" @submit="handlePrompt($event)" @cancel="handlePrompt('')" />
46+
<DesktopMediaSourceDialog v-if="showDialog"
47+
:sources="sources"
48+
@submit="handlePrompt($event)"
49+
@cancel="handlePrompt('')" />
4050
</template>

src/talk/renderer/components/DesktopMediaSourceDialog.vue

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,13 @@ import NcLoadingIcon from '@nextcloud/vue/dist/Components/NcLoadingIcon.js'
1616
import { translate as t } from '@nextcloud/l10n'
1717
import DesktopMediaSourcePreview from './DesktopMediaSourcePreview.vue'
1818
19+
const props = defineProps({
20+
sources: {
21+
type: Array,
22+
required: true,
23+
},
24+
})
25+
1926
const emit = defineEmits(['submit', 'cancel'])
2027
2128
const RE_REQUEST_SOURCES_TIMEOUT = 1000
@@ -47,10 +54,10 @@ const dialogButtons = computed(() => [
4754
])
4855
4956
const requestDesktopCapturerSources = async () => {
50-
sources.value = await window.TALK_DESKTOP.getDesktopCapturerSources()
57+
// props.sources = await window.TALK_DESKTOP.getDesktopCapturerSources()
5158
5259
// There is no source. Probably the user hasn't granted the permission.
53-
if (!sources.value) {
60+
if (!props.sources) {
5461
emit('cancel')
5562
}
5663

src/talk/renderer/getDesktopMediaSource.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,14 @@ let appGetDesktopMediaSourceInstance
1313
/**
1414
* Prompt user to select a desktop media source to share and return the selected sourceId or an empty string if canceled
1515
*
16+
* @param sources
1617
* @return {Promise<{ sourceId: string }>} sourceId of the selected mediaSource or an empty string if canceled
1718
*/
18-
export async function getDesktopMediaSource() {
19+
export async function getDesktopMediaSource(sources) {
1920
if (!appGetDesktopMediaSourceInstance) {
2021
const container = document.body.appendChild(document.createElement('div'))
21-
appGetDesktopMediaSourceInstance = new Vue(AppGetDesktopMediaSource).$mount(container)
22+
const AppGetDesktopMediaSourceCreator = Vue.extend(AppGetDesktopMediaSource)
23+
appGetDesktopMediaSourceInstance = new AppGetDesktopMediaSourceCreator({ propsData: { sources } }).$mount(container)
2224
}
2325

2426
return appGetDesktopMediaSourceInstance.promptDesktopMediaSource()

src/talk/renderer/talk.main.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,9 @@ registerTalkDesktopSettingsSection()
3939
await import('./notifications/notifications.store.js')
4040

4141
subscribeBroadcast('talk:conversation:open', ({ token, directCall }) => openConversation(token, { directCall }))
42+
43+
window.TALK_DESKTOP.onPromptDesktopCaptureSource(async (sources) => {
44+
const { sourceId } = await getDesktopMediaSource(sources)
45+
console.log('Selected sourceId:', sourceId)
46+
window.TALK_DESKTOP.desktopCaptureSourceSelected(sourceId)
47+
})

0 commit comments

Comments
 (0)