Skip to content

Commit

Permalink
[wip] feat(screensharing): use setDisplayMediaRequestHandler
Browse files Browse the repository at this point in the history
Signed-off-by: Grigorii K. Shartsev <[email protected]>
  • Loading branch information
ShGKme committed Jan 15, 2025
1 parent 306ebcc commit ce5e393
Show file tree
Hide file tree
Showing 6 changed files with 64 additions and 6 deletions.
32 changes: 31 additions & 1 deletion src/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

const path = require('node:path')
const { spawn } = require('node:child_process')
const { app, dialog, ipcMain, desktopCapturer, systemPreferences, shell } = require('electron')
const { app, dialog, ipcMain, desktopCapturer, systemPreferences, shell, session } = require('electron')
const { setupMenu } = require('./app/app.menu.js')
const { setupReleaseNotificationScheduler } = require('./app/githubReleaseNotification.service.js')
const { enableWebRequestInterceptor, disableWebRequestInterceptor } = require('./app/webRequestInterceptor.js')
Expand Down Expand Up @@ -80,6 +80,7 @@ ipcMain.on('app:relaunch', () => {
})
ipcMain.handle('app:config:get', (event, key) => getAppConfig(key))
ipcMain.handle('app:config:set', (event, key, value) => setAppConfig(key, value))

ipcMain.handle('app:getDesktopCapturerSources', async () => {
// macOS 10.15 Catalina or higher requires consent for screen access
if (isMac && systemPreferences.getMediaAccessStatus('screen') !== 'granted') {
Expand Down Expand Up @@ -140,6 +141,35 @@ app.whenReady().then(async () => {
console.log()
}

session.defaultSession.setDisplayMediaRequestHandler(async (request, callback) => {
if (isMac && systemPreferences.getMediaAccessStatus('screen') !== 'granted') {
// Open System Preferences to allow screen recording
await shell.openExternal('x-apple.systempreferences:com.apple.preference.security?Privacy_ScreenCapture')
// We cannot detect that the user has granted access, so return no sources
// The user will have to try again after granting access
return null
}

// We cannot show live previews on Wayland, so we show thumbnails
const thumbnailWidth = isWayland ? 320 : 0

const sources = await desktopCapturer.getSources({
types: ['screen', 'window'],
fetchWindowIcons: true,
thumbnailSize: {
width: thumbnailWidth,
height: thumbnailWidth * 9 / 16,
},
})

sources.map((source) => ({
id: source.id,
name: source.name,
icon: source.appIcon && !source.appIcon.isEmpty() ? source.appIcon.toDataURL() : null,
thumbnail: source.thumbnail && !source.thumbnail.isEmpty() ? source.thumbnail.toDataURL() : null,
}))
}, { useSystemPicker: true })

// TODO: add windows manager
/**
* @type {import('electron').BrowserWindow}
Expand Down
3 changes: 3 additions & 0 deletions src/preload.js
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,9 @@ const TALK_DESKTOP = {
* @return {Promise<void>}
*/
showUpgrade: () => ipcRenderer.invoke('upgrade:show'),

onPromptDesktopCaptureSource: (callback) => ipcRenderer.on('talk:onPromptDesktopCaptureSource', (_event, sources) => callback(sources)),
desktopCaptureSourceSelected: (source) => ipcRenderer.send('talk:desktopCaptureSourceSelected', source),
}

// Set global window.TALK_DESKTOP
Expand Down
12 changes: 11 additions & 1 deletion src/talk/renderer/AppGetDesktopMediaSource.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,13 @@ import { ref } from 'vue'

import DesktopMediaSourceDialog from './components/DesktopMediaSourceDialog.vue'

const props = defineProps({
sources: {
type: Array,
required: true,
},
})

const showDialog = ref(false)

let promiseWithResolvers = null
Expand Down Expand Up @@ -36,5 +43,8 @@ defineExpose({ promptDesktopMediaSource })
</script>

<template>
<DesktopMediaSourceDialog v-if="showDialog" @submit="handlePrompt($event)" @cancel="handlePrompt('')" />
<DesktopMediaSourceDialog v-if="showDialog"
:sources="sources"
@submit="handlePrompt($event)"
@cancel="handlePrompt('')" />
</template>
11 changes: 9 additions & 2 deletions src/talk/renderer/components/DesktopMediaSourceDialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@ import NcLoadingIcon from '@nextcloud/vue/dist/Components/NcLoadingIcon.js'
import { translate as t } from '@nextcloud/l10n'
import DesktopMediaSourcePreview from './DesktopMediaSourcePreview.vue'

const props = defineProps({
sources: {
type: Array,
required: true,
},
})

const emit = defineEmits(['submit', 'cancel'])

const RE_REQUEST_SOURCES_TIMEOUT = 1000
Expand Down Expand Up @@ -47,10 +54,10 @@ const dialogButtons = computed(() => [
])

const requestDesktopCapturerSources = async () => {
sources.value = await window.TALK_DESKTOP.getDesktopCapturerSources()
// props.sources = await window.TALK_DESKTOP.getDesktopCapturerSources()

// There is no source. Probably the user hasn't granted the permission.
if (!sources.value) {
if (!props.sources) {
emit('cancel')
}

Expand Down
6 changes: 4 additions & 2 deletions src/talk/renderer/getDesktopMediaSource.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,14 @@ let appGetDesktopMediaSourceInstance
/**
* Prompt user to select a desktop media source to share and return the selected sourceId or an empty string if canceled
*
* @param sources
* @return {Promise<{ sourceId: string }>} sourceId of the selected mediaSource or an empty string if canceled
*/
export async function getDesktopMediaSource() {
export async function getDesktopMediaSource(sources) {
if (!appGetDesktopMediaSourceInstance) {
const container = document.body.appendChild(document.createElement('div'))
appGetDesktopMediaSourceInstance = new Vue(AppGetDesktopMediaSource).$mount(container)
const AppGetDesktopMediaSourceCreator = Vue.extend(AppGetDesktopMediaSource)
appGetDesktopMediaSourceInstance = new AppGetDesktopMediaSourceCreator({ propsData: { sources } }).$mount(container)
}

return appGetDesktopMediaSourceInstance.promptDesktopMediaSource()
Expand Down
6 changes: 6 additions & 0 deletions src/talk/renderer/talk.main.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,9 @@ registerTalkDesktopSettingsSection()
await import('./notifications/notifications.store.js')

subscribeBroadcast('talk:conversation:open', ({ token, directCall }) => openConversation(token, { directCall }))

window.TALK_DESKTOP.onPromptDesktopCaptureSource(async (sources) => {
const { sourceId } = await getDesktopMediaSource(sources)
console.log('Selected sourceId:', sourceId)
window.TALK_DESKTOP.desktopCaptureSourceSelected(sourceId)
})

0 comments on commit ce5e393

Please sign in to comment.